/* ============================================================
   HOMEPAGE — matched to the live Squarespace layout.
   Colours sampled from vistaralabs.ca, not eyeballed:
     #21609D  section headings + hero title
     #3178A0  product names
     #239B97  teal accent, review text, hero subtitle
     #158081  "Why Vistara" item headings
     #314D5D  circular action buttons
     #DBDBDB  body text on the black Resources panel
   ============================================================ */

.vl-section,
.vl-hero,
.vl-resources { width: 100%; }

/* ---- Shared section heading ---- */
.vl-section__title {
  color: #21609D;
  font-weight: 700;
  text-align: center;
  letter-spacing: -0.02em;
  font-size: clamp(2rem, 1.4rem + 2.2vw, 3.1rem);
  line-height: 1.15;
  margin: 0 0 2.5rem;
}

/* ---- Buttons: italic, uppercase, squared ---- */
.vl-btn {
  display: inline-block;
  font-style: italic;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: .82rem;
  font-weight: 400;
  padding: 18px 26px;
  border-radius: 0;
  text-decoration: none;
  line-height: 1.25;
  text-align: center;
  transition: background .18s ease, color .18s ease;
}
.vl-btn--solid { background: #000; color: #FAFAFA; border: 1px solid #000; }
.vl-btn--solid:hover { background: #21609D; border-color: #21609D; color: #fff; }
.vl-btn--ghost { background: transparent; color: #000; border: 1px solid #000; }
.vl-btn--ghost:hover { background: #000; color: #FAFAFA; }

/* ---- HERO ---- */
.vl-hero { padding: 3.5rem 0 3rem; background: #fff; }
.vl-hero__inner {
  max-width: 1280px; margin-inline: auto; padding-inline: 5vw;
  display: grid; grid-template-columns: 1fr 1fr; gap: 3rem; align-items: center;
}
.vl-hero__title {
  color: #21609D; font-weight: 700; letter-spacing: -0.02em;
  font-size: clamp(2.1rem, 1.3rem + 2.9vw, 3.4rem); line-height: 1.1; margin: 0 0 1.1rem;
}
.vl-hero__sub {
  color: #239B97; font-weight: 700; letter-spacing: -0.01em;
  font-size: clamp(1.05rem, .9rem + .6vw, 1.4rem); margin: 0 0 1.6rem;
}
.vl-hero__trust { color: #000; font-size: 1rem; margin: 0 0 2.2rem; }
.vl-hero__actions { display: flex; flex-wrap: wrap; gap: 1.1rem; }
.vl-hero__actions .vl-btn { min-width: 210px; }
.vl-hero__art { text-align: center; }
.vl-hero__art img { max-width: min(100%, 430px); height: auto; }

/* ---- TRUST BAR ---- */
.vl-trustbar {
  background: #EDF2F7; color: #158081;
  font-weight: 700; text-align: center; letter-spacing: -0.01em;
  padding: 1.15rem 1.25rem; font-size: clamp(.95rem, .85rem + .35vw, 1.25rem);
  border: 0;
}

/* ---- Generic section shell ---- */
.vl-section { padding: 4rem 5vw 1rem; max-width: 1440px; margin-inline: auto; }
.vl-section--tint { padding-bottom: 0; }
.vl-section__cta { text-align: center; padding: 1.5rem 5vw 4rem; }
.vl-section__cta .vl-btn { min-width: 300px; }

/* ---- BEST SELLERS grid (WooCommerce handpicked block) ---- */
.wp-block-woocommerce-handpicked-products {
  max-width: 1440px; margin-inline: auto; padding-inline: 5vw;
}
.wc-block-grid__products { gap: 0 !important; }
.wc-block-grid__product { text-align: center; padding: 1rem .5rem; }
.wc-block-grid__product-image img { max-width: 190px; height: auto; margin-inline: auto; }
.wc-block-grid__product-title,
.wc-block-grid__product-link .wc-block-grid__product-title {
  color: #3178A0; font-weight: 700; font-size: 1.12rem; line-height: 1.25;
  letter-spacing: -0.02em; margin-top: .9rem;
}
.wc-block-grid__product-price { color: #000; font-size: 1.05rem; display: block; margin-top: .5rem; }
.wc-block-grid__product-price .woocommerce-Price-amount { color: #000; }
.wc-block-grid__product-onsale { display: none; }
.wc-block-grid__product-add-to-cart .wp-block-button__link {
  background: #314D5D; color: #fff; border-radius: 999px;
  width: 46px; height: 46px; padding: 0; font-size: 0;
  display: inline-flex; align-items: center; justify-content: center;
}

/* ---- CLIENT REVIEWS ---- */
.vl-reviews { max-width: 1440px; margin-inline: auto; padding: 0 5vw 1rem; }
.vl-reviews__track {
  display: flex; gap: 24px; scroll-snap-type: x mandatory;
  padding-bottom: .5rem; scrollbar-width: none;

  /*
   * Both axes stated. Declaring only `overflow-x: auto` does NOT leave the
   * other axis alone: per spec, when one axis is scrollable and the other is
   * `visible`, the `visible` one computes to `auto` as well. So this box was a
   * vertical scroll container too, and the moment its content was even a pixel
   * taller than the box -- which is the case on a fresh load, before the photos
   * have settled into their reserved squares -- a wheel or a thumb over the
   * carousel scrolled the strip instead of the page, and the page appeared to
   * seize. It cleared up on its own once the images resolved, which is why it
   * only ever happened right after load.
   *
   * `hidden` rather than `clip` for compatibility; nothing legitimately
   * overflows vertically here, since the cards clip their own contents. The
   * only thing it costs is the outer 12px of each card's shadow, which is
   * rgba(0,0,0,.05) offset 2px down -- not perceptible.
   */
  overflow-x: auto;
  overflow-y: hidden;
}
.vl-reviews__track::-webkit-scrollbar { display: none; }
.vl-review {
  flex: 0 0 clamp(260px, 30%, 420px); scroll-snap-align: start;
  margin: 0; background: #fff; border: 1px solid #ECECEC; border-radius: 10px;
  box-shadow: 0 2px 14px rgba(0,0,0,.05); overflow: hidden;
  display: flex; flex-direction: column;
}
.vl-review__photo {
  width: 100%; aspect-ratio: 1/1; object-fit: cover; display: block;
  /* The reserved box is white until the pixels land, so a photo that is still
     loading reads as a hole in the strip. A tint close to the page ground
     makes the gap look like part of the card instead of a missing image --
     insurance for a slow connection, on top of the pre-warm in the script. */
  background: #EEF2F5;
}
.vl-review__body { padding: 1.4rem 1.5rem 1.6rem; display: flex; flex-direction: column; flex: 1; }
.vl-review__text { color: #239B97; font-size: .98rem; line-height: 1.55; margin: 0 0 1.4rem; flex: 1; }
.vl-review__name { color: #1F2937; font-weight: 700; font-size: .82rem; margin: 0 0 .35rem; }
.vl-review__stars { color: #239B97; font-size: 1.05rem; letter-spacing: .12em; margin: 0; }
.vl-reviews__nav {
  display: flex; align-items: center; justify-content: space-between;
  padding-top: 1.4rem; gap: 2rem;
}
.vl-reviews__progress { flex: 1; height: 3px; background: #ECECEC; max-width: 220px; }
.vl-reviews__progress span { display: block; height: 100%; background: #239B97; width: 10%; transition: width .2s ease; }
.vl-reviews__arrows { display: flex; gap: .35rem; }
.vl-reviews__arrow {
  background: none; border: 0; color: #6B7280; font-size: 1.9rem; line-height: 1;
  cursor: pointer; padding: 0 .5rem;
}
.vl-reviews__arrow:hover { color: #21609D; }

/* ---- WHY VISTARA LABS (maple watermark behind) ---- */
.vl-why { position: relative; padding-bottom: 1.6rem; overflow: hidden; } /* pulled up to the leaf tip, owner 30 Aug */
.vl-why::before {
  content: ""; position: absolute; inset: 0;
  background-image: var(--vl-maple, none);
  background-repeat: no-repeat; background-position: center 12%;
  background-size: min(92%, 1180px) auto;
  opacity: .5; z-index: 0; pointer-events: none;
}
.vl-why > * { position: relative; z-index: 1; }
.vl-why__grid {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: 4.5rem 3rem; max-width: 1080px; margin: 3rem auto 0; text-align: center;
}
.vl-why__item h3 {
  color: #158081; text-transform: uppercase; font-weight: 700;
  font-size: 1rem; letter-spacing: .01em; margin: 0 0 .9rem;
}
.vl-why__item p { color: #000; font-size: 1.05rem; line-height: 1.55; margin: 0; }

/* ---- WHAT ARE PEPTIDES ---- */
.vl-peptides { padding-bottom: 3rem; }
.vl-peptides__inner {
  display: grid; grid-template-columns: 1.15fr .85fr; gap: 3rem; align-items: center;
  max-width: 1280px; margin-inline: auto;
}
.vl-peptides__lede {
  color: #000; font-weight: 400; letter-spacing: -0.02em;
  font-size: clamp(1.4rem, 1.1rem + 1.1vw, 2.05rem); line-height: 1.25; margin: 0 0 1.5rem;
}
.vl-peptides__copy p { color: #000; font-size: 1rem; line-height: 1.62; margin: 0 0 1.1rem; }
.vl-peptides__copy .vl-btn { margin-top: 1.4rem; }
.vl-peptides__art { text-align: center; }
.vl-peptides__art img { max-width: min(100%, 340px); height: auto; }

/* ---- RESOURCES (black panel) ---- */
.vl-resources { background: #000; padding: 4rem 5vw 4.5rem; margin-top: 3rem; }
.vl-resources__title { color: #21609D; margin-bottom: 3rem; }
.vl-resources__grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 3rem;
  max-width: 1280px; margin-inline: auto;
}
.vl-resources__item h3 {
  color: #fff; font-weight: 400; letter-spacing: -0.02em;
  font-size: clamp(1.35rem, 1.1rem + 1vw, 2rem); margin: 0 0 1.2rem;
}
.vl-resources__item p { color: #DBDBDB; font-size: 1rem; line-height: 1.55; margin: 0 0 1.2rem; }
.vl-resources__item a { color: #fff; text-decoration: underline; font-size: 1rem; }
.vl-resources__item a:hover { color: #239B97; }

/* ---- FOOTER ---- */
.vl-footer { background: #fff; padding: 3.5rem 5vw 3rem; border-top: 0; }
.vl-footer__inner {
  max-width: 1280px; margin-inline: auto;
  display: grid; grid-template-columns: 1fr 1fr 1.6fr; gap: 3rem; align-items: start;
}
.vl-footer__brand { text-align: center; }
.vl-footer__logo { max-width: 180px; height: auto; }
.vl-footer__pay { text-align: center; padding-top: 1rem; }
.vl-footer__interac { max-width: 140px; height: auto; }
.vl-footer__heading { color: #21609D; font-weight: 700; font-size: 1rem; margin: 0 0 .9rem; }
.vl-footer__legal p { color: #000; font-size: .86rem; line-height: 1.6; margin: 0 0 1rem; }
.vl-footer__copy { color: #000; }

/* ---- Responsive ---- */
@media (max-width: 1024px) {
  .vl-resources__grid { grid-template-columns: 1fr; gap: 2.5rem; }
  .vl-footer__inner { grid-template-columns: 1fr; text-align: center; }
}
@media (max-width: 860px) {
  .vl-hero__inner { grid-template-columns: 1fr; text-align: center; }
  .vl-hero__art { order: -1; }
  .vl-hero__actions { justify-content: center; }
  .vl-peptides__inner { grid-template-columns: 1fr; }
  .vl-why__grid { grid-template-columns: 1fr; gap: 2.5rem; }
  .vl-review { flex-basis: 82%; }
}

/* ---- BEST SELLERS tiles (replaces the Woo block) ---- */
.vl-bestsellers {
  display: grid; grid-template-columns: repeat(6, 1fr); gap: 1.5rem;
  max-width: 1440px; margin: 0 auto; padding: 0 5vw;
}
.vl-tile { text-align: center; display: flex; flex-direction: column; }
.vl-tile__img { display: block; }
.vl-tile__img img { max-width: 100%; height: auto; aspect-ratio: 3/4; object-fit: contain; }
.vl-tile__name { margin: .9rem 0 .35rem; font-size: 1.1rem; line-height: 1.25; font-weight: 700; letter-spacing: -0.02em; }
.vl-tile__name a { color: #3178A0; text-decoration: none; }
.vl-tile__name a:hover { color: #21609D; }
.vl-tile__sub { color: #8A8A8A; font-size: .9rem; line-height: 1.35; margin: 0 0 .5rem; }
.vl-tile__price { color: #000; font-size: 1.02rem; margin: 0 0 1rem; flex: 1; }
.vl-tile__actions { display: flex; gap: .55rem; justify-content: center; }
.vl-tile__act {
  width: 44px; height: 44px; border-radius: 999px; background: #314D5D; color: #fff;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .18s ease;
}
.vl-tile__act:hover { background: #21609D; color: #fff; }
@media (max-width: 1200px) { .vl-bestsellers { grid-template-columns: repeat(3, 1fr); gap: 2rem 1.5rem; } }
@media (max-width: 640px)  { .vl-bestsellers { grid-template-columns: repeat(2, 1fr); } }

/* ============================================================
   WIDER MEASURE
   The first build sat in a ~1100px column inside a 1440px window.
   Sections now run to 1600px with a tighter gutter so content
   actually fills the viewport.
   ============================================================ */
:root { --vl-measure: 1600px; --vl-gutter: 3.2vw; }

.vl-hero__inner,
.vl-section,
.vl-bestsellers,
.vl-reviews,
.vl-peptides__inner,
.vl-resources__grid,
.vl-footer__inner,
.wp-block-woocommerce-handpicked-products {
  max-width: var(--vl-measure);
}
.vl-hero__inner,
.vl-bestsellers,
.vl-reviews,
.vl-resources,
.vl-footer { padding-inline: var(--vl-gutter); }
.vl-section { padding-inline: var(--vl-gutter); }
.vl-hero { padding-block: 4.5rem 3.5rem; }
.vl-hero__inner { gap: 4rem; }
.vl-hero__art img { max-width: min(100%, 480px); }
.vl-why__grid { max-width: 1240px; gap: 5rem 5rem; }
.vl-peptides__inner { gap: 4.5rem; }
.vl-resources__grid { gap: 4rem; }
.vl-bestsellers { gap: 2rem; }

/* Kadence's own content wrapper also needs the wider measure. */
.content-container.site-container,
.content-width-normal .content-container,
#inner-wrap .content-container { max-width: var(--vl-measure); }

/* WooCommerce archives match. */
.woocommerce .site-container,
.woocommerce-page .site-container { max-width: var(--vl-measure); }

/* ============================================================
   STICKY HEADER — nav stays reachable at any scroll depth.
   ============================================================ */
#masthead, .site-header {
  position: sticky;
  top: 0;
  z-index: 500;
  background: #FAFAFA;
  box-shadow: 0 1px 0 rgba(0,0,0,.06);
}
/* The gate overlays everything, including a sticky header. */
.vl-gate { z-index: 99999; }

/* ============================================================
   BUTTON HOVER — a small confident lift, not a bounce.
   ============================================================ */
.vl-btn,
.wp-block-button__link,
.woocommerce a.button,
.woocommerce button.button,
.woocommerce .single_add_to_cart_button {
  transition: transform .22s cubic-bezier(.22,.61,.36,1),
              background-color .22s ease,
              color .22s ease,
              box-shadow .22s ease;
  will-change: transform;
}
.vl-btn:hover,
.wp-block-button__link:hover,
.woocommerce a.button:hover,
.woocommerce button.button:hover,
.woocommerce .single_add_to_cart_button:hover {
  transform: scale(1.045);
  box-shadow: 0 6px 18px rgba(0,0,0,.13);
}
.vl-btn:active,
.wp-block-button__link:active { transform: scale(.99); }

/* Circular tile actions get the same treatment. */
.vl-tile__act { transition: transform .22s cubic-bezier(.22,.61,.36,1), background-color .18s ease; }
.vl-tile__act:hover { transform: scale(1.12); }

/* Product tiles lift gently on hover too. */
.vl-tile__img img { transition: transform .28s cubic-bezier(.22,.61,.36,1); }
.vl-tile:hover .vl-tile__img img { transform: scale(1.04); }

@media (prefers-reduced-motion: reduce) {
  .vl-btn, .wp-block-button__link, .vl-tile__act, .vl-tile__img img,
  .woocommerce a.button, .woocommerce button.button { transition: none; }
  .vl-btn:hover, .wp-block-button__link:hover, .vl-tile__act:hover,
  .vl-tile:hover .vl-tile__img img { transform: none; }
}

/* Vials are transparent PNGs now — no plate behind them. */
.vl-tile__img img,
.woocommerce ul.products li.product img,
.woocommerce div.product div.images img { background: transparent; }

/* Marquee sits directly under the sticky header. */
.vl-marquee { border-top: 1px solid var(--vl-rule); border-bottom: 1px solid var(--vl-rule); }

/* ============================================================
   HERO REFINEMENTS
   The title was wrapping "Peptides" onto its own line, so the copy
   column gets more of the grid and the art is pushed right.
   Top whitespace trimmed ~20%.
   ============================================================ */
.vl-hero { padding-block: 2.6rem 3rem; }
.vl-hero__inner {
  grid-template-columns: 1.32fr 0.68fr;
  gap: 3rem;
  align-items: center;
}
.vl-hero__copy { max-width: 760px; }
.vl-hero__title {
  font-size: clamp(2.1rem, 1.1rem + 2.6vw, 3.35rem);
  text-wrap: balance;
}
@media (min-width: 1200px) {
  /* Enough measure for the title to hold one line at desktop width. */
  .vl-hero__title { white-space: nowrap; }
}
.vl-hero__art { justify-self: end; text-align: right; }
.vl-hero__art img { max-width: min(100%, 430px); }

/* Marquee sits tight under the sticky header. */
.vl-marquee { padding: .95rem 0; }

/* ============================================================
   SHOP ARCHIVE — five across, transparent vials
   ============================================================ */
.woocommerce ul.products.columns-5 {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 2.5rem 2rem;
  margin: 0;
}
.woocommerce ul.products.columns-5 li.product {
  width: auto !important; margin: 0 !important; float: none !important; clear: none !important;
}
.woocommerce ul.products li.product img { aspect-ratio: 3/4; object-fit: contain; background: transparent; }
.woocommerce ul.products li.product .woocommerce-loop-product__title {
  color: #3178A0; font-weight: 700; font-size: 1.08rem; letter-spacing: -0.02em;
}
.woocommerce ul.products li.product .price { color: #000; }
.woocommerce nav.woocommerce-pagination { display: none; }
@media (max-width: 1200px) { .woocommerce ul.products.columns-5 { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 700px)  { .woocommerce ul.products.columns-5 { grid-template-columns: repeat(2, 1fr); } }

/* ============================================================
   FOOTER SIZING — logo down 40%, Interac badge down 50%
   ============================================================ */
.vl-footer__logo    { max-width: 108px; }   /* was 180px */
.vl-footer__interac { max-width: 70px;  }   /* was 140px */

/* ============================================================
   PAGE HEADERS — two patterns lifted from the live site
   ============================================================ */
.vl-pagehead {
  max-width: var(--vl-measure);
  margin-inline: auto;
  padding: 3rem var(--vl-gutter) 0;
}
.vl-pagehead__title {
  color: #21609D;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.12;
  font-size: clamp(2rem, 1.3rem + 2.4vw, 3.3rem);
  margin: 0;
}
/* Mark pattern: brand mark centred above a centred title. */
.vl-pagehead--mark { text-align: center; padding-top: 3.5rem; }
.vl-pagehead__mark { margin-bottom: 2.5rem; }
.vl-pagehead__mark img { max-width: 190px; height: auto; display: inline-block; }
/* Plain pattern: left-aligned title, no mark. */
.vl-pagehead--plain { text-align: left; }

/* Intro copy under a page header. */
.vl-calcpage__intro,
.vl-recon__lede { max-width: 900px; margin-inline: auto; text-align: center; }
.vl-calcpage__intro p { font-size: 1.02rem; line-height: 1.62; margin: 0 0 1rem; }

/* Reconstitution guidelines */
.vl-recon { text-align: center; }
.vl-recon__head { color: #21609D; font-weight: 700; font-size: 1.15rem; margin: 2.2rem 0 1rem; }
.vl-recon__list { list-style: none; padding: 0; margin: 0 auto; max-width: 880px; }
.vl-recon__list li { font-size: 1.02rem; line-height: 1.6; margin-bottom: .6rem; }
.vl-recon__list--num { counter-reset: recon; }
.vl-recon__list--num li { counter-increment: recon; }
.vl-recon__list--num li::before { content: counter(recon) ". "; }
.vl-recon__close { max-width: 900px; margin: 2rem auto 0; }

/* The calculator widget already carries its own stylesheet — just give it room. */
.vl-calc { margin-block: 2.5rem 1rem; }

/* ============================================================
   PRODUCT TILE ALIGNMENT
   Names and subtitles run to different line counts, so prices and
   buttons landed at different heights across a row. Subgrid makes
   every tile share the same five row tracks, so the rows line up
   regardless of copy length. min-heights carry older browsers.
   ============================================================ */
.vl-bestsellers { grid-auto-rows: auto; }
.vl-tile {
  display: grid;
  grid-template-rows: auto auto auto auto auto;
  row-gap: 0;
}
@supports (grid-template-rows: subgrid) {
  .vl-bestsellers { grid-template-rows: auto auto auto auto auto; }
  .vl-tile { grid-row: span 5; grid-template-rows: subgrid; }
}
@supports not (grid-template-rows: subgrid) {
  .vl-tile__name { min-height: 2.7em; }
  .vl-tile__sub  { min-height: 2.9em; }
}
.vl-tile__img    { align-self: end; }
.vl-tile__name   { align-self: start; margin: .9rem 0 .35rem; }
.vl-tile__sub    { align-self: start; }
.vl-tile__price  { align-self: center; flex: none; margin: .4rem 0 1rem; }
.vl-tile__actions{ align-self: end; }
/* Uniform image box so the vials all sit on the same baseline. */
.vl-tile__img img {
  aspect-ratio: auto;
  height: 300px;
  width: 100%;
  object-fit: contain;
  object-position: center bottom;
}

/* Same treatment for the WooCommerce catalogue grid. */
.woocommerce ul.products li.product {
  display: flex; flex-direction: column;
}
.woocommerce ul.products li.product img {
  aspect-ratio: auto;
  height: 300px;
  width: 100%;
  object-fit: contain;
  object-position: center bottom;
}
.woocommerce ul.products li.product .woocommerce-loop-product__title { min-height: 2.7em; }
.woocommerce ul.products li.product .product-details p,
.woocommerce ul.products li.product .woocommerce-product-details__short-description { min-height: 2.9em; }
.woocommerce ul.products li.product .price { margin-top: auto; }

/* ============================================================
   HERO — sits 20% closer to the banner
   ============================================================ */
.vl-hero { padding-block: 2.1rem 2.6rem; }
.home .content-container,
.home .site-container { padding-top: 0; }
.home .entry-content > *:first-child { margin-top: 0; }

/* ============================================================
   STACKS PAGE
   ============================================================ */
.vl-stackspage { text-align: center; padding-top: 1.5rem; padding-bottom: 0; }
.vl-stackspage p { max-width: 900px; margin: 0 auto 1rem; font-size: 1.02rem; line-height: 1.6; }
.vl-stackspage__lede { font-size: 1.1rem !important; }
/* Stack photos are wide group shots — give them a landscape box so the
   outer vials are not clipped. */
.vl-stacks .vl-tile__img img,
.vl-tile--stack .vl-tile__img img { height: 260px; object-fit: contain; }
.vl-whystack { text-align: left; padding-top: 2.5rem; }
.vl-whystack__title {
  color: #21609D; font-weight: 700; letter-spacing: -0.02em;
  font-size: clamp(1.6rem, 1.2rem + 1.4vw, 2.35rem); margin: 0 0 1.2rem;
}
.vl-whystack p { max-width: 1100px; font-size: 1.02rem; line-height: 1.65; margin: 0 0 1rem; }

/* ============================================================
   SINGLE PRODUCT — image right, summary dropped to align
   ============================================================ */
.woocommerce div.product { display: grid; grid-template-columns: 1fr 1fr; gap: 3.5rem; align-items: start; }
.woocommerce div.product div.images { max-width: 100%; margin: 0; justify-self: end; padding-right: 2rem; }
.woocommerce div.product div.images img { max-width: 420px; margin-inline: auto; }
.woocommerce div.product div.summary { margin: 0; padding-top: 3.5rem; }
.woocommerce div.product .product_title { margin-top: 0; }
.woocommerce div.product .woocommerce-tabs,
.woocommerce div.product .vl-disclaimer,
.woocommerce div.product .related { grid-column: 1 / -1; }
@media (max-width: 900px) {
  .woocommerce div.product { grid-template-columns: 1fr; gap: 1.5rem; }
  .woocommerce div.product div.images { justify-self: center; padding-right: 0; }
  .woocommerce div.product div.summary { padding-top: 0; }
}

/* ============================================================
   CONTACT FORM
   ============================================================ */
.vl-contact { max-width: 780px; margin: 2.5rem auto 0; text-align: left; }
.vl-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.vl-contact__notice {
  padding: 1rem 1.15rem; margin-bottom: 1.5rem; font-size: .98rem; line-height: 1.55;
  border-left: 3px solid #239B97; background: #F6F7F6;
}
.vl-contact__notice.is-bad { border-left-color: #C0392B; background: #FDF3F2; }
.vl-contact__names { border: 0; padding: 0; margin: 0 0 1.25rem; }
.vl-contact__names legend { padding: 0; font-size: 1.05rem; color: #233642; margin-bottom: .6rem; }
.vl-contact__row { display: grid; grid-template-columns: 1fr 1fr; gap: 1.25rem; }
.vl-contact__field { margin: 0 0 1.25rem; display: flex; flex-direction: column; }
.vl-contact__field label { font-size: .95rem; color: #233642; margin-bottom: .45rem; }
.vl-contact__field label span { color: #8A8A8A; font-size: .85rem; }
.vl-contact__field input,
.vl-contact__field textarea {
  font-family: var(--vl-font); font-size: 1rem; color: #233642;
  background: #fff; border: 1px solid #233642; border-radius: 26px;
  padding: 13px 18px; width: 100%;
}
.vl-contact__field textarea { border-radius: 18px; resize: vertical; min-height: 150px; }
.vl-contact__field input:focus,
.vl-contact__field textarea:focus {
  outline: none; border-color: #239B97; box-shadow: 0 0 0 3px rgba(35,155,151,.15);
}
.vl-contact__field .is-invalid { border-color: #C0392B; }
.vl-contact__submit { margin: .5rem 0 0; }
.vl-contact__submit .vl-btn { min-width: 130px; }
@media (max-width: 640px) { .vl-contact__row { grid-template-columns: 1fr; gap: 0; } }

/* ============================================================
   FLOATING CART
   ============================================================ */
.vl-cart { position: fixed; right: 22px; bottom: 22px; z-index: 900; }
.vl-cart__toggle {
  position: relative; width: 58px; height: 58px; border-radius: 999px;
  background: #fff; color: #233642; border: 1px solid var(--vl-rule);
  box-shadow: 0 6px 22px rgba(0,0,0,.14);
  display: flex; align-items: center; justify-content: center; cursor: pointer;
  transition: transform .2s cubic-bezier(.22,.61,.36,1), box-shadow .2s ease;
}
.vl-cart__toggle:hover { transform: scale(1.06); box-shadow: 0 10px 28px rgba(0,0,0,.18); }
.vl-cart__count {
  position: absolute; top: -5px; right: -5px; min-width: 22px; height: 22px;
  padding: 0 6px; border-radius: 999px; background: #239B97; color: #fff;
  font-size: .78rem; font-weight: 700; line-height: 22px; text-align: center;
}
.vl-cart__count.is-empty { display: none; }
.vl-cart__count.is-bumped { animation: vl-bump .42s cubic-bezier(.22,.61,.36,1); }
@keyframes vl-bump { 0%{transform:scale(1)} 40%{transform:scale(1.45)} 100%{transform:scale(1)} }

.vl-cart__panel {
  position: absolute; right: 0; bottom: 72px; width: min(360px, calc(100vw - 44px));
  background: #fff; border: 1px solid var(--vl-rule); border-radius: 8px;
  box-shadow: 0 18px 46px rgba(0,0,0,.20); overflow: hidden;
  max-height: min(70vh, 560px); display: flex; flex-direction: column;
}
.vl-cart__panel[hidden] { display: none; }
.vl-cart__head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 1rem 1.15rem; border-bottom: 1px solid var(--vl-rule);
  font-weight: 700; color: #21609D;
}
.vl-cart__close { background: none; border: 0; font-size: 1.5rem; line-height: 1; cursor: pointer; color: #8A8A8A; }
.vl-cart__close:hover { color: #233642; }
.vl-cart__panelinner { padding: 1.15rem; overflow-y: auto; }
.vl-cart__empty { color: #8A8A8A; margin: 0; text-align: center; padding: 1.2rem 0; }
.vl-cart__items { list-style: none; margin: 0 0 1rem; padding: 0; }
.vl-cart__item { display: flex; gap: .75rem; align-items: center; padding: .65rem 0; border-bottom: 1px solid #F0F1EF; }
.vl-cart__thumb img { width: 46px; height: 46px; object-fit: contain; display: block; }
.vl-cart__meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: .15rem; }
.vl-cart__name { font-size: .92rem; color: #3178A0; font-weight: 700; line-height: 1.3; }
.vl-cart__qty { font-size: .84rem; color: #666; }
.vl-cart__remove { color: #BBB; text-decoration: none; font-size: 1.2rem; line-height: 1; padding: 0 .2rem; }
.vl-cart__remove:hover { color: #C0392B; }
.vl-cart__subtotal { display: flex; justify-content: space-between; align-items: baseline; margin: 0 0 1rem; font-size: 1rem; }
.vl-cart__checkout { display: block; text-align: center; width: 100%; }
.vl-cart__viewcart { display: block; text-align: center; margin-top: .7rem; font-size: .88rem; color: #666; }
.vl-cart__viewcart:hover { color: #21609D; }
@media (max-width: 600px) { .vl-cart { right: 14px; bottom: 14px; } }

/* Payment method description on the block checkout. */
.vl-pm__desc { font-size: .95rem; line-height: 1.55; color: #233642; padding: .25rem 0 .1rem; }

/* ============================================================
   CHECKOUT — Canada only, so the country selector is noise.
   The value is still submitted (WooCommerce defaults it to CA);
   only the control is hidden.
   ============================================================ */
.wc-block-components-address-form .wc-block-components-country-input,
.wc-block-components-address-form #billing-country,
.wc-block-components-address-form #shipping-country,
.woocommerce-billing-fields #billing_country_field,
.woocommerce-shipping-fields #shipping_country_field {
  display: none !important;
}

/* ============================================================
   HERO — a further 15% closer to the banner
   ============================================================ */
.vl-hero { padding-block: 1.8rem 2.3rem; }

/* ============================================================
   CATALOGUE — narrower measure, smaller photos
   The grid ran edge to edge and the vials read as oversized.
   ============================================================ */
.woocommerce .site-container,
.woocommerce-page .site-container { max-width: 1420px; }
.woocommerce ul.products.columns-5 {
  padding-inline: 2.5rem;
  gap: 2.75rem 2.25rem;
}
/* 15% smaller than the previous 300px box. */
.woocommerce ul.products li.product img { height: 255px; }
.vl-tile__img img { height: 255px; }

/* Intro copy sits directly under the page title. */
.vl-pagehead--plain + .vl-shopintro,
.vl-shopintro { max-width: var(--vl-measure); margin: .9rem auto 0; padding-inline: var(--vl-gutter); }
.vl-shopintro p { font-size: 1.02rem; line-height: 1.6; margin: 0; max-width: 1100px; }

/* ---- Search + category filter ---- */
.vl-shopfilters {
  display: flex; align-items: center; justify-content: center; gap: 2.5rem;
  flex-wrap: wrap; margin: 2.25rem 0 2rem;
}
.vl-shopfilters__search { display: flex; align-items: center; gap: .5rem; }
.vl-shopfilters__search input[type="search"] {
  font-family: var(--vl-font); font-size: 1rem; color: #233642;
  border: 0; border-bottom: 1px solid var(--vl-rule); background: transparent;
  padding: .5rem .25rem; min-width: 220px;
}
.vl-shopfilters__search input[type="search"]:focus { outline: none; border-bottom-color: #239B97; }
.vl-shopfilters__search button {
  background: none; border: 0; cursor: pointer; color: #8A8A8A; padding: .3rem; display: flex;
}
.vl-shopfilters__search button:hover { color: #21609D; }
.vl-shopfilters__cats select {
  font-family: var(--vl-font); font-size: 1rem; color: #233642;
  border: 0; border-bottom: 1px solid var(--vl-rule); background: transparent;
  padding: .5rem 1.6rem .5rem .25rem; cursor: pointer;
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='%23233642' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right .25rem center; background-size: 10px;
}
.vl-shopfilters__cats select:focus { outline: none; border-bottom-color: #239B97; }

/* ---- Research area + alias ---- */
.vl-loop__area, .vl-tile__sub {
  color: #8A8A8A; font-size: .92rem; line-height: 1.35; margin: .25rem 0 .5rem;
}
.vl-loop__alias, .vl-tile__alias, .vl-single__alias {
  color: #8A8A8A; font-size: .92rem; line-height: 1.3; margin: -.1rem 0 .15rem; font-style: italic;
}
.vl-single__alias { font-size: 1rem; margin: -.4rem 0 .8rem; }

/* ---- Sold out ---- */
.woocommerce ul.products li.product { position: relative; }
/* Centred on the vial, not near the top. Measured on a sold-out card: the
   vial's visible ink runs 930..1105 inside a crop box of 922..1113, so its
   optical centre lands at 49.9% -- 50% with the -50% translate puts the badge
   dead on it. At the old 38% it sat 22px high and read as sloppy. */
.vl-soldout {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  background: #233642; color: #fff; z-index: 3;
  font-size: .82rem; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  padding: .6rem 1.15rem; white-space: nowrap; pointer-events: none;
}
.woocommerce ul.products li.product.outofstock img { opacity: .78; }
.woocommerce ul.products li.product.outofstock .button,
.woocommerce ul.products li.product.outofstock a.button {
  background: #6B7A83; pointer-events: none; opacity: 1;
}
.woocommerce div.product { position: relative; }
.woocommerce div.product .vl-soldout { top: 50%; }

/* ============================================================
   HERO SPACING — the real cause
   The 85px gap under the banner was only ~29px of hero padding.
   Kadence contributes the rest: .content-area margin-top 32px and
   .entry-content-wrap padding-top 24px. Trimming the hero alone
   could never move it much.
   ============================================================ */
.home .content-area { margin-top: 0; }
.home .entry-content-wrap { padding-top: 0; }
.vl-hero { padding-block: 2.4rem 2.3rem; }

/* ============================================================
   HEADER — same full-width measure on every page.
   The catalogue sat flush because WooCommerce pages resolved a
   different container width; this pins them all to one rule.
   ============================================================ */
#masthead .site-container,
.site-header .site-container,
#masthead .site-header-row-container-inner,
.site-header .site-header-row-container-inner {
  max-width: 100%;
  padding-inline: 2rem;
}
@media (max-width: 767px) {
  #masthead .site-container,
  .site-header .site-container { padding-inline: 1.1rem; }
}

/* ============================================================
   FULL-BLEED MEASURE
   Content now runs to the window edge with only a small gutter,
   so the banner and sections read as continuous with the frame.
   ============================================================ */
:root { --vl-measure: 100%; --vl-gutter: 1.5rem; }

.content-container.site-container,
.content-width-normal .content-container,
#inner-wrap .content-container,
.woocommerce .site-container,
.woocommerce-page .site-container,
.site-container { max-width: 100%; }

.vl-hero__inner,
.vl-section,
.vl-bestsellers,
.vl-reviews,
.vl-peptides__inner,
.vl-resources__grid,
.vl-footer__inner,
.vl-pagehead,
.vl-shopintro { max-width: 100%; }

.woocommerce ul.products.columns-5 { padding-inline: var(--vl-gutter); }

/* The marquee spans the window with nothing boxing it in. */
.vl-marquee { width: 100%; max-width: 100%; }

/* ============================================================
   VERTICAL RHYTHM — Kadence's wrappers were adding 56px on every
   page, on top of section padding. Cleared globally; the page
   header and first section carry the spacing instead.
   ============================================================ */
.content-area { margin-top: 0; }
.entry-content-wrap { padding-top: 0; }

/* Hero back down slightly — it was a touch tight against the banner. */
.vl-hero { padding-block: 2.7rem 2.3rem; }

/* Page headers and the first section below them. */
.vl-pagehead { padding-top: 1.6rem; }
.vl-pagehead--mark { padding-top: 1.8rem; }
.vl-pagehead__mark { margin-bottom: 1.4rem; }
.vl-pagehead__mark img { max-width: 130px; }
.vl-pagehead + .vl-section,
.vl-pagehead + * > .vl-section:first-child,
.vl-shopintro + * .vl-section:first-child { padding-top: 1.25rem; }
.entry-content > .vl-section:first-child,
.entry-content-wrap .vl-section:first-of-type { padding-top: 1.25rem; }
.vl-calcpage { padding-top: 1rem; padding-bottom: 0; }
.vl-calc { margin-block: 1.5rem 1rem; }
.vl-contact { margin-top: 1.5rem; }
.vl-stackspage { padding-top: 1rem; }
.vl-shopfilters { margin: 1.5rem 0 1.75rem; }

/* ============================================================
   CLIENT REVIEWS — smaller cards, heading untouched
   ============================================================ */
.vl-review { flex-basis: clamp(220px, 22%, 300px); }
.vl-review__photo { aspect-ratio: 1/1; }
.vl-review__body { padding: 1rem 1.1rem 1.15rem; }
.vl-review__text { font-size: .88rem; line-height: 1.5; margin-bottom: 1rem; }
.vl-review__name { font-size: .76rem; }
.vl-review__stars { font-size: .92rem; }
.vl-reviews__track { gap: 18px; }

/* ============================================================
   CART PANEL — clickable items and quantity steppers
   ============================================================ */
.vl-cart__thumb { display: block; flex: 0 0 auto; }
.vl-cart__thumb img { width: 46px; height: 46px; object-fit: contain; display: block; }
.vl-cart__name { text-decoration: none; }
.vl-cart__name:hover { color: #21609D; text-decoration: underline; }
.vl-cart__price { font-size: .84rem; color: #666; }
.vl-cart__stepper { display: inline-flex; align-items: center; gap: .1rem; margin-top: .3rem; }
.vl-cart__step {
  width: 26px; height: 26px; border: 1px solid var(--vl-rule); background: #fff;
  color: #233642; font-size: 1rem; line-height: 1; cursor: pointer; border-radius: 4px;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .15s ease, border-color .15s ease;
}
.vl-cart__step:hover:not(:disabled) { background: #F6F7F6; border-color: #239B97; }
.vl-cart__step:disabled { opacity: .5; cursor: default; }
.vl-cart__num { min-width: 30px; text-align: center; font-size: .92rem; }

/* ============================================================
   EMPTY PAGE-HERO — the cause of the huge gap on inner pages
   vistara_page_head() replaces Kadence's title band, but removing
   the action only empties the container; the container itself still
   reserved 200px on every page. The homepage rule below was doing
   this already — it just needed to apply everywhere.
   ============================================================ */
.entry-hero,
.entry-hero-container-inner,
.page-hero-section,
.product-archive-hero-section,
.hero-section-overlay,
.entry-hero .entry-header {
  display: none !important;
}
/* Our own header is the only one that renders. */
.vl-pagehead { display: block !important; }

/* ============================================================
   FLUSH TO THE BROWSER FRAME
   No horizontal gutter anywhere — the banner runs edge to edge.
   ============================================================ */
:root { --vl-gutter: 0px; }
#masthead .site-container,
.site-header .site-container,
#masthead .site-header-row-container-inner,
.site-header .site-header-row-container-inner { padding-inline: 1.6rem; }

.vl-hero__inner,
.vl-section,
.vl-bestsellers,
.vl-reviews,
.vl-resources,
.vl-footer,
.vl-pagehead,
.vl-shopintro { padding-inline: 1.6rem; max-width: none; }

.content-container.site-container,
.content-width-normal .content-container,
#inner-wrap .content-container,
.woocommerce .site-container,
.woocommerce-page .site-container { max-width: none; padding-inline: 0; }

.woocommerce ul.products.columns-5 { padding-inline: 1.6rem; }
.vl-marquee { padding-inline: 0; }

/* Keep long-form reading measure sane even at full bleed. */
.vl-calcpage__intro, .vl-recon__lede, .vl-recon__list, .vl-recon__close,
.vl-contact, .vl-stackspage p { max-width: 900px; margin-inline: auto; }
.vl-whystack p, .vl-shopintro p { max-width: 1100px; }

/* ============================================================
   HERO — nudged ~5% back down; it had gone too tight
   ============================================================ */
.vl-hero { padding-block: 2.75rem 2.4rem; }

/* Inner pages start right under their title. */
.vl-pagehead { padding-top: 2.2rem; }
.vl-pagehead--mark { padding-top: 2.4rem; }
.vl-pagehead--mark .vl-pagehead__mark { margin-bottom: 1.6rem; }
.vl-section { padding-top: 1.6rem; }
.content-area { margin-top: 0 !important; }
.entry-content-wrap { padding-top: 0 !important; }

/* ============================================================
   CLIENT REVIEWS — smaller cards, heading untouched
   ============================================================ */
.vl-review { flex-basis: clamp(220px, 23%, 320px); }
.vl-review__photo { aspect-ratio: 1/1; }
.vl-review__body { padding: 1rem 1.1rem 1.2rem; }
.vl-review__text { font-size: .88rem; line-height: 1.5; margin-bottom: 1rem; }
.vl-review__name { font-size: .76rem; }
.vl-review__stars { font-size: .92rem; }
@media (max-width: 860px) { .vl-review { flex-basis: 78%; } }

/* ============================================================
   CART — links, quantity steppers
   ============================================================ */
/* Inert, like .vl-cartctl.is-busy. Dimming the panel for the length of every
   cart request is what made removing a line look like the cart blinking out.
   The optimistic row removal in inc/floating-cart.php is the feedback. */
.vl-cart__panel.is-busy { opacity: 1; pointer-events: auto; }
.vl-cart__thumb { display: block; flex: 0 0 auto; }
.vl-cart__name { text-decoration: none; }
.vl-cart__name:hover { color: #21609D; text-decoration: underline; }
.vl-cart__price { font-size: .84rem; color: #666; }
.vl-cart__qtyctl { display: inline-flex; align-items: center; gap: .5rem; margin-top: .35rem; }
.vl-cart__step {
  width: 24px; height: 24px; border: 1px solid var(--vl-rule); background: #fff;
  border-radius: 4px; cursor: pointer; font-size: 1rem; line-height: 1; color: #233642;
  display: inline-flex; align-items: center; justify-content: center;
}
.vl-cart__step:hover { border-color: #239B97; color: #239B97; }
.vl-cart__qtynum { min-width: 18px; text-align: center; font-size: .9rem; }
.vl-cart__remove { background: none; border: 0; cursor: pointer; }

/* ============================================================
   HERO — a further 2.5% down
   ============================================================ */
.vl-hero { padding-block: 3.1rem 2.4rem; }

/* ============================================================
   BANNER — no rules above or below; it should read as part of
   the page, not a bordered strip.
   ============================================================ */
.vl-marquee { border: 0 !important; }

/* ============================================================
   TILE ROWS
   Alias and research-area rows always exist now (empty ones just
   collapse), so every tile keeps identical row tracks and the
   prices sit on one line across the row.
   ============================================================ */
.vl-tile { grid-template-rows: auto auto auto auto auto auto; }
@supports (grid-template-rows: subgrid) {
  .vl-bestsellers { grid-template-rows: auto auto auto auto auto auto; }
  .vl-tile { grid-row: span 6; grid-template-rows: subgrid; }
}
.vl-tile__alias, .vl-loop__alias { align-self: start; margin: 0 0 .2rem; min-height: 0; }
.vl-tile__alias.is-empty, .vl-loop__alias.is-empty { margin: 0; min-height: 0; }
.vl-tile__sub, .vl-loop__area { align-self: start; margin: 0 0 .5rem; }
.vl-tile__sub.is-empty, .vl-loop__area.is-empty { margin: 0; }
.vl-tile__price { align-self: end; margin: .5rem 0 1rem; }
@supports not (grid-template-rows: subgrid) {
  .vl-tile__name  { min-height: 2.7em; }
  .vl-tile__alias { min-height: 1.3em; }
  .vl-tile__sub   { min-height: 2.6em; }
}
/* Catalogue cards get the same consistent rows. */
.woocommerce ul.products li.product .woocommerce-loop-product__title { min-height: 2.7em; }
.woocommerce ul.products li.product .vl-loop__alias { min-height: 1.3em; margin: 0 0 .2rem; }
.woocommerce ul.products li.product .vl-loop__area  { min-height: 2.6em; margin: 0 0 .5rem; }
.woocommerce ul.products li.product .price { margin-top: auto; margin-bottom: .8rem; }

/* ============================================================
   CLIENT REVIEWS — smaller, dropped, arrows clear of the cart
   ============================================================ */
.vl-reviews { padding-top: 1.6rem; }               /* 5% down from the heading */
.vl-review { flex-basis: clamp(200px, 20.5%, 288px); }  /* ~10% smaller again */
.vl-review__body { padding: .9rem 1rem 1.05rem; }
.vl-review__text { font-size: .84rem; }
.vl-reviews__nav { padding-right: 5.5rem; }        /* clear of the floating cart */
@media (max-width: 860px) {
  .vl-review { flex-basis: 76%; }
  .vl-reviews__nav { padding-right: 4rem; }
}

/* ============================================================
   WHAT ARE PEPTIDES — graphic flush right, copy raised
   ============================================================ */
.vl-peptides { padding-top: 1.4rem; padding-inline: 0; }
.vl-peptides .vl-section__title { padding-inline: 1.6rem; }
.vl-peptides__inner {
  grid-template-columns: 1fr minmax(300px, 42%);
  gap: 2rem;
  align-items: center;
  max-width: none;
  padding-left: 1.6rem;
  padding-right: 0;
}
.vl-peptides__copy { align-self: center; padding-top: 0; }
.vl-peptides__lede { margin-top: 0; }
.vl-peptides__art { justify-self: end; text-align: right; }
.vl-peptides__art img { max-width: 100%; width: 100%; height: auto; display: block; }
@media (max-width: 860px) {
  .vl-peptides__inner { grid-template-columns: 1fr; padding-right: 1.6rem; }
  .vl-peptides__art { justify-self: center; }
  .vl-peptides__art img { max-width: 340px; margin-inline: auto; }
}

/* Kadence's content wrapper carries a 32px horizontal padding that kept the
   full-bleed sections off the edge. Our own sections supply their gutter, so
   the wrapper's is removed. */
/* Single product pages reuse .entry-content-wrap on the product container itself,
   so this reset has to skip it — otherwise it stripped the product's own gutter
   and the description, tabs and gallery all sat flush at 0. */
.entry-content-wrap:not(.product) { padding-inline: 0 !important; }
.entry-content.single-content { padding-inline: 0; }

/* ============================================================
   CATALOGUE GRID — correcting the selector
   Kadence marks the list `grid-cols grid-lg-col-5`, not `columns-5`,
   so the earlier padding and image rules never matched anything.
   ============================================================ */
.woocommerce ul.products,
.woocommerce-page ul.products {
  padding-inline: 3.5rem !important;
  gap: 3.25rem 2.5rem !important;
  margin-bottom: 3rem;
}
/* 15% smaller than the 341px the browser was resolving. */
.woocommerce ul.products li.product img,
.woocommerce ul.products li.product .attachment-woocommerce_thumbnail {
  height: 290px !important;
  width: auto !important;
  max-width: 100%;
  object-fit: contain;
  object-position: center bottom;
  margin-inline: auto;
  background: transparent;
}
.woocommerce ul.products li.product { text-align: center; }

/* Kadence prints the archive description a second time in its own products
   header; ours sits under the title, so this one goes. */
.woocommerce-products-header .page-description,
.woocommerce-products-header { display: none !important; }

/* Catalogue intro indented from the gutter, matching the grid. */
.vl-shopintro { padding-inline: 3.5rem; }
.vl-pagehead { padding-inline: 3.5rem; }

/* ============================================================
   HEADER WORDMARK — letterspaced, matching the logo lockup
   ============================================================ */
.site-branding .site-title,
.site-title-wrap .site-title {
  letter-spacing: .18em;
  font-weight: 400;
}

/* ============================================================
   WHAT ARE PEPTIDES — copy sits close under the headline
   ============================================================ */
.vl-peptides { padding-top: 1rem; padding-bottom: 1.5rem; }
.vl-peptides .vl-section__title { margin-bottom: 1.2rem; }
.vl-peptides__inner { align-items: start; gap: 1.5rem 2rem; }
.vl-peptides__copy { padding-top: .25rem; }
.vl-peptides__copy p { margin: 0 0 .8rem; }
.vl-peptides__lede { margin: 0 0 1rem; }
.vl-peptides__copy .vl-btn { margin-top: .8rem; }

/* ============================================================
   HEADER — no bottom rule
   ============================================================ */
#masthead, .site-header {
  box-shadow: none !important;
  border-bottom: 0 !important;
}
#masthead .site-header-row-container,
.site-header .site-header-row-container,
.site-header-main-section { border-bottom: 0 !important; box-shadow: none !important; }

/* ============================================================
   CATALOGUE — tighter cards, smaller vials
   The gaps between name, research area and price were eating a
   lot of vertical space for no reason.
   ============================================================ */
.woocommerce ul.products li.product img,
.woocommerce ul.products li.product .attachment-woocommerce_thumbnail {
  height: 260px !important;   /* a further 10% down from 290px */
}
.woocommerce ul.products li.product .woocommerce-loop-product__title {
  min-height: 0 !important;
  margin: .7rem 0 .15rem !important;
  line-height: 1.25;
}
.woocommerce ul.products li.product .vl-loop__alias {
  min-height: 0 !important; margin: 0 0 .1rem !important; line-height: 1.25;
}
.woocommerce ul.products li.product .vl-loop__alias.is-empty { margin: 0 !important; }
.woocommerce ul.products li.product .vl-loop__area {
  min-height: 0 !important; margin: 0 0 .35rem !important; line-height: 1.3;
}
.woocommerce ul.products li.product .vl-loop__area.is-empty { margin: 0 !important; }
.woocommerce ul.products li.product .price {
  margin: .15rem 0 .7rem !important; line-height: 1.3;
}
.woocommerce ul.products li.product { padding-bottom: .5rem; }
/* Rows still align because every card keeps the same tracks. */
.woocommerce ul.products { align-items: start; }

/* ============================================================
   INTRO COPY — flush with its page heading
   ============================================================ */
.vl-shopintro { padding-inline: 3.5rem; margin-top: .6rem; }
.vl-shopintro p { margin-left: 0; max-width: 1100px; }

.vl-stackspage { text-align: left; padding-inline: 3.5rem; }
.vl-stackspage p { margin-left: 0; margin-right: 0; max-width: 1100px; text-align: left; }
.vl-whystack { padding-inline: 3.5rem; }

/* ============================================================
   GIVEAWAY PAGE — matched to the live layout
   Colours sampled from vistaralabs.ca/giveaway:
     #21609D  eyebrow + countdown numbers + section headings
     #239B97  the $500 headline
     #4A4A4A  countdown unit labels
   ============================================================ */
.vl-give { text-align: center; max-width: 1000px; margin-inline: auto; padding: 0 1.6rem 3rem; }
.vl-give__mark { margin-bottom: 2.2rem; }
.vl-give__mark img { max-width: 150px; height: auto; }

.vl-give__eyebrow {
  color: #21609D; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; font-size: clamp(1.35rem, 1.1rem + .9vw, 2.05rem);
  margin: 0 0 .3rem;
}
.vl-give__headline {
  color: #239B97; font-weight: 700; letter-spacing: -0.02em;
  font-size: clamp(1.9rem, 1.3rem + 2.4vw, 3.2rem);
  margin: 0 0 2rem; line-height: 1.15;
}

/* ---- Countdown ---- */
.vl-give__countdown {
  display: flex; align-items: flex-start; justify-content: center;
  gap: .9rem; margin: 0 0 2rem;
}
.vl-give__unit { display: flex; flex-direction: column; align-items: center; min-width: 78px; }
.vl-give__num {
  color: #21609D; font-weight: 700; font-size: clamp(2.2rem, 1.4rem + 2.6vw, 3.6rem);
  line-height: 1; font-variant-numeric: tabular-nums;
}
.vl-give__lbl {
  color: #4A4A4A; font-weight: 700; font-size: .72rem;
  text-transform: uppercase; letter-spacing: .12em; margin-top: .5rem;
}
.vl-give__sep { color: #C7D2DA; font-size: 2rem; line-height: 1; padding-top: .2rem; }

/* ---- Prizes ---- */
.vl-give__prizes { list-style: none; margin: 0 0 2.2rem; padding: 0; }
.vl-give__prizes li {
  color: #000; font-style: italic; font-weight: 700; letter-spacing: -0.02em;
  font-size: clamp(1.2rem, .95rem + 1.1vw, 1.95rem); line-height: 1.35;
}

/* ---- Entry form ---- */
.vl-give__formwrap { margin: 0 auto 2.5rem; max-width: 620px; }
.vl-give__form { display: flex; justify-content: center; gap: 0; flex-wrap: wrap; }
.vl-give__form input[type="email"] {
  flex: 1 1 300px; min-width: 0;
  font-family: var(--vl-font); font-size: 1rem; color: #233642;
  border: 1px solid var(--vl-rule); background: #fff;
  padding: 1.1rem 1.2rem; border-radius: 0;
}
.vl-give__form input[type="email"]:focus { outline: none; border-color: #239B97; }
.vl-give__submit {
  flex: 0 0 auto; background: #000; color: #FAFAFA; border: 0; border-radius: 0;
  font-family: var(--vl-font); font-style: italic; text-transform: uppercase;
  letter-spacing: .08em; font-size: .8rem; padding: 1.1rem 1.8rem; cursor: pointer;
  transition: transform .2s cubic-bezier(.22,.61,.36,1), background .2s ease;
}
.vl-give__submit:hover { background: #21609D; transform: scale(1.04); }
.vl-give__confirm {
  background: #F6F7F6; border-left: 3px solid #239B97;
  padding: .9rem 1.1rem; margin: 0 0 1rem; font-size: .95rem; text-align: left;
}
.vl-give__confirm.is-bad { border-left-color: #C0392B; background: #FDF3F2; }

/* ---- Vials + copy ---- */
.vl-give__vials { margin: 1rem 0 2.5rem; }
.vl-give__vials img { max-width: min(100%, 460px); height: auto; }
.vl-give__trust,
.vl-give__enter {
  color: #21609D; font-weight: 700; letter-spacing: -0.02em;
  font-size: clamp(1.7rem, 1.2rem + 1.9vw, 2.9rem); margin: 0 0 1.4rem; line-height: 1.15;
}
.vl-give__enter { font-style: italic; margin-top: 2.5rem; }
.vl-give__copy { max-width: 820px; margin: 0 auto 2rem; }
.vl-give__copy p { font-size: 1.05rem; line-height: 1.6; margin: 0 0 1.1rem; color: #000; }

@media (max-width: 640px) {
  .vl-give__countdown { gap: .4rem; }
  .vl-give__unit { min-width: 58px; }
  .vl-give__sep { display: none; }
  .vl-give__form input[type="email"] { flex-basis: 100%; }
  .vl-give__submit { width: 100%; }
}

/* ============================================================
   HEADER — same white as the page, no seam
   ============================================================ */
#masthead, .site-header,
#masthead .site-header-row-container,
.site-header .site-header-row-container,
.site-header-main-section { background: #FFFFFF !important; }
body, html, .content-bg, #inner-wrap, .site { background: #FFFFFF; }

/* ============================================================
   HOMEPAGE TILES — close the vertical gaps
   Subgrid sizes each row track to the tallest card, so slack in
   any one element opens a gap across the whole row. Margins are
   tightened per element rather than relying on the tracks.
   ============================================================ */
.vl-bestsellers { row-gap: 0; }
.vl-tile__name   { margin: .6rem 0 .1rem !important; line-height: 1.25; }
.vl-tile__alias  { margin: 0 0 .1rem !important; line-height: 1.2; font-size: .88rem; }
.vl-tile__alias.is-empty { margin: 0 !important; line-height: 0; }
.vl-tile__sub    { margin: 0 0 .3rem !important; line-height: 1.3; }
.vl-tile__sub.is-empty { margin: 0 !important; line-height: 0; }
.vl-tile__price  { margin: .1rem 0 .6rem !important; line-height: 1.25; }
.vl-tile__actions{ margin-top: 0; }
.vl-tile__img img { height: 230px; }

/* ============================================================
   CATALOGUE — no card shadows, tighter stack
   ============================================================ */
.woocommerce ul.products li.product,
.woocommerce ul.products li.product .content-bg,
.woocommerce ul.products li.product .loop-entry {
  box-shadow: none !important;
  border: 0 !important;
  background: transparent !important;
}
.woocommerce ul.products li.product img { box-shadow: none !important; }
.woocommerce ul.products { gap: 2.25rem 2rem !important; }
.woocommerce ul.products li.product .woocommerce-loop-product__title { margin: .55rem 0 .1rem !important; }
.woocommerce ul.products li.product .vl-loop__alias { margin: 0 0 .08rem !important; }
.woocommerce ul.products li.product .vl-loop__area  { margin: 0 0 .25rem !important; }
.woocommerce ul.products li.product .price { margin: .05rem 0 .55rem !important; }

/* ============================================================
   WHY VISTARA LABS — more room
   ============================================================ */
.vl-why { padding-top: 3rem; padding-bottom: 2rem; }
.vl-why__grid { gap: 6rem 5rem; margin-top: 4rem; }
.vl-why__item h3 { font-size: 1.05rem; margin-bottom: 1.1rem; }
.vl-why__item p { font-size: 1.08rem; line-height: 1.6; }
/* Watermark opacity, and the ONLY place it is set -- mobile deliberately does
   not override it any more, because the two had drifted apart (.336 vs .229)
   and it was impossible to reason about "make it lighter" with two numbers.

   The artwork is fully opaque saturated teal, rgba(34,153,152,255), with no
   softness of its own, so this value does all the work. Measured against the
   white section: .42 painted rgb(162,212,212), 53.6 luminance points of
   contrast. .10 paints rgb(233,245,245), 12.6 points -- present as a texture,
   nowhere near competing with the claims sitting on top of it. */
.vl-why::before { background-size: min(78%, 980px) auto; opacity: .10; }

/* ============================================================
   WHAT ARE PEPTIDES — tighter still
   ============================================================ */
.vl-peptides { padding-top: .5rem; padding-bottom: 1rem; }
.vl-peptides .vl-section__title { margin-bottom: .5rem; }
.vl-peptides__inner { gap: .5rem 2rem; align-items: center; }
.vl-peptides__copy { padding-top: 0; }
.vl-peptides__lede { margin: 0 0 .7rem; }
.vl-peptides__copy p { margin: 0 0 .6rem; }
.vl-peptides__copy .vl-btn { margin-top: .6rem; }

/* ============================================================
   FOOTER — bigger marks, logo further right
   ============================================================ */
.vl-footer__logo    { max-width: 124px; }   /* +15% */
.vl-footer__interac { max-width: 81px;  }   /* +15% */
.vl-footer__brand   { text-align: right; padding-right: 1rem; }

/* ============================================================
   CALCULATOR PAGE — breathing room above the widget, centred copy
   ============================================================ */
.vl-calc { margin-top: 3.5rem; }
.vl-recon { text-align: center; }
.vl-recon__list { text-align: center; }
.vl-recon__list li { text-align: center; }

/* ============================================================
   RESEARCH USE POLICY — long-form document layout
   ============================================================ */
.vl-policy {
  max-width: 900px;
  margin: 1.5rem auto 3rem;
  padding-inline: 1.6rem;
  text-align: left;
}
.vl-policy p {
  font-size: 1.02rem;
  line-height: 1.65;
  margin: 0 0 1.1rem;
  color: #000;
}
.vl-policy__head {
  color: #21609D;                 /* same blue as the homepage hero headline */
  font-weight: 700;
  letter-spacing: -0.01em;
  font-size: 1.35rem;
  line-height: 1.3;
  margin: 2.4rem 0 1rem;
}
.vl-policy__head:first-of-type { margin-top: 2rem; }
.vl-policy__list { list-style: none; margin: 0 0 1.2rem; padding: 0; }
.vl-policy__list li {
  font-size: 1.02rem; line-height: 1.6; margin-bottom: .35rem; color: #000;
  padding-left: 1rem; position: relative;
}
.vl-policy__list li::before { content: "•"; position: absolute; left: 0; color: #000; }
.vl-policy a { color: #21609D; text-decoration: underline; }

/* ============================================================
   WHAT ARE PEPTIDES — the copy was 411px below the heading.
   The grid was centring the text column against a very tall
   molecule graphic. Top-align instead so it starts under the title.
   ============================================================ */
.vl-peptides__inner { align-items: start !important; }
.vl-peptides__copy { align-self: start !important; padding-top: 0; }
.vl-peptides__art  { align-self: start; }
.vl-peptides__art img { max-height: 420px; width: auto; margin-left: auto; }

/* ============================================================
   TILE GAPS
   Subgrid sizes each row to the tallest card, so a two-line name
   on one product opens slack on every other card in the row.
   Shrinking the tallest content shrinks every track.
   ============================================================ */
.vl-tile__name  { font-size: 1.02rem; line-height: 1.2; }
.vl-tile__alias { font-size: .84rem; line-height: 1.15; }
.vl-tile__sub   { font-size: .86rem; line-height: 1.22; }
.vl-tile__price { font-size: .98rem; line-height: 1.2; }
.vl-tile > *    { align-self: start; }
.vl-tile__img   { align-self: end; }
.vl-tile__actions { align-self: start; }

.woocommerce ul.products li.product .woocommerce-loop-product__title { font-size: 1rem; line-height: 1.2; }
.woocommerce ul.products li.product .vl-loop__area { font-size: .86rem; line-height: 1.22; }
.woocommerce ul.products li.product .vl-loop__alias { font-size: .84rem; line-height: 1.15; }

/* ============================================================
   TILE TEXT BLOCK — one row instead of three
   ============================================================ */
.vl-tile { grid-template-rows: auto auto auto auto; }
@supports (grid-template-rows: subgrid) {
  .vl-bestsellers { grid-template-rows: auto auto auto auto; }
  .vl-tile { grid-row: span 4; grid-template-rows: subgrid; }
}
.vl-tile__text { align-self: start; margin: .6rem 0 0; }
.vl-tile__text .vl-tile__name  { margin: 0 0 .15rem !important; }
.vl-tile__text .vl-tile__alias { margin: 0 0 .15rem !important; }
.vl-tile__text .vl-tile__sub   { margin: 0 !important; }
.vl-tile__price { align-self: end; margin: .5rem 0 .6rem !important; }

/* Catalogue cards: same idea, achieved with a tighter fixed block. */
.woocommerce ul.products li.product .woocommerce-loop-product__title { margin: .55rem 0 .12rem !important; }
.woocommerce ul.products li.product .vl-loop__alias { margin: 0 0 .12rem !important; }
.woocommerce ul.products li.product .vl-loop__area  { margin: 0 0 .2rem !important; }

/* ============================================================
   ONE EDGE FOR EVERYTHING
   The header wordmark sat at 51px while page content sat at 26px.
   Both now derive from a single value so they cannot drift apart.
   ============================================================ */
:root { --vl-edge: 3.2rem; }

#masthead .site-container,
.site-header .site-container,
#masthead .site-header-row-container-inner,
.site-header .site-header-row-container-inner { padding-inline: var(--vl-edge) !important; }
.site-branding, .site-header-main-section-left { padding-left: 0 !important; margin-left: 0 !important; }

.vl-hero__inner,
.vl-section,
.vl-bestsellers,
.vl-reviews,
.vl-resources,
.vl-footer,
.vl-pagehead,
.vl-shopintro,
.vl-stackspage,
.vl-whystack,
.vl-section__cta,
.woocommerce ul.products { padding-inline: var(--vl-edge) !important; }

.vl-peptides__inner { padding-left: var(--vl-edge); padding-right: 0; }
.vl-peptides .vl-section__title { padding-inline: var(--vl-edge); }

/* Banner: dropped 10%. */
.vl-marquee { padding: 1.05rem 0; margin-top: .6rem; }

/*
 * The track carries no padding, and must not.
 *
 * It used to take `padding-left: var(--vl-edge)` to inset the banner like the
 * sections around it. That padding is part of the track's own width, and the
 * loop is `translateX(-50%)` -- so the animation moved half of (content +
 * padding) while only half the content had to pass to line the copies back up.
 * Every cycle ended half the padding out of register and jumped to correct
 * itself: 25.6px on desktop where --vl-edge is 3.2rem, 9.2px on a phone where
 * it is 1.15rem. Same defect on both, but a third the size on mobile, which is
 * why only desktop looked broken.
 *
 * The inset is not worth restoring elsewhere either: the content scrolls
 * continuously, so a starting offset is visible for one moment and never again,
 * and the edge gradients already soften both sides.
 *
 * Anything added here has to be margin on .vl-marquee, never padding on the
 * track, or the seam comes straight back.
 */

/* ============================================================
   HERO — tighter sub/trust, smaller mark pulled inward
   ============================================================ */
.vl-hero__sub { margin-bottom: .7rem; }
.vl-hero__trust { margin: 0 0 2rem; }
.vl-hero__art { justify-self: end; padding-right: 20%; }
.vl-hero__art img { max-width: min(100%, 378px); }   /* 10% smaller */

/* ============================================================
   CALCULATOR PAGE — everything centred
   ============================================================ */
.vl-recon, .vl-recon__lede, .vl-recon__close,
.vl-recon__head, .vl-recon__list, .vl-recon__list li { text-align: center !important; }
.vl-recon__list { list-style: none; padding-left: 0; }
.vl-recon__list li { padding-left: 0; }
.vl-recon__list li::before { content: none; }
.vl-recon__list--num li::before { content: counter(recon) ". "; position: static; }

/* ============================================================
   CLIENT REVIEWS — clamp to 4 lines with a Read more toggle
   ============================================================ */
/*
 * No line clamp. It used to cut reviews at four lines with a "Read more",
 * which worked only because the old set was short enough never to reach the
 * limit. The August 2026 batch does, and the clamp then failed in the ugliest
 * possible way: the element carries `flex: 1` so it is a flex item, which
 * blockifies its display -- computed `flow-root`, not `-webkit-box` -- and a
 * clamp that cannot limit the box still emits its ellipsis. The result was
 * "arrived in perfect... condition.", with the ellipsis stranded mid-sentence
 * and the rest of the line printed after it.
 *
 * These reviews run four to seven lines, so they are simply shown in full.
 * Nothing overflows, so the "Read more" button stays hidden on its own -- the
 * script only reveals it when scrollHeight exceeds clientHeight.
 */
.vl-review__text {
  margin-bottom: .5rem;
}
.vl-review__more {
  background: none; border: 0; padding: 0; cursor: pointer;
  color: #21609D; font-family: var(--vl-font); font-size: .8rem;
  text-decoration: underline; margin: 0 0 .8rem; align-self: flex-start;
}
.vl-review__more[hidden] { display: none; }
.vl-review__more:hover { color: #239B97; }

/* ============================================================
   HEADER EDGE — apply the gutter once, not to nested containers
   Both .site-container and .site-header-row-container-inner were
   getting it, so the wordmark sat at 102px instead of 51px.
   ============================================================ */
#masthead .site-header-row-container-inner,
.site-header .site-header-row-container-inner { padding-inline: 0 !important; }

/* ============================================================
   HERO COPY SPACING
   Kadence's `.entry-content p` (specificity 0,1,1) was beating the
   single-class rules here, so the margins never took. Scoped with
   the section class to win cleanly rather than with !important.
   ============================================================ */
.vl-hero .vl-hero__sub   { margin: 0 0 .6rem; }
.vl-hero .vl-hero__trust { margin: 0 0 1.8rem; }
.vl-hero .vl-hero__title { margin: 0 0 .9rem; }
.entry-content .vl-hero__sub   { margin: 0 0 .6rem; }
.entry-content .vl-hero__trust { margin: 0 0 1.8rem; }

/* ============================================================
   RECONSTITUTION LISTS — centring, properly scoped
   Kadence styles `.entry-content ul/ol` at specificity (0,1,1),
   which outranked the single-class rules here: margin-inline:auto
   never applied and a 34px padding-left stayed, pushing the lists
   219px left of centre while the paragraphs sat centred.
   Scoping under .vl-recon gives (0,2,0) and wins cleanly.
   ============================================================ */
.vl-recon .vl-recon__list,
.entry-content .vl-recon .vl-recon__list {
  max-width: 900px;
  margin-left: auto;
  margin-right: auto;
  padding-left: 0;
  padding-right: 0;
  list-style: none;
  text-align: center;
}
.vl-recon .vl-recon__list li,
.entry-content .vl-recon .vl-recon__list li {
  margin-left: 0;
  margin-right: 0;
  padding-left: 0;
  text-align: center;
  list-style: none;
}
.vl-recon .vl-recon__list li::marker { content: none; }
.vl-recon .vl-recon__list--num { counter-reset: recon; }
.vl-recon .vl-recon__list--num li { counter-increment: recon; }
.vl-recon .vl-recon__list--num li::before { content: counter(recon) ". "; }

/* ============================================================
   BUTTON HOVER — brand blue
   ============================================================ */
.vl-btn--solid:hover,
.vl-btn--ghost:hover,
.wp-block-button__link:hover,
.woocommerce a.button:hover,
.woocommerce button.button:hover,
.woocommerce .single_add_to_cart_button:hover,
.vl-give__submit:hover,
.vl-contact__submit .vl-btn:hover {
  background: #21609D !important;
  border-color: #21609D !important;
  color: #FFFFFF !important;
}
.vl-tile__act:hover { background: #21609D; }

/* ============================================================
   BANNER — cut off at the content edge, not the gutter
   ============================================================ */
.vl-marquee {
  -webkit-mask-image: linear-gradient(to right,
    transparent 0, transparent var(--vl-edge),
    #000 var(--vl-edge), #000 calc(100% - var(--vl-edge)),
    transparent calc(100% - var(--vl-edge)), transparent 100%);
  mask-image: linear-gradient(to right,
    transparent 0, transparent var(--vl-edge),
    #000 var(--vl-edge), #000 calc(100% - var(--vl-edge)),
    transparent calc(100% - var(--vl-edge)), transparent 100%);
}

/* ============================================================
   CALCULATOR PAGE
   ============================================================ */
.vl-calc { margin-top: 5rem; }
/* Compliance note reads as contract small print, in a narrow column. */
.vl-disclaimer--calculator {
  max-width: 640px;
  margin: 3rem auto 0;
  padding-top: 1.2rem;
  border-top: 1px solid var(--vl-rule);
  text-align: left;
}
.vl-disclaimer--calculator p {
  font-size: .78rem !important;
  line-height: 1.55 !important;
  color: #777 !important;
  margin: 0 0 .7rem !important;
  text-align: left !important;
}

/* ============================================================
   SINGLE PRODUCT — premium measure, no zoom
   ============================================================ */
.woocommerce div.product {
  max-width: none;
  padding-inline: var(--vl-edge);
  gap: 4rem;
  align-items: start;
}
.woocommerce div.product div.summary { max-width: 560px; }
.woocommerce div.product .woocommerce-product-details__short-description,
.woocommerce div.product .woocommerce-tabs .panel,
.woocommerce div.product .vl-disclaimer { max-width: 760px; }
.woocommerce div.product .woocommerce-tabs,
.woocommerce div.product .vl-disclaimer { padding-inline: 0; }
/* Remove the magnifier / lightbox trigger. */
.woocommerce div.product .woocommerce-product-gallery__trigger,
.woocommerce-product-gallery__trigger { display: none !important; }
.woocommerce div.product div.images img { cursor: default; }

/* ============================================================
   HOME — products sit lower under the heading
   ============================================================ */
.vl-section--tint { padding-bottom: 1.5rem; }
.vl-bestsellers { margin-top: 1.5rem; }

/* ============================================================
   REVIEWS — name closer to the text
   ============================================================ */
.vl-review__body { padding-bottom: .9rem; }
.vl-review__text { margin-bottom: .35rem; }
.vl-review__more { margin: 0 0 .5rem; }
.vl-review__name { margin: 0 0 .3rem; }
.vl-review__stars { margin: 0; }

/* ============================================================
   HOMEPAGE FOLD
   The trust bar ends at 664px against a 900px viewport, so the
   Best Sellers heading was peeking above the fold. Pushing the
   rest of the page down leaves the hero and trust bar as the
   whole first screen.
   ============================================================ */
.vl-section--tint { padding-top: 15rem; }
@media (max-height: 800px) { .vl-section--tint { padding-top: 9rem; } }
@media (max-width: 860px)  { .vl-section--tint { padding-top: 4rem; } }

/* ============================================================
   WHAT ARE PEPTIDES
   The section inherits .vl-section's 51px side padding, which was
   holding the graphic off the right edge. Zero it on that side only.
   ============================================================ */
.vl-peptides { padding-right: 0 !important; }
.vl-peptides__inner { padding-right: 0 !important; }
.vl-peptides__art { padding-right: 0 !important; margin-right: 0 !important; }
.vl-peptides__art img { margin-right: 0; display: block; }
/* Copy sits lower, clear of the headline. */
.vl-peptides .vl-section__title { margin-bottom: 2.5rem; }
.vl-peptides__copy { padding-top: 2.5rem; }

/* ============================================================
   CLIENT REVIEWS — smaller again
   ============================================================ */
.vl-review { flex-basis: clamp(180px, 17%, 240px); }
.vl-review__body { padding: .8rem .9rem .9rem; }
.vl-review__text { font-size: .8rem; line-height: 1.45; }
.vl-review__name { font-size: .72rem; }
.vl-review__stars { font-size: .85rem; }
.vl-review__more { font-size: .74rem; }
@media (max-width: 860px) { .vl-review { flex-basis: 72%; } }

/* ============================================================
   PRODUCT ROW — columns follow the item count
   A four-stack row was rendering into a six-column grid, leaving
   508px of dead space on the right.
   ============================================================ */
.vl-bestsellers--3 { grid-template-columns: repeat(3, 1fr); }
.vl-bestsellers--4 { grid-template-columns: repeat(4, 1fr); }
.vl-bestsellers--5 { grid-template-columns: repeat(5, 1fr); }
.vl-bestsellers--6 { grid-template-columns: repeat(6, 1fr); }
@supports (grid-template-rows: subgrid) {
  .vl-bestsellers--3, .vl-bestsellers--4,
  .vl-bestsellers--5, .vl-bestsellers--6 { grid-template-rows: auto auto auto auto; }
}
@media (max-width: 1200px) {
  .vl-bestsellers--4, .vl-bestsellers--5, .vl-bestsellers--6 { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 640px) {
  .vl-bestsellers--3, .vl-bestsellers--4,
  .vl-bestsellers--5, .vl-bestsellers--6 { grid-template-columns: repeat(2, 1fr); }
}

/* ============================================================
   HERO — smaller title, tighter buttons sitting lower
   ============================================================ */
.vl-hero__title { font-size: clamp(1.9rem, 1rem + 2.4vw, 3.08rem); }   /* 8% down */
.vl-hero__actions { margin-top: 1.6rem; gap: .9rem; }
.vl-hero__actions .vl-btn { min-width: 0; padding: 15px 22px; }

/* ============================================================
   GIVEAWAY — mark centred
   ============================================================ */
.vl-give__mark { display: flex; justify-content: center; }
.vl-give__mark img { margin-inline: auto; display: block; }

/* ============================================================
   PEPTIDES + STACKS — smaller headline, more air under the header
   ============================================================ */
.vl-pagehead--plain .vl-pagehead__title { font-size: clamp(1.75rem, 1.15rem + 2vw, 2.85rem); }
.vl-pagehead { padding-top: 3.4rem; }
.vl-shopintro { margin-top: 1.1rem; }
.vl-stackspage { padding-top: 1.1rem; }

/* ============================================================
   QUANTITY STEPPERS — visible on every product card from load
   ============================================================ */
.vl-qty {
  display: inline-flex; align-items: center; gap: .35rem;
  border: 1px solid var(--vl-rule); border-radius: 999px;
  padding: .18rem .3rem; background: #fff;
}
.vl-qty__step {
  width: 24px; height: 24px; border: 0; background: none; cursor: pointer;
  color: #233642; font-size: 1rem; line-height: 1; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .15s ease, color .15s ease;
}
.vl-qty__step:hover { background: #21609D; color: #fff; }
.vl-qty__num {
  width: 34px; border: 0; background: none; text-align: center;
  font-family: var(--vl-font); font-size: .9rem; color: #233642;
  -moz-appearance: textfield;
}
.vl-qty__num::-webkit-outer-spin-button,
.vl-qty__num::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.vl-qty__num:focus { outline: none; }

.vl-tile__actions { flex-wrap: wrap; gap: .5rem; }
.woocommerce ul.products li.product .vl-qty { margin: 0 auto .6rem; }

/* ============================================================
   CATALOGUE CARDS — same hover behaviour as the homepage tiles
   ============================================================ */
.woocommerce ul.products li.product img { transition: transform .28s cubic-bezier(.22,.61,.36,1); }
.woocommerce ul.products li.product:hover img { transform: scale(1.04); }
.woocommerce ul.products li.product a.button,
.woocommerce ul.products li.product .button {
  transition: transform .22s cubic-bezier(.22,.61,.36,1), background-color .22s ease, box-shadow .22s ease;
}
.woocommerce ul.products li.product a.button:hover,
.woocommerce ul.products li.product .button:hover {
  transform: scale(1.045);
  box-shadow: 0 6px 18px rgba(0,0,0,.13);
  background: #21609D !important;
  color: #fff !important;
}
@media (prefers-reduced-motion: reduce) {
  .woocommerce ul.products li.product img,
  .woocommerce ul.products li.product a.button { transition: none; }
  .woocommerce ul.products li.product:hover img,
  .woocommerce ul.products li.product a.button:hover { transform: none; }
}

/* ============================================================
   SINGLE PRODUCT — description aligned to the same edge
   ============================================================ */
.woocommerce div.product .woocommerce-tabs,
.woocommerce div.product .woocommerce-tabs .panel,
.woocommerce div.product .vl-disclaimer,
.woocommerce div.product .related,
.woocommerce div.product .up-sells { padding-inline: 0 !important; margin-inline: 0 !important; }
.woocommerce div.product .woocommerce-tabs ul.tabs { padding-left: 0 !important; margin-left: 0 !important; }

/* ============================================================
   TRUST BAR — 10% lower
   ============================================================ */
.vl-trustbar { margin-top: 2.4rem; }

/* ============================================================
   TYPOGRAPHY WEIGHT — lighter headlines read more premium
   ============================================================ */
.vl-hero__title,
.vl-section__title,
.vl-pagehead__title,
.vl-whystack__title,
.vl-give__headline,
.vl-give__trust,
.vl-give__enter,
.vl-give__eyebrow { font-weight: 600; }
.vl-hero__sub { font-weight: 600; }
.vl-tile__name a,
.woocommerce ul.products li.product .woocommerce-loop-product__title { font-weight: 600; }
.vl-trustbar { font-weight: 600; }
.vl-why__item h3 { font-weight: 600; }


/* Product container keeps the shared edge. */
.woocommerce div.product.entry-content-wrap,
.woocommerce div.product { padding-inline: var(--vl-edge) !important; }

/* ============================================================
   NO TEXT MOVEMENT ON HOVER
   Kadence's `woo-archive-action-on-hover` translates the whole
   text block up by 2rem when a card is hovered, which reads as the
   copy jumping. Only the image should respond.
   ============================================================ */
.woocommerce ul.products.woo-archive-action-on-hover li.product:hover .entry-content-wrap,
.woocommerce ul.products.woo-archive-action-on-hover li.product:focus-within .entry-content-wrap,
.wc-block-grid__product.woo-archive-action-on-hover:hover .entry-content-wrap,
.wc-block-grid__product.woo-archive-action-on-hover:focus-within .entry-content-wrap {
  transform: none !important;
}
.woocommerce ul.products li.product .woocommerce-loop-product__title,
.woocommerce ul.products li.product .vl-loop__area,
.woocommerce ul.products li.product .vl-loop__alias,
.woocommerce ul.products li.product .price,
.vl-tile__text, .vl-tile__price {
  transition: none;
  transform: none !important;
}

/* ============================================================
   PRODUCT IMAGES — 10% larger so the labels are legible
   ============================================================ */
.woocommerce ul.products li.product img,
.woocommerce ul.products li.product .attachment-woocommerce_thumbnail { height: 286px !important; }
.vl-tile__img img { height: 253px; }



/* ============================================================
   SCROLL REVEAL
   Driven by keyframe animations, not plain declarations. Something
   in the archive stack was winning the cascade on `opacity` for
   li.product — a bare declaration computed as 1 even with
   !important, while the same rule worked on a plain element.
   Animations sit above normal declarations in the cascade, so the
   hold and the fade are both expressed as animations and cannot be
   overridden the same way.

   JS is the only gate: it adds .vl-reveal, and refuses to under
   reduced motion, so nothing is ever left invisible without a way back.
   ============================================================ */
@keyframes vl-hold { from, to { opacity: 0; transform: translateY(26px); } }
@keyframes vl-fade {
  from { opacity: 0; transform: translateY(26px); }
  to   { opacity: 1; transform: none; }
}
.vl-reveal { animation: vl-hold 1ms linear forwards; }
.vl-reveal.is-in {
  animation: vl-fade .75s cubic-bezier(.22,.61,.36,1) var(--vl-reveal-delay, 0ms) both;
}

/* ============================================================
   HOMEPAGE — banner and everything under it, 8% lower
   ============================================================ */
.vl-marquee { margin-top: 1.5rem; }
.home .vl-hero { padding-top: 3.4rem; }

/* ============================================================
   HEADING WEIGHT — lighter again
   ============================================================ */
.vl-hero__title, .vl-section__title, .vl-pagehead__title,
.vl-whystack__title, .vl-give__headline, .vl-give__trust,
.vl-give__enter, .vl-give__eyebrow, .vl-hero__sub,
.vl-trustbar, .vl-why__item h3, .vl-policy__head,
.vl-tile__name a,
.woocommerce ul.products li.product .woocommerce-loop-product__title,
.woocommerce div.product .product_title { font-weight: 500; }

/* ============================================================
   HEADER — larger nav, more air beneath it on every page
   ============================================================ */
.site-header .header-navigation .menu-item > a { font-size: 1.1rem; }
.vl-pagehead { padding-top: 3.75rem; }
.home .vl-hero { padding-top: 3.75rem; }

/* ============================================================
   NO QUANTITY STEPPER ON LISTINGS
   Steppers belong on the individual product page only; on a grid
   they read as clutter.
   ============================================================ */
.vl-tile .vl-qty,
.woocommerce ul.products li.product .vl-qty { display: none !important; }

/* ============================================================
   HOMEPAGE SECTION RHYTHM
   ============================================================ */
.vl-peptides { padding-top: 2.4rem; }            /* "What Are Peptides?" 7% lower */
.vl-resources { margin-top: 1.2rem; padding-top: 3.2rem; }   /* black panel 10% higher */

/* ============================================================
   CALCULATOR — a further 5% clear of the intro copy
   ============================================================ */
.vl-calc { margin-top: 6.5rem; }

/* ============================================================
   CATALOGUE ACTIONS — icon pair, matching the homepage tiles
   ============================================================ */
.vl-loop__actions {
  display: flex; justify-content: center; gap: .55rem; margin-top: .5rem;
}
.vl-loop__actions .vl-tile__act.is-disabled {
  background: #9BA7AE; pointer-events: none;
}
/* Kadence's own loop button and any hover-revealed action bar. */
.woocommerce ul.products li.product .button,
.woocommerce ul.products li.product a.add_to_cart_button,
.woocommerce ul.products li.product .kadence-woo-loop-action,
.woocommerce ul.products li.product .woo-archive-btn-text .button { display: none !important; }
.woocommerce ul.products li.product .vl-loop__actions .vl-tile__act { display: inline-flex !important; }

/* ============================================================
   HERO — title raised, mark pulled further in
   ============================================================ */
.home .vl-hero { padding-top: 3.55rem; }
.vl-hero__inner { align-items: start; }
.vl-hero__copy { padding-top: 0; }
.vl-hero__art { padding-right: 25%; align-self: start; }

/* ============================================================
   FIRST-ORDER PROMO POPUP
   ============================================================ */
/* Locking the page hides the scrollbar, and without a reserved gutter the
   content behind the veil jumps sideways by its width at the exact moment the
   panel appears -- read as a snap, no matter how soft the panel's own motion.
   Reserving the gutter permanently means nothing moves when it is taken away. */
html { scrollbar-gutter: stable; }
html.vl-promo-open, html.vl-promo-open body { overflow: hidden; }
.vl-promo {
  position: fixed; inset: 0; z-index: 99000;
  display: flex; align-items: center; justify-content: center;
  background: rgba(35, 54, 66, .6); backdrop-filter: blur(3px);
  padding: 1.25rem;
}
.vl-promo[hidden] { display: none; }

/* ============================================================
   PROMO — arrives, rather than appears
   The panel used to snap in at full opacity the instant the timer fired,
   which reads as an interruption. The veil fades and the panel rises a
   little behind it, so it resolves out of the page instead of landing on it.

   Driven by `:not([hidden])` and `animation-fill-mode: both` rather than a
   JS-added class. A class would mean a frame where the element is visible
   and un-animated -- and worse, if the class ever failed to land, a
   full-screen `inset: 0` veil sitting at opacity 0 over the page, invisible
   and swallowing every click. Keying off `hidden` means the only two states
   are "gone" and "animating in".
   ============================================================ */
@keyframes vl-promo-veil {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes vl-promo-rise {
  from { opacity: 0; transform: translateY(28px) scale(.97); }
  to   { opacity: 1; transform: none; }
}

.vl-promo:not([hidden]) {
  animation: vl-promo-veil .42s ease-out both;
}
.vl-promo:not([hidden]) .vl-promo__panel {
  /* Trails the veil so the two are not one flat move.
     An ease-out with a long tail: most of the distance is covered early and
     the last few pixels take their time, which is what reads as "settling"
     rather than "arriving". The earlier .42s on a gentler curve still landed
     hard because it was over almost as soon as the eye found it. */
  animation: vl-promo-rise .64s cubic-bezier(.16, 1, .3, 1) 90ms both;
}

/* Leaving is quicker than arriving -- a slow exit reads as the thing
   refusing to go. Matches the 190ms the script waits before hiding. */
.vl-promo.is-out { animation: vl-promo-veil .19s ease reverse both; }
.vl-promo.is-out .vl-promo__panel { animation: vl-promo-rise .19s ease reverse both; }

@media (prefers-reduced-motion: reduce) {
  .vl-promo:not([hidden]),
  .vl-promo:not([hidden]) .vl-promo__panel,
  .vl-promo.is-out,
  .vl-promo.is-out .vl-promo__panel { animation: none; }
}
.vl-promo__panel {
  position: relative; background: #fff; width: 100%; max-width: 620px;
  padding: 3.2rem 3rem 3rem; text-align: center;
  box-shadow: 0 26px 70px rgba(0,0,0,.28);
  max-height: 92vh; overflow-y: auto;
}
.vl-promo__close {
  position: absolute; top: .7rem; right: .9rem;
  background: none; border: 0; font-size: 1.7rem; line-height: 1;
  color: #9AA1A8; cursor: pointer; padding: .2rem .4rem;
}
.vl-promo__close:hover { color: #233642; }
.vl-promo__title {
  color: #239B97; font-weight: 500; letter-spacing: -0.02em; line-height: 1.1;
  font-size: clamp(1.9rem, 1.2rem + 2.4vw, 3rem); margin: 0 0 1.1rem;
}
.vl-promo__body {
  color: #233642; font-size: 1.02rem; line-height: 1.55;
  margin: 0 auto 1.8rem; max-width: 460px;
}
.vl-promo__form { display: flex; justify-content: center; flex-wrap: wrap; gap: 0; }
.vl-promo__form.is-done { display: none; }
.vl-promo__form input[type="email"] {
  flex: 1 1 280px; min-width: 0;
  font-family: var(--vl-font); font-size: 1rem; color: #233642;
  border: 1px solid var(--vl-rule); background: #fff;
  padding: 1.05rem 1.15rem; border-radius: 0;
}
.vl-promo__form input[type="email"]:focus { outline: none; border-color: #239B97; }
.vl-promo__submit {
  flex: 0 0 auto; background: #000; color: #fff; border: 0; border-radius: 0;
  font-family: var(--vl-font); text-transform: uppercase; letter-spacing: .06em;
  font-size: .84rem; padding: 1.05rem 1.6rem; cursor: pointer;
  transition: transform .2s cubic-bezier(.22,.61,.36,1), background .2s ease;
}
.vl-promo__submit:hover { background: #21609D; transform: scale(1.03); }
.vl-promo__confirm {
  background: #FDF3F2; border-left: 3px solid #C0392B;
  padding: .85rem 1rem; margin: 0 0 1.2rem; font-size: .95rem; text-align: left;
}
/* Welcome state */
.vl-promo__welcome { margin-top: .4rem; }
.vl-promo__welcometitle {
  color: #21609D; font-weight: 500; font-size: 1.3rem; margin: 0 0 .6rem;
}
.vl-promo__welcome p { color: #233642; font-size: 1rem; line-height: 1.55; margin: 0 0 .8rem; }
.vl-promo__code {
  font-family: var(--vl-font); font-weight: 500; letter-spacing: .18em;
  font-size: 1.8rem; color: #239B97;
  border: 1px dashed #239B97; padding: .9rem 1rem; margin: 0 0 1rem !important;
  background: #F6F7F6;
}
.vl-promo__copy {
  background: #000; color: #fff; border: 0; cursor: pointer;
  font-family: var(--vl-font); text-transform: uppercase; letter-spacing: .06em;
  font-size: .78rem; padding: .8rem 1.5rem;
  transition: background .2s ease, transform .2s cubic-bezier(.22,.61,.36,1);
}
.vl-promo__copy:hover { background: #21609D; transform: scale(1.03); }
.vl-promo__small { color: #777; font-size: .84rem; margin-top: .9rem !important; }
@media (max-width: 640px) {
  .vl-promo__panel { padding: 2.6rem 1.4rem 2rem; }
  .vl-promo__form input[type="email"] { flex-basis: 100%; }
  .vl-promo__submit { width: 100%; }
}

/* ============================================================
   BREATHING ROOM — +8%
   Applied to each contributing value rather than one wrapper, so
   the increase carries through consistently:
     page header padding   3.75rem -> 4.05rem   (60 -> 65px)
     homepage banner top   1.50rem -> 1.62rem   (24 -> 26px)
     homepage hero top     3.55rem -> 3.83rem   (56.8 -> 61.3px)
     hero button offset    1.60rem -> 1.73rem   (25.6 -> 27.6px)
   ============================================================ */
.vl-pagehead { padding-top: 4.05rem; }
.vl-marquee { margin-top: 1.62rem; }
.home .vl-hero { padding-top: 3.83rem; }
.vl-hero__actions { margin-top: 1.73rem; }
/* Inner-page copy keeps pace with its heading. */
.vl-shopintro { margin-top: 1.19rem; }
.vl-stackspage { padding-top: 1.19rem; }

/* Hero mark +10% (378px -> 416px). */
.vl-hero__art img { max-width: min(100%, 416px); }

/* Hero mark: the container's 25% right padding was the real constraint, not
   max-width, so the image never grew. Widen the column and size the image. */
.vl-hero__art { padding-right: 18%; }
.vl-hero__art img { width: 416px; max-width: 100%; }

/* The trust-to-buttons gap comes from the trust line's own margin-bottom, which
   outranks the buttons' margin-top. +8%: 1.8rem -> 1.94rem. */
.vl-hero .vl-hero__trust,
.entry-content .vl-hero__trust { margin: 0 0 1.94rem; }

/* ============================================================
   CATALOGUE CART CONTROL
   Permanently visible — never revealed on hover — and no magnifier.
   Swaps to a stepper once the product is in the cart.
   ============================================================ */
.vl-loop__actions { display: none !important; }   /* the old icon pair */

.vl-cartctl { display: flex; justify-content: center; margin-top: .55rem; min-height: 42px; }
/* Deliberately inert. This used to dim the control and set pointer-events:none
   for the length of every cart request, which made each tap look like a stall
   and swallowed rapid presses. The optimistic repaint in inc/shop-archive.php
   is the feedback now, so the control stays live and tappable throughout. */
.vl-cartctl.is-busy { opacity: 1; pointer-events: auto; }

.vl-cartctl__btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .45rem;
  background: #314D5D; color: #fff; border: 0; border-radius: 999px;
  font-family: var(--vl-font); font-size: .82rem; letter-spacing: .01em;
  padding: .68rem 1.25rem; cursor: pointer; text-decoration: none;
  transition: background .2s ease, transform .2s cubic-bezier(.22,.61,.36,1);
}
.vl-cartctl__btn:hover { background: #21609D; color: #fff; transform: scale(1.04); }
.vl-cartctl__btn.is-disabled { background: #9BA7AE; pointer-events: none; }

.vl-cartctl__stepper {
  display: none; align-items: center; gap: .2rem;
  border: 1px solid #314D5D; border-radius: 999px; padding: .18rem .25rem; background: #fff;
}
.vl-cartctl__step {
  width: 30px; height: 30px; border: 0; background: none; cursor: pointer;
  color: #314D5D; font-size: 1.05rem; line-height: 1; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .15s ease, color .15s ease;
}
.vl-cartctl__step:hover { background: #21609D; color: #fff; }
.vl-cartctl__qty {
  min-width: 26px; text-align: center; font-size: .92rem; color: #233642;
  font-variant-numeric: tabular-nums;
}
/* Once in the cart, the button gives way to the stepper. */
.vl-cartctl.is-active .vl-cartctl__btn { display: none; }
.vl-cartctl.is-active .vl-cartctl__stepper { display: inline-flex; }

/* Nothing on a card may be hover-revealed. */
.woocommerce ul.products li.product .vl-cartctl,
.woocommerce ul.products li.product .vl-cartctl__btn { opacity: 1 !important; visibility: visible !important; }

/* ============================================================
   FOOTER MARKS — 20% smaller
   ============================================================ */
.vl-footer__logo    { max-width: 99px; }   /* 124 -> 99 */
.vl-footer__interac { max-width: 65px; }   /* 81 -> 65 */

/* Footer marks: single-class rules were losing to broader image styles, so these
   are scoped under the footer to win on specificity. 20% down from 124/81. */
.vl-footer .vl-footer__brand img,
.vl-footer img.vl-footer__logo { max-width: 99px !important; width: 99px; height: auto; }
.vl-footer .vl-footer__pay img,
.vl-footer img.vl-footer__interac { max-width: 65px !important; width: 65px; height: auto; }

/* ============================================================
   HERO COPY
   Title down 8% (49.28px -> ~45.3px) and the whole copy column
   dropped 23px, which is 5% of the hero's height. Applied to the
   copy column only, so the logo stays where it is.
   ============================================================ */
.vl-hero__title { font-size: clamp(1.75rem, .92rem + 2.21vw, 2.83rem); }
.vl-hero__copy { padding-top: 23px; }

/* ============================================================
   SINGLE PRODUCT — image left, everything else in a right column
   ============================================================ */
/* The breadcrumb was rendering at 64px while the sticky header ends at 80px,
   so it slid underneath and showed as debris. */
.woocommerce .kadence-breadcrumbs,
.woocommerce-page .kadence-breadcrumbs {
  position: relative; z-index: 1;
  padding-top: 1.6rem; padding-inline: var(--vl-edge);
  margin: 0;
}
/* Any zoom overlay the script still manages to create is inert. */
img.zoomImg { display: none !important; }

.woocommerce div.product { grid-template-columns: minmax(280px, 34%) 1fr; gap: 3rem; }
.woocommerce div.product div.images {
  justify-self: start; padding-right: 0; margin: 0; max-width: 100%;
}
.woocommerce div.product div.images img { max-width: 340px; margin: 0; }
.woocommerce div.product div.summary { max-width: 820px; padding-top: 0; }
.woocommerce div.product .product_title { margin-top: 0; }

/* Description sits under the title block, in the same column. */
.vl-single__desc { margin-top: 2rem; padding-top: 1.6rem; border-top: 1px solid var(--vl-rule); }
.vl-single__desctitle {
  color: #233642; font-weight: 500; letter-spacing: -0.02em;
  font-size: 1.5rem; margin: 0 0 1rem;
}
.vl-single__desc p { font-size: 1rem; line-height: 1.65; margin: 0 0 1rem; }
.vl-single__desc strong { font-weight: 600; color: #233642; }
.woocommerce div.product .woocommerce-tabs { display: none; }

@media (max-width: 900px) {
  .woocommerce div.product { grid-template-columns: 1fr; }
  .woocommerce div.product div.images { justify-self: center; }
  .woocommerce div.product div.images img { margin-inline: auto; }
}

/* ============================================================
   CATALOGUE — cart control must not be clipped by the next row
   ============================================================ */
.woocommerce ul.products li.product {
  overflow: visible !important;
  position: relative;
  z-index: 1;
  padding-bottom: 1.2rem;
}
.woocommerce ul.products li.product:hover { z-index: 3; }
.woocommerce ul.products li.product .entry-content-wrap { overflow: visible !important; }
.vl-cartctl { position: relative; z-index: 2; }
.woocommerce ul.products { row-gap: 3rem !important; align-items: start; }

/* The cart control was extending 13px past its card's box. Overflow is visible
   so it was not being clipped, but the card should contain its own content —
   otherwise any future overflow rule re-introduces the clipping. */
.woocommerce ul.products li.product { padding-bottom: 2.4rem; }
.woocommerce ul.products li.product .vl-cartctl { margin-bottom: 0; }

/* ============================================================
   BANNER — soft fade at both ends instead of a hard cut
   ============================================================ */
.vl-marquee {
  --vl-fade: 90px;
  -webkit-mask-image: linear-gradient(to right,
    transparent 0,
    transparent var(--vl-edge),
    #000 calc(var(--vl-edge) + var(--vl-fade)),
    #000 calc(100% - var(--vl-edge) - var(--vl-fade)),
    transparent calc(100% - var(--vl-edge)),
    transparent 100%);
  mask-image: linear-gradient(to right,
    transparent 0,
    transparent var(--vl-edge),
    #000 calc(var(--vl-edge) + var(--vl-fade)),
    #000 calc(100% - var(--vl-edge) - var(--vl-fade)),
    transparent calc(100% - var(--vl-edge)),
    transparent 100%);
}
@media (max-width: 700px) { .vl-marquee { --vl-fade: 48px; } }

/* ============================================================
   CATALOGUE HOVER — smooth fade, no snap
   Kadence toggles its action area abruptly; giving the control and
   the card's own transitions a shared easing removes the flicker.
   ============================================================ */
.vl-cartctl,
.vl-cartctl__btn,
.vl-cartctl__stepper,
.woocommerce ul.products li.product .entry-content-wrap,
.woocommerce ul.products li.product .woocommerce-loop-product__link {
  transition: opacity .28s cubic-bezier(.22,.61,.36,1),
              transform .28s cubic-bezier(.22,.61,.36,1),
              background-color .22s ease !important;
}
.woocommerce ul.products li.product,
.woocommerce ul.products li.product * { backface-visibility: hidden; }
.woocommerce ul.products li.product img { transition: transform .32s cubic-bezier(.22,.61,.36,1) !important; }
/* Nothing on the card may pop in or out. */
.woocommerce ul.products li.product .vl-cartctl { opacity: 1 !important; visibility: visible !important; }

/* ============================================================
   HOMEPAGE SPACING
   ============================================================ */
.vl-section__cta { padding-top: 2.6rem; }          /* CTA button 8% lower */
.vl-why__grid { margin-top: 4.2rem; }              /* "Why Vistara" items 5% lower */
.vl-resources { margin-top: 0; padding-top: 2.9rem; }  /* black panel 5% higher */
.vl-peptides { padding-bottom: .5rem; }

/* ============================================================
   PEPTIDES PAGE — intro wraps at mid-page, content 5% lower
   ============================================================ */
.vl-shopintro p { max-width: 70%; }
.vl-shopintro { margin-top: 1.85rem; }
.vl-marquee { margin-top: 1.85rem; }

/* ============================================================
   FOOTER MARKS — +15%
   ============================================================ */
.vl-footer .vl-footer__brand img,
.vl-footer img.vl-footer__logo { max-width: 114px !important; width: 114px; }
.vl-footer .vl-footer__pay img,
.vl-footer img.vl-footer__interac { max-width: 75px !important; width: 75px; }

/* "Why Vistara Labs" items sit 5% lower. Scoped under the section because the
   bare .vl-why__grid rule was being outranked and resolving to 25px. */
.vl-why .vl-why__grid,
.entry-content .vl-why__grid { margin-top: 4.2rem; }

/* ============================================================
   HOVER FLICKER — remove Kadence's competing action layer
   `.product-action-wrap` animates `bottom` and `opacity` on hover,
   sitting in the same space as our always-visible cart control.
   Two elements fighting over one slot is what read as choppy.
   ============================================================ */
.product-action-wrap,
.woocommerce ul.products li.product .product-action-wrap,
.wc-block-grid__product .product-action-wrap { display: none !important; }

/* Nothing on a card animates `bottom` — only composited properties. */
.woocommerce ul.products li.product .entry-content-wrap { transform: none !important; bottom: auto !important; }

/* The card's own fade, matching the page-load reveal easing. */
.woocommerce ul.products li.product .vl-cartctl__btn,
.woocommerce ul.products li.product .vl-cartctl__stepper {
  transition: background-color .25s ease, transform .25s cubic-bezier(.22,.61,.36,1) !important;
}
.woocommerce ul.products li.product .woocommerce-loop-product__link img {
  transition: transform .45s cubic-bezier(.22,.61,.36,1), opacity .45s cubic-bezier(.22,.61,.36,1) !important;
}

/* ============================================================
   HOMEPAGE — everything from Best Sellers down, 5% higher
   ============================================================ */
.vl-section--tint { padding-top: 14.25rem; }

/* ============================================================
   PRODUCT GALLERY — visible regardless of script state
   WooCommerce ships the gallery at opacity 0 and reveals it with JS.
   If that script ever fails the product image simply vanishes, so it
   is forced visible here as a safety net.
   ============================================================ */
.woocommerce div.product div.images.woocommerce-product-gallery {
  opacity: 1 !important;
  transition: none !important;
}
.woocommerce div.product div.images .woocommerce-product-gallery__wrapper,
.woocommerce div.product div.images .woocommerce-product-gallery__image { opacity: 1 !important; }

/* Larger vial on the left, text column on the right. */
.woocommerce div.product { grid-template-columns: minmax(320px, 40%) 1fr; gap: 3.5rem; }
.woocommerce div.product div.images img {
  max-width: 100%; width: 100%; max-height: 620px; object-fit: contain; object-position: top center;
}
@media (max-width: 900px) {
  .woocommerce div.product div.images img { max-width: 360px; margin-inline: auto; }
}

/* ============================================================
   PRODUCT LAYOUT — explicit column widths
   The grid holds more items than the two columns (breadcrumb,
   gallery, summary, description), so auto-placement was sizing the
   gallery to its content at 257px rather than the intended share.
   Both columns are pinned directly instead.
   ============================================================ */
.woocommerce div.product.entry {
  display: grid !important;
  grid-template-columns: 42% 1fr !important;
  column-gap: 3.5rem;
  align-items: start;
}
.woocommerce div.product .woocommerce-product-gallery {
  grid-column: 1;
  width: 100% !important;
  max-width: 100% !important;
}
.woocommerce div.product div.summary {
  grid-column: 2;
  width: 100%;
  max-width: 100%;
}
/* Anything else in the grid spans the full width beneath. */
.woocommerce div.product > .kadence-breadcrumbs,
.woocommerce div.product > nav,
.woocommerce div.product .woocommerce-tabs,
.woocommerce div.product .related,
.woocommerce div.product .vl-disclaimer { grid-column: 1 / -1; }

.woocommerce div.product .woocommerce-product-gallery__wrapper,
.woocommerce div.product .woocommerce-product-gallery__image { width: 100% !important; max-width: 100% !important; }
.woocommerce div.product div.images img {
  width: 100% !important; max-width: 100% !important;
  height: auto !important; max-height: none !important;
  object-fit: contain;
}
@media (max-width: 900px) {
  .woocommerce div.product.entry { grid-template-columns: 1fr !important; }
  .woocommerce div.product .woocommerce-product-gallery,
  .woocommerce div.product div.summary { grid-column: 1; }
  .woocommerce div.product div.images img { max-width: 360px !important; margin-inline: auto; }
}

/* ============================================================
   GIVEAWAY — vials centred, "Drop your email" matched to the
   prize lines rather than towering over them
   ============================================================ */
.vl-give__vials { display: flex; justify-content: center; }
.vl-give__vials img { margin-inline: auto; display: block; }
.vl-give__enter {
  font-size: clamp(1.2rem, .95rem + 1.1vw, 1.95rem);
  margin-top: 2rem;
}

/* ============================================================
   SINGLE PRODUCT — colour and alignment
   ============================================================ */
/* Research area picks up the teal used for the hero subtitle. */
.woocommerce div.product .woocommerce-product-details__short-description,
.woocommerce div.product .woocommerce-product-details__short-description p {
  color: #239B97;
  font-weight: 500;
  font-size: 1.05rem;
}
/* Breadcrumb in the hero blue. */
.woocommerce .kadence-breadcrumbs,
.woocommerce .kadence-breadcrumbs a,
.woocommerce .kadence-breadcrumbs span,
.woocommerce-page .kadence-breadcrumbs,
.woocommerce-page .kadence-breadcrumbs a {
  color: #21609D !important;
}
.woocommerce .kadence-breadcrumbs a:hover { color: #239B97 !important; }

/* The product photography carries whitespace above the vial, so the title is
   dropped to meet where the vial actually starts rather than the image box. */
/* Measured: the vial's first opaque pixel lands 131px below the image box top. */
.woocommerce div.product div.summary { padding-top: 131px; }
@media (max-width: 900px) { .woocommerce div.product div.summary { padding-top: 0; } }

/* Kadence's hover layer stays hidden, but our control now renders outside it. */
.vl-cartctl { display: flex !important; }

/* ============================================================
   PRODUCT PAGE — vial follows the reader, image 10% smaller
   ============================================================ */
.woocommerce div.product .woocommerce-product-gallery {
  position: sticky;
  top: 96px;                 /* clears the sticky header */
  align-self: start;
}
.woocommerce div.product div.images img { max-width: 90% !important; }
@media (max-width: 900px) {
  .woocommerce div.product .woocommerce-product-gallery { position: static; }
  .woocommerce div.product div.images img { max-width: 324px !important; }
}
/* Catalogue vials 10% down as well. */
.woocommerce ul.products li.product img,
.woocommerce ul.products li.product .attachment-woocommerce_thumbnail { height: 257px !important; }

/* ============================================================
   PRODUCT GALLERY — sticky, and sized explicitly
   An earlier rule pins .woocommerce-product-gallery to
   position: relative, so the sticky declaration needs to outrank it.
   The image is sized in px rather than % because the % was resolving
   against a container that had already been narrowed.
   ============================================================ */
.woocommerce div.product.entry .woocommerce-product-gallery,
.woocommerce div.product .woocommerce-product-gallery.woocommerce-product-gallery--with-images {
  position: -webkit-sticky !important;
  position: sticky !important;
  top: 96px !important;
  align-self: start !important;
}
.woocommerce div.product.entry div.images img,
.woocommerce div.product div.images img.wp-post-image {
  width: 506px !important;      /* 562 - 10% */
  max-width: 100% !important;
  height: auto !important;
}
@media (max-width: 900px) {
  .woocommerce div.product.entry .woocommerce-product-gallery,
  .woocommerce div.product .woocommerce-product-gallery.woocommerce-product-gallery--with-images {
    position: static !important;
  }
  .woocommerce div.product.entry div.images img,
  .woocommerce div.product div.images img.wp-post-image { width: 324px !important; margin-inline: auto; }
}

/* Sticky needs room to travel. With `align-self: start` the gallery's grid item
   collapses to the image height, so there is no track to stick within — the
   element scrolls away like a static one. Stretching the item to the row height
   gives it the full column to move down. */
.woocommerce div.product.entry .woocommerce-product-gallery,
.woocommerce div.product .woocommerce-product-gallery.woocommerce-product-gallery--with-images {
  align-self: stretch !important;
}
.woocommerce div.product.entry { align-items: stretch !important; }
@media (max-width: 900px) {
  .woocommerce div.product.entry .woocommerce-product-gallery,
  .woocommerce div.product .woocommerce-product-gallery.woocommerce-product-gallery--with-images {
    align-self: start !important;
  }
}

/* ============================================================
   STICKY GALLERY — correct nesting
   The grid item must stretch to the row height (so there is a track
   to travel in) while the element that actually sticks must be its
   own natural height. Making the gallery both stretch AND sticky
   cancelled itself out. So: gallery stretches, the inner wrapper sticks.
   ============================================================ */
.woocommerce div.product.entry .woocommerce-product-gallery,
.woocommerce div.product .woocommerce-product-gallery.woocommerce-product-gallery--with-images {
  position: relative !important;
  align-self: stretch !important;
  top: auto !important;
}
.woocommerce div.product .woocommerce-product-gallery__wrapper {
  position: -webkit-sticky;
  position: sticky;
  top: 96px;
}
@media (max-width: 900px) {
  .woocommerce div.product .woocommerce-product-gallery__wrapper { position: static; }
}

/* ============================================================
   RESTORE STICKY
   .site.wp-site-blocks carries `overflow: clip`, which silently
   disables position:sticky for every descendant — the gallery was
   correctly sticky and simply had no effect.
   Horizontal containment is kept (the marquee track is wider than
   the viewport); only the vertical axis is opened back up. `clip`
   is the one overflow value the spec allows to pair with `visible`.
   ============================================================ */
#wrapper.site.wp-site-blocks,
.site.wp-site-blocks {
  overflow-x: clip;
  overflow-y: visible;
}

/* ============================================================
   BATCH — 21 August
   ============================================================ */

/* ---- Stacks: one image footprint per tile -------------------
   The four source renders were normalised offline onto a common
   1254x1254 canvas, the vial row scaled to a single height and
   sitting on a shared baseline. Repair & Recovery had been a
   1448x1086 landscape file, so `contain` was scaling it up by a
   third and standing its vials taller than every neighbour.
   The box below now only has to hold them.
   ------------------------------------------------------------ */
.vl-stacks .vl-tile__img img {
  width: 100%;
  height: 253px;
  aspect-ratio: auto;
  object-fit: contain;
  object-position: center bottom;
}

/* "Why Stack?" and its copy sit 8% lower. */
.vl-whystack { padding-top: 2.7rem; }

/* ---- Catalogue: cart control is hover-revealed --------------
   Reversing the earlier "nothing on a card may be hover-revealed"
   rules, which were written to fight Kadence hiding the control
   and now have to be beaten in turn.

   Revealed on the whole tile rather than the image alone: the
   control renders *below* the photo, so an image-only trigger
   would hide it the instant the pointer travelled towards it and
   leave the button impossible to click.
   ------------------------------------------------------------ */
.woocommerce ul.products li.product .vl-cartctl,
.woocommerce ul.products li.product .vl-cartctl__btn {
  opacity: 0 !important;
  visibility: visible !important;
  transition: opacity .5s cubic-bezier(.22,.61,.36,1) !important;
}
.woocommerce ul.products li.product .vl-cartctl { pointer-events: none; }
.woocommerce ul.products li.product:hover .vl-cartctl,
.woocommerce ul.products li.product:focus-within .vl-cartctl,
.woocommerce ul.products li.product:hover .vl-cartctl__btn,
.woocommerce ul.products li.product:focus-within .vl-cartctl__btn {
  opacity: 1 !important;
}
.woocommerce ul.products li.product:hover .vl-cartctl,
.woocommerce ul.products li.product:focus-within .vl-cartctl { pointer-events: auto; }

/* A stepper already holding a quantity is state, not an affordance —
   it stays put so nobody loses sight of what is in their cart. */
.woocommerce ul.products li.product .vl-cartctl.is-active {
  opacity: 1 !important;
  pointer-events: auto;
}

/* Touch has no hover, so the control would never appear at all. */
/* ============================================================
   REVIEWS CAROUSEL -- do not steal a vertical scroll
   Putting a finger on a review photo mid-flick used to drag the carousel
   sideways instead of carrying on down the page. Two causes, both fixed here:

   1. `scroll-snap-type: x mandatory` forces the container to rest on a snap
      point, so the few pixels of horizontal drift in a fast vertical swipe
      were enough to fire a snap animation. `proximity` only snaps when the
      gesture ends near a snap point, so incidental drift is ignored while a
      deliberate swipe still snaps.
   2. `touch-action: pan-y` on the photo -- and only the photo -- means a touch
      starting there can drive nothing but vertical panning, so it can never be
      claimed by the carousel. The photo is the part of the card a thumb lands
      on, and it is what the owner reported. Touches beginning on the card body
      still allow horizontal panning, so the carousel stays swipeable.

   Deliberately CSS-only: pinning scrollLeft from a scroll handler would run
   after the carousel had already moved and would read as a jump-and-snap-back.
   `touch-action` is enforced by the compositor before any movement happens.
   ============================================================ */
@media (max-width: 640px) {
  .vl-reviews__track {
    scroll-snap-type: x proximity;
    overscroll-behavior-x: contain;
    /* touch-action is deliberately NOT locked to pan-y here any more.
       It was added as belt-and-braces alongside the real fix, and the cost was
       the carousel could no longer be swiped with a thumb at all. The genuine
       cause of a finger getting stuck on a photo was the native image drag --
       draggable="false" plus -webkit-user-drag below -- and that is handled at
       source. Left to itself the browser direction-locks the gesture: a mostly
       vertical drag scrolls the page, a mostly horizontal one moves the
       carousel, which is what a thumb expects. */
  }
  .vl-review__photo {
    -webkit-user-drag: none;          /* the actual culprit -- see note above */
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    user-select: none;
  }
}

/* ============================================================
   TRUST BAR -- last claim on its own line, mobile only
   At 375 the three claims wrap into a ragged block wherever the words happen
   to run out. Dropping the shipping cut-off to its own line gives two clean
   rows: the two purity claims together, the timing claim beneath them. The
   separator before it would otherwise be left dangling at the end of row one,
   so it is removed with the break. Desktop keeps all three inline.
   The spans come from vistara_trustbar_markup() in inc/display.php.
   ============================================================ */
@media (max-width: 640px) {
  .vl-trustbar__sep--last  { display: none; }
  .vl-trustbar__item--last { display: block; }
}

/* ============================================================
   MOBILE-ONLY typography, hero entrance and CTA breathing room
   ============================================================ */

/* Body copy sits at regular, matching the live Squarespace site: its headings
   are bold but its running text is 400, and 500 here read heavier than the
   reference throughout. The weight contrast the owner wanted comes from the
   headings being 700 (below), not from thickening the body underneath them. */
@media (max-width: 640px) {
  html body { font-weight: 400; }
}

/* "Shop All Research Peptides" sat 14px below the last product image, which
   read as crowded. 5% of the 812px screen puts a normal 55px of air under the
   grid. Prefixed with `.home` to outrank the bare `.vl-section__cta` rule
   further down the file -- same specificity there, and it wins on order. */
@media (max-width: 640px) {
  .home .vl-section__cta { padding-top: 5.06rem; }        /* 40px -> 81px */
}

/* Hero logo emerges rather than simply fading. Three things move together over
   2.2s, which is what sells "out of the mist" instead of "an element appearing":

     blur 18px -> 0   the mark resolves out of fog; this is the whole effect,
                      and it is why the timing can be slow without feeling slow
     scale 1.06 -> 1  a barely-there settle back, so it reads as coming forward
                      through depth rather than sliding on a plane
     opacity 0 -> 1   carried on the same curve as the other two

   No translate any more: a rise reads as UI, a settle reads as atmosphere.
   The curve is deliberately not a snappy ease-out -- cubic-bezier(0.33, 0,
   0.15, 1) leaves gently, spends its middle unhurried, and takes a long time
   to arrive, which is the part that reads as expensive. The 140ms delay lets
   the page paint first so the logo emerges into a settled hero rather than
   racing it.

   Deliberately a single from/to pair: an intermediate keyframe would apply the
   easing curve twice, once per segment, and put a visible hitch mid-fade.

   Fill mode is `backwards`, not `both`: it holds opacity 0 before the first
   frame so there is no flash of the finished logo on a slow paint, but it does
   NOT hold the end state afterwards. That matters -- the end state is the
   element's natural state anyway, so if the animation is ever skipped, cut
   short, or overridden, the logo is simply visible rather than stranded at
   opacity 0. `both` would have risked an invisible logo. */
.vl-hero__art img {
  animation: vl-hero-emerge 2.2s cubic-bezier(0.33, 0, 0.15, 1) 140ms backwards;
  will-change: opacity, filter, transform;
}
@keyframes vl-hero-emerge {
  from { opacity: 0; filter: blur(18px); transform: scale(1.06); }
  to   { opacity: 1; filter: blur(0);    transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .vl-hero__art img { animation: none; }
}

@media (hover: none) {
  .woocommerce ul.products li.product .vl-cartctl,
  .woocommerce ul.products li.product .vl-cartctl__btn { opacity: 1 !important; }
  .woocommerce ul.products li.product .vl-cartctl { pointer-events: auto; }
}
@media (prefers-reduced-motion: reduce) {
  .woocommerce ul.products li.product .vl-cartctl,
  .woocommerce ul.products li.product .vl-cartctl__btn { transition: none !important; }
}

/* Catalogue rows sit 8% closer together (3rem -> 2.76rem). */
.woocommerce ul.products { row-gap: 2.76rem !important; }

/* The closing banner needs the same air the opening one has. */
.vl-marquee-foot { margin-top: 3.5rem; }

/* ---- Homepage rhythm ---------------------------------------
   "Our Best Sellers" and everything under it rises 13%; the trust
   bar was leaving a canyon before the first row of product.
   ------------------------------------------------------------ */
.vl-section--tint { padding-top: 12.4rem; }

/* ...and the shop-all button sits 8% closer to the row above (2.73 -> 2.51rem),
   on top of the ~50px the tiles gave back when the magnifier came out. */
.vl-section__cta { padding-top: 2.51rem; }

/* Resources band: content 7% lower, and the black panel itself taller. */
.vl-resources { padding: 4.28rem 5vw 6rem; }

/* Interac mark 5% larger, everywhere it appears. */
.vl-footer .vl-footer__pay img,
.vl-footer img.vl-footer__interac { max-width: 79px !important; width: 79px; }

/* ---- Peptide calculator ------------------------------------ */
/* The tool drops clear of the intro copy above it.
   Scoped under .entry-content deliberately. calculator.css is enqueued with
   home.css as a dependency, so it loads *after* it and its `margin: 0 auto`
   on .vl-calc beat every bare `.vl-calc { margin-top: ... }` written here —
   which is why the calculator kept sitting flush against the paragraph above
   no matter what the value was. (0,2,0) wins regardless of source order. */
.entry-content .vl-calc,
.entry-content-wrap .vl-calc { margin-top: 4.5rem; }

/* The compliance note is centred on the page rather than hanging off
   the left gutter. Its own text stays ranged left — three paragraphs
   of centred small print is harder to read, not more considered. */
.vl-calcpage__notice,
.entry-content .vl-disclaimer--calculator,
.vl-disclaimer--calculator {
  max-width: 640px;
  margin-left: auto !important;
  margin-right: auto !important;
  float: none !important;
}

/* ---- Contact: centred and held to a comfortable measure ---- */
.vl-contactpage { text-align: center; }
.vl-contactpage p {
  max-width: 620px;
  margin-left: auto;
  margin-right: auto;
  text-align: center;
}

/* ---- Checkout / cart gutters -------------------------------
   The checkout block is `alignwide` and was running edge to edge
   while the page's own H1 sat at the standard inset, so the first
   form field started 51px left of the title above it.
   ------------------------------------------------------------ */
.woocommerce-checkout .wp-block-woocommerce-checkout,
.woocommerce-cart .wp-block-woocommerce-cart,
.wp-block-woocommerce-checkout.alignwide,
.wp-block-woocommerce-cart.alignwide { padding-inline: var(--vl-edge); }

/* ---- Research Use Policy ----------------------------------- */
/* The appended section is a continuation, not a new block. */
.vl-policy--append { margin-top: 0; }

/* ============================================================
   PRODUCT DISCLAIMER ALIGNED TO THE FOOTER'S LEGAL COLUMN
   The two blocks of legal text used to sit on opposite sides of
   the page — this one hard against the left gutter, the footer's
   in the right-hand column directly below it. Reproducing the
   footer's exact grid (1fr 1fr 1.6fr, 3rem gap, same gutter and
   measure) lands them in one continuous column as you scroll.
   ============================================================ */
.woocommerce div.product .vl-disclaimer {
  max-width: none !important;
  padding-inline: 0 !important;
  display: grid;
  grid-template-columns: 1fr 1fr 1.6fr;
  gap: 3rem;
}
.woocommerce div.product .vl-disclaimer .vl-disclaimer__text {
  grid-column: 3;
  margin: 0;
  font-size: .86rem;   /* the footer's own size */
  line-height: 1.6;
}
/* The footer collapses to a single column here, so this must too. */
@media (max-width: 1024px) {
  .woocommerce div.product .vl-disclaimer { display: block; }
  .woocommerce div.product .vl-disclaimer .vl-disclaimer__text { grid-column: auto; }
}

/* The photo is inert — no pointer, no link affordance. */
.woocommerce div.product .woocommerce-product-gallery__image,
.woocommerce div.product .woocommerce-product-gallery img { cursor: default; }

/* Interac mark 10% larger again (79px -> 87px). */
.vl-footer .vl-footer__pay img,
.vl-footer img.vl-footer__interac { max-width: 87px !important; width: 87px; }

/* ============================================================
   ORDER CONFIRMATION
   The order-received view renders through the classic
   `.woocommerce` wrapper, which carries no gutter of its own —
   so the whole confirmation ran edge to edge while the page's
   own H1 sat at the standard inset.
   ============================================================ */
.woocommerce-order-received .woocommerce,
.woocommerce-order-received .woocommerce-order { padding-inline: var(--vl-edge); }
.woocommerce-order-received .woocommerce .woocommerce-order { padding-inline: 0; }

/* The payment table's inline styles carry this on the page as well as in the
   email; these are only here so a stray theme `table { width: 100% }` cannot
   stretch the label column and fling the values across the page again. */
.vl-payment-details { width: auto !important; border-collapse: collapse; }
.vl-payment-details td { border: 0; }
.vl-payment-details td:first-child { white-space: nowrap; width: 1%; }

/* ============================================================
   CATALOGUE ROW RHYTHM — 15% tighter
   The gap a reader actually sees between rows is the grid gap
   *plus* the ~52px the cart control still reserves while it is
   sitting at opacity 0 waiting for a hover. That reserve has to
   stay — collapsing it would make every tile jump on hover — so
   the reduction comes out of the grid gap instead.
   Measured: 100px of whitespace -> 85px.
   ============================================================ */
.woocommerce ul.products { row-gap: 1.82rem !important; }

/* ============================================================
   CALCULATOR PAGE — guidelines block drops clear of the tool
   The reconstitution guidelines and the compliance note that
   follows them were butting straight up against the calculator
   (0px between them; the section's own 1.6rem of padding was all
   that separated the two). Given the same 8%-of-viewport step the
   calculator itself takes above, so the page reads in one rhythm.
   ============================================================ */
.entry-content .vl-recon,
.entry-content-wrap .vl-recon { padding-top: 4.5rem; }

/* Homepage: "Our Best Sellers" and everything under it rises a
   further 8% (12.4rem -> 11.41rem). */
.vl-section--tint { padding-top: 11.41rem; }

/* ============================================================
   CATALOGUE ROWS — even heights, constant gap
   The gap between rows was never really the row-gap. `align-items:
   start` let every card keep its own height, so a card whose name
   or research area wrapped to an extra line set the row's height
   and every shorter card in that row trailed dead space beneath it.
   Measured 68-103px of ragged whitespace depending on which names
   wrapped at that viewport width.

   Stretching the cards makes each row one height, and `.price`
   already carries `margin-top:auto`, so price and cart settle onto
   a common baseline across the row exactly as they do on the
   homepage tiles. The gap is then just the row-gap, everywhere.
   ============================================================ */
.woocommerce ul.products { align-items: stretch !important; row-gap: 1rem !important; }
.woocommerce ul.products li.product { height: 100%; }

/* `.product-details` is Kadence's own block-level wrapper, so the
   `margin-top:auto` already on `.price` had nothing to push against and
   computed to 0.8px. Making the wrapper a column flexbox that fills the
   stretched card gives price and cart a common baseline across the row. */
.woocommerce ul.products li.product .product-details {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
}
.woocommerce ul.products li.product .price { margin-top: auto !important; }

/* ============================================================
   HOMEPAGE — hero, "What Are Peptides", Resources, footer seam
   Where a percentage is applied to a spacing value that is already
   tiny, the change is invisible (7% of a 27px margin is 2px), so
   those steps are taken against the viewport instead — the same
   8%-of-viewport measure used on the calculator page.
   ============================================================ */
/* Hero buttons drop clear of "Trusted by 2,000+ clients". */
.vl-hero__actions { margin-top: 3.75rem; }

/* "What Are Peptides" and its artwork sit 8% lower, off the back of
   the Why Vistara Labs block above. */
.vl-peptides { padding-top: 4.5rem; }

/* Resources heading rises 5% within its band (68.5px -> 65.1px). */
.vl-resources { padding-top: 4.07rem; }

/* The black Resources band runs straight into the footer.
   The 112px of white below it was not the band or the footer — it was
   `.entry-content-wrap` padding-bottom (32px) plus `.content-area`
   margin-bottom (80px). Scoped to the front page, which is the only
   template that ends on a full-bleed dark band. */
.home .entry-content-wrap { padding-bottom: 0 !important; }
.home .content-area { margin-bottom: 0 !important; }

/* ============================================================
   GIVEAWAY — mark aligned with Contact / Peptide Calculator
   That page renders its own header inside the shortcode rather than
   through vistara_page_head(), so it never picked up the
   .vl-pagehead--mark top padding every other marked page has.
   ============================================================ */
/* Measured against Contact: its mark sits 65px below the header, this one sat
   at 38px. 4.09rem closes the difference exactly. */
.vl-give { padding-top: 4.09rem; }

/* ============================================================
   VARIABLE PRODUCT — a dropdown the width of its content
   The variations table inherits `width:100%`, so a two-option Size
   select stretched most of the way across the page.
   ============================================================ */
.woocommerce div.product table.variations { width: auto; margin-bottom: 1rem; }
.woocommerce div.product table.variations td,
.woocommerce div.product table.variations th { padding: 0 0 .6rem; border: 0; }
.woocommerce div.product table.variations th.label { padding-right: 1.2rem; white-space: nowrap; width: 1%; }
.woocommerce div.product table.variations td.value { display: flex; align-items: center; gap: .9rem; }
.woocommerce div.product table.variations select {
  width: auto;
  min-width: 210px;
  max-width: 320px;
}
/* Add to cart stays on its own line beneath the selector. */
.woocommerce div.product .woocommerce-variation-add-to-cart { display: flex; align-items: center; gap: .6rem; }
@media (max-width: 640px) {
  .woocommerce div.product table.variations select { min-width: 0; width: 100%; max-width: none; }
  .woocommerce div.product table.variations td.value { flex-wrap: wrap; }
}

/* ============================================================
   TILE CART CONTROL (homepage + stacks)
   The tiles now render the catalogue's AJAX control instead of an
   add-to-cart link, so nothing navigates. Only the buyable state
   collapses to the round icon the tiles already use — "Sold Out"
   and "Choose Options" keep their readable pill, since neither
   fits in 44px.
   ============================================================ */
.vl-tile__actions { display: flex; justify-content: center; align-items: center; gap: .5rem; }
.vl-tile__actions .vl-cartctl { display: inline-flex; margin: 0; min-height: 0; }

.vl-tile__actions .vl-cartctl__btn[data-action="add"] {
  width: 44px; height: 44px; padding: 0; gap: 0; min-width: 0;
  border-radius: 999px; background: #314D5D; color: #fff;
  display: inline-flex; align-items: center; justify-content: center;
  transition: transform .22s cubic-bezier(.22,.61,.36,1), background-color .18s ease;
}
.vl-tile__actions .vl-cartctl__btn[data-action="add"]:hover {
  background: #21609D; color: #fff; transform: scale(1.12);
}
/* The label stays for screen readers; the icon carries it visually. */
.vl-tile__actions .vl-cartctl__btn[data-action="add"] span {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.vl-tile__actions .vl-cartctl__stepper {
  height: 44px; border-radius: 999px; background: #314D5D; color: #fff;
  align-items: center; padding: 0 .2rem;
}
.vl-tile__actions .vl-cartctl__step {
  width: 32px; height: 44px; background: none; border: 0; color: #fff;
  font-size: 1.15rem; line-height: 1; cursor: pointer;
}
.vl-tile__actions .vl-cartctl__step:hover { color: #8FD3D1; background: none; }
.vl-tile__actions .vl-cartctl__qty {
  min-width: 20px; text-align: center; font-weight: 600; font-size: .95rem; color: #fff;
}
@media (prefers-reduced-motion: reduce) {
  .vl-tile__actions .vl-cartctl__btn[data-action="add"] { transition: background-color .18s ease; }
  .vl-tile__actions .vl-cartctl__btn[data-action="add"]:hover { transform: none; }
}

/* Kadence styles form controls at a higher specificity than the block above,
   so the Size selector kept resolving to its content width. */
.woocommerce div.product form.cart table.variations td.value select,
.woocommerce div.product table.variations select#size,
.woocommerce div.product table.variations select {
  width: 260px !important;
  min-width: 0 !important;
  max-width: 100% !important;
}
@media (max-width: 640px) {
  .woocommerce div.product table.variations select { width: 100% !important; }
}

/* ============================================================
   CATALOGUE IMAGES — crop the vial cutouts' dead margin
   Every product cutout is a 1086x1448 canvas holding a vial that
   is only 455x987 of it: 252px of transparent padding above and
   ~210px below. Scaled into the 257px tile box that is 45px of
   empty space above each vial and 37px below — 82px per card that
   reads as gap between rows and between vial and name, and which
   no row-gap value could ever remove.

   The image keeps its size; the link box is cropped to the vial
   and the padding is pulled out of view. 257 - 45 - 37 = 175.
   ============================================================ */
.woocommerce ul.products li.product .woocommerce-loop-image-link,
.woocommerce ul.products li.product a.woocommerce-LoopProduct-link {
  display: block;
  height: 175px;
  overflow: hidden;
  position: relative;
}
.woocommerce ul.products li.product .woocommerce-loop-image-link img,
.woocommerce ul.products li.product a.woocommerce-LoopProduct-link img {
  margin-top: -45px;
  margin-bottom: -37px;
}

/* Same dead margin on the homepage and stacks tiles (300px box there). */
/* Sized against the image's real height (253px, not the 300px these numbers
   were first written for), which is why the vial caps were being shaved by 8px
   on the homepage row. 188px window at -36/-29 clears the vial at every tile
   width in use: 8px at 196px and 255px, 20px at 162px. */
.vl-tile__img { height: 188px; overflow: hidden; display: block; position: relative; }
.vl-tile__img img { margin-top: -36px; margin-bottom: -29px; }
/* The stack composites are already cropped to their vials, so they opt out. */
.vl-stacks .vl-tile__img { height: auto; overflow: visible; }
.vl-stacks .vl-tile__img img { margin-top: 0; margin-bottom: 0; }

/* Kadence pads `.product-details` 24px at the foot and leaves a further 38px
   below it inside the card. Together with the cart control's reserve that put
   123px between a price and the bottom of its card — the rest of what read as
   the gap between rows. */
.woocommerce ul.products li.product { padding-bottom: 0 !important; }
.woocommerce ul.products li.product .product-details { padding-bottom: 0 !important; }
.woocommerce ul.products li.product .vl-cartctl { margin-top: .4rem; }

/* ============================================================
   VARIABLE PRODUCT TILE — sizes open in place
   No "Choose Options" anywhere: the cart icon reveals the sizes,
   and each size becomes its own stepper once it is in the cart,
   so 10mg and 20mg of one peptide can be held side by side.
   ============================================================ */
.vl-cartctl--variable { flex-direction: column; align-items: center; gap: .4rem; }
.vl-cartctl__sizes { display: flex; flex-direction: column; gap: .3rem; align-items: center; }
.vl-cartctl__sizes[hidden] { display: none; }
.vl-cartctl__size { display: flex; align-items: center; gap: .35rem; }

.vl-cartctl__pick {
  border: 1px solid #314D5D; background: #fff; color: #314D5D;
  border-radius: 999px; padding: .3rem .85rem; font-size: .82rem; font-weight: 600;
  cursor: pointer; white-space: nowrap; line-height: 1.2;
  transition: background-color .18s ease, color .18s ease;
}
.vl-cartctl__pick:hover { background: #314D5D; color: #fff; }
.vl-cartctl__pick.is-disabled { border-color: #C3CCD2; color: #9BA7AE; cursor: default; }

/* The stepper for a size only exists once that size is in the cart. */
.vl-cartctl__sizestep { display: none; align-items: center; }
.vl-cartctl__size.is-active .vl-cartctl__sizestep { display: inline-flex; }
.vl-cartctl__size.is-active .vl-cartctl__pick { background: #314D5D; color: #fff; }

/* Sizes read as a stacked list on the tiles too. */
.vl-tile__actions .vl-cartctl--variable { flex-direction: column; }
.vl-tile__actions .vl-cartctl--variable .vl-cartctl__sizes { margin-top: .15rem; }

/* Stack tiles carry the à-la-carte total alongside the bundle price. */
.vl-tile__was {
  color: #9BA7AE;
  text-decoration: line-through;
  margin-right: .45rem;
  font-weight: 400;
}
.vl-tile__was .woocommerce-Price-amount { color: inherit; }

/* "Why Stack?" and its copy drop clear of the grid. 8% of the section's own
   2.7rem padding is 3px, which is invisible, so this takes the same
   8%-of-viewport step used elsewhere on the site. */
.vl-whystack { padding-top: 4.5rem; }

/* ============================================================
   SOLD OUT BADGE ON THE PRODUCT PAGE
   It now lives inside the gallery image rather than being
   positioned against the whole product row, where 50% of the
   full width landed it across the SKU and category links.
   ============================================================ */
.woocommerce div.product .woocommerce-product-gallery__image { position: relative; }
.woocommerce div.product .woocommerce-product-gallery__image .vl-soldout {
  top: 50%; left: 50%; transform: translate(-50%, -50%);
}
/* The old row-level placement is gone. */
.woocommerce div.product > .vl-soldout { display: none; }

/* ============================================================
   HOMEPAGE — Best Sellers rises a further 40%
   ============================================================ */
.vl-section--tint { padding-top: 6.85rem; }

/* Tiles use the catalogue's control exactly as the Peptides page
   does — the same pill, the same hover reveal, the same size
   picker — instead of the round icon they had before. */
.vl-tile__actions .vl-cartctl__btn[data-action="add"],
.vl-tile__actions .vl-cartctl__btn[data-action="options"] {
  width: auto; height: auto; border-radius: 999px;
  padding: .55rem 1.15rem; gap: .45rem; min-width: 0;
}
.vl-tile__actions .vl-cartctl__btn[data-action="add"] span,
.vl-tile__actions .vl-cartctl__btn[data-action="options"] span {
  position: static; width: auto; height: auto; margin: 0;
  overflow: visible; clip: auto; white-space: nowrap;
}
.vl-tile__actions .vl-cartctl__stepper { border-radius: 999px; height: auto; padding: .18rem .3rem; }
.vl-tile__actions { flex-wrap: wrap; }

/* ============================================================
   CATALOGUE — product rows drop clear of the filters
   ============================================================ */
.woocommerce ul.products { margin-top: 4.5rem !important; }

/* The discount-code panel reads as a field, not a disclosure link. */
.wc-block-components-totals-coupon { padding-block: .4rem; }
.wc-block-components-totals-coupon .wc-block-components-panel__button {
  font-weight: 600;
}

/* The stack pricing caveat, stated where the saving is claimed. */
.vl-stackspage__note {
  color: #6B7A83;
  font-size: .92rem !important;
  font-style: italic;
  margin-top: .4rem !important;
}

/* ============================================================
   MAPLE WATERMARK — more of the leaf below the fold of the block
   `.vl-why { overflow: hidden }` was cropping the lower half flat.
   The leaf is lifted and the section given room beneath it so the
   lobes and stem sit inside the visible area.
   ============================================================ */
/* The watermark is a 1254x1254 square, so at min(86%, 1080px) it wanted 1015px
   of height inside a section only 545px tall — the lower half was simply
   cropped off. Trimming the leaf and lengthening the section together brings
   the bottom lobes and stem back without the block turning into empty space. */
.vl-why { padding-bottom: 18rem; }
.vl-why::before { background-position: center 1%; background-size: min(64%, 780px) auto; }

/* The size list is a popover, not part of the card.
   In flow it made a card holding sizes taller than its neighbours, and with
   the cards stretched to a common height that pushed the prices in that row
   out of line and reopened the gap this page was fixed for. */
.vl-cartctl--variable { position: relative; }
.vl-cartctl__sizes {
  position: absolute;
  top: calc(100% + .35rem);
  left: 50%;
  transform: translateX(-50%);
  z-index: 6;
  background: #fff;
  border: 1px solid #E3E9EC;
  border-radius: 10px;
  padding: .55rem .6rem;
  box-shadow: 0 10px 26px rgba(35, 54, 66, .14);
}
/* A count on the trigger, so a closed list still shows what is held. */
.vl-cartctl__held {
  position: absolute; top: -6px; right: -6px; z-index: 7;
  min-width: 20px; height: 20px; padding: 0 5px;
  border-radius: 999px; background: #239B97; color: #fff;
  font-size: .72rem; font-weight: 700; line-height: 20px; text-align: center;
}
.vl-cartctl__held[hidden] { display: none; }

/* ============================================================
   MORE ABOUT PEPTIDES
   Rebuilt to the live Squarespace layout, measured at 1440px:
   headings centred and black, body copy ranged left, and each
   section a two-column split whose image sits left or right to
   match the original. The page had been carrying the raw scraped
   markup — nested empty divs with every attribute stripped — so
   none of it had structure to style in the first place.
   ============================================================ */
.vl-map { padding-top: 3.5rem; padding-bottom: 1rem; }
.vl-map:first-of-type { padding-top: 1.5rem; }

.vl-map__h2 {
  color: #000;
  font-weight: 700;
  letter-spacing: var(--vl-tracking, -0.02em);
  font-size: clamp(1.9rem, 1.2rem + 2.2vw, 3rem);
  line-height: 1.12;
  text-align: center;
  margin: 0 auto 2.2rem;
  max-width: 20ch;
}
.vl-map__h2--left { text-align: left; margin-left: 0; max-width: none; font-size: clamp(1.5rem, 1.1rem + 1.1vw, 2.08rem); }

.vl-map__h3 {
  color: #000;
  font-weight: 700;
  font-size: clamp(1.15rem, 1rem + .5vw, 1.45rem);
  line-height: 1.2;
  margin: 0 0 .6rem;
}

.vl-map p { color: #000; font-size: 1.02rem; line-height: 1.62; margin: 0 0 1.1rem; }
.vl-map__label { font-weight: 700; margin-bottom: .3rem !important; }

/* Copy left, artwork right — reversed where the original reversed it. */
.vl-map__split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 3.5rem;
  align-items: start;
}
.vl-map__split--slimart { grid-template-columns: 1.9fr .8fr; align-items: center; }
.vl-map__split--artfirst { align-items: start; }

.vl-map__art { margin: 0; }
.vl-map__art img { width: 100%; height: auto; display: block; }
.vl-map__art--slim img { max-width: 300px; margin-inline: auto; }

.vl-map__intro { max-width: 546px; }
.vl-map__lede {
  max-width: 780px;
  margin: -0.6rem auto 2.4rem !important;
  text-align: center;
  font-size: 1.18rem !important;
  line-height: 1.5 !important;
}

.vl-map__entry + .vl-map__entry { margin-top: 1.4rem; }
.vl-map__step + .vl-map__step { margin-top: 1.6rem; }

.vl-map__quad { display: grid; grid-template-columns: repeat(4, 1fr); gap: 2.6rem 2.4rem; }

/* Closing band: the wave artwork sits behind the copy and the button. */
.vl-map--cta { padding-bottom: 3.5rem; }
.vl-map__band { position: relative; display: grid; place-items: center; }
.vl-map__bandbg {
  width: 100%; max-width: 1024px; height: auto; display: block;
  grid-area: 1 / 1;
}
.vl-map__bandinner {
  grid-area: 1 / 1;
  text-align: center;
  padding: 1rem;
  max-width: 620px;
}
.vl-map__bandinner p { font-size: 1.18rem !important; margin-bottom: 1.4rem !important; }

@media (max-width: 1024px) {
  .vl-map__quad { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 860px) {
  .vl-map__split,
  .vl-map__split--slimart { grid-template-columns: 1fr; gap: 2rem; }
  .vl-map__split--artfirst .vl-map__art { order: -1; }
  .vl-map__quad { grid-template-columns: 1fr; }
  .vl-map__band { display: block; }
  .vl-map__bandbg { position: absolute; inset: 0; height: 100%; object-fit: cover; opacity: .5; }
  .vl-map__bandinner { position: relative; margin-inline: auto; padding-block: 2.4rem; }
}

/* The page header follows the live page's centring, but takes the site's blue
   rather than the live page's black. */
.page-id-68 .vl-pagehead--plain { text-align: center; }
.page-id-68 .vl-pagehead__title { color: #21609D; }

/* Section headings in the same blue as the homepage hero.
   Deliberately not the "Small molecules. Essential Signals." lead-in, nor the
   SIGNAL / RECEPTOR / RESPONSE steps — those stay black, as listed. */
.vl-map__h2:not(.vl-map__h2--left) { color: #21609D; }
.vl-map__field .vl-map__h3 { color: #21609D; }

/* Kadence zeroes the inline margins on headings inside .entry-content, so the
   `margin: 0 auto` on the centred headings never took and they sat at the left
   gutter with a 20ch measure. */
.vl-map .vl-map__h2 { margin-left: auto !important; margin-right: auto !important; }
.vl-map .vl-map__h2--left { margin-left: 0 !important; margin-right: 0 !important; }

/* ============================================================
   SOLD OUT — one treatment everywhere
   The badge takes the same blue as the homepage headline rather
   than the near-black it had, and on a product page it sits in
   the summary where the quantity and Add to cart normally are.
   ============================================================ */
.vl-soldout { background: #21609D; }

.vl-soldout--single {
  position: static;
  transform: none;
  display: inline-block;
  margin: .2rem 0 1.1rem;
  color: #fff;
  border-radius: 2px;
}
/* The gallery no longer carries one. */
.woocommerce div.product .woocommerce-product-gallery .vl-soldout { display: none; }

/* ============================================================
   HOMEPAGE + STACKS TILES — hover reveal, as on the catalogue
   Nothing sits under a product until the pointer is on it; then
   the controls fade in over half a second. Touch has no hover,
   so there they stay visible.
   ============================================================ */
.vl-tile .vl-tile__actions {
  opacity: 0;
  transition: opacity .5s cubic-bezier(.22,.61,.36,1);
  pointer-events: none;
}
.vl-tile:hover .vl-tile__actions,
.vl-tile:focus-within .vl-tile__actions {
  opacity: 1;
  pointer-events: auto;
}
/* A tile already holding stock keeps its stepper on show — that is state,
   not an affordance, and hiding it loses track of what is in the cart. */
.vl-tile .vl-cartctl.is-active,
.vl-tile:has(.vl-cartctl.is-active) .vl-tile__actions,
.vl-tile:has(.vl-cartctl__held:not([hidden])) .vl-tile__actions {
  opacity: 1;
  pointer-events: auto;
}
@media (hover: none) {
  .vl-tile .vl-tile__actions { opacity: 1; pointer-events: auto; }
}
@media (prefers-reduced-motion: reduce) {
  .vl-tile .vl-tile__actions { transition: none; }
}

/* ============================================================
   HOMEPAGE — Why Vistara Labs items drop 12%
   ============================================================ */
.vl-why .vl-why__grid { margin-top: 4.48rem; }

/* Resources: more air between the heading and the three columns.
   Scoped under the band — the element also carries .vl-section__title, whose
   own margin was winning and holding the gap at 25px. */
.vl-resources .vl-resources__title { margin-bottom: 4.6rem; }

/* ============================================================
   HOMEPAGE — mid-page banner centred in its gap
   Measured: 93px of air above it (the shop-all section's own
   64px of bottom padding plus the banner's margin) against 26px
   below, so it sat almost against the Client Reviews heading.
   Trimming the padding above and adding margin below splits the
   188px between the button and the heading evenly, which also
   lifts the banner itself.
   The header banner is outside .entry-content, so scoping here
   reaches only the mid-page one.
   ============================================================ */
.home .vl-section__cta { padding-bottom: 2rem; }
.home .entry-content .vl-marquee { margin-bottom: 2.1rem; }

/* ============================================================
   "WHAT ARE PEPTIDES" ARTWORK — no hard crop along the top
   The molecule render carries its own pale background, which met
   the page as a straight horizontal edge. A mask fades the top
   into white so it reads as artwork rather than a pasted square.
   ============================================================ */
.vl-peptides__art img {
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 16%);
          mask-image: linear-gradient(to bottom, transparent 0, #000 16%);
}

/* ============================================================
   FOOTER — Interac mark 8% larger and 5% lower
   ============================================================ */
.vl-footer .vl-footer__pay img,
.vl-footer img.vl-footer__interac { max-width: 94px !important; width: 94px; }
.vl-footer .vl-footer__pay { padding-top: 2.06rem; }

/* ============================================================
   SIZE POPOVER — above the row beneath it
   The popover lives inside .vl-cartctl, which carries its own
   z-index and therefore opens a stacking context; nothing inside
   it can paint above a card that comes later in the grid, which
   is why the size list slid behind the vial on the next row. The
   card itself is what has to be raised, so the JS flags the card
   and this lifts it while a list is open.
   ============================================================ */
.woocommerce ul.products li.product,
.vl-bestsellers .vl-tile { position: relative; }

.woocommerce ul.products li.product.vl-sizes-open,
.vl-bestsellers .vl-tile.vl-sizes-open { z-index: 40; }

/* Belt and braces for browsers that support :has() — covers a list opened
   by anything other than the toggle handler. */
.woocommerce ul.products li.product:has(.vl-cartctl__sizes:not([hidden])),
.vl-bestsellers .vl-tile:has(.vl-cartctl__sizes:not([hidden])) { z-index: 40; }

/* ============================================================
   PRODUCT PAGE — footer rises now the disclaimer is gone
   ============================================================ */
.woocommerce div.product .vl-disclaimer { display: none; }
.single-product .entry-content-wrap { padding-bottom: 0 !important; }
.single-product .content-area { margin-bottom: 2.5rem !important; }

/* An open size list pins its control visible.
   The control is hover-revealed, and the popover hangs below the card — so
   reaching for a size took the pointer off the card, dropped :hover, and faded
   the list out mid-reach, leaving it behind the vial on the next row. While a
   list is open the card is no longer relying on hover at all. */
.woocommerce ul.products li.product.vl-sizes-open .vl-cartctl,
.woocommerce ul.products li.product.vl-sizes-open .vl-cartctl__btn,
.vl-tile.vl-sizes-open .vl-tile__actions,
.vl-tile.vl-sizes-open .vl-cartctl,
.vl-tile.vl-sizes-open .vl-cartctl__btn {
  opacity: 1 !important;
  pointer-events: auto !important;
}

/* The list always opens downward, as it does on the Peptides page, and paints
   over whatever is beneath it.
   Getting it over the Shop All button took more than a z-index on the tile: the
   scroll-reveal animation gives every tile a transform and an opacity, so each
   one is already its own stacking context at `z-index: auto`. That put it in the
   same painting layer as the button — which is positioned and later in the DOM,
   so the button won. Giving the open tile a real z-index settles the order, and
   the shop-all block is pinned below it. */
.home .vl-bestsellers .vl-tile.vl-sizes-open,
.home .vl-bestsellers .vl-tile:has(.vl-cartctl__sizes:not([hidden])) {
  z-index: 60 !important;
}
.home .vl-section__cta { position: relative; z-index: 1; }

/* ============================================================
   TOUCH — the buy controls are never hover-gated
   On a touch device there is no hover, so a control revealed by
   :hover is simply unreachable. An earlier @media (hover: none)
   block was being outranked, which left the Add to Cart button on
   the catalogue at opacity 0 while still accepting taps: an
   invisible button that adds to the cart is worse than a missing
   one. This block is last in the file and prefixed with `html
   body` so nothing can outrank it.
   ============================================================ */
@media (hover: none) {
  html body .woocommerce ul.products li.product .vl-cartctl,
  html body .woocommerce ul.products li.product .vl-cartctl__btn,
  html body .vl-tile .vl-tile__actions,
  html body .vl-tile .vl-cartctl,
  html body .vl-tile .vl-cartctl__btn {
    opacity: 1 !important;
    visibility: visible !important;
    pointer-events: auto !important;
    transition: none !important;
  }
}

/* ============================================================
   MOBILE LAYOUT
   The responsive column rules were written against `.columns-5`,
   which Kadence does not emit — it marks the list `grid-cols
   grid-lg-col-5` — so the catalogue never stepped down and simply
   collapsed to one product per row, making /peptides/ 9240px tall
   on a phone. These target the list itself.
   ============================================================ */
@media (max-width: 900px) {
  .woocommerce ul.products,
  .woocommerce-page ul.products {
    grid-template-columns: repeat(3, 1fr) !important;
    padding-inline: var(--vl-edge) !important;
    gap: 1.6rem 1.1rem !important;
  }
}
@media (max-width: 640px) {
  /* A 51px gutter is 27% of a 375px screen. */
  :root { --vl-edge: 1.15rem; }

  .woocommerce ul.products,
  .woocommerce-page ul.products {
    grid-template-columns: repeat(2, 1fr) !important;
    gap: 1.4rem .9rem !important;
  }

  /* Tap targets reach 44px. */
  .vl-cartctl__btn,
  .vl-cartctl__pick { min-height: 44px; padding-inline: 1rem; }
  .vl-cartctl__step { min-width: 44px; min-height: 44px; }
  .vl-cartctl { min-height: 44px; }

  /* The section rhythm is desktop-sized; halve the biggest steps. */
  .vl-section--tint { padding-top: 6rem; }        /* 3.5rem + 40px */
  .vl-peptides { padding-top: 2.25rem; }
  /* Claims sit 8% of the section height (52px of 648) lower. The same 52px
     comes back out of the bottom padding so the section height is unchanged
     and the maple watermark, positioned as a percentage of that height,
     stays exactly where it was. */
  .vl-why { padding-bottom: calc(7rem - 52px); }
  .vl-resources { padding: 2.75rem var(--vl-edge) 3rem; }
  .vl-resources .vl-resources__title { margin-bottom: 2.2rem; }
  .woocommerce ul.products { margin-top: 2rem !important; }
  .entry-content .vl-calc,
  .entry-content-wrap .vl-calc { margin-top: 2.25rem; }
  .entry-content .vl-recon,
  .entry-content-wrap .vl-recon { padding-top: 2.25rem; }
  /* Products 18px closer to the top -- 15% of the 118px gap above them. The
     rest of that gap is the promo marquee, which is a shared component, so
     only the grid's own margin moves. The 18px goes back on above "Why
     Stack?" so that block stays exactly where the owner wants it. */
  .vl-stacks { margin-top: 6px; }                         /* was 24px */
  .vl-whystack { padding-top: 3.375rem; }                 /* 36px -> 54px */
}

@media (max-width: 900px) {
  /* Kadence gives `.entry.content-bg` a -16px inline margin so the card can
     bleed past its wrapper. At desktop widths there is room for that; at 375px
     it made every section 407px wide starting at x=-16, so both edges of the
     page were being clipped by the wrapper's overflow rather than scrolling. */
  .entry.content-bg { margin-inline: 0 !important; }

  /* The hero's own breakpoint was being outranked, leaving a 212px copy column
     beside a 109px logo on a phone. */
  .vl-hero__inner {
    grid-template-columns: 1fr !important;
    gap: 1.75rem !important;
    text-align: center;
  }
  .vl-hero__art { order: -1; }
  .vl-hero__art img { max-width: min(58%, 220px); }
  .vl-hero__actions { justify-content: center; margin-top: 1.75rem; }
  .vl-hero__actions .vl-btn { min-width: 0; flex: 1 1 auto; }

  /* Two-up artwork/copy splits stack. */
  .vl-peptides__inner { grid-template-columns: 1fr !important; padding-inline: var(--vl-edge) !important; }
  .vl-peptides__art img { max-width: min(60%, 240px); margin-inline: auto; }
}

/* ============================================================
   GRID TRACKS MUST BE ALLOWED TO SHRINK
   `1fr` is `minmax(auto, 1fr)`, and that `auto` floor is the
   widest item's min-content. On a phone a name like "CJC-1295 +
   Ipamorelin 10mg (no DAC)" pushed one bestseller column to 254px
   against its neighbour's 129px and hung the row off the screen.
   minmax(0, 1fr) lets the track go narrower than its contents.
   ============================================================ */
/* Each grid keeps its own desktop column count — the point is only to swap the
   `1fr` tracks for `minmax(0, 1fr)` so they may shrink below their contents. */
.woocommerce ul.products,
.woocommerce-page ul.products { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.vl-bestsellers--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.vl-bestsellers--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.vl-bestsellers--5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.vl-bestsellers--6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }
.vl-why__grid      { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.vl-map__quad      { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 1200px) {
  .vl-bestsellers--4, .vl-bestsellers--5, .vl-bestsellers--6 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
@media (max-width: 900px) {
  .woocommerce ul.products,
  .woocommerce-page ul.products { grid-template-columns: repeat(3, minmax(0, 1fr)) !important; }
  .vl-bestsellers { grid-template-columns: repeat(3, minmax(0, 1fr)) !important; }
}
@media (max-width: 640px) {
  .woocommerce ul.products,
  .woocommerce-page ul.products,
  .vl-bestsellers { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }
  .vl-bestsellers { gap: 1.4rem .9rem !important; }
  .vl-why__grid,
  .vl-resources__grid,
  .vl-map__quad { grid-template-columns: minmax(0, 1fr) !important; }
  /* Long names wrap instead of setting the track width. */
  .vl-tile__name, .woocommerce-loop-product__title { overflow-wrap: anywhere; }
}

/* The tile is itself a grid — it uses subgrid rows to line prices up across a
   row — and its single *column* was left at `auto`, so it sized to the widest
   thing inside it. On a phone that made a 162px tile lay out a 254px column and
   spill its image and text past the screen edge. */
.vl-tile { grid-template-columns: minmax(0, 1fr); }
.vl-tile > * { min-width: 0; }

@media (max-width: 640px) {
  /* The 44px tap targets are right for a full-width control but too wide for a
     162px tile once the stepper is showing, so the control is held to the tile
     and the steps take what is left. */
  .vl-tile__actions,
  .vl-tile .vl-cartctl,
  .vl-tile .vl-cartctl__stepper,
  .vl-tile .vl-cartctl__btn { max-width: 100%; }
  .vl-tile .vl-cartctl__btn[data-action="add"],
  .vl-tile .vl-cartctl__btn[data-action="options"] {
    padding-inline: .55rem; font-size: .82rem; gap: .3rem;
  }
  .vl-tile .vl-cartctl__step { min-width: 38px; width: 38px; }
  .vl-tile .vl-cartctl__stepper { padding-inline: 0; }
  .vl-tile .vl-cartctl__sizes { max-width: calc(100vw - 2rem); }
}

@media (max-width: 640px) {
  /* Product page buy controls reach a 44px touch target and use the column. */
  .woocommerce div.product table.variations select {
    width: 100% !important; min-height: 44px; font-size: 16px;
  }
  .woocommerce div.product table.variations td.value { flex-wrap: wrap; }
  .woocommerce div.product .single_add_to_cart_button,
  .woocommerce div.product .quantity input.qty {
    min-height: 46px; font-size: 16px;
  }
  .woocommerce div.product .single_add_to_cart_button { flex: 1 1 auto; }
  .woocommerce div.product form.cart { display: flex; flex-wrap: wrap; gap: .6rem; align-items: stretch; }
  .woocommerce div.product .quantity { flex: 0 0 88px; }

  /* Header toggle is a real target. */
  .site-header .menu-toggle-open { min-width: 44px; min-height: 44px; }

  /* Checkout and cart fields: 16px stops iOS zooming on focus. */
  .wc-block-components-text-input input,
  .wc-block-components-select select,
  .vl-contact input, .vl-contact textarea, .vl-contact select { font-size: 16px !important; }
}

@media (max-width: 640px) {
  /* The contact form was running edge to edge while every other block on the
     page sits at the 18px gutter. */
  .vl-contact { padding-inline: var(--vl-edge); }
}

/* ============================================================
   FLOATING CART — filled while it holds something, pulse on add
   It is the only checkout affordance on the page, so it has to
   read as "you have items here" rather than sitting quietly in
   the same white as when it is empty.
   ============================================================ */
.vl-cart.has-items .vl-cart__toggle {
  background: #21609D;
  border-color: #21609D;
  color: #fff;
  box-shadow: 0 8px 26px rgba(33, 96, 157, .34);
}
.vl-cart.has-items .vl-cart__toggle:hover { background: #1B5185; }

.vl-cart__toggle.is-pulsing { animation: vl-cart-pulse .62s cubic-bezier(.22,.61,.36,1); }
@keyframes vl-cart-pulse {
  0%   { transform: scale(1);    box-shadow: 0 8px 26px rgba(33,96,157,.34); }
  35%  { transform: scale(1.16); box-shadow: 0 0 0 12px rgba(33,96,157,.16); }
  100% { transform: scale(1);    box-shadow: 0 8px 26px rgba(33,96,157,.34); }
}
@media (prefers-reduced-motion: reduce) {
  .vl-cart__toggle.is-pulsing { animation: none; }
  .vl-cart__count.is-bumped { animation: none; }
}

/* ============================================================
   CHECKOUT ORDER SUMMARY
   The bordered summary block carries no inner padding, so a line
   price sat hard against the right border and read as though it
   had escaped the box. This gives it a margin to sit in, and
   pins the price cell so it can never outgrow the panel whatever
   the amount or the viewport.
   ============================================================ */
.wp-block-woocommerce-checkout-order-summary-block {
  padding-inline: 1rem;
  overflow: hidden;          /* nothing may paint past the border */
}
.wc-block-components-order-summary-item {
  gap: .75rem;
  align-items: flex-start;
}
/* The name column is what should give when space is short. */
.wc-block-components-order-summary-item__description,
.wc-block-components-order-summary-item__description * { min-width: 0; }
.wc-block-components-order-summary-item__description .wc-block-components-product-name {
  overflow-wrap: anywhere;
}
/* The amount keeps its own line and never wraps mid-figure. */
.wc-block-components-order-summary-item__total-price,
.wc-block-components-order-summary-item .wc-block-formatted-money-amount {
  flex: 0 0 auto;
  white-space: nowrap;
  text-align: right;
}

/* Everyone is a guest — there is no account or login on this store, so the
   notice states a distinction that does not exist here. */
.wc-block-checkout__guest-checkout-notice { display: none !important; }

/* ============================================================
   VIAL CROP — give the bottom of the vial room to breathe
   The crop window was landing exactly on the vial's own edges at
   desktop column widths (measured: 0.3px of clearance, i.e. the
   base was being shaved). Widening the window to 191px with a
   -37/-29 offset clears the vial at every column width in use:
   8px top and bottom at 242px, 21px at 162px.
   Shared, not mobile-only — desktop was the worse case.
   ============================================================ */
.woocommerce ul.products li.product .woocommerce-loop-image-link,
.woocommerce ul.products li.product a.woocommerce-LoopProduct-link { height: 191px; }
.woocommerce ul.products li.product .woocommerce-loop-image-link img,
.woocommerce ul.products li.product a.woocommerce-LoopProduct-link img {
  margin-top: -37px;
  margin-bottom: -29px;
}

/* An active stepper replaces the button — it must not sit beside it.
   The tile styling set `display:inline-flex` on the button at a higher
   specificity than the `display:none` that hides it once a quantity is held,
   so on the tiles the two overlapped. */
.vl-tile__actions .vl-cartctl.is-active .vl-cartctl__btn[data-action="add"],
.vl-tile__actions .vl-cartctl.is-active .vl-cartctl__btn[data-action="options"] {
  display: none !important;
}

/* ============================================================
   MOBILE ONLY — everything below is inside the breakpoint
   ============================================================ */
@media (max-width: 640px) {
  /* Footer marks centred; logo +20% (114 -> 137), Interac +10% (94 -> 103). */
  .vl-footer__inner { text-align: center; }
  .vl-footer__brand,
  .vl-footer__pay { text-align: center !important; }
  .vl-footer .vl-footer__brand img,
  .vl-footer img.vl-footer__logo {
    max-width: 137px !important; width: 137px;
    margin-inline: auto; display: block;
  }
  .vl-footer .vl-footer__pay img,
  .vl-footer img.vl-footer__interac {
    max-width: 103px !important; width: 103px;
    margin-inline: auto; display: block;
  }

  /* Hero mark centred and 20% larger. */
  .vl-hero__art { text-align: center; }
  .vl-hero__art img { max-width: min(77%, 290px); margin-inline: auto; display: block; }  /* +10% */

  /* Banner text 10% up (16px -> 17.6px). Scoped here, so the desktop
     banner is untouched. */
  .vl-marquee__item { font-size: 1.1rem; }

  /* "What Are Peptides" artwork is decorative — it costs a phone a screenful
     of scrolling for nothing, so it is dropped rather than shrunk. */
  .vl-peptides__art { display: none; }

  /* The add button has to fit its bubble at a 162px tile. */
  .vl-tile__actions .vl-cartctl__btn[data-action="add"],
  .vl-tile__actions .vl-cartctl__btn[data-action="options"] {
    font-size: .78rem;
    padding-inline: .5rem;
    gap: .25rem;
    max-width: 100%;
    white-space: nowrap;
    overflow: hidden;
  }
  .vl-tile__actions .vl-cartctl__btn svg { flex: 0 0 auto; }
}

/* The intro's Read more control exists only on small screens. */
.vl-shopintro__more { display: none; }
/* No clamp. Once the intro uses the full width it is short enough to read
   whole, which is also the safest thing for the keywords it carries — nothing
   is hidden from a crawler or a reader. The Read more control stays in the
   markup but is never shown. */

@media (max-width: 640px) {
  /* Both marks were sitting off-centre because their containers carry a
     desktop-layout right padding — 60.9px on the hero art, 16px on the footer
     brand. That padding also shrank the hero mark, since its max-width is a
     percentage of the content box. */
  .vl-hero__art { padding-right: 0 !important; padding-left: 0 !important; }
  .vl-footer__brand,
  .vl-footer__pay { padding-right: 0 !important; padding-left: 0 !important; }
}

@media (max-width: 640px) {
  /* The intro carried a 3.5rem inline padding from the desktop layout — 56px a
     side on a 375px screen, leaving the copy wrapping at barely half the width.
     It now runs gutter to gutter like everything else. */
  .vl-shopintro { padding-inline: var(--vl-edge) !important; max-width: none; }
  .vl-shopintro p { max-width: none; }

  /* Banner text up a further 8% (17.6px -> 19px). */
  .vl-marquee__item { font-size: 1.19rem; }
}

/* The footer mark links home. The anchor has to carry the same block/centring
   the image had, or wrapping it would knock the logo off centre on mobile. */
.vl-footer__brandlink { display: inline-block; line-height: 0; }
@media (max-width: 640px) {
  .vl-footer__brandlink { display: block; margin-inline: auto; width: 137px; }
}

/* ============================================================
   FIXED FURNITURE ON iOS
   The cart is already a direct child of <body> with position:
   fixed, and it holds its place under emulation — but iOS Safari
   does not repaint fixed elements continuously while a scroll is
   in flight, so they drift up the page until the scroll settles.
   Promoting them to their own compositing layer makes the engine
   track the viewport instead of re-rasterising, which is the
   standard remedy. The safe-area inset keeps them clear of the
   home indicator and the bottom toolbar.
   ============================================================ */
/* Layer promotion removed: on iOS, forcing a fixed element onto its own
   compositing layer can make it lag *further* behind a momentum scroll rather
   than less, and it plainly did not help here. The safe-area inset stays, so
   the cart clears the home indicator and Safari's bottom bar. */
.vl-cart {
  bottom: calc(22px + env(safe-area-inset-bottom, 0px));
}
@media (max-width: 600px) {
  .vl-cart { bottom: calc(14px + env(safe-area-inset-bottom, 0px)); }
}

/* The chat widget is injected by a third-party script, so it is reached by
   what it lands as rather than by a class we own. */
/* The chat widget positions itself from its own --vl-bottom variable and
   actively measures the corner for things to sit above, so it is left alone
   rather than fought with CSS. */

@media (max-width: 640px) {
  /* Resources reads as a centred block on a phone rather than a left-hung list. */
  .vl-resources__grid,
  .vl-resources__item { text-align: center; }
  .vl-resources__item p { margin-inline: auto; max-width: 30ch; }

  /* "What Are Peptides": the copy sits close under its heading. The 80px gap
     came from the desktop two-column layout, where the artwork filled it —
     and the artwork is hidden here. */
  .vl-peptides__inner { gap: 0 !important; }
  .vl-peptides__lede { margin-top: 0; }
  .vl-peptides .vl-section__title { margin-bottom: 1.5rem; }

  /* "Why Vistara Labs": four items 96px apart read as four separate sections.
     Halved, so the block holds together. */
  .vl-why .vl-why__grid { margin-top: calc(2.4rem + 52px); gap: 2.6rem 2rem; }
  .vl-why__item h3 { margin-bottom: .55rem; }
}

@media (max-width: 640px) {
  /* The remaining 40px was `.vl-peptides__copy` padding-top — desktop uses it to
     drop the copy against the tall artwork column, which is hidden here. */
  .vl-peptides__copy { padding-top: 0 !important; }
}

/* Second label on the variable control, from when the phone version navigated
   to the product page rather than adding. Neither label is shown any more --
   the button itself is gone at every width -- but the markup still ships it. */
.vl-cartctl__lbl-sm { display: none; }

@media (max-width: 640px) {
  /* "Add to Cart" is the label on the phone -- see the dropdown block below,
     where the button becomes the way in for a product with sizes. */

  /* The tall diagram was 300x375 — most of a screen for one illustration. */
  .vl-map__art--slim img { max-width: 170px !important; }
}

@media (max-width: 780px) {
  /* The discount field reads as a field, not fine print, now that it is the
     first thing under the order summary on a phone. */
  .wc-block-components-totals-coupon .wc-block-components-panel__button {
    font-size: 1rem;
    font-weight: 700;
    color: #21609D;
  }
  .wc-block-components-totals-coupon { padding-block: .6rem; }
}

@media (max-width: 780px) {
  /* WooCommerce folds the whole order summary behind a title bar on a phone,
     and the discount field lives inside it — so someone holding a code has to
     guess that tapping "Order summary" reveals it, and only sees the discounted
     total after that. It is collapsed with a plain `display:none`, no animation,
     so it can simply be shown: the items, the code field and the running total
     are all in view from the start.
     The chevron goes with it, since there is no longer anything to expand. */
  .wc-block-components-checkout-order-summary__content { display: block !important; }
  .wc-block-components-checkout-order-summary__title svg { display: none; }
  .wc-block-components-checkout-order-summary__title { cursor: default; }
}

/* ============================================================
   PRODUCT PAGE — size picker, every width
   One row per size, each with its own stepper, in place of WooCommerce's
   "Choose an option" dropdown plus a typed quantity box. Nobody should have to
   open a select, then tap a number field, then dismiss a keyboard, to buy one
   vial -- and most of this shop's traffic is on a phone.

   These rules used to live inside (max-width: 640px) with desktop left on the
   stock WooCommerce control. They are global now, so both behave the same.

   Steppers show from load at 0, unlike the catalogue tiles where they appear
   on add. On a product page you have already chosen the product, so the
   controls should be present and ready; on a grid of 22 cards they would be
   22 rows of zeroes.
   ============================================================ */

/* WooCommerce's dropdown, quantity box and Add to cart step aside. */
.single-product form.variations_form.cart { display: none !important; }

.vl-sizepick { display: block; margin: .25rem 0 1.4rem; }
.vl-sizepick__label {
  font-size: .78rem; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: #6B7A83; margin: 0 0 .7rem;
}
/* No toggle — the list is the control. */
.vl-sizepick .vl-cartctl__btn[data-action="options"],
.vl-sizepick .vl-cartctl__held,
.vl-sizepick .vl-cartctl__stepper--solo { display: none !important; }

.vl-sizepick .vl-cartctl { display: block; position: static; }
.vl-sizepick .vl-cartctl__sizes {
  display: block !important;
  position: static; transform: none; box-shadow: none;
  border: 0; padding: 0; max-width: none;
}
.vl-sizepick .vl-cartctl__size {
  display: flex; align-items: center; justify-content: space-between;
  gap: .75rem; padding: .55rem .85rem; margin-bottom: .5rem;
  border: 1px solid #E3E9EC; border-radius: 12px;
  max-width: 420px;
}
.vl-sizepick .vl-cartctl__size.is-active { border-color: #21609D; background: #F4F8FC; }
.vl-sizepick .vl-cartctl__pick {
  flex: 1 1 auto; text-align: left; border: 0; background: none;
  padding: 0; font-size: 1.02rem; font-weight: 700; color: #233642; min-height: 44px;
}
.vl-sizepick .vl-cartctl__pick:hover { background: none; color: #233642; }
.vl-sizepick .vl-cartctl__pick.is-disabled { color: #9BA7AE; }

/* Always on, including at zero — the plus IS the add here. */
.vl-sizepick .vl-cartctl__sizestep,
.vl-sizepick .vl-cartctl__size.is-active .vl-cartctl__sizestep {
  display: inline-flex !important; align-items: center;
  border: 1px solid #C9D6DE; border-radius: 999px; overflow: hidden; background: #fff;
}
/* ...so the row must NOT also grow a "+" pseudo-element, or a size shows two. */
.vl-sizepick .vl-cartctl__size .vl-cartctl__pick::after { content: none !important; }

/* A sold-out row is the size and the notice, ranged left, and no control. */
.vl-sizepick .vl-cartctl__size--soldout { justify-content: flex-start; gap: .6rem; }
.vl-sizepick .vl-cartctl__size--soldout .vl-cartctl__pick { flex: 0 0 auto; }
.vl-sizepick .vl-cartctl__sizesold {
  font-size: .78rem; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: #9BA7AE; white-space: nowrap;
}
.vl-sizepick .vl-cartctl__step {
  width: 46px; min-height: 46px; background: #fff; color: #21609D;
  font-size: 1.3rem; line-height: 1; border: 0;
}
.vl-sizepick .vl-cartctl__step:hover { background: #F4F8FC; color: #21609D; }
.vl-sizepick .vl-cartctl__qty {
  min-width: 34px; text-align: center; font-weight: 700; font-size: 1rem; color: #233642;
}

/* Simple products get the same row: one size, one stepper, no typed quantity. */
.vl-sizepick--simple .vl-cartctl { display: flex; justify-content: flex-start; }
.vl-sizepick--simple .vl-cartctl__stepper {
  display: inline-flex !important; align-items: center;
  border: 1px solid #C9D6DE; border-radius: 999px; overflow: hidden;
  background: #fff; padding: 0; height: auto;
}
.vl-sizepick--simple .vl-cartctl__step {
  width: 46px; min-height: 46px; background: #fff; color: #21609D;
  font-size: 1.3rem; line-height: 1; border: 0;
}
.vl-sizepick--simple .vl-cartctl__step:hover { background: #F4F8FC; color: #21609D; }
.vl-sizepick--simple .vl-cartctl__qty {
  min-width: 34px; text-align: center; font-weight: 700; font-size: 1rem; color: #233642;
}
.vl-sizepick--simple .vl-cartctl__btn { display: none !important; }

/* ============================================================
   ORDER RECEIVED
   ============================================================ */
/* The billing address tells a shopper nothing they did not just type. */
.woocommerce-column--billing-address { display: none !important; }
.woocommerce-columns--addresses .woocommerce-column--shipping-address { width: 100%; float: none; }

@media (max-width: 640px) {
  /* The order meta was four fields crammed into a strip. Given room it reads
     as a summary rather than a receipt stub. */
  .woocommerce-order-overview {
    display: grid !important;
    grid-template-columns: minmax(0, 1fr);
    gap: .9rem;
    border: 1px solid #E3E9EC;
    border-radius: 12px;
    padding: 1rem 1.1rem !important;
    margin: 0 0 1.6rem !important;
    list-style: none;
  }
  .woocommerce-order-overview li {
    float: none !important; width: auto !important; border: 0 !important;
    margin: 0 !important; padding: 0 !important;
    display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
    font-size: .78rem; letter-spacing: .1em; text-transform: uppercase; color: #6B7A83;
  }
  .woocommerce-order-overview li strong {
    font-size: 1.05rem; letter-spacing: 0; text-transform: none; color: #233642; text-align: right;
  }
}

/* ============================================================
   DESKTOP — maple lifted, Resources and footer pulled up
   Scoped above the mobile breakpoint so the phone layout, which
   has its own leaf size and section padding, is untouched.

   The leaf (780px) is taller than its section (695px), so a
   percentage background-position resolves against a negative
   range and barely moves it — a pixel offset is the honest way
   to say "up by 15% of the leaf".
   ============================================================ */
@media (min-width: 641px) {
  /* The watermark carries 10.6% transparent padding above the leaf, so at 780px
     the tip only starts 83px down. At -118px that put the tip 35px off the top
     of the section — clipped. -60px keeps the tip 23px clear and still sits the
     leaf 59px higher than it began; the stem lands at 640px inside a 652px
     section, so nothing is lost at the bottom either. */
  .vl-why::before {
    background-position: 50% -60px;
    /* 20% smaller than the min(64%, 780px) set outside this block. Both halves
       scale, or the cap would win on wide screens and nothing would change
       above 1219px. */
    background-size: min(51.2%, 624px) auto;
  }

  /* Resources and the footer below it ride up 80px. A negative margin rather
     than trimming `.vl-why`'s bottom padding: that padding is the box the
     maple leaf is sized and positioned against, so taking 80px out of it would
     have shrunk the leaf again on top of the 20% above. */
  .vl-resources { margin-top: -80px; }
  /* The 15% type bump adds ~7.5px per grid row, 15px over the two rows, which
     would have pushed Resources and the footer back down by 15px. Taking it
     back out below the claims holds them where they were; the stem still
     clears the section edge by 12px, same as before. */
  .vl-why { padding-bottom: calc(15.3rem - 15px); }       /* 288px -> 245px -> 230px */

  /* Claim text up 15%. Scoped to desktop: the base sizes on lines ~1226-1227
     are global, so raising them there would have carried into mobile too. */
  .vl-why__item h3 { font-size: 1.2075rem; }              /* 1.05rem + 15% */
  .vl-why__item p  { font-size: 1.242rem; }               /* 1.08rem + 15% */

  /* "Our Best Sellers" rides up. The heading has no margin of its own -- the
     tinted band's top padding is what places it -- so the reduction comes off
     there and the product grid follows, keeping the heading-to-grid gap intact.

     Measured against the heading's position on the page (871px), NOT against
     the padding. Two earlier passes took 8% and then 7% off the padding, which
     was 109px and then 100px, so they moved the heading 9px and then 7px --
     changes small enough to be invisible, and the second was reported as such.
     7% of 871px is 61px, which is a move you can actually see.

     `.vl-section--tint` appears only on the homepage, so this cannot reach
     another page, and the mobile value is set separately further up: the 8%
     pass was explicitly desktop-only at the owner's request, so this follows
     it rather than silently undoing spacing that was left alone on purpose. */
  .vl-section--tint { padding-top: 4.55rem; }             /* 2.05rem + 40px */
}

@media (max-width: 640px) {
  /* Mobile leaf: larger and lower than desktop's. Both overrides have to live
     here because the globals they replace are NOT in a media query -- the
     background-size near line 2867 feeds desktop's 780px as well, so editing
     it there would have resized the desktop leaf too.
       size: 64% -> 76.8% -> 92.16% -> 110.59%, three +20% steps (240px ->
             288px -> 346px -> 415px at 375). Ink then spans 226-554, still
             clear of both section edges.
       pos:  percentage positioning stays centred at any size, so the +65px is
             the only thing that moves it -- 10% of the 648px section. */
  .vl-why::before {
    background-position: 50% calc(50% + 65px);
    background-size: min(110.59%, 780px) auto;   /* 92.16% + 20% -> 415px at 375 */
  }

  /* The size pill was drawing its active background tight to the text with a
     999px radius, which pinched "10mg" at both ends. Given real padding it
     reads as a pill rather than a clipped circle. */
  .vl-sizepick .vl-cartctl__size.is-active .vl-cartctl__pick,
  .vl-sizepick .vl-cartctl__pick {
    flex: 0 0 auto;
    padding: .5rem 1.1rem;
    border-radius: 999px;
    line-height: 1.25;
  }
  .vl-sizepick .vl-cartctl__size.is-active .vl-cartctl__pick {
    background: #314D5D;
    color: #fff;
  }
  .vl-sizepick .vl-cartctl__size { justify-content: space-between; }

  /* Drawer menu: the links were set at body size in a full-screen panel. */
  .vl-mobile-nav a,
  #mobile-drawer .menu-item > a,
  .mobile-navigation .menu-item > a,
  .drawer-inner .menu-item > a {
    font-size: 1.32rem !important;
    padding-block: 1rem !important;
  }
}

/* ============================================================
   FOOTER (desktop three-column layout only, >=1025px)
   Below 1025 the footer collapses to a single centred column, so every rule
   here is gated: applying the horizontal nudges to the stacked layout would
   shove both marks off to one side.
   ============================================================ */
@media (min-width: 1025px) {
  /* Wordmark 20% further left. The column is right-aligned, so widening its
     right padding is what moves the logo: 215px from the footer's left edge
     becomes 172px. */
  .vl-footer__brand { padding-right: 59px; }

  /* The Interac mark is a block-level image, so the column's `text-align:
     center` never reached it -- it has been sitting flush against the left
     edge of its column this whole time. `margin-inline: auto` is what actually
     centres it.
     That centres it in its *column* (x=617), which is not the middle of the
     footer (x=720): the grid is 1fr 1fr 1.6fr, so the middle column sits left
     of the true midline. The gap works out to exactly 30% of a 1fr column
     -- (1.6fr - 1fr) / 2 -- so translating the column by 30% of its own width
     lands the mark on the footer's centre line and stays correct at any
     desktop width, because the columns are proportional. */
  .vl-footer__pay { transform: translateX(30%); }

  .vl-footer .vl-footer__pay img,
  .vl-footer img.vl-footer__interac {
    max-width: 113px !important;      /* 94px + 20% */
    width: 113px;
    margin-inline: auto;
  }
}

/* ============================================================
   ARCHIVE CARDS — research-area line actually centred
   `.product-details` is a column flex container, so `align-self` there is the
   HORIZONTAL axis, not the vertical one. The global `align-self: start` on
   .vl-loop__area / .vl-loop__alias was therefore shrink-wrapping each
   paragraph to its text and pinning it to the left edge of the card. The
   `text-align: center` on the card still centred the words *inside that narrow
   box*, which is why a long subtitle looked correct (its box filled the card)
   while a short one sat visibly left. Every card on /peptides/ was affected,
   desktop worse than mobile -- up to 58px off centre at 1440.

   `stretch` gives the paragraph the full card width so text-align can do its
   job. Deliberately NOT applied to .vl-tile__sub / .vl-tile__alias: those sit
   in a grid, where align-self is the vertical axis and `start` is correct and
   load-bearing for the subgrid row rhythm.
   ============================================================ */
.woocommerce ul.products li.product .vl-loop__area,
.woocommerce ul.products li.product .vl-loop__alias {
  align-self: stretch;
}

/* ============================================================
   MOBILE TYPE WEIGHT — undo the "lighter headlines" passes
   Two earlier passes walked every heading down from 700 to 600 and then to
   500 ("lighter reads more premium"). On a phone that lands as dainty: the
   owner compared it against the live Squarespace site, whose headings are
   properly bold, and could not see any difference from the earlier body-copy
   bump -- because these headings were already 500 before it and after it.

   Mobile only. Desktop keeps the lighter treatment, which was a deliberate
   choice there and is not what was being complained about.

   700 and 500 are both real loaded Roboto faces. 600 is deliberately avoided:
   there is no 600 file, so it would either snap to 700 anyway or be
   synthesised into a smeared faux-bold.
   ============================================================ */
@media (max-width: 640px) {
  .vl-hero__title,
  .vl-hero__sub,
  .vl-section__title,
  .vl-pagehead__title,
  .vl-whystack__title,
  .vl-give__headline,
  .vl-give__trust,
  .vl-give__enter,
  .vl-give__eyebrow,
  .vl-trustbar,
  .vl-trustbar__item,
  .vl-why__item h3,
  .vl-policy__head,
  .vl-tile__name a,
  .vl-footer__heading,
  .woocommerce ul.products li.product .woocommerce-loop-product__title,
  .woocommerce div.product .product_title {
    font-weight: 700;
  }

  /* Trust line and prices track the body copy -- on the live site both read as
     ordinary running text, not as emphasis. Buttons stay a step up: they are
     UI rather than prose, and the reference sets them heavy too. */
  .vl-hero__trust,
  .woocommerce ul.products li.product .price {
    font-weight: 400;
  }
  .vl-btn { font-weight: 500; }

  /* The banner runs at a larger size than the body copy, so 500 there landed
     heavier than everything around it. Regular is enough at that size. */
  .vl-marquee__item { font-weight: 400; }
}

/* ============================================================
   MOBILE — header wordmark, Resources block, trust bar
   ============================================================ */
@media (max-width: 640px) {
  /* Header wordmark was 20px/400 and read as thin against the rest of the
     header. The Λ shapes are literal lambda characters in Roboto, not a custom
     face, so 500 is a real loaded weight here rather than a synthesised one. */
  .mobile-site-branding .site-title,
  .mobile-site-branding .site-title-wrap .site-title {
    font-size: 24px;                 /* 20px + 20% */
    /* Half a weight step, not a whole one. 500 came out too thick and 400 was
       the "wimpy" starting point, but there is no usable 450: Roboto here is a
       set of static faces, and CSS font-matching resolves a 400-500 request
       upwards, so `font-weight: 450` would silently land back on 500. A hairline
       stroke in the text colour thickens the stems by roughly half the 400-to-500
       difference, which is the middle ground that weight alone cannot express. */
    font-weight: 400;
    -webkit-text-stroke: 0.15px currentColor;   /* was 0.3px -- lighter again */
  }

  /* Resources sized against the live site: the item headings and body copy
     there are appreciably larger relative to the "Resources" heading than they
     were here. Alignment is untouched -- the centring rule above stays. */
  .vl-resources__item h3 { font-size: 26px; }   /* was 21.6px */
  .vl-resources__item p  { font-size: 18px; }   /* was 16px */
  .vl-resources__item a  { font-size: 18px; }   /* was 16px */

  /* Trust bar up 15%. */
  .vl-trustbar,
  .vl-trustbar__item,
  .vl-trustbar__sep { font-size: 17.5px; }      /* was 15.2px */
}

/* ============================================================
   MOBILE — air between a product name and its research area
   The name sat 2px above the summary line, which read as one run-on block.
   The gap only looked right on the few products that carry an alias, because
   that line was doing the separating. Widening the name's own bottom margin
   fixes both cases at once: with an alias it pushes the alias down, without
   one it pushes the summary down.
   ============================================================ */
@media (max-width: 640px) {
  .woocommerce ul.products li.product .woocommerce-loop-product__title {
    margin-bottom: 10px !important;          /* was ~2px */
  }
  /* Matches the specificity of `.vl-tile__text .vl-tile__name` further up,
     which carries !important at (0,2,0); a bare `.vl-tile__name` loses to it
     even with !important of its own. */
  .vl-tile__text .vl-tile__name,
  .vl-tile__name {
    margin-bottom: 10px !important;          /* homepage tiles, same gap */
  }
}

/* ============================================================
   HERO SIZING, AND A LIGHTER DESKTOP WORDMARK
   ============================================================ */

/* Desktop wordmark down a step. 400 was the floor for a "half step" trick --
   a stroke can only add weight, never remove it -- so going lighter means the
   next real face, and Roboto ships a 300. */
@media (min-width: 641px) {
  .site-branding .site-title,
  .site-title-wrap .site-title { font-weight: 300; }
}

@media (min-width: 1025px) {
  /* Logo 5% of the viewport further left. `transform` rather than padding on
     purpose: the artwork is right-aligned inside a fixed grid column, so
     padding would have shrunk the space it sits in and fought the 10% size
     increase below. A transform moves it without touching layout, and 5vw is
     exactly 5% of the hero, which is full-bleed. */
  .vl-hero__art { transform: translateX(-5vw); }

  /* Logo and hero copy up 10%. The two clamps are scaled term-by-term rather
     than pinned to a single size, so the hero still scales between 1025 and
     1440 instead of freezing. */
  .vl-hero__art img { width: 396px; max-width: none; }        /* 360px + 10% */
  .vl-hero__title { font-size: clamp(1.925rem, 1.012rem + 2.431vw, 3.113rem); }
  .vl-hero__sub   { font-size: clamp(1.155rem, .99rem + .66vw, 1.54rem); }
  .vl-hero__trust { font-size: 1.1rem; }                      /* 16px + 10% */
  .vl-hero__actions .vl-btn { font-size: .902rem; }           /* 13.12px + 10% */
}

@media (max-width: 640px) {
  /* Same lockup, 5% larger. */
  .vl-hero__art img { max-width: min(80.85%, 304.5px); }      /* 260px -> 273px */

  /* Headline block rises 28px -- 5% of how far down the page it was sitting.
     The only space above it is the grid's row gap plus the copy's own top
     padding; closing the gap moves the text without touching the logo, which
     is what was asked for. The 23px of padding stays, so the headline still
     clears the wordmark in the logo above it rather than crowding it. */
  /* !important is required: the <=900px block sets `gap: 1.75rem !important`,
     which a plain declaration here cannot beat regardless of source order. */
  .vl-hero__inner { row-gap: 0 !important; }                  /* was 28px */
  .vl-hero__title { font-size: 29.4px; }                      /* 28px + 5% */
  .vl-hero__sub   { font-size: 17.64px; }                     /* 16.8px + 5% */
  .vl-hero__trust { font-size: 16.8px; }                      /* 16px + 5% */
  .vl-hero__actions .vl-btn { font-size: 13.78px; }           /* 13.12px + 5% */
}

/* ============================================================
   MOBILE — product grids sit higher on their pages
   ============================================================ */
@media (max-width: 640px) {
  /* Stacks: 15px off the 100px run between the intro copy and the grid, which
     is 15% of it. The grid's own margin is already down to 6px, so the space
     has to come from the marquee above it. Scoped with a sibling selector so
     only the marquee on this page moves -- `.vl-marquee` is shared, and the
     homepage copy must not shift. */
  .vl-stackspage + .vl-marquee { margin-top: 14.6px; }        /* was 29.6px */

  /* ...and put the 15px back above "Why Stack?", which the owner asked to
     keep exactly where it is. */
  .vl-whystack { padding-top: 69px; }                         /* was 54px */

  /* Products archive: 9px, 5% of the run from the banner down to the grid.
     Both margins have to come down together. They are adjacent siblings, so
     their vertical margins COLLAPSE -- the gap is max(filters-bottom,
     grid-top), not the sum. Dropping only the grid's 32px to 23px moved the
     grid just 4px, because the filters' 28px then set the floor.
     !important on the grid: the rule it overrides is itself !important. */
  .vl-shopfilters { margin-bottom: 23px; }                    /* was 28px */
  .woocommerce ul.products { margin-top: 23px !important; }   /* was 32px */
}

/* ============================================================
   GIVEAWAY — logo matches the other page heads exactly
   The calculator and contact pages both render their mark through
   `.vl-pagehead--mark` at 130px; the giveaway page has its own `.vl-give__mark`
   block that was set to 150px. Its top edge and centre line already agreed
   (y=145, x=720), so size was the only thing breaking the illusion that the
   logo is pinned in place as you move between pages.
   The 20px it loses is added back below it, so the headline underneath does
   not ride up and undo the alignment further down the page.
   ============================================================ */
@media (min-width: 641px) {
  .vl-give__mark img { max-width: 130px; }        /* was 150px */
  .vl-give__mark { margin-bottom: calc(2.2rem + 20px); }
}

/* ============================================================
   ORDER-RECEIVED — shipping address ranged left with the page
   WooCommerce lays the addresses out as a two-column float: billing in col-1,
   shipping floated right into col-2. Billing is hidden on this build (the owner
   removed it), so the shipping block was left stranded in the right-hand half
   at x=747 with an empty column beside it.
   Unfloating it and letting it take the full width puts it back on the page's
   left edge (x=51), in line with the header wordmark.
   ============================================================ */
/* Specificity note: WooCommerce ships `.woocommerce .col2-set .col-2
   { float: right; width: 48% }` at (0,3,0). A rule keyed on the friendlier
   `.woocommerce-column--shipping-address` class is only (0,2,0) and loses to
   it no matter where it sits in the file, so the selector below deliberately
   mirrors WooCommerce's own shape and goes one class deeper. */
.woocommerce .woocommerce-customer-details .col2-set .col-2 {
  float: none;
  width: 100%;
  margin-left: 0;
}
.woocommerce .woocommerce-customer-details .col2-set .col-1 {
  display: none;
}

/* ============================================================
   VARIABLE PRODUCTS — no button, ever
   This block used to juggle a collapsed state: hide the button for one size
   held, bring it back for two, and swap a summary stepper in and out. All of
   that is gone now that the size rows are permanently on show, and the
   leftover "restore the button when two sizes are held" rule was the reason an
   Add to Cart reappeared under a card as soon as a second size went in.
   The button and the summary stepper are hidden unconditionally in the
   (min-width: 641px) block above; nothing here should re-reveal them.
   ============================================================ */
.vl-cartctl--variable .vl-cartctl__stepper--solo,
.vl-cartctl--variable.is-solo .vl-cartctl__stepper--solo { display: none !important; }

/* Desktop only. There the size rows are the control and the button that used
   to reveal them is never the way in. On a phone the rows live in a dropdown
   and this button is what opens it, so hiding it there would leave a product
   with sizes no way to be bought at all.

   Scoped by media query rather than by fighting on specificity: these
   selectors are (0,6,2) and (0,5,1), so any phone rule trying to undo them
   would have to be more specific still, which is how this file has gone wrong
   before. */
@media (min-width: 641px) {
  .vl-tile__actions .vl-cartctl--variable .vl-cartctl__btn[data-action="options"],
  .woocommerce ul.products li.product .vl-cartctl--variable .vl-cartctl__btn[data-action="options"] {
    display: none !important;
  }
}

/* ============================================================
   SIZE PICKER — never the collapsed stepper
   The solo stepper exists for the catalogue card, where it stands in for a
   button that `.is-solo` hides. The single-product size picker lists every
   size with its own stepper, so the solo one appeared above them as a second,
   ambiguous quantity control for a size it did not name.
   Specificity is deliberate: `.vl-cartctl--variable.is-solo
   .vl-cartctl__stepper--solo` reveals it at (0,3,0) with !important, so
   suppressing it here needs (0,4,0) and !important of its own.
   ============================================================ */
.vl-sizepick .vl-cartctl--variable .vl-cartctl__stepper--solo,
.vl-sizepick .vl-cartctl--variable.is-solo .vl-cartctl__stepper--solo {
  display: none !important;
}

/* ============================================================
   CHECKOUT — one order summary, not two
   WooCommerce Blocks renders the summary in the sidebar AND again inside
   `.wc-block-checkout__actions` beside the place-order button. On a phone the
   sidebar stacks above the form, so both ended up in the same column: once at
   the top (wanted) and once more below the terms line, complete with a second
   discount-code field.
   ============================================================ */
.wc-block-checkout__actions .wp-block-woocommerce-checkout-order-summary-block {
  display: none;
}

/* The chat widget was removed; nothing shares the bottom-right corner with
   the floating cart any more. */

/* ============================================================
   VARIABLE PRODUCTS — the sizes ARE the control
   No dropdown, no "Choose size" button, no popover: every size is a row with
   its own stepper, which is the treatment already used on the single-product
   page. One mental model everywhere, and no hidden state to discover.

   Ported from the .vl-sizepick rules above rather than reinvented, so the two
   stay in step. The steppers previously inherited a pale colour that all but
   vanished against the white card; #21609D is the headline blue.

   This was `min-width: 641px` and the phone got a "Choose size" button that
   navigated to the product page instead. Two objections killed that: it is a
   page load to add a second size, and the popover it replaced was the thing
   that overhung neighbouring cards. Rows rendered in the card's own flow
   overlap nothing, so both breakpoints can now run the same markup. `@media
   all` rather than deleting the wrapper, so the block's own closing brace and
   the indentation of ~90 declarations stay put.
   ============================================================ */
@media all {
  .vl-cartctl--variable .vl-cartctl__btn,
  .vl-cartctl--variable .vl-cartctl__held { display: none !important; }

  /* The collapsed stepper is redundant now the size rows are always on show,
     and it was appearing above them as an unlabelled third control. The
     `.is-solo` selector is repeated because the rule that reveals it is
     (0,3,0); a bare `.vl-cartctl--variable .vl-cartctl__stepper--solo` is
     (0,2,0) and loses however late it appears. */
  .vl-cartctl--variable .vl-cartctl__stepper--solo,
  .vl-cartctl--variable.is-solo .vl-cartctl__stepper--solo { display: none !important; }

  /* Beats the `hidden` attribute the markup ships with — the list is no longer
     something to reveal. */
  .vl-cartctl--variable .vl-cartctl__sizes {
    display: flex !important;
    position: static; transform: none; box-shadow: none;
    border: 0; padding: 0; max-width: none; gap: .4rem;
    /* Shrink-wrap to the widest row rather than filling the card.
       This one property was the whole difference between the two grids. On a
       Best Sellers tile `.vl-cartctl` is a flex item, so it already sizes to
       content and the rows come out 174px -- label, gap and stepper, exactly
       what they need. On a catalogue card `.vl-cartctl` fills the card, so
       `width: 100%` here handed the rows whatever the column happened to be:
       137px at five columns, which left 35px for the label against a 108px
       stepper and made the stepper look enormous.
       The rows keep `width: 100%` of THIS box, so both sizes still match each
       other -- the shorter "20mg +" row lines up with the taller stepper row. */
    width: auto;
  }

  .vl-cartctl--variable .vl-cartctl__size {
    display: flex; align-items: center; justify-content: space-between;
    gap: .6rem; width: 100%; padding: .3rem .5rem .3rem .7rem;
    border: 1px solid #E3E9EC; border-radius: 10px;
    /* A Best Sellers tile is 170px, so its rows are compact by accident of the
       card. A catalogue card is 210px on desktop and 257px at two columns, and
       the label -- `flex: 1 1 auto` -- absorbed all of it, stranding the
       stepper at the far edge of a 240px row. Capping the row makes the two
       identical instead of leaving it to whatever width the grid happens to
       give the card. The single-product picker overrides this back to `none`
       at (0,3,0); there the row is the whole control, not a chip in a card. */
    max-width: 180px;
  }
  .vl-cartctl--variable .vl-cartctl__size.is-active {
    border-color: #21609D; background: #F4F8FC;
  }

  .vl-cartctl--variable .vl-cartctl__pick,
  .vl-cartctl--variable .vl-cartctl__size.is-active .vl-cartctl__pick {
    flex: 1 1 auto; text-align: left; border: 0; background: none;
    padding: 0; font-size: .88rem; font-weight: 700; color: #233642;
  }
  .vl-cartctl--variable .vl-cartctl__pick:hover { background: none; color: #233642; }

  /* The stepper belongs to a size that is actually in the cart. At zero it goes
     away and the row becomes an add control again -- the same two states a
     simple product moves between, rather than a stepper parked on 0 that no
     other product on the site shows. */
  .vl-cartctl--variable .vl-cartctl__sizestep {
    display: none; align-items: center; flex: 0 0 auto;
    border: 1px solid #C9D6DE; border-radius: 999px; overflow: hidden; background: #fff;
  }
  .vl-cartctl--variable .vl-cartctl__size.is-active .vl-cartctl__sizestep {
    display: inline-flex !important;
  }

  /* Not in the cart: the whole row is the "add", so it has to look pressable. */
  .vl-cartctl--variable .vl-cartctl__size:not(.is-active):not(.vl-cartctl__size--soldout) {
    cursor: pointer;
  }
  .vl-cartctl--variable .vl-cartctl__size:not(.is-active):not(.vl-cartctl__size--soldout):hover {
    border-color: #21609D; background: #F4F8FC;
  }
  .vl-cartctl--variable .vl-cartctl__size:not(.is-active) .vl-cartctl__pick {
    cursor: pointer;
  }
  /* A plus on the right of an un-added row says what tapping it will do.
     The pick becomes its own flex row rather than floating the ::after -- a
     float inside a flex item does not push to the edge, it just sits after the
     text, which left the plus jammed against the label. */
  .vl-cartctl--variable .vl-cartctl__size:not(.is-active):not(.vl-cartctl__size--soldout) .vl-cartctl__pick {
    display: flex; align-items: center; justify-content: space-between; width: 100%;
  }
  .vl-cartctl--variable .vl-cartctl__size:not(.is-active):not(.vl-cartctl__size--soldout) .vl-cartctl__pick::after {
    content: "+";
    color: #21609D; font-size: 1.15rem; line-height: 1; font-weight: 400;
  }
  .vl-cartctl--variable .vl-cartctl__step {
    width: 30px; height: 30px; border: 0; background: #fff;
    color: #21609D;                       /* was near-invisible against the card */
    font-size: 1.05rem; line-height: 1;
  }
  .vl-cartctl--variable .vl-cartctl__step:hover { background: #F4F8FC; color: #21609D; }
  .vl-cartctl--variable .vl-cartctl__qty {
    min-width: 22px; text-align: center; font-weight: 700; font-size: .9rem; color: #233642;
  }

  /* A size that is out of stock keeps its row, says so, and cannot be used. */
  .vl-cartctl--variable .vl-cartctl__size--soldout { background: #FAFBFC; }
  .vl-cartctl--variable .vl-cartctl__size--soldout .vl-cartctl__pick { color: #9BA7AE; }
  .vl-cartctl--variable .vl-cartctl__sizesold {
    flex: 0 0 auto; font-size: .7rem; font-weight: 700; letter-spacing: .08em;
    text-transform: uppercase; color: #9BA7AE; white-space: nowrap;
  }
  /* Sold-out rows carry no stepper at all now (see inc/shop-archive.php), so the
     row is just the size and the notice. */
  .vl-cartctl--variable .vl-cartctl__size--soldout { justify-content: flex-start; gap: .5rem; }
  .vl-cartctl--variable .vl-cartctl__size--soldout .vl-cartctl__pick { flex: 0 0 auto; }
}

/* ============================================================
   SINGLE PRODUCT — a variable product is just N quantity controls
   A simple product shows "QUANTITY" over a rounded pill stepper. A product
   with sizes is the same thing twice: the size name takes the label's place
   and each gets its own pill. Nothing about having two sizes calls for a
   different control, and the bordered rows it used to draw made it look like
   a different kind of thing entirely.

   All of it restated at (0,3,0). The catalogue block above is (0,2,0) and
   used to be scoped to `min-width: 641px`; widening it for the phone swept up
   this picker, whose `display: block` then lost to `display: flex !important`
   and inherited an `align-items: center` that pushed every row 103px in from
   its own heading. Winning on specificity rather than on source order is what
   stops that recurring.
   ============================================================ */
.vl-sizepick .vl-cartctl--variable .vl-cartctl__sizes {
  display: block !important;
  align-items: initial;
  /* Full width, deliberately. The catalogue rule shrink-wraps this box so a
     card's rows size to their content -- but `.vl-cartctl` is a column flex
     with `align-items: center`, so a shrink-wrapped box gets centred, and here
     that pushed the whole list 250px in from its own "Choose your sizes"
     heading. Filling the column leaves nothing to centre. The pills inside
     still size to their own content. */
  width: 100%;
}

/* No row chrome: the pill is the control, exactly as on a simple product. */
.vl-sizepick .vl-cartctl--variable .vl-cartctl__size,
.vl-sizepick .vl-cartctl--variable .vl-cartctl__size.is-active {
  display: block;
  border: 0; background: none; padding: 0; max-width: none;
  margin: 0 0 1.5rem;
}
.vl-sizepick .vl-cartctl--variable .vl-cartctl__size:last-child { margin-bottom: 0; }

/* The size name is the label. Same type as "QUANTITY" over a simple product,
   because it is doing the same job. It stays a button so the markup is
   unchanged, but the stepper below is always visible here, so there is
   nothing left for a click on it to do -- hence the default cursor. */
.vl-sizepick .vl-cartctl--variable .vl-cartctl__pick,
.vl-sizepick .vl-cartctl--variable .vl-cartctl__size.is-active .vl-cartctl__pick {
  display: block; width: auto; text-align: left;
  border: 0; background: none; padding: 0; min-height: 0;
  font-size: .78rem; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: #6B7A83;
  margin: 0 0 .7rem;
  cursor: default;
}
.vl-sizepick .vl-cartctl--variable .vl-cartctl__pick:hover {
  background: none; color: #6B7A83;
}

/* The pill itself, matching .vl-sizepick--simple line for line. */
.vl-sizepick .vl-cartctl--variable .vl-cartctl__sizestep,
.vl-sizepick .vl-cartctl--variable .vl-cartctl__size.is-active .vl-cartctl__sizestep {
  display: inline-flex !important; align-items: center;
  border: 1px solid #C9D6DE; border-radius: 999px; overflow: hidden;
  background: #fff; padding: 0; height: auto;
}
.vl-sizepick .vl-cartctl--variable .vl-cartctl__sizestep .vl-cartctl__step {
  width: 46px; min-width: 46px; height: auto; min-height: 46px;
  padding: 0; border: 0; background: #fff; color: #21609D;
  font-size: 1.3rem; line-height: 1;
}
.vl-sizepick .vl-cartctl--variable .vl-cartctl__sizestep .vl-cartctl__step:hover {
  background: #F4F8FC; color: #21609D;
}
.vl-sizepick .vl-cartctl--variable .vl-cartctl__sizestep .vl-cartctl__qty {
  min-width: 34px; text-align: center;
  font-weight: 700; font-size: 1rem; color: #233642;
}

/* A sold-out size keeps its label and says so, with no control under it.
   On one line: with nothing to label underneath, a stacked "10MG" over
   "SOLD OUT" reads as two separate things rather than one unavailable size. */
.vl-sizepick .vl-cartctl--variable .vl-cartctl__size--soldout {
  display: flex; align-items: baseline; gap: .6rem;
}
.vl-sizepick .vl-cartctl--variable .vl-cartctl__size--soldout .vl-cartctl__pick {
  margin-bottom: 0;
}
.vl-sizepick .vl-cartctl--variable .vl-cartctl__size--soldout .vl-cartctl__sizesold {
  display: inline-block;
  font-size: .8rem; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: #9BA7AE;
}


/* ============================================================
   TOUCH — no hover state, so none can get stuck
   A tap on a touch screen leaves :hover latched on the element, and the
   browser restores that state from bfcache when the visitor comes back. The
   button they left through was still enlarged and shadowed on their return.
   Hover is a pointer idea; on a touch device these should never apply at all.
   ============================================================ */
@media (hover: none) {
  .vl-btn:hover,
  .wp-block-button__link:hover,
  .woocommerce a.button:hover,
  .woocommerce button.button:hover,
  .woocommerce .single_add_to_cart_button:hover,
  .vl-tile__act:hover {
    transform: none;
    box-shadow: none;          /* matches the base rule, which declares no shadow */
  }
  .vl-tile:hover .vl-tile__img img { transform: none; }
}

/* ============================================================
   SINGLE PRODUCT — "Sold Out" legible in its blue box
   The element carries WooCommerce's own `stock` class, which is coloured by
   the theme's stock styling at a higher specificity than `.vl-soldout--single`.
   The result was rgb(43,108,176) text on an rgb(33,96,157) background: the same
   blue twice over, so the words all but disappeared.
   ============================================================ */
.woocommerce div.product p.stock.vl-soldout--single,
.woocommerce div.product .stock.vl-soldout--single,
p.stock.vl-soldout--single {
  color: #fff !important;
}

/* WooCommerce's own add-to-cart form stands down only where our picker has
   actually rendered — keyed with :has() rather than a blanket rule, so any
   product type we do not replace keeps a working form rather than losing its
   only way to buy. */
.single-product .summary:has(.vl-sizepick) form.cart { display: none !important; }

/* The Best Sellers tile has four children -- image, text, price, actions --
   and the grid's four row tracks match them. An earlier attempt to widen this
   to six tracks produced two zero-height rows and moved nothing; noted here so
   it is not tried again. */

/* ============================================================
   ARCHIVE CARDS — prices align across a mixed row
   The card is a flex column and the price carried `margin-top: auto`, so it
   absorbed whatever space was left over and floated just above the cart
   control. That works while every control is the same height. It stopped
   working when variable products grew size rows: a 66px control against a
   43px one left 24px less slack, and the price on a variable card sat 24px
   higher than its neighbours. Only the top row showed it, because that is the
   only row mixing the two kinds.

   The `auto` belongs on the control, which is the thing that should sit at the
   bottom. The price then follows the research-area line, which is already
   aligned, so every price in a row lands on the same line.
   ============================================================ */
/* The price floats to a fixed distance from the bottom of the card... */
.woocommerce ul.products li.product .price {
  margin-top: auto !important;
  margin-bottom: .55rem !important;
}
/* ...which only aligns if every control below it is the same height. A
   variable control is two size rows tall and a simple one a single stepper,
   so the shorter one is padded up to match.

   The reserve is the height of the TALLEST control the catalogue renders — a
   variable product showing two size rows. 68px was right while the phone
   collapsed those rows behind a button; now that both breakpoints render them
   inline, the two are measured separately.

   Add a product with a THIRD size and these have to grow with it, or its card
   will push its own price up and the row will misalign again.

   This is a COLUMN flex, so the two alignment properties do the opposite of
   what they read like: `justify-content` runs vertically and `align-items`
   runs across. `align-items: flex-start` was therefore pinning the control to
   the left edge of the card while every other element -- image, title, area,
   price -- was centred. `center` puts it under them where it belongs. The
   price alignment above is unaffected either way: it measures from this box's
   fixed outer height, not from how its contents sit inside it. */
.woocommerce ul.products li.product .vl-cartctl {
  margin-top: 0 !important;
  min-height: 98px;   /* two 46px size rows plus their .4rem gap = 98.4 */
  display: flex;
  align-items: center;
  justify-content: center;
}
@media (max-width: 640px) {
  /* Taller on a phone: two 52px size rows plus their gap measure 110.4px. */
  .woocommerce ul.products li.product .vl-cartctl { min-height: 111px; }
}

/* ============================================================
   PHONE SIZE ROWS — the stepper has to fit the card
   A catalogue card is 162px wide and hands its size row 145px. The global
   phone tap-target rule sets `.vl-cartctl__step { min-width: 44px }`, so two
   buttons and a quantity came to 110px of that before the size label was
   drawn, and the stepper pushed straight through the row's right border and
   out over the gutter.

   Height is what a thumb actually needs, so that is what is kept; only the
   width comes down. Selectors are (0,3,0) deliberately — the 44px floor is
   (0,1,0) and `min-width` cannot be beaten by setting `width` alone.
   ============================================================ */
@media (max-width: 640px) {
  .vl-cartctl--variable .vl-cartctl__size {
    padding: 3px 4px 3px 7px;
    gap: .25rem;
  }
  .vl-cartctl--variable .vl-cartctl__pick {
    font-size: .8rem;
    padding-inline: 0;
    min-width: 0;
  }
  .vl-cartctl--variable .vl-cartctl__sizestep .vl-cartctl__step {
    /* The inherited button padding is 16px 21px, which floors the box at 42px
       however narrow `width` is set -- that overrun is what pushed the stepper
       over the size label. The tap area is the 34x40 box itself. */
    padding: 0;
    min-width: 34px; width: 34px;
    min-height: 40px; height: 40px;
    font-size: 1rem;
  }
  .vl-cartctl--variable .vl-cartctl__sizestep .vl-cartctl__qty {
    min-width: 16px;
    font-size: .82rem;
  }

  /* A sold-out row keeps the 44px tap box on its label even though the label
     is not tappable, so the size sat at the top of that box while "Sold Out"
     centred against the row -- two words on one line at two different
     heights. Centring the label inside its own box lines them up without
     changing the row's height, which has to match the rows around it. */
  .vl-cartctl--variable .vl-cartctl__size--soldout .vl-cartctl__pick {
    display: flex;
    align-items: center;
  }
}

/* Everything above the price has to occupy the same height on every card, or
   the price cannot land on the same line across a row. Two things varied:
   the alias — only some products carry one, like BPC-157's "(Wolverine)" — and
   the title, which wraps to three lines for the longest names and two for the
   rest. Both now reserve their tallest case, so the run below them is level.
   These override the `min-height: 0 !important` set further up, which was
   written back when the price used `margin-top: auto` to paper over the gap. */
/* Reserving height on the TITLE aligned the prices but pushed the research
   area away from the name it belongs to -- a short title left blank space
   underneath it. The alignment has to come from below the text instead.
   The cart control now carries a uniform minimum height and the price goes
   back to `margin-top: auto`, so the slack collects between the description
   and the price rather than between the name and the description. Titles and
   the research area sit at their natural height, tight together. */
.woocommerce ul.products li.product .woocommerce-loop-product__title {
  min-height: 0 !important;
  margin-bottom: 4px !important;
}
.woocommerce ul.products li.product .vl-loop__alias,
.woocommerce ul.products li.product .vl-loop__alias.is-empty {
  min-height: 0 !important;
  margin: 0 !important;
}

/* ============================================================
   RECONSTITUTION GUIDELINES — matched to the Research Use Policy page
   The block was centred with !important and its list markers suppressed, so
   every wrapped line began somewhere new and a numbered procedure had nothing
   to anchor to. Instructions get scanned, not admired.

   Rather than invent a treatment, these values are read off .vl-policy on the
   Research Use Policy page so the two read as one document: a 900px measure,
   21.6px/500 headings in the house blue, 16.32px body, and a bullet indented
   16px. Only the section title stays centred, as it is on every other section.
   ============================================================ */
.vl-recon { text-align: left !important; }
.vl-recon .vl-section__title { text-align: center !important; }

/* Specificity note: the rules near line 1449 are scoped `.vl-recon .vl-recon__list`
   and `.entry-content .vl-recon ...`, i.e. (0,3,0) and (0,4,0). Everything below
   repeats that shape -- a bare `.vl-recon__list li` is (0,2,0) and loses, which
   is why a first pass left the bullets unindented and the old numbered marker
   in place. */
.vl-recon .vl-recon__lede,
.vl-recon .vl-recon__head,
.vl-recon .vl-recon__list,
.vl-recon .vl-recon__list li,
.vl-recon .vl-recon__close,
.entry-content .vl-recon .vl-recon__list,
.entry-content .vl-recon .vl-recon__list li { text-align: left !important; }

/* Same column as .vl-policy: 900px, centred, with the same inner gutter. */
.vl-recon .vl-recon__lede,
.vl-recon .vl-recon__head,
.vl-recon .vl-recon__list,
.vl-recon .vl-recon__close,
.entry-content .vl-recon .vl-recon__list {
  max-width: 900px;
  margin-left: auto;
  margin-right: auto;
  padding-inline: 1.6rem;
  box-sizing: border-box;
}

.vl-recon .vl-recon__lede,
.vl-recon .vl-recon__close {
  font-size: 1.02rem;      /* 16.32px, as .vl-policy p */
  line-height: 1.65;
  color: #000;
}
.vl-recon .vl-recon__lede { margin-bottom: 1.6rem; }
.vl-recon .vl-recon__close { margin-top: 1.6rem; }

.vl-recon .vl-recon__head {
  color: #21609D;
  font-size: 1.35rem;      /* 21.6px, as .vl-policy__head */
  font-weight: 500;
  line-height: 1.3;
  letter-spacing: -0.01em;
  margin-top: 2rem;
  margin-bottom: .675rem;
}

.vl-recon .vl-recon__list { list-style: none; }
.vl-recon .vl-recon__list li,
.entry-content .vl-recon .vl-recon__list li {
  position: relative;
  padding-left: 1.6rem;    /* 1.6rem gutter + the marker's own 1rem inset */
  margin-bottom: .35rem;
  font-size: 1.02rem;
  line-height: 1.6;
  color: #000;
  list-style: none;
}
.vl-recon .vl-recon__list li::before,
.entry-content .vl-recon .vl-recon__list li::before {
  content: "\2022";        /* the same bullet the policy page uses */
  position: absolute;
  left: .6rem;
  color: #000;
}
/* The numbered procedure keeps its counter in place of the bullet. */
.vl-recon .vl-recon__list--num li::before,
.entry-content .vl-recon .vl-recon__list--num li::before {
  content: counter(recon) ".";
  left: .35rem;
  color: #21609D;
  font-weight: 700;
}

/* ============================================================
   MOBILE CARDS — research area sits under the name, not adrift
   The 54px title reserve exists so prices line up across a five-across desktop
   row. On a two-across phone grid that alignment is barely perceptible, while
   the reserved space is: a short name left 17px of blank between it and the
   research area beneath. The reserve is desktop's problem, so it stays there.
   ============================================================ */
@media (max-width: 640px) {
  .woocommerce ul.products li.product .woocommerce-loop-product__title {
    min-height: 0 !important;
    margin-bottom: 4px !important;
  }
  .woocommerce ul.products li.product .vl-loop__alias,
  .woocommerce ul.products li.product .vl-loop__alias.is-empty {
    min-height: 0 !important;
  }
  .woocommerce ul.products li.product .vl-loop__alias.is-empty {
    margin: 0 !important;
  }

  /* The "Choose size" button used to be restored here, carrying data-href to
     the product page. The size rows render inline on the phone now, so there is
     nothing to route to and nothing to reveal. */
}

/* ============================================================
   MOBILE HOME — banner tucks under the header
   The marquee sat 30px below the header, and everything after it inherited
   that offset. 8% of the hero's 174px start is 14px, which halves the gap and
   carries the whole page up with it. A literal 8% of the screen would be 65px
   and there are only 30 to give, so this takes what the layout actually has.
   Scoped to .home: the same banner appears on other pages with their own
   spacing above it.
   ============================================================ */
@media (max-width: 640px) {
  .home .vl-marquee { margin-top: 15.6px; }   /* was 29.6px */
}

/* The hero logo's entrance is held at frame zero while the age gate is up, so
   a first-time visitor sees it play after they accept rather than behind the
   dialog. `backwards` fill means the paused state is the from-frame -- fully
   transparent and blurred -- and removing the class starts it from there.
   Set in <head> before first paint; see vistara_gate_hold_hero(). */
html.vl-gate-pending .vl-hero__art img { animation-play-state: paused; }

/* ============================================================
   CHECKOUT — no billing-address choice
   One country, one currency, payment by e-Transfer: a billing address that
   differs from the shipping address buys nothing, so the checkbox is a
   decision asked of every customer that none of them need to make.
   Blocks ships it ticked and does not render the billing block while it is,
   so removing it from view leaves billing following shipping for good.
   ============================================================ */
.wc-block-checkout__use-address-for-billing { display: none !important; }

/* ============================================================
   HERO HEADLINE — the one heading that should carry weight
   Two successive "lighter headlines" passes took every heading 700 -> 600 ->
   500. That reads well on section titles, but it left the first words a
   visitor sees floating rather than landing. This is the anchor the eye
   should settle on while the wordmark fades in beside it, so it is pulled
   back out of the group and given real mass.

   700 because it is the heaviest Roboto face actually loaded (300/400/500/700
   only). 800 or 900 would look heavier in the stylesheet and ship as a
   synthesised faux-bold -- smeared, lighter-looking, and worse.

   Tracking and leading tighten with the weight: at 700 the default spacing
   leaves the words as separate shapes, and closing it up binds them into one
   block, which is what actually reads as fastened to the page rather than set
   on top of it.

   Scoped at (0,2,0) deliberately. The two rules that lightened it are both
   (0,1,0), so this wins on specificity rather than on sitting lower in the
   file. Mobile already forced 700 further down; this makes both breakpoints
   agree instead of arriving at the same number by different routes.
   ============================================================ */
.vl-hero .vl-hero__title {
  font-weight: 700;
  letter-spacing: -0.032em;
  line-height: 1.04;
}

/* ============================================================
   ADD-TO-CART CONFIRMATION
   Slides out to the left of the floating cart, says what went in and what
   that line cost, then leaves. On a variable product it is the only place a
   shopper is told the price of the size they just picked without opening the
   product page -- the grid shows a range.

   Positioned against `.vl-cart`, which is already the fixed corner anchor, so
   the toast tracks the button rather than being placed twice. `right: 100%`
   puts it beside the button at any button size.
   ============================================================ */
.vl-cart__toast {
  position: absolute;
  right: calc(100% + 10px);
  top: 50%;
  transform: translateY(-50%) translateX(12px);
  display: flex;
  align-items: center;
  gap: .7rem;
  width: max-content;
  max-width: min(300px, calc(100vw - 6rem));
  padding: .55rem .95rem .55rem .55rem;
  background: #fff;
  border-radius: 14px;
  box-shadow: 0 10px 30px rgba(35, 54, 66, .18);
  cursor: pointer;
  opacity: 0;
  transition: opacity .22s ease, transform .22s cubic-bezier(.16, 1, .3, 1);
}
.vl-cart__toast.is-in { opacity: 1; transform: translateY(-50%) translateX(0); }
.vl-cart__toast[hidden] { display: none; }

.vl-cart__toastimg {
  flex: 0 0 auto;
  width: 44px; height: 44px;
  object-fit: contain;
  border-radius: 9px;
  background: #EEF2F5;
  -webkit-user-drag: none;
  user-select: none;
}

.vl-cart__toasttext { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

/* Two lines is the ceiling: a long name plus a size should wrap once and then
   stop, or the toast grows taller than the button it is attached to. */
.vl-cart__toastname {
  font-size: .82rem; font-weight: 700; line-height: 1.25; color: #233642;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.vl-cart__toastprice { font-size: .88rem; font-weight: 700; color: #21609D; }

/* The cart sits in the corner; on a phone the toast has to fit beside it
   without reaching the screen edge. */
@media (max-width: 640px) {
  .vl-cart__toast {
    right: calc(100% + 8px);
    max-width: calc(100vw - 5.5rem);
    padding: .45rem .8rem .45rem .45rem;
    gap: .55rem;
  }
  .vl-cart__toastimg { width: 38px; height: 38px; }
  .vl-cart__toastname { font-size: .78rem; }
  .vl-cart__toastprice { font-size: .84rem; }
}

@media (prefers-reduced-motion: reduce) {
  .vl-cart__toast { transition: none; transform: translateY(-50%); }
  .vl-cart__toast.is-in { transform: translateY(-50%); }
}

/* ============================================================
   HOMEPAGE — everything under "Our Best Sellers" sits lower
   Down 100px, added to the grid's top margin (24px -> 124px), which pushes
   the products and every section after them down together while the heading
   stays where the tinted band's padding puts it.

   This was 184px first -- 5% of the page's 3672px length, read literally. It
   worked out to a 232px gap between the heading and the first row of vials,
   which left the heading stranded on its own. 100px is the number the owner
   set after seeing it.
   ============================================================ */
.home .vl-bestsellers { margin-top: 84px; }   /* 124px - 40px */

/* Phone only: 15px tighter again. The rule above is unscoped, so this needs
   its own media query rather than a smaller number there -- desktop stays
   at 84px. */
@media (max-width: 640px) {
  .home .vl-bestsellers { margin-top: 69px; }   /* 84px - 15px */
}

/* ============================================================
   HEADER WORDMARK — a link you can actually hit
   The wordmark already links home on every page, and always has. What it did
   not have was a target worth aiming at: the anchor wraps the text exactly, so
   on a phone it was 212x26 -- 26px tall against a 44px minimum, sitting right
   at the top edge of the screen where thumbs are least accurate. Missing it
   twice reads as "this is not a link".

   Extended with a positioned pseudo-element rather than padding or min-height,
   so the hit area grows without the wordmark moving a pixel or the header
   getting taller.
   ============================================================ */
.site-branding a.brand { position: relative; }
.site-branding a.brand::after {
  content: "";
  position: absolute;
  inset: -12px -10px;
}

/* ============================================================
   PHONE PRODUCT PAGE — smaller vial, name closer to it
   The gallery ran the full column at 338px, which pushed the product name
   most of a screen down. 80% of that is 270px, and the height follows, so
   the name rises ~90px on the image alone.

   The remaining space is the product grid's own 56px row gap, not a margin on
   anything. Pulling the summary up rather than shrinking that gap keeps the
   spacing between summary, tabs and related products where it is -- the gap
   is shared, and editing it would have moved three joins to fix one.
   ============================================================ */
@media (max-width: 640px) {
  /* `width`, not `max-width`. The image is driven by
     `.woocommerce div.product div.images img.wp-post-image { width: 324px
     !important }` at <=900px, with max-width pinned to 100%; capping
     max-width lower does nothing while width holds. 259 was 324 x 0.8; the
     owner's 30 Aug +20% (with the new vial cutouts in) makes it 311
     (310.8 rounded). Height follows on its own. Selector and !important
     matched to the rule being overridden, which is (0,3,3). Stacks are
     untouched: their (0,6,3) !important rule at ~6226 outranks this. */
  .woocommerce div.product.entry div.images img,
  .woocommerce div.product div.images img.wp-post-image {
    width: 311px !important;
  }

  /* Specificity matched to the rules that own this element: `.single-product
     .summary` is (0,2,0) and loses to `.woocommerce div.product div.summary`
     at (0,3,2) however late it appears. */
  .woocommerce div.product div.summary { margin-top: -28px; }
}

/* ============================================================
   PHONE GRID — a product with sizes gets an Add to Cart button
   On a phone the sizes were laid out permanently in the card, so a product
   with options looked nothing like the one beside it: two bordered rows where
   its neighbour had a single button. The button is the entry point again, and
   the sizes drop out of it when tapped -- same shape as every other card until
   the shopper asks for the choice.

   Scoped to `li.product` and `.vl-tile` on purpose. The single-product picker
   shares this markup and must keep its sizes on show, and its rules are
   (0,3,0) -- an unscoped `[hidden]` rule here would be (0,3,0) too and would
   win on source order, collapsing that page's picker as well.
   ============================================================ */
@media (max-width: 640px) {
  /* The button is back, labelled like every other card's. */
  li.product .vl-cartctl--variable .vl-cartctl__btn[data-action="options"],
  .vl-tile .vl-cartctl--variable .vl-cartctl__btn[data-action="options"] {
    display: inline-flex !important;
  }
  li.product .vl-cartctl--variable .vl-cartctl__lbl,
  .vl-tile .vl-cartctl--variable .vl-cartctl__lbl { display: inline; }

  /* Still the way in once something is held, or there would be no route back
     to the other size. The count rides on the button instead. */
  li.product .vl-cartctl--variable.is-active .vl-cartctl__btn[data-action="options"],
  .vl-tile .vl-cartctl--variable.is-active .vl-cartctl__btn[data-action="options"] {
    display: inline-flex !important;
  }
  /* `:not([hidden])` matters: an `!important` display would otherwise beat the
     hidden attribute the markup ships, and an empty cart would show a "0". */
  li.product .vl-cartctl--variable .vl-cartctl__held:not([hidden]),
  .vl-tile .vl-cartctl--variable .vl-cartctl__held:not([hidden]) {
    display: inline-flex !important;
  }

  /* Closed by default: the `hidden` attribute governs again. */
  li.product .vl-cartctl--variable .vl-cartctl__sizes[hidden],
  .vl-tile .vl-cartctl--variable .vl-cartctl__sizes[hidden] {
    display: none !important;
  }

  /* Open: a panel hanging off the button rather than part of the card, so a
     card never changes height and the row's prices stay in line. The card is
     raised by `.vl-sizes-open` (set in inc/shop-archive.php) -- `.vl-cartctl`
     has its own z-index and forms a stacking context, so no z-index here could
     lift this above a card that comes later in the grid. */
  li.product .vl-cartctl--variable .vl-cartctl__sizes:not([hidden]),
  .vl-tile .vl-cartctl--variable .vl-cartctl__sizes:not([hidden]) {
    display: flex !important;
    position: absolute;
    top: calc(100% + 8px);
    left: 50%;
    transform: translateX(-50%);
    z-index: 60;
    width: max-content;
    min-width: 168px;
    max-width: calc(100vw - 2rem);
    padding: .45rem;
    gap: .35rem;
    background: #fff;
    border: 1px solid #E3E9EC;
    border-radius: 12px;
    box-shadow: 0 14px 34px rgba(35, 54, 66, .18);
  }

  /* Rows inside the panel size to the panel, not to the card behind it. */
  li.product .vl-cartctl--variable .vl-cartctl__sizes:not([hidden]) .vl-cartctl__size,
  .vl-tile .vl-cartctl--variable .vl-cartctl__sizes:not([hidden]) .vl-cartctl__size {
    width: 100%;
    max-width: none;
  }

  /* The control now holds a single button, so the reserve that keeps prices
     level across a row comes back down to what a simple product needs. It was
     raised to 111px only because two size rows used to live here. */
  .woocommerce ul.products li.product .vl-cartctl { min-height: 68px; }
}

/* ============================================================
   GIVEAWAY — headline block sits 15px higher
   The negative margin goes on the headline, so the countdown and the prize
   list travel with it; they follow in normal flow with nothing between them
   to hold. The eyebrow above keeps its place.

   `.vl-give` prefix is load-bearing: Kadence's content.min.css carries
   `.single-content h1 { margin-top: 1.5em }` at (0,1,1), which outranks a bare
   `.vl-give__headline` at (0,1,0) -- 1.5em of this headline is exactly the
   76.8px that would not budge. (0,2,0) settles it.

   Expressed as `calc(1.5em - 15px)` rather than a flat number so the 1.5em
   basis is kept: the headline's size is a clamp() that grows with the
   viewport, and a fixed margin would change the relationship at every other
   width. ============================================================ */
.vl-give .vl-give__headline { margin-top: calc(1.5em - 15px); }


/* ============================================================
   HOMEPAGE — reviews and everything after them sit 20px lower
   On the section, so the carousel, the claims below it and every section
   after travel together. Both breakpoints.
   ============================================================ */
.home .vl-reviews { margin-top: 20px; }

/* ============================================================
   REVIEW CARDS — one height
   Fourteen cards ended 32px above the fifteenth. Not the text: every card is
   a <figure>, and Kadence's content.min.css carries
   `.single-content figure { margin-bottom: var(--global-md-spacing) }` at
   (0,1,1), which outranks this theme's own `.vl-review { margin: 0 }` at
   (0,1,0). The last card escapes it, so fourteen cards carried a 32px bottom
   margin and one did not.

   That also explains why `align-items: stretch` looked broken: stretch sizes
   the MARGIN box, so a card of 389px plus 32px of margin already filled the
   421px line and had nothing left to stretch into. Take the margin away and
   they all stretch to the same 389px on their own.

   A bottom margin has no job here anyway -- the cards sit in a horizontal
   row, and the track carries its own padding-bottom for the scrollbar.
   ============================================================ */
.vl-reviews__track .vl-review { margin-bottom: 0; }

/* ============================================================
   PHONE RESOURCES — the three blocks sit 15px lower
   Margin on the grid rather than padding on the section, so the black band
   grows by the same 15px on its own: the grid is in flow, so pushing it down
   makes the section taller and the 48px of bottom padding is carried along
   underneath it. The heading stays where it is, and the gap to the footer is
   unchanged -- the text cannot run into it.
   ============================================================ */
@media (max-width: 640px) {
  /* padding, not margin: the heading above carries a 35.2px bottom margin, and
     adjacent vertical margins collapse to the larger of the two -- a 15px
     margin here simply disappeared into it. Padding does not collapse. */
  .vl-resources__grid { padding-top: 15px; }
}

/* ============================================================
   REVIEW PHOTOS — arrive rather than appear
   On a cold first load the photos snapped from the placeholder tint to full
   opacity in a single frame, one after another, while the strip was being
   scrolled past. A short fade turns fifteen pops into fifteen arrivals.

   Keyed off `.is-fading` on the TRACK, which the script adds. If the script
   never runs the photos are simply visible -- no rule can leave them stranded
   at zero. Anything already cached is marked ready before paint, so a return
   visit never fades at all.
   ============================================================ */
.vl-reviews__track.is-fading .vl-review__photo {
  opacity: 0;
  transition: opacity .34s ease;
}
.vl-reviews__track.is-fading .vl-review__photo.is-ready { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .vl-reviews__track.is-fading .vl-review__photo {
    opacity: 1;
    transition: none;
  }
}

/* ============================================================
   BANNER — edge fade without a mask
   The fade was a mask-image on the marquee. A mask has to be re-applied on
   every frame of the animation inside it, which is main-thread work sixty
   times a second for a purely decorative edge.

   Two gradient overlays do the same job as plain paint: the banner's ground is
   solid white, so white-to-transparent over the top is visually identical to
   masking the content out. They are `pointer-events: none` and sit above the
   track, and nothing about them is animated.

   Both mask declarations are cancelled explicitly -- they are set in two
   earlier blocks, and leaving either would keep the per-frame cost that this
   exists to remove.
   ============================================================ */
.vl-marquee {
  position: relative;
  -webkit-mask-image: none !important;
  mask-image: none !important;
}
.vl-marquee::before,
.vl-marquee::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: var(--vl-fade, 90px);
  pointer-events: none;
  z-index: 2;
}
.vl-marquee::before {
  left: 0;
  background: linear-gradient(to right, #fff 0%, rgba(255,255,255,0) 100%);
}
.vl-marquee::after {
  right: 0;
  background: linear-gradient(to left, #fff 0%, rgba(255,255,255,0) 100%);
}
@media (max-width: 700px) { .vl-marquee { --vl-fade: 48px; } }

/* ============================================================
   BANNER — a shorter track on phones
   The track is sized so its first half always covers the widest viewport it
   might meet -- but almost no one is on the widest viewport, so on everything
   narrower the track is far longer than it needs to be. At a 2x pixel ratio
   that surplus is what pushes the layer past the GPU's texture ceiling and
   starts it tiling.

   Six items give a half of three; four give a half of two. Where that stops
   covering the viewport is set by how wide an item is, and the item font caps
   at 1.45rem, so above that width an item is a fixed ~907px and the four-item
   half is a fixed ~1814px. 1750 leaves ~60px of margin under that ceiling.

   The reason to push it this high rather than sit at a safe 1500: at 1512 the
   six-item track is 5442px, which is 10,883 device pixels at 2x. Chrome tiles a
   layer that wide and rasterises tiles as they scroll into view, and a tile
   that is not ready yet shows stale. Four items make it 3628px -- 7256 device
   pixels -- so the common desktop widths (1440, 1512, 1728) all sit in the
   cheaper branch instead of the expensive one.

   Dropping items 3 and 6 takes one from each half rather than two off the
   tail. That is the whole trick: `translateX(-50%)` only lands the second copy
   on the first if both halves are the same width, so whatever comes out of one
   has to come out of the other. Take two off the end instead and the loop
   jumps once per cycle.
   ============================================================ */
@media (max-width: 1750px) {
  .vl-marquee__item:nth-child(3),
  .vl-marquee__item:nth-child(6) {
    display: none;
  }
  /* Duration follows the shorter run so the rate holds at ~83px/s here. */
  .vl-marquee__track { animation-duration: 18s; }
}
/* A phone's smaller type makes the same four items a shorter run, so it needs
   its own duration to land on the same ~76px/s rather than racing. */
@media (max-width: 700px) {
  .vl-marquee__track { animation-duration: 17.5s; }
}

/* ============================================================
   CALCULATOR SMALL PRINT — hold the page gutter on phones
   Both disclaimers were laid out full-bleed: 0 to 375 on a phone, so the text
   touched both edges while the h1 above and the calculator card below sat at
   an 18px inset. Small grey type running into the bezel is the least readable
   thing on the page, and it read as a mistake rather than as a choice.

   18px is not an arbitrary nudge -- it is the inset the rest of the page
   already uses, so the small print now lines up with the heading above it
   instead of sitting proud of everything.

   Phones only: on desktop the 640px max-width already keeps these well inside
   the gutter, and padding there would only narrow the measure.
   ============================================================ */
@media (max-width: 700px) {
  .vl-calc p.vl-disclaimer,
  .entry-content .vl-disclaimer--calculator,
  .vl-disclaimer--calculator {
    padding-inline: 18px;
  }
}

/* ============================================================
   CHECKOUT ORDER SUMMARY — quantity and remove controls
   Injected by assets/checkout-qty.js into each summary line. The stepper
   deliberately matches the catalogue's: same 28px targets, same hairline
   border, so changing quantity at the payment step feels like the same control
   the customer already used to get here.

   `is-busy` is set while the Store API request is in flight. It dims rather
   than hides, because collapsing the control would shift the summary under the
   cursor mid-click.
   ============================================================ */
.vl-ckqty {
  display: flex;
  align-items: center;
  gap: .75rem;
  margin-top: .5rem;
}
.vl-ckqty__stepper {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--vl-rule, #e3e3e3);
  border-radius: 999px;
  overflow: hidden;
}
.vl-ckqty__btn {
  width: 28px;
  height: 28px;
  padding: 0;
  border: 0;
  background: transparent;
  font-size: 1rem;
  line-height: 1;
  color: var(--vl-ink, #111);
  cursor: pointer;
}
.vl-ckqty__btn:hover:not(:disabled) { background: var(--vl-paper, #f5f6f7); }
.vl-ckqty__btn:disabled { opacity: .35; cursor: default; }
.vl-ckqty__num {
  min-width: 2ch;
  padding: 0 .1rem;
  text-align: center;
  font-size: .85rem;
  font-variant-numeric: tabular-nums;
}
.vl-ckqty__remove {
  width: auto;
  padding: 0 .25rem;
  border: 0;
  background: none;
  font-size: .78rem;
  color: var(--vl-grey, #767676);
  text-decoration: underline;
  cursor: pointer;
}
.vl-ckqty__remove:hover:not(:disabled) { color: var(--vl-ink, #111); }
.vl-ckqty__remove:disabled { opacity: .35; cursor: default; }
.vl-ckqty.is-busy { opacity: .5; }

/* The summary line is a tight three-column grid; the controls belong under the
   name, not squeezed beside the price. */
.wc-block-components-order-summary-item__description .vl-ckqty { flex-wrap: wrap; }

/* ============================================================
   HOMEPAGE — "Shop All Research Peptides" sits 5px higher
   A negative margin rather than trimming the block's padding-top: that padding
   is set in rem and shared with the section's own rhythm, so subtracting a flat
   5px from it would drift at other root font sizes. This says what it means and
   leaves the padding alone.
   ============================================================ */
.home .vl-section__cta { margin-top: -15px; }

/* ============================================================
   HOMEPAGE — "Why Vistara Labs?" sits 10px lower on phones
   Phones only, so it is written as margin-top rather than added to the
   section's padding-top: that padding is shared by every .vl-section and
   changing it here would need overriding back at every other breakpoint.
   ============================================================ */
@media (max-width: 700px) {
  .home .vl-section.vl-why { margin-top: 10px; }
}

/* ============================================================
   PAYMENT INSTRUCTIONS — stack the detail rows on phones
   The table is laid out with inline styles because the same markup is posted
   into the confirmation email, where a stylesheet does not survive. Those
   inline styles give the label column `white-space:nowrap` and a 26px gutter,
   which on a 375px screen left about 130px for a 25-character email address
   with no natural break point in it -- so it ran off the side of the page.

   On a phone the pairs read better stacked than squeezed into two columns, so
   the cells become blocks: label above, value beneath, full width for both.

   `!important` throughout, and not by preference: the values being overridden
   are inline attributes, which outrank any selector without it.
   ============================================================ */
@media (max-width: 700px) {
  .vl-payment-details,
  .vl-payment-details tbody,
  .vl-payment-details tr,
  .vl-payment-details td {
    display: block !important;
    width: 100% !important;
  }
  .vl-payment-details td:first-child {
    white-space: normal !important;
    padding: 0 !important;
    font-size: .85rem;
    color: var(--vl-grey, #767676);
  }
  .vl-payment-details td:last-child {
    padding: 0 0 .85rem !important;
    overflow-wrap: anywhere;
    word-break: break-word;
  }
  .vl-payment-details tr:last-child td:last-child { padding-bottom: 0 !important; }
}

/* ============================================================
   BANNER — phones only
   The marquee is off above 700px. On desktop it shimmered as it scrolled and
   no amount of work on the animation fixed it: not shortening the track, not
   the compositing hints, not an opaque layer, not stepping it to whole device
   pixels, not reverting to the original declaration, and not scrolling a
   pre-rendered bitmap of the text instead of the text. It is being rebuilt
   rather than patched again.

   Mobile is untouched because mobile was never affected -- smaller type, and
   nothing about it has ever been reported as wrong.

   `display: none`, so it takes no height and leaves no gap where it was. The
   markup still renders; only this rule decides whether it is shown, which
   keeps the rebuild to one place.
   ============================================================ */
@media (min-width: 701px) {
  .vl-marquee { display: none; }
  /* The catalogue's footer marquee sits in its own wrapper, and that wrapper
     carries the 3.5rem that separates it from the content above. Hiding only
     the marquee left the wrapper behind at zero height still contributing 56px
     of margin -- a gap above the footer with nothing in it. */
  .vl-marquee-foot { display: none; }
}

/* ============================================================
   DESKTOP TICKER
   The banner that runs from 701px up. The phone banner (.vl-marquee) is a
   separate component and is untouched by anything here.

   Written from nothing rather than by reusing .vl-marquee, whose rules carry
   every attempt at fixing the old banner. Three of those attempts are
   deliberately absent and should stay absent:

     - no `will-change` or `backface-visibility` on the track. Pinning it to its
       own layer means the text is rasterised once and that bitmap resampled as
       it moves, which is what made the old one shimmer.
     - no padding on the track. The loop travels -50% of the track's own width;
       padding is inside that width, so it overshoots by half of it every cycle.
       The gap between repeats is item padding instead.
     - no background on the track, which was only ever there to make a pinned
       layer behave.
   ============================================================ */
.vl-ticker { display: none; }

@media (min-width: 701px) {
  .vl-ticker {
    display: block;
    overflow: hidden;
    padding: 0.95rem 0;
    background: #fff;

    /* The fade at both gutters. A mask rather than gradient overlays: overlays
       have to match whatever sits behind them, and this band is used on three
       pages against two different backgrounds. A mask asks nothing of them. */
    --vl-ticker-fade: 110px;
    -webkit-mask-image: linear-gradient(
      to right,
      transparent 0,
      #000 var(--vl-ticker-fade),
      #000 calc(100% - var(--vl-ticker-fade)),
      transparent 100%
    );
    mask-image: linear-gradient(
      to right,
      transparent 0,
      #000 var(--vl-ticker-fade),
      #000 calc(100% - var(--vl-ticker-fade)),
      transparent 100%
    );
  }

  .vl-ticker__track {
    display: flex;
    width: max-content;
    /* 28.3s, not 34s: same distance in 1/1.2 of the time, so 20% faster. */
    animation: vl-ticker-scroll 28.3s linear infinite;
  }

  .vl-ticker__item {
    flex: 0 0 auto;
    /*
     * Three choices here are about how visible the shimmer is rather than how
     * the animation runs, because the animation measures clean and the problem
     * is what the eye catches on a moving edge.
     *
     * Weight was raised to 500 to show the shimmer less -- a thicker stroke is
     * proportionally less edge, and it is edge pixels that change shade as a
     * glyph moves between subpixel positions. It read too heavy, so it is back
     * at 400. The colour and tracking below still do some of that work.
     *
     * Slate rather than pure black: #000 on #fff is the maximum-contrast case,
     * so every antialiased edge pixel swings the full range every frame.
     * Dropping to the brand ink cuts that swing by about a fifth without
     * reading as grey.
     *
     * Normal tracking rather than the site's negative default: tighter letters
     * put more edges per inch into the same moving band.
     */
    font-weight: 400;
    color: #233642;
    letter-spacing: 0;
    /* Half the in-message gap, so the space between one repeat and the next
       reads the same as the space inside it. Padding on the item is safe --
       it is inside the item's own width, so the two halves still measure
       equal. Padding on the track is what is not. */
    padding: 0 2.7rem;
    font-size: clamp(1rem, 0.85rem + 0.6vw, 1.45rem);
    /* The gap in the message is literal spaces; `nowrap` would collapse them. */
    white-space: pre;
  }

  /* Right to left. Both ends are percentages: a length interpolating to a
     percentage is not something the compositor can take on directly. */
  @keyframes vl-ticker-scroll {
    from { transform: translateX(0%); }
    to   { transform: translateX(-50%); }
  }

  @media (prefers-reduced-motion: reduce) {
    .vl-ticker__track { animation: none; width: 100%; justify-content: center; }
    .vl-ticker__item:not(:first-child) { display: none; }
  }

  /* ---- Room underneath -------------------------------------------------
     The band pushes everything below it down by its own height, and sitting
     hard against the next thing it reads as cramped. Each placement gets the
     clearance its neighbour wants rather than one shared number: the hero has
     its own generous top spacing already, a product grid has none, and the
     stacks note is body copy that wants a paragraph's worth. */
  .vl-ticker--home   { margin-bottom: 26px; }
  .vl-ticker--shop   { margin: 22px 0 30px; }
  .vl-ticker--stacks { margin: 26px 0 34px; }
}

/* ============================================================
   CERTIFICATE OF ANALYSIS — one quiet row on the product page
   Rendered by inc/coa.php only when a product has a purity figure or a COA
   link; a product with neither emits nothing. Deliberately understated: it
   is proof, not a promotion, and it reads best as a matter-of-fact line.
   ============================================================ */
.vl-coa {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .4rem 1.4rem;
  margin: .9rem 0 .2rem;
  padding-top: .8rem;
  border-top: 1px solid var(--vl-rule, #e3e3e3);
  font-size: .92rem;
}
.vl-coa__purity {
  font-weight: 600;
  color: var(--vl-slate, #2B3E51);
}
.vl-coa__link {
  color: var(--vl-teal-dark, #158081);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.vl-coa__link:hover { color: var(--vl-slate, #2B3E51); }

/* ============================================================
   Best Sellers — desktop-only position swap (>=1201px, the 6-across row):
   GHK-Cu (DOM tile 2) and BPC-157 + TB-500 (DOM tile 3) trade places.
   Scoped to the wide single-row layout so the mobile/tablet order,
   which follows the shortcode slug list, is left untouched.
   ============================================================ */
@media (min-width: 1201px) {
  .vl-bestsellers--6 .vl-tile:nth-child(1) { order: 1; }
  .vl-bestsellers--6 .vl-tile:nth-child(2) { order: 3; } /* GHK-Cu -> position 3 */
  .vl-bestsellers--6 .vl-tile:nth-child(3) { order: 2; } /* BPC-157 + TB-500 -> position 2 */
  .vl-bestsellers--6 .vl-tile:nth-child(4) { order: 4; }
  .vl-bestsellers--6 .vl-tile:nth-child(5) { order: 5; }
  .vl-bestsellers--6 .vl-tile:nth-child(6) { order: 6; }
}

/* View All Peptides CTA sits higher on desktop only (>=1201px);
   mobile/tablet keep the -15px set above. */
@media (min-width: 1201px) {
  .home .vl-section__cta { margin-top: -45px; }
}

/* ============================================================
   RESEARCH NOTES — article + archive styling.

   Posts previously fell through to the Kadence default: body copy
   ran the full window width with no measure, which is unreadable at
   1500px. This gives articles the site's own type and colour tokens
   and a proper measure, without importing the homepage furniture
   (marquee, hero, product tiles) that belongs only on the front page.
   ============================================================ */

/* --- shared shell for single posts and the archive --- */
.single-post .entry-content,
.single-post .entry-header,
.blog .entry-header,
.archive.category .entry-header {
  max-width: 44rem;
  margin-left: auto;
  margin-right: auto;
  padding-left: var(--vl-gutter, 1.5rem);
  padding-right: var(--vl-gutter, 1.5rem);
}

/* --- title --- */
.single-post .entry-title {
  font-family: var(--vl-font);
  font-weight: 700;
  letter-spacing: var(--vl-tracking, -0.02em);
  color: var(--vl-slate, #2B3E51);
  font-size: clamp(1.9rem, 4vw, 2.6rem);
  line-height: 1.15;
  text-wrap: balance;
  margin: 0 0 .6rem;
}

/* --- byline: keep the date and category, drop the author link.
       The account behind the site is not a public identity. --- */
.single-post .entry-meta .entry-author-wrap,
.single-post .entry-meta .posted-by { display: none; }
.single-post .entry-meta,
.single-post .entry-taxonomies {
  font-family: var(--vl-font);
  font-size: .82rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--vl-grey, #666);
}
.single-post .entry-taxonomies a { color: var(--vl-blue, #21609D); text-decoration: none; }
.single-post .entry-taxonomies a:hover { text-decoration: underline; }

/* --- body copy --- */
.single-post .entry-content {
  font-family: var(--vl-font);
  font-size: 1.06rem;
  line-height: 1.72;
  color: #24303B;
}
.single-post .entry-content > p { margin: 0 0 1.15rem; }

/* Opening paragraph carries the summary answer, so it is set larger:
   it is what an AI overview or a featured snippet lifts. */
.single-post .entry-content > p:first-of-type {
  font-size: 1.2rem;
  line-height: 1.6;
  color: var(--vl-slate, #2B3E51);
}

.single-post .entry-content h2 {
  font-family: var(--vl-font);
  font-weight: 700;
  letter-spacing: var(--vl-tracking, -0.02em);
  color: var(--vl-slate, #2B3E51);
  font-size: 1.45rem;
  line-height: 1.25;
  margin: 2.4rem 0 .7rem;
}
.single-post .entry-content h3 {
  font-family: var(--vl-font);
  font-weight: 700;
  color: var(--vl-slate-mid, #314D5D);
  font-size: 1.12rem;
  margin: 1.8rem 0 .5rem;
}

.single-post .entry-content a {
  color: var(--vl-blue, #21609D);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
}
.single-post .entry-content a:hover { color: #3178A0; }

.single-post .entry-content ul,
.single-post .entry-content ol { margin: 0 0 1.15rem; padding-left: 1.3rem; }
.single-post .entry-content li { margin-bottom: .45rem; }

.single-post .entry-content strong { color: var(--vl-slate-deep, #233642); }

/* Pull-quote style aside for the one fact worth stopping on. */
.single-post .entry-content .vl-note {
  border-left: 3px solid var(--vl-gold, #C09159);
  background: var(--vl-paper, #F6F7F6);
  padding: .9rem 1.1rem;
  margin: 1.6rem 0;
  font-size: 1rem;
  color: var(--vl-slate, #2B3E51);
}
.single-post .entry-content .vl-note p { margin: 0; }

/* Research-use notice, set quiet and clearly separated. */
.single-post .entry-content .vl-ruo {
  border-top: 1px solid var(--vl-rule, #DBDBDB);
  margin-top: 2.4rem;
  padding-top: 1rem;
  font-size: .84rem;
  line-height: 1.6;
  color: var(--vl-grey, #666);
}
.single-post .entry-content .vl-ruo p { margin: 0; }

/* --- archive (Research Notes index) --- */
.blog .site-main, .archive.category .site-main { padding-top: 2rem; }
.blog .entry-title, .archive.category .entry-title {
  font-family: var(--vl-font);
  font-weight: 700;
  letter-spacing: var(--vl-tracking, -0.02em);
  color: var(--vl-slate, #2B3E51);
  line-height: 1.2;
}
.blog .entry-title a, .archive.category .entry-title a { color: inherit; text-decoration: none; }
.blog .entry-title a:hover, .archive.category .entry-title a:hover { color: var(--vl-blue, #21609D); }
.blog .entry-author-wrap, .archive.category .entry-author-wrap,
.blog .posted-by, .archive.category .posted-by { display: none; }

@media (max-width: 640px) {
  .single-post .entry-content { font-size: 1.02rem; }
  .single-post .entry-content > p:first-of-type { font-size: 1.1rem; }
}

/* ============================================================
   RESEARCH NOTES — specimen-plate hero + centered header.

   Kadence renders the featured image as a 2:3 ratio-cropped banner
   behind the title, which turned a 1086x1448 transparent vial cutout
   into a full-width strip of cropped label text. These rules disable
   the crop and set the vial as a contained specimen on a quiet paper
   band, with the header centered above it.
   ============================================================ */

/* --- centered header, editorial style --- */
.single-post .entry-header { text-align: center; }
.single-post .entry-header .entry-taxonomies {
  display: block;
  margin-bottom: .4rem;
}
.single-post .entry-header .entry-meta { justify-content: center; }

/* --- specimen plate: kill the ratio crop, contain the vial --- */
.single-post .post-thumbnail.article-post-thumbnail {
  background: var(--vl-paper, #F6F7F6);
  max-width: 100%;
  height: auto !important;
  margin: 0 0 2.6rem;
  padding: 3.2rem 1rem;
}
.single-post .post-thumbnail.article-post-thumbnail .post-thumbnail-inner {
  position: static !important;
  padding: 0 !important;
  height: auto !important;
  inset: auto !important;
}
.single-post .post-thumbnail.article-post-thumbnail img {
  position: static !important;
  width: auto !important;
  max-width: 100%;
  height: 360px !important;
  object-fit: contain;
  display: block;
  margin: 0 auto;
}
@media (max-width: 640px) {
  .single-post .post-thumbnail.article-post-thumbnail { padding: 2.2rem 1rem; }
  .single-post .post-thumbnail.article-post-thumbnail img { height: 264px !important; }
}

/* --- archive cards: same containment, tile-sized --- */
.blog .post-thumbnail, .archive.category .post-thumbnail {
  background: var(--vl-paper, #F6F7F6);
}
.blog .post-thumbnail .post-thumbnail-inner,
.archive.category .post-thumbnail .post-thumbnail-inner {
  position: static !important;
  padding: 1.4rem 1rem !important;
  height: auto !important;
  inset: auto !important;
}
.blog .post-thumbnail img, .archive.category .post-thumbnail img {
  position: static !important;
  width: auto !important;
  max-width: 100%;
  height: 170px !important;
  object-fit: contain;
  display: block;
  margin: 0 auto;
}

/* Archive tiles: the ratio class also pads .post-thumbnail itself (66% =
   ~330px of empty paper below a 170px vial). Kill it so tiles hug the vial. */
.blog .post-thumbnail, .archive.category .post-thumbnail {
  padding-bottom: 0 !important;
  height: auto !important;
}

/* ------------------------------------------------------------
   Bigger article vials on desktop -- owner, 1 Sep: the headline
   image is too small, on the post and on the category grid.
   Plus 25 percent on both: 360 -> 450 and 170 -> 212.

   Both bumps are min-width bounded on purpose. The phone sizes
   set above -- 264px single post below 640, 110px blog tile
   below 599 -- were tuned separately and must survive
   untouched. Raising the base values instead would have pushed
   the category grid on phones from 170 to 212, which nobody
   asked for.
   ------------------------------------------------------------ */
@media (min-width: 641px) {
  .single-post .post-thumbnail.article-post-thumbnail img { height: 450px !important; }
}
@media (min-width: 600px) {
  .blog .post-thumbnail img,
  .archive.category .post-thumbnail img { height: 212px !important; }
}
.blog .post-thumbnail .post-thumbnail-inner,
.archive.category .post-thumbnail .post-thumbnail-inner {
  padding: 1.6rem 1rem !important;
}

/* ============================================================
   RESEARCH NOTES — gutters and Similar Posts containment.

   Three things ran full-bleed to the viewport edge: the archive grid
   (main.wrap has no padding), the prev/next post navigation, and the
   Similar Posts band (.entry-related.alignfull). The Similar Posts
   thumbnails also missed the earlier containment rules, which were
   scoped to .article-post-thumbnail, so the ratio crop blew the vials
   out of frame. Gutters follow the homepage rhythm (5vw, 1440px max).
   ============================================================ */

/* --- archive grid gutters --- */
.blog main.wrap, .archive.category main.wrap {
  max-width: 1440px;
  margin-left: auto;
  margin-right: auto;
  padding-left: 5vw;
  padding-right: 5vw;
  box-sizing: border-box;
}

/* --- prev/next navigation gutters on articles --- */
.single-post .post-navigation {
  max-width: 1440px;
  margin-left: auto;
  margin-right: auto;
  padding-left: 5vw;
  padding-right: 5vw;
  box-sizing: border-box;
}

/* --- Similar Posts band: gutters + heading breathing room --- */
.single-post .entry-related.alignfull {
  padding-left: 5vw;
  padding-right: 5vw;
  box-sizing: border-box;
}

/* --- Similar Posts vials: same specimen treatment, tile-sized --- */
.single-post .entry-related .post-thumbnail {
  background: var(--vl-paper, #F6F7F6);
  padding: 1.4rem 1rem !important;
  height: auto !important;
}
.single-post .entry-related .post-thumbnail .post-thumbnail-inner {
  position: static !important;
  padding: 0 !important;
  height: auto !important;
  inset: auto !important;
}
.single-post .entry-related .post-thumbnail img {
  position: static !important;
  width: auto !important;
  max-width: 100%;
  height: 150px !important;
  object-fit: contain;
  display: block;
  margin: 0 auto;
}

/* Similar Posts: Kadence's alignwide pulls the inner content back out past
   the section gutters with negative margins (-130px). Neutralise it so the
   heading and cards respect the 5vw padding. */
.single-post .entry-related .entry-related-inner-content.alignwide {
  margin-left: 0 !important;
  margin-right: 0 !important;
}

/* ============================================================
   RESEARCH NOTES — mobile archive: two tiles across, like the shop.

   Kadence's posts grid collapses to one column on phones, so the
   newest post's full card fills the screen and the archive reads as
   a single article rather than an index. Mirror the product-grid
   pattern instead: two compact tiles per row, vial + title + date,
   excerpt and read-more hidden. Tap the tile to open the article.
   ============================================================ */
@media (max-width: 599px) {
  .blog .kadence-posts-list.grid-cols {
    display: grid !important;
    grid-template-columns: 1fr 1fr !important;
    gap: 1.1rem 0.8rem !important;
  }
  .blog .kadence-posts-list .entry-list-item { width: 100% !important; margin: 0 !important; }
  .blog .kadence-posts-list article { height: 100%; }

  /* compact card: no excerpt, no footer/read-more */
  .blog .kadence-posts-list .entry-summary,
  .blog .kadence-posts-list .entry-footer { display: none !important; }

  .blog .kadence-posts-list .entry-content-wrap { padding: 0.7rem 0.6rem 0.9rem !important; }
  .blog .kadence-posts-list .entry-title { font-size: 0.92rem; line-height: 1.3; margin: 0.15rem 0 0.2rem; }
  .blog .kadence-posts-list .entry-taxonomies { font-size: 0.62rem; letter-spacing: 0.05em; }
  .blog .kadence-posts-list .entry-meta { font-size: 0.68rem; }

  .blog .post-thumbnail img { height: 110px !important; }
  .blog .post-thumbnail .post-thumbnail-inner { padding: 0.9rem 0.5rem !important; }
}

/* Article shell on phones: the theme sets --vl-gutter to 0px at mobile
   widths, and a var() fallback only applies when the variable is UNSET,
   not when it is zero - so the 44rem shell collapsed to edge-to-edge
   text. Explicit padding, independent of the token. */
@media (max-width: 767px) {
  .single-post .entry-content,
  .single-post .entry-header {
    padding-left: 1.25rem !important;
    padding-right: 1.25rem !important;
  }
}

/* Best Sellers row centered between headline and CTA (desktop) */
@media (min-width: 1201px) {
  .home .vl-bestsellers--6 { margin-top: 58px; }
  .home .vl-section__cta { margin-top: -19px; }
}

/* Client Reviews section: extra space under the CTA (mobile) */
@media (max-width: 767px) {
  .home .vl-section__cta + .vl-section { margin-top: 20px; }
}

/* Verified product reviews */
.vl-prodreviews { max-width: 44rem; margin: 48px auto 24px; padding: 0 1.25rem; }
.vl-prodreviews .vl-section__title { text-align: center; margin-bottom: 18px; }
.vl-prodreviews .commentlist { list-style: none; margin: 0; padding: 0; }
.vl-prodreviews .commentlist li { border-top: 1px solid var(--vl-rule, #DBDBDB); padding: 18px 0; }
.vl-prodreviews .comment_container img.avatar { display: none; }
.vl-prodreviews .comment-text { margin: 0; }
.vl-prodreviews .star-rating { color: var(--vl-gold, #C09159); }
.vl-prodreviews .meta { color: var(--vl-grey, #666); font-size: .9rem; }
.vl-prodreviews .comment-reply-title { font-weight: 600; color: var(--vl-slate, #2B3E51); display:block; margin-bottom: 6px; }
.vl-prodreviews .comment-form label { display: block; margin: 10px 0 4px; color: var(--vl-slate, #2B3E51); }
.vl-prodreviews .comment-form input[type=text], .vl-prodreviews .comment-form input[type=email], .vl-prodreviews .comment-form textarea { width: 100%; border: 1px solid var(--vl-rule, #DBDBDB); padding: 10px; font: inherit; }
.vl-prodreviews .form-submit .submit { background: var(--vl-slate, #2B3E51); color: #fff; border: 0; padding: 12px 22px; cursor: pointer; font: inherit; }
.vl-prodreviews .form-submit .submit:hover { background: var(--vl-blue, #21609D); }
.vl-prodreviews .woocommerce-Reviews-title { display: none; }

/* Variant A header polish: airy nav + light dropdown */
@media (min-width: 1025px) {
  #masthead .main-navigation > div > ul > li > a,
  .site-header .primary-menu-container > ul > li > a { letter-spacing: .05em; }
}
.site-header ul.sub-menu, #masthead ul.sub-menu {
  background: #fff !important;
  border: 1px solid var(--vl-rule, #DBDBDB);
  box-shadow: 0 12px 32px rgba(43,62,81,.12);
  min-width: 220px;
  padding: 6px 0;
}
.site-header ul.sub-menu li a, #masthead ul.sub-menu li a {
  color: var(--vl-slate, #2B3E51) !important;
  background: transparent !important;
  padding: 10px 18px;
  font-size: .95rem;
}
.site-header ul.sub-menu li a:hover, #masthead ul.sub-menu li a:hover {
  color: var(--vl-blue, #21609D) !important;
  background: var(--vl-paper, #F6F7F6) !important;
}

/* Best Sellers title +10% on mobile (2rem floor -> 2.2rem) */
@media (max-width: 767px) {
  .home .vl-section--tint .vl-section__title { font-size: 2.2rem; }
}

/* Client Reviews block (and everything after) down 15px on desktop */
@media (min-width: 1201px) {
  .home .vl-section__cta + .vl-section { margin-top: 15px; }
}

/* Mobile hero buttons reclaim the trust-line gap (removed Aug 26) */
@media (max-width: 767px) {
  .home .vl-hero__actions { margin-top: 3.9rem; }
}

/* ============================================================
   TILE CART BUTTON — icon only (Aug 26)
   The labelled pill becomes a round cart icon, echoing the floating
   cart button. Markup untouched: labels are hidden and the button is
   a 42px circle, so the hover reveal, AJAX add, stepper swap and
   aria-labels all carry on exactly as before. Scoped to <button> so
   the text-bearing "Sold Out" span and "View product" link keep
   their pill shape.
   ============================================================ */
button.vl-cartctl__btn {
  width: 42px; height: 42px; min-width: 42px;
  padding: 0; gap: 0; border-radius: 50%;
}
button.vl-cartctl__btn > span { display: none !important; }
button.vl-cartctl__btn svg { width: 20px; height: 20px; }


/* ============================================================
   MOBILE CARD CART BUTTON - small circle + badge on its corner (Aug 26)
   Owner phone review, two fixes:
   (1) The icon-only button still inherited the 44px tap-target
       min-height (and on home tiles the old auto-width pill padding),
       so it rendered as a pill. Now a true 36px circle on every card
       and tile.
   (2) The held-count badge anchors to the control's right edge, but
       the control can span the whole card while the button sits
       centred inside it, floating the badge at the card corner.
       Every control centres its button, so 50% of the control IS the
       button centre: anchoring off the centre line pins the badge to
       the button's top-right corner in both geometries.
   ============================================================ */
@media (max-width: 640px) {
  html body button.vl-cartctl__btn,
  html body .vl-tile__actions button.vl-cartctl__btn[data-action="add"],
  html body .vl-tile__actions button.vl-cartctl__btn[data-action="options"] {
    width: 36px; height: 36px; min-width: 36px; min-height: 36px;
    padding: 0; gap: 0; border-radius: 50%;
  }

  li.product .vl-cartctl--variable .vl-cartctl__held,
  .vl-tile .vl-cartctl--variable .vl-cartctl__held {
    right: auto;
    left: calc(50% + 8px);
  }
  /* Loop cards centre the button inside a 68px-tall control... */
  li.product .vl-cartctl--variable .vl-cartctl__held { top: 10px; }
  /* ...tiles shrink-wrap the control to the button itself. */
  .vl-tile .vl-cartctl--variable .vl-cartctl__held { top: -6px; }
}

/* ============================================================
   ORDER RECEIVED - owner-requested spacing (Aug 26)
   "Additional information" sits 20px lower; padding rather than
   margin so the shift is exact regardless of block-gap margins.
   The e-transfer note also gets air after the payment details
   table (inline-styled in inc/gateways.php too, for the email
   copy of the same markup).
   ============================================================ */
.wp-block-woocommerce-order-confirmation-additional-fields-wrapper,
.wc-block-order-confirmation-additional-fields-wrapper { padding-top: 20px; }

.vl-payment-instructions .vl-payment-note { margin-top: 14px; }

/* ============================================================
   HOMEPAGE - View All Peptides CTA (Aug 26, owner request)
   Matches the hero's Enter $500 Giveaway ghost button on desktop
   only (>=1201px, same breakpoint as its position rule above);
   mobile and tablet keep the solid black. Hover stays the shared
   brand-blue rule.
   ============================================================ */
@media (min-width: 1201px) {
  .home .vl-section__cta .vl-btn--solid {
    background: transparent;
    color: #000;
    border: 1px solid #000;
  }
}

/* ============================================================
   LANGUAGE SWITCHER DROPDOWN - hug the item (Aug 26, owner request)
   The shared header dropdown panel is 220px for the Explore menu;
   the language list holds one short row, so the panel shrinks to
   its content and right-aligns under the switcher item.
   ============================================================ */
.site-header li.current-language-menu-item ul.sub-menu,
#masthead li.current-language-menu-item ul.sub-menu {
  min-width: 0 !important;
  width: max-content !important;
  left: auto !important;
  right: 0 !important;
}
.site-header li.current-language-menu-item ul.sub-menu li a,
#masthead li.current-language-menu-item ul.sub-menu li a {
  padding: 10px 14px;
  white-space: nowrap;
}

/* The 200px Kadence submenu width lives on the inner li/anchor, not the
   panel itself, so those must shrink too for the language dropdown. */
.site-header li.current-language-menu-item ul.sub-menu li,
#masthead li.current-language-menu-item ul.sub-menu li,
.site-header li.current-language-menu-item ul.sub-menu li a,
#masthead li.current-language-menu-item ul.sub-menu li a {
  width: auto !important;
  min-width: 0 !important;
}

/* ============================================================
   SINGLE PRODUCT PAGE - cart glyph beside the steppers (Aug 26)
   TEST on GHK-Cu (postid-56) and BPC-157+TB-500 (rowcart markup
   gated to id 46 in inc/shop-archive.php) before going site-wide.
   The bare quantity stepper read as a readout, not a buy control;
   a customer hunting for "Add to Cart" found nothing. The
   catalogue cart icon now sits beside each stepper: on the simple
   layout it is the control's own hidden button turned back on
   (clicking adds 1), on variable rows a per-size button wired to
   the same inc action as the "+".
   ============================================================ */
body.postid-56 .vl-sizepick--simple .vl-cartctl { align-items: center; gap: .6rem; }
body.postid-56 .vl-sizepick--simple .vl-cartctl__btn {
  display: inline-flex !important;
  order: 2;
  width: 42px; height: 42px; min-width: 42px; min-height: 42px;
  padding: 0; gap: 0; border-radius: 50%;
}

.vl-cartctl__rowcart {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; min-width: 40px;
  border: 0; border-radius: 50%;
  background: #314D5D; color: #fff;
  cursor: pointer; flex: 0 0 auto;
  transition: background-color .18s ease;
}
.vl-cartctl__rowcart:hover { background: #21609D; }

/* The row is a flex container, so an unconstrained SVG child gets shrunk to
   zero width and the circle stretches to an oval. Both need a fixed basis. */
.vl-cartctl__rowcart {
  flex: 0 0 40px;
  height: 40px;
  box-sizing: border-box;
}
.vl-cartctl__rowcart svg { flex: 0 0 auto; }

/* The theme gives every <button> 16px/21px padding, which beat the shorthand
   above and forced the circle 2px wide. */
.vl-cartctl__rowcart { padding: 0 !important; width: 40px !important; }

/* ---------------------------------------------------------------------------
   Stacks page: image treatment and grid.

   This MUST live at the end of the file. An earlier rule (~line 2503) sets
   `.vl-stacks .vl-tile__img img { height: 253px; object-fit: contain }` with
   identical specificity, so only later source order beats it — putting this
   treatment further up the file silently loses to that rule.

   The composites anchor the vial row to the bottom of a square canvas, so a
   bottom-anchored cover window shows the vials plus a small pad and crops only
   whitespace. Applied at every width: the 253px contain box showed the whole
   square on phones — mostly empty canvas — which read as huge vertical gaps
   between stacks and shrank the vials to nothing.
   --------------------------------------------------------------------------- */
.vl-stacks .vl-tile__img img {
  width: 100%;
  height: auto;
  aspect-ratio: 5 / 3;
  object-fit: cover;
  object-position: 50% 100%;
}

@media (min-width: 1201px) {
  .vl-stacks { grid-template-columns: repeat(3, 1fr); }
}

/* Phones: one stack per row. Two-up left each tile ~162px wide, far too narrow
   for the vial labels to be legible, which defeats the point of the images.

   !important is required, not decorative: two earlier rules in this file set
   the column count with !important (a 3-col rule at max-width 900px and a
   2-col rule at max-width 640px), and a plain declaration loses to both no
   matter how late it appears. */
@media (max-width: 640px) {
  .vl-stacks { grid-template-columns: 1fr !important; }
}

/* ---------------------------------------------------------------------------
   Single STACK product page: size the gallery for a landscape composite.

   The rules above size single-product images for one PORTRAIT vial — 506px on
   desktop, 324px under 900, 259px under 640, all !important. A stack composite
   is a three-vial landscape row on a square canvas, so those widths rendered
   the vials tiny inside a mostly-empty square (111px tall vials on a phone).

   Fill the gallery column instead and crop to the vial row, the same treatment
   the stack tiles use. Higher specificity AND !important are both required to
   beat the fixed widths: `.product_cat-stacks` sits on the same div.product.entry
   element, so adding it outranks `.woocommerce div.product.entry div.images img`.
   --------------------------------------------------------------------------- */
.woocommerce div.product.entry.product_cat-stacks div.images img:not(.zoomImg) {
  width: 100% !important;
  max-width: 100% !important;
  height: auto !important;
  aspect-ratio: 5 / 3;
  object-fit: cover;
  object-position: 50% 100%;
}

/* ---------------------------------------------------------------------------
   Checkout and cart gutters (RESTORED + widened).

   The original gutter rule from 28 Aug was lost during the home.css
   restore incident later that night - the checkout form went back to sitting
   flush against the viewport edges. Same fix as before: pad the inner
   WooCommerce wrapper (the outer containers are zeroed by rules that beat
   plain declarations, this element is untouched), using var(--vl-edge) so the
   gutter matches the page titles at every breakpoint.

   New on top of the restore: a wider gap between the billing column and the
   order-summary column - they were separated by only 32px a side, which read
   as no gap at all. Scoped to widths where the two columns sit side by side.
   --------------------------------------------------------------------------- */
.woocommerce-checkout .entry-content .woocommerce,
.woocommerce-cart .entry-content .woocommerce {
  padding-inline: var(--vl-edge);
}

@media (min-width: 900px) {
  .woocommerce-checkout #customer_details { padding-right: 3rem; }
  .woocommerce-checkout .woocommerce-checkout-review-order,
  .woocommerce-checkout #order_review { padding-left: 3rem; }
}

/* ============================================================
   PHASE 3 — WHEELED CART IDENTITY + A PERMANENT BUY CONTROL
   (Aug 29)

   Everything below has to beat something written earlier in this
   file, several of those with !important, so it lives at the end
   and each block names what it is beating and roughly where that
   rule sits. Nothing above this line was edited.

   Three changes:
     1. the cart controls take the brand blue,
     2. the control is visible at every width -- no hover gate,
     3. a variable product's sizes are a popover at every width,
        not a permanent pair of rows inside the card.

   ONE NOTE ON THE `html ` PREFIXES BELOW. They are the cheapest
   way to add specificity without inventing a class, and `html `
   is used rather than `html body ` on purpose: WooCommerce puts
   `.woocommerce` on the BODY element, so `html body .woocommerce`
   asks for a `.woocommerce` INSIDE the body and matches nothing
   on the catalogue. `html .woocommerce` matches whether the class
   sits on the body or on a wrapper, and still adds the type
   selector these rules need to win.
   ============================================================ */

/* ---- 1. Brand blue on the buy controls ----------------------
   The controls were #314D5D slate with a #21609D hover, which
   put the brand blue on the one state a phone never shows. The
   trolley is the site's buy affordance now, so blue is the
   resting colour and the hover darkens instead of changing hue.

   No !important: `.vl-cartctl__btn` (~2068) and the tile pair
   `.vl-tile__actions .vl-cartctl__btn[data-action=...]` (~2905)
   are plain declarations at (0,1,0) and (0,3,0). These match
   those specificities and come later, so source order settles
   it. `.vl-cartctl__btn.is-disabled` (~2076) is (0,2,0) and
   still wins, so a Sold Out control keeps its grey.
   ------------------------------------------------------------ */
.vl-cartctl__btn { background: #21609D; }
.vl-cartctl__btn:hover { background: #1B5185; }

.vl-tile__actions .vl-cartctl__btn[data-action="add"],
.vl-tile__actions .vl-cartctl__btn[data-action="options"] { background: #21609D; }
.vl-tile__actions .vl-cartctl__btn[data-action="add"]:hover,
.vl-tile__actions .vl-cartctl__btn[data-action="options"]:hover { background: #1B5185; }

/* The floating cart is white until it holds something, so there
   the glyph carries the blue rather than the background. Beats
   `color: #233642` on `.vl-cart__toggle` (~588) on source order
   at the same (0,1,0). `.vl-cart.has-items .vl-cart__toggle`
   (~3585) is (0,2,0) and still flips it to a white glyph on a
   blue button the moment something lands in the cart -- diameter,
   badge and pulse are all untouched. */
.vl-cart__toggle { color: #21609D; }

/* ---- 2. The buy control is permanent at every width ---------
   Catalogue cards. The hover gate at ~2516 sets
   `opacity: 0 !important` at (0,4,0) and only lifts it on
   :hover / :focus-within, so at desktop widths there was nothing
   under a card until the pointer arrived and the grid read as
   browse-only. `html ` takes these to (0,4,3); the !important is
   needed only because the rule being beaten carries one.

   pointer-events is set on the control, never on the button:
   `.vl-cartctl__btn.is-disabled { pointer-events: none }`
   (~2076) has to keep working on a sold-out card, and a parent
   set to `auto` does not override a child set to `none`.
   ------------------------------------------------------------ */
html .woocommerce ul.products li.product .vl-cartctl,
html .woocommerce ul.products li.product .vl-cartctl__btn {
  opacity: 1 !important;
  visibility: visible !important;
}
html .woocommerce ul.products li.product .vl-cartctl {
  pointer-events: auto !important;
}

/* Homepage and stacks tiles, same change. The gate at ~3262 is a
   plain (0,2,0) declaration with no !important, so (0,2,1) and a
   later position are enough. */
html .vl-tile .vl-tile__actions {
  opacity: 1;
  pointer-events: auto;
}

/* ---- 3. Sizes are a popover at every width ------------------
   A variable product used to lay its sizes out permanently in
   the card above 640px (`@media all` at ~4396, plus the
   min-width: 641px pair at ~4343 that hid the trigger). That
   made a card with options twice the height of the card beside
   it and crowded the tile, which is what this phase undoes. The
   phone treatment -- one trolley button, sizes in a dropdown --
   is now what every width gets.

   The `@media (max-width: 640px)` block at ~5083 still does the
   same job on phones. It is left in place rather than fought:
   the values here match it, so the two agree.
   ------------------------------------------------------------ */

/* The trigger comes back. Two rules to beat: the unconditional
   `.vl-cartctl--variable .vl-cartctl__btn { display: none
   !important }` at ~4399 (0,2,0), and the min-width: 641px pair
   at ~4344 whose catalogue selector reaches (0,6,1). The paths
   below are (0,6,3) and (0,5,1), so both are covered -- and so
   is `.vl-tile__actions .vl-cartctl.is-active
   .vl-cartctl__btn[data-action="options"]` at ~3659 (0,4,1),
   which is deliberate: with the sizes behind a popover the
   trigger is the only route to a SECOND size once a first is in. */
html .woocommerce ul.products li.product .vl-cartctl--variable .vl-cartctl__btn[data-action="options"],
html .vl-tile .vl-tile__actions .vl-cartctl--variable .vl-cartctl__btn[data-action="options"] {
  display: inline-flex !important;
}

/* With the list closed, the count on the trigger is the only
   thing saying anything is held. `:not([hidden])` matters -- an
   unconditional `display` would beat the hidden attribute the
   markup ships and print a "0" on an empty cart. Beats
   `.vl-cartctl--variable .vl-cartctl__held { display: none
   !important }` at ~4400, (0,2,0). */
html .woocommerce ul.products li.product .vl-cartctl--variable .vl-cartctl__held:not([hidden]),
html .vl-tile .vl-tile__actions .vl-cartctl--variable .vl-cartctl__held:not([hidden]) {
  display: inline-flex !important;
}

/* Closed by default: the `hidden` attribute governs again. Beats
   `display: flex !important` at ~4412, (0,2,0). */
html .woocommerce ul.products li.product .vl-cartctl--variable .vl-cartctl__sizes[hidden],
html .vl-tile .vl-tile__actions .vl-cartctl--variable .vl-cartctl__sizes[hidden] {
  display: none !important;
}

/* Open: a panel hanging off the trigger rather than part of the
   card, so a card never changes height and a row's prices stay on
   one line. Anchored to `.vl-cartctl--variable`, which is
   `position: relative` (~3107) and centres its button, so
   `left: 50%` puts the panel under the trolley in both
   geometries -- the tile shrink-wraps the control to the button,
   the catalogue card lets it fill the column.

   The CARD is what gets raised, by `.vl-sizes-open` (set in
   inc/shop-archive.php). No z-index here can do that job, because
   `.vl-cartctl` carries its own z-index and therefore opens a
   stacking context of its own. Only `display` needs !important;
   the rest beat plain declarations from the ~4412 block on
   specificity alone. */
html .woocommerce ul.products li.product .vl-cartctl--variable .vl-cartctl__sizes:not([hidden]),
html .vl-tile .vl-tile__actions .vl-cartctl--variable .vl-cartctl__sizes:not([hidden]) {
  display: flex !important;
  position: absolute;
  top: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 60;
  width: max-content;
  min-width: 176px;
  max-width: calc(100vw - 2rem);
  padding: .45rem;
  gap: .35rem;
  background: #fff;
  border: 1px solid #E3E9EC;
  border-radius: 12px;
  box-shadow: 0 14px 34px rgba(35, 54, 66, .18);
}

/* Rows size to the panel, not to the card behind it. Beats the
   `max-width: 180px` cap at ~4429, (0,2,0), which existed to stop
   a row stretching across a 257px card -- irrelevant now that the
   panel shrink-wraps its own content. */
html .woocommerce ul.products li.product .vl-cartctl--variable .vl-cartctl__sizes:not([hidden]) .vl-cartctl__size,
html .vl-tile .vl-tile__actions .vl-cartctl--variable .vl-cartctl__sizes:not([hidden]) .vl-cartctl__size {
  width: 100%;
  max-width: none;
}

/* The card holds one button again, so the reserve that keeps
   prices level across a row comes back down to what a single
   control needs. Beats `min-height: 98px` at ~4680 (0,4,0) -- two
   46px size rows plus their gap -- and the 111px phone value at
   ~4689 the same way. */
html .woocommerce ul.products li.product .vl-cartctl { min-height: 68px; }

@media (max-width: 640px) {
  /* The size rows live in a floating panel now rather than inside
     a 162px card, so the steppers no longer have to be cut to 34px
     to fit and can take the full thumb target. Beats
     `min-width: 34px; width: 34px; height: 40px` at ~4714, (0,3,0).

     Scoped to the catalogue card and the tile, NOT left bare. The
     single-product size picker shares this markup and sets its own
     46px steppers at ~4569, (0,4,0) -- an unscoped rule here would
     be (0,4,1), win, and quietly shrink that page's controls by
     2px while also pinning a height it deliberately leaves auto. */
  html li.product .vl-cartctl--variable .vl-cartctl__sizes .vl-cartctl__sizestep .vl-cartctl__step,
  html .vl-tile .vl-cartctl--variable .vl-cartctl__sizes .vl-cartctl__sizestep .vl-cartctl__step {
    min-width: 44px; width: 44px;
    min-height: 44px; height: 44px;
  }
}

/* ============================================================
   SINGLE-PRODUCT PURCHASE PANEL — PILOT (.vl-buybox)

   Rendered by vistara_buybox_render() in inc/shop-archive.php, and only for
   the ids in VISTARA_BUYBOX_PILOT. Every selector below is keyed to
   `.vl-buybox`, so on every other product page in the shop this whole section
   matches nothing at all — the element does not exist there, so no product
   that is not piloting the panel can be reached by any of it.

   At end of file per the standing rule: home.css is a sequence of appended
   override sections, so a rule placed earlier loses to them at equal
   specificity.

   NO `!important` in this section. What was enumerated as possibly needing to
   be beaten, and why none of it does:

     * `.vl-tile .vl-qty, .woocommerce ul.products li.product .vl-qty
        { display: none !important }` (~1859) hides the stepper on listings.
       It cannot reach this one: the panel renders inside `div.summary` on a
       single product page, which has no `li.product` and no `.vl-tile`
       ancestor (checked in the rendered page, not assumed).
     * `.vl-qty` / `.vl-qty__step` / `.vl-qty__num` (~1692–1711) are all plain
       declarations at (0,1,0). The rules here are (0,2,0) and win outright.
     * `.vl-cartctl { display: flex !important }` (~2382) and the 46px
       `.vl-sizepick` steppers (~3898, ~4569) are on markup the panel does not
       render — it replaces `.vl-sizepick` on the piloted product rather than
       living inside it.
     * The single-product image widths at 506/324/259px !important (~2417,
       ~2427, ~5063) are on `div.images img`. Nothing here touches the gallery.
   ============================================================ */

.vl-buybox { margin: 1.2rem 0 1.6rem; }

/* ---- In Stock / Sold Out badge ------------------------------
   The reference's pill, in the site's own deep slate rather than pure black.
   The dot carries the state: green in stock, the site's grey when not — no red
   is introduced, because the palette has never had one. The dot is decorative
   and aria-hidden; the words beside it are the actual answer. */
.vl-buybox__stock {
  display: inline-flex; align-items: center; gap: .55rem;
  margin: 0 0 1.3rem; padding: .42rem 1rem .42rem .8rem;
  border-radius: 999px;
  background: #233642; color: #fff;
  font-size: .82rem; font-weight: 700; letter-spacing: .02em;
}
.vl-buybox__dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: #45C46E; box-shadow: 0 0 0 3px rgba(69, 196, 110, .28);
}
.vl-buybox__stock.is-out .vl-buybox__dot {
  background: #9BA7AE; box-shadow: 0 0 0 3px rgba(155, 167, 174, .28);
}

/* ---- Size pills --------------------------------------------
   The label copies `.vl-sizepick__label` (~3853) line for line rather than
   inventing a second small-caps treatment for the same job. */
.vl-buybox__label {
  font-size: .78rem; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: #6B7A83; margin: 0 0 .7rem;
}
.vl-buybox__pills { display: flex; flex-wrap: wrap; gap: .6rem; margin: 0 0 1.4rem; }

.vl-buybox__pill {
  position: relative;
  display: inline-flex; align-items: center; gap: .5rem;
  min-height: 46px; padding: .5rem 1.35rem;
  border: 1px solid #C9D6DE; border-radius: 999px;
  background: #fff; color: #233642;
  font-family: var(--vl-font); font-size: 1rem; font-weight: 500; line-height: 1.2;
  cursor: pointer;
  transition: background .15s ease, border-color .15s ease, color .15s ease;
  /* A pill is a control, not prose. Without this a thumb that travels a pixel
     mid-tap selects the word "20mg" and leaves it sitting there highlighted
     instead of choosing that size — seen at 375px in testing, not theorised.
     Same treatment `.vl-review__photo` (~2576) already gets for the same
     reason. */
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}
/* Clipped, never `display: none` and never `opacity: 0` — the input IS the
   control, and both of those take it out of the accessibility tree along with
   the tab stop and the arrow-key behaviour that is the whole reason it is a
   radio rather than a button with aria-checked. `clip` plus a 1px box is the
   long-standing visually-hidden recipe and is what WordPress's own
   `.screen-reader-text` uses. `opacity: 0` was written first and changed for
   this: Chrome treats a fully transparent subtree as invisible, which is
   exactly the state an assistive technology is entitled to skip. */
.vl-buybox__radio {
  position: absolute;
  width: 1px; height: 1px;
  margin: 0; padding: 0; border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}
.vl-buybox__pill:hover { border-color: #21609D; }

/* Selected: filled deep slate. The reference fills the chosen pill black; this
   is the site's own dark neutral doing the same job. Deliberately NOT the
   brand blue — the Add to Cart button sitting under it is #21609D, and two
   filled blue controls stop reading as two different kinds of thing. */
.vl-buybox__pill:has(.vl-buybox__radio:checked) {
  background: #233642; border-color: #233642; color: #fff; font-weight: 700;
}
/* The ring goes on the pill because the input it belongs to is 1px wide. */
.vl-buybox__pill:has(.vl-buybox__radio:focus-visible) {
  outline: 2px solid #21609D; outline-offset: 2px;
}

/* A size that cannot be bought. `disabled` on the input already refuses the
   click and takes it out of the tab order; this only makes it look the part,
   and the caption repeats what the catalogue's sold-out rows say. */
.vl-buybox__pill--soldout {
  background: #F6F7F6; border-color: #E3E9EC; color: #9BA7AE; cursor: not-allowed;
}
.vl-buybox__pill--soldout:hover { border-color: #E3E9EC; }
.vl-buybox__pillsold {
  font-size: .7rem; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: #9BA7AE;
}

/* ---- Quantity + Add to Cart --------------------------------
   The stepper is the catalogue's `.vl-qty`, reused whole so the plus and minus
   come from vistara_quantity_script() and there is no second stepper script on
   the site. There it is drawn to fit a 257px card (24px buttons); on a product
   page it has the room to be a real touch target. */
.vl-buybox__buy { display: flex; flex-wrap: wrap; align-items: center; gap: .75rem; }

.vl-buybox .vl-qty { gap: 0; padding: 0; border-color: #C9D6DE; }
.vl-buybox .vl-qty__step {
  width: 46px; height: 46px; font-size: 1.3rem; color: #21609D;
}
.vl-buybox .vl-qty__step:hover { background: #F4F8FC; color: #21609D; }
.vl-buybox .vl-qty__num {
  width: 44px; font-size: 1rem; font-weight: 700; color: #233642;
}

.vl-buybox__add {
  display: inline-flex; align-items: center; justify-content: center; gap: .55rem;
  flex: 1 1 auto; max-width: 320px; min-height: 48px;
  padding: .7rem 1.6rem;
  border: 0; border-radius: 999px;
  background: #21609D; color: #fff;
  font-family: var(--vl-font); font-size: 1rem; font-weight: 500; letter-spacing: .01em;
  cursor: pointer;
  transition: background .2s ease;
}
.vl-buybox__add:hover { background: #1B5185; color: #fff; }
.vl-buybox__add:focus-visible { outline: 2px solid #233642; outline-offset: 2px; }

/* WooCommerce's own add-to-cart form stands down wherever the panel rendered —
   the job `.single-product .summary:has(.vl-sizepick) form.cart` (~4635) does
   for the size picker. No `!important` here, unlike that line: the only
   competing declaration is `.woocommerce div.product form.cart { display: flex }`
   in the <=640px block at ~3561, which is (0,3,2) and loses to this rule's
   (0,4,1). For the piloted variable product the blanket rule at ~3850 already
   hides the variations form; this is what covers the panel wherever it lands
   next. */
.single-product .summary:has(.vl-buybox) form.cart { display: none; }

@media (max-width: 640px) {
  /* 375px: pills wrap to as many rows as they need and keep their 46px target;
     the buy row keeps the stepper at full size and lets the button take what is
     left rather than being squeezed to a stub beside it. */
  .vl-buybox__pill { padding: .5rem 1.15rem; }
  .vl-buybox__buy { gap: .6rem; }
  .vl-buybox__add { flex: 1 1 150px; max-width: none; padding: .7rem 1rem; }
}

/* ============================================================
   PURCHASE PANEL — OWNER REVISION, 29 Aug (.vl-buybox, cont.)

   A sub-section of the block immediately above, appended rather than edited
   into it, per the standing rule for this file. Three changes, all still keyed
   to `.vl-buybox`, so like the parent section every selector here matches
   nothing at all on a product that is not in VISTARA_BUYBOX_PILOT:

     1. Add to Cart is a compact pill that hugs its label instead of
        stretching to 320px.
     2. "Buy it now" takes a full-width row of its own beneath it.
     3. Three trust rows sit above the stock badge, and the vertical rhythm
        from the purity line down to SIZE is opened up to carry them.

   Still no `!important` anywhere in this section. What it has to beat, all of
   it in the sibling block ~140 lines above and all of it won on source order
   at equal specificity rather than by out-specifying anything:

     * `.vl-buybox__add { flex: 1 1 auto; max-width: 320px }` (~6600) — the
       stretch this change removes. Same (0,1,0), later in the file, wins.
     * `@media (max-width: 640px) { .vl-buybox__add { flex: 1 1 150px;
       max-width: none; padding: .7rem 1rem } }` (~6627). A media query adds
       no specificity, so that rule was only ever winning on order too — and
       it now loses to the unconditional rule below. The <=640px block at the
       end of THIS section restates it deliberately, because the narrow pill
       is a desktop decision: see the note there.
     * `.vl-buybox { margin: 1.2rem 0 1.6rem }` (~6490) and
       `.vl-buybox__stock { margin: 0 0 1.3rem }` (~6497) — both re-set below
       for the new rhythm. Same selectors, same specificity, later.

   Nothing outside `.vl-buybox` is touched. In particular `.vl-coa` (~5593),
   which prints the purity line the trust rows sit under, keeps its own
   margins: it renders on every product that has a purity figure, and the
   spacing this design wants is the panel's business, so the extra air is
   taken on `.vl-buybox`'s own top margin instead.
   ============================================================ */

/* ---- Rhythm ------------------------------------------------
   The owner's note was that purity / trust rows / In Stock / SIZE were
   squished. Each of these is one step of that column, opened up. */
.vl-buybox { margin: 1.6rem 0 1.6rem; }
.vl-buybox__stock { margin: 0 0 1.5rem; }

/* ---- Trust rows --------------------------------------------
   `list-style: none` and the zeroed box are the whole reset needed. The
   `!important` list rules in this file are every one of them scoped to
   `.woocommerce ul.products li.product` (the shop grid), and Kadence's prose
   list styling keys off `.entry-content`; the panel renders inside
   `div.summary.entry-summary`, which is neither. Checked in the rendered page
   rather than assumed. */
.vl-buybox__trust {
  list-style: none;
  margin: 0 0 1.4rem;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: .7rem;
}
.vl-buybox__trustrow {
  display: flex; align-items: center; gap: .75rem;
  margin: 0; padding: 0;
}
/* The chip is a fixed square, never a flex item that can be squeezed: without
   `flex: 0 0 auto` a long row shrinks the icon rather than wrapping the text,
   and "Ships From Canada Within 24 Hours" is long enough to do it at 375px. */
.vl-buybox__trusticon {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px;
  border-radius: 9px;
  background: #E8F0F8;
  color: #21609D;
}
.vl-buybox__trusttext {
  font-family: var(--vl-font);
  font-size: 1rem; font-weight: 500; line-height: 1.3;
  color: #233642;
}

/* ---- The buy area is a block, not the whole column ----------
   The panel sits in `div.summary`, which measures 720px at a 1440px viewport —
   so "spanning the row" only means anything once the row itself stops being
   the full width of that column. 380px IS the row: 138px stepper + .75rem gap
   + 230px button. That is the reference's proportion — a stepper and a button
   filling their row, with the second button beneath matching it exactly —
   rather than a 720px bar under a 350px pair.

   Dropped again below 640px, where the column is already narrower than this. */
.vl-buybox__buy { max-width: 380px; }

/* ---- Add to Cart: compact -----------------------------------
   No longer stretches. 380px less the stepper and the gap leaves exactly the
   230px cap below, so the pill is the compact one the reference shows instead
   of a 320px bar, and its right edge lines up with Buy it now's underneath.

   Kept flexible between a 200px floor and that 230px cap rather than pinned to
   a single width: between 641px and ~800px the summary column is itself
   narrower than 380px, and there the stepper and the button share what there
   is instead of the button overflowing or dropping onto a line of its own. It
   is also the room a longer label needs — TranslatePress renders this panel
   under /fr/ too. */
.vl-buybox__add {
  flex: 1 1 auto;
  min-width: 200px;
  max-width: 230px;
}

/* ---- Buy it now ---------------------------------------------
   `flex: 1 0 100%` is what puts it on its own line and spans it: the row is
   already `flex-wrap: wrap`, and a child whose basis is the full container
   width cannot share a line with anything, so no second wrapper element has to
   exist to hold it. It also means the button inherits the row's `if
   ( $in_stock )` gate in PHP — sold out on every size and the whole buy area,
   this included, is simply not printed.

   Ghost of the button above it, matching its 48px and its radius exactly so
   the two read as one control stacked twice, not two different widgets. The
   2px border is inside the 48px: `box-sizing: border-box` is set globally by
   the parent theme (verified in the rendered page), so the outline button and
   the filled one measure the same height. */
.vl-buybox__now {
  flex: 1 0 100%;
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 48px;
  padding: .7rem 1.6rem;
  border: 2px solid #21609D; border-radius: 999px;
  background: #fff; color: #21609D;
  font-family: var(--vl-font); font-size: 1rem; font-weight: 600; letter-spacing: .01em;
  /* Without this the inherited 1.6 line-height plus the padding and the two
     borders measures 52px and `min-height` never binds — a 52px ghost under a
     48px solid, which is exactly the kind of 4px the eye catches on two
     stacked pills. Pinned so the pair are the same height at any font size. */
  line-height: 1.2;
  cursor: pointer;
  transition: background .2s ease, color .2s ease, border-color .2s ease;
}
.vl-buybox__now:hover { background: #F4F8FC; color: #1B5185; border-color: #1B5185; }
.vl-buybox__now:focus-visible { outline: 2px solid #233642; outline-offset: 2px; }

/* In flight. The same dim `.vl-ckqty.is-busy` (~5391) uses for the same job,
   rather than a spinner this stylesheet has no other instance of. The button is
   `disabled` in the same breath, so `pointer-events` is not needed to stop a
   second press — and is deliberately NOT set, because a disabled element with
   pointer-events removed shows no cursor at all.

   Written first with a companion `.vl-buybox__now:disabled { cursor: default }`
   for a disabled-but-idle state. Removed: there is no such state — the button
   is only ever disabled while a request is out — and being (0,2,0) and later in
   the file, it beat this line and the wait cursor never appeared. Confirmed in
   the browser mid-request, not reasoned about. */
.vl-buybox__now.is-busy { opacity: .55; cursor: wait; }

@media (max-width: 640px) {
  /* 375px. The narrow pill is a desktop decision and is reverted here on
     purpose: the column is ~327px wide inside the gutters, the stepper takes
     ~138px of it, and a 212px floor would push Add to Cart onto a line of its
     own — three stacked full-width rows where the reference has two. Flexible
     again, it fills what is left beside the stepper and the row reads as one
     control. Restated rather than left to the ~6627 block, which this
     section's unconditional rule above now beats on source order.

     The 380px cap goes with it: at 375px the column is ~327px, so the cap
     would never bind, and leaving it set would only be a number waiting to be
     wrong on a 420px phone. Buy it now still spans the row — the row is just
     the column here, which is what the reference shows. */
  .vl-buybox__buy { max-width: none; }
  .vl-buybox__add { flex: 1 1 150px; min-width: 0; max-width: none; padding: .7rem 1rem; }

  /* Slightly tighter than desktop, but still opened up from where it was:
     the panel has a whole viewport width less to spend. */
  .vl-buybox { margin: 1.3rem 0 1.6rem; }
  .vl-buybox__trust { gap: .6rem; margin: 0 0 1.2rem; }
  .vl-buybox__stock { margin: 0 0 1.3rem; }
  .vl-buybox__trusticon { width: 30px; height: 30px; border-radius: 8px; }
  .vl-buybox__trusttext { font-size: .95rem; }
}

/* ---- Bulk Order pill (owner request, 29 Aug) -----------------
   An anchor wearing the size-pill look. Everything visual comes
   from .vl-buybox__pill itself; this block only settles what an
   <a> brings that a <label> does not. The blue text (rather than
   the pills' slate) is deliberate -- it is a link and says so.
   ------------------------------------------------------------ */
.vl-buybox__pill--bulk {
  text-decoration: none;
  color: #21609D;
  font-weight: 600;
}
.vl-buybox__pill--bulk:hover {
  background: #E8F0F8;
  color: #1B5185;
}

/* ============================================================
   PURCHASE PANEL — SITE-WIDE ROLLOUT, 29 Aug (.vl-buybox, cont.)

   The rollout (VISTARA_BUYBOX_ALL = true, inc/shop-archive.php) puts the panel
   on every product page, which introduced exactly one shape the pilot never
   rendered: a product with NO size to choose — the simple stacks, which carry
   no `Size` attribute — where the pills row exists only to hold the Bulk Order
   link and the SIZE label above it is correctly not printed.

   ONE rule, for that one case. Everything else the rollout added — the single
   preselected pill a simple product with a size takes, its sold-out state, the
   longer variation labels the stacks carry ("Retatrutide 20mg") — was already
   covered: a pill is a pill, and `.vl-buybox__pills` has been `flex-wrap: wrap`
   from the start, so the long pairs wrap on their own. Measured, not assumed:
   at 375px the two stack pills sit side by side and Bulk Order takes the next
   row, with no horizontal overflow at 375, 641, 768, 900, 1200 or 1440px.

   NO `!important`, and none needed. The only rule in this file matching
   `.vl-buybox__pills` is `.vl-buybox__pills { margin: 0 0 1.4rem }` (~6519) at
   (0,1,0); the rule below is (0,2,0) and later, so it wins twice over. Checked
   against the live cascade in the rendered page, not by reading the file:
   enumerating every stylesheet rule matching this element returned that rule
   and Kadence's `box-sizing` reset, nothing else. In particular WooCommerce's
   `.woocommerce div.product div.summary p { margin: 1em 0 }` (0,3,3) does not
   reach it — that selector ends in `p`, and this element is a `div`.

   Keyed to `:not([role])` rather than to a new class. The `role="radiogroup"`
   is printed if and only if there are sizes (see vistara_buybox_render()), so
   its absence IS the condition — a modifier class would be a second name for a
   fact the markup already states, and the two could drift.
   ============================================================ */

/* The panel's vertical rhythm does not change just because a product has no
   size. With a SIZE row the gap under the stock badge is the badge's own
   13.12px bottom margin plus the label's 12.48px top margin — both of them
   coming from WooCommerce's `div.summary p { margin: 1em 0 }`, which outranks
   this file's `.vl-buybox__stock { margin: 0 0 1.5rem }` at (0,3,3) against
   (0,1,0) and always has (see the note in the report; deliberately left alone
   here, because that is the spacing the owner reviewed and approved).

   With no label there is no second margin, and the Bulk Order pill sat 13px
   under the badge against ~26px everywhere else — visibly tighter on the four
   simple stacks and nowhere else. .78rem is the label's 12.48px exactly: the
   label is `font-size: .78rem` (~6516) and its margin is 1em of that, so this
   restores the identical gap rather than approximating it.

   Unconditional, so it holds at every width. The <=640px block does not resize
   `.vl-buybox__label`, so the gap it is matching is the same 12.48px there. */
.vl-buybox__pills:not([role]) {
  margin-top: .78rem;
}

/* ============================================================
   PHASE C — CATALOGUE POLISH  (29 Aug, owner-directed)

   At end of file per the standing rule: home.css is a sequence of
   appended override sections, so a rule placed earlier loses to
   them at equal specificity. Nothing above this line was edited.

   Nine changes, each of them naming what it has to beat and
   roughly where that rule sits:

     1. a token for the round buy control's diameter,
     2. vial images +15% on the homepage row and /peptides/,
     3. real vertical padding between catalogue and stack rows,
     4. ONE tile-control format: the /peptides/ geometry wins,
     5. the held-count badge sits on the trolley, everywhere,
     6. the size popover gains "Adding…" and a Bulk Order row,
     7. the homepage CTA drops clear of the tiles (which is also
        what was eating their click target),
     8. hero button geometry, per width,
     9. the stack product page's image: +10% and level with the
        buy panel.

   !IMPORTANT AUDIT — every one in this section, and why it is
   not decoration. Where a plain declaration suffices, there is
   none, and the note says so.

     · #2 catalogue image height — beats
       `.woocommerce ul.products li.product img { height: 257px
       !important }` (~2399), which carries one.
     · #3 catalogue row-gap — beats `.woocommerce ul.products {
       row-gap: 1rem !important }` (~2823), which carries one.
     · #6 the size row's stepper while adding — beats
       `.vl-sizepick .vl-cartctl--variable .vl-cartctl__size
       .is-active .vl-cartctl__sizestep { display: flex }`
       (~4564) at (0,4,0), which a scoped rule here cannot
       outrank without inventing a fifth class.
     · #9 stack gallery width — beats this file's own
       `width: 100% !important; max-width: 100% !important`
       (~6229), which carries one.

   Nothing else below uses `!important`. In particular #4, #5,
   #7 and #8 win on specificity and source order alone; each
   names the rule it beats and the two specificities involved.
   ============================================================ */

/* ---- 1. One number for the round buy control ----------------
   The button is a 42px circle on /peptides/ and a 36px circle on
   a phone, and those two numbers were written out four times
   between them (~6036, ~6059, and the tile pair at ~2905/~3058
   which resolved to neither). A token means the size is stated
   once and every surface asks the same question.

   On `:root` rather than on a component, because the badge
   offset in #5 is derived from it and the two must never drift.
   ------------------------------------------------------------ */
:root { --vl-ctlbtn: 42px; }
@media (max-width: 640px) { :root { --vl-ctlbtn: 36px; } }

/* ---- 2. Vial images +15% -----------------------------------
   Owner-directed, so the labels read at a glance. Applied to the
   two surfaces named and to nothing else: /stacks/ tiles keep the
   landscape 5/3 crop they were given for their composites, and
   are excluded explicitly below rather than by hoping the
   cascade misses them.
   ------------------------------------------------------------ */

/* /peptides/ and every category archive. 257 x 1.15 = 295.6.
   `html ` takes this to (0,4,4) against the (0,3,0) it beats; the
   !important is needed only because that rule carries one. */
html .woocommerce ul.products li.product img,
html .woocommerce ul.products li.product .attachment-woocommerce_thumbnail {
  height: 296px !important;
}

/* Homepage Best Sellers. The tile crops its vial: a 188px window
   over a 253px image, pulled up 36 and down 29 (~2980). All three
   numbers are scaled by the same 1.15 so the crop is identical and
   only the vial is bigger — 291 - 41 - 34 = 216, exactly the new
   window height.

   `:not(.vl-stacks)` is load-bearing, not defensive. /stacks/
   renders through the SAME shortcode and its wrapper really does
   carry `.vl-bestsellers` (see vistara_stacks_shortcode() in
   inc/catalog.php), so a bare `.vl-bestsellers .vl-tile__img img`
   would be (0,3,0) — equal to the stacks rule at ~6190 and LATER
   in the file, which would silently replace the stack composites'
   5/3 crop with a 291px portrait box. `:not()` takes the selector
   to (0,4,0) and states the exclusion out loud. */
.vl-bestsellers:not(.vl-stacks) .vl-tile__img { height: 216px; }
.vl-bestsellers:not(.vl-stacks) .vl-tile__img img {
  height: 291px;
  margin-top: -41px;
  margin-bottom: -34px;
}

/* ---- 3. Vertical padding between rows -----------------------
   Two different faults with one shape.

   /peptides/ was on a 16px row gap (`row-gap: 1rem !important`,
   ~2823) and the images have just grown by 39px, so the rows
   needed air whether or not they had it before.

   /stacks/ was on a row gap of ZERO — `.vl-bestsellers {
   row-gap: 0 }` (~1227) is written for the six-across homepage
   row, and /stacks/ inherits it by sharing the class. Measured at
   1440px, the first row's cart control ended at y=915 and the
   second row's image began at y=914: touching. `.vl-tile:hover
   .vl-tile__img img { transform: scale(1.04) }` (~345) then grew
   that image about 5px past its own box in every direction, and
   `.vl-stacks .vl-tile__img { overflow: visible }` (~2983) let it
   through — so hovering a stack painted its vials over the Add to
   Cart control of the stack above. That is the overlap the owner
   reported, and it is a spacing bug rather than a z-index one:
   two things that must not touch were sharing a pixel.

   Fixed with real space, not with paint order. 44px of gap
   against ~5px of overshoot leaves the zoom nowhere near the
   control, at any width, and needs no stacking-context reasoning
   to stay true. */
html .woocommerce ul.products { row-gap: 2.5rem !important; }
@media (max-width: 640px) {
  html .woocommerce ul.products { row-gap: 2rem !important; }
}
html .vl-stacks { row-gap: 2.75rem; }

/* ---- 4. ONE tile-control format ----------------------------
   The /peptides/ control is now canonical, as directed.

   It was a 42px circle there (`button.vl-cartctl__btn`, ~6036, at
   (0,1,1)) and a 57x38 lozenge on the homepage and /stacks/,
   because `.vl-tile__actions .vl-cartctl__btn[data-action="add"]`
   (~3058) sets `width: auto; height: auto; padding: .55rem
   1.15rem` at (0,3,0) and outranks it. Same markup, same icon,
   two different shapes — which is what the owner was looking at.

   The token from #1 drives both, so "the same size" is now a fact
   about the stylesheet rather than a coincidence between four
   hard-coded numbers. (0,4,4) and (0,4,2) here against the
   (0,3,0) being beaten; no !important anywhere, because none of
   the rules involved carries one. */
html .woocommerce ul.products li.product button.vl-cartctl__btn,
html .vl-tile .vl-tile__actions button.vl-cartctl__btn[data-action="add"],
html .vl-tile .vl-tile__actions button.vl-cartctl__btn[data-action="options"] {
  width: var(--vl-ctlbtn);
  height: var(--vl-ctlbtn);
  min-width: var(--vl-ctlbtn);
  min-height: var(--vl-ctlbtn);
  padding: 0;
  gap: 0;
  border-radius: 50%;
}

/* The stepper that replaces the button once something is held was
   the other half of the mismatch: white with slate glyphs on
   /peptides/ (~2078), solid slate with white glyphs on a tile
   (~2920, ~3068). The /peptides/ treatment wins, so a product in
   the cart looks the same wherever the shopper meets it.
   (0,3,1) against the (0,2,0) pair. */
html .vl-tile .vl-tile__actions .vl-cartctl__stepper {
  height: auto;
  background: #fff;
  border: 1px solid #314D5D;
  border-radius: 999px;
  padding: .18rem .25rem;
  gap: .2rem;
}
html .vl-tile .vl-tile__actions .vl-cartctl__step {
  width: 30px;
  height: 30px;
  color: #314D5D;
  font-size: 1.05rem;
  border-radius: 999px;
}
html .vl-tile .vl-tile__actions .vl-cartctl__step:hover {
  background: #21609D;
  color: #fff;
}
html .vl-tile .vl-tile__actions .vl-cartctl__qty {
  min-width: 26px;
  font-size: .92rem;
  font-weight: 400;
  color: #233642;
}

/* ---- 5. The held-count badge sits ON the trolley ------------
   `.vl-cartctl__trigger` is new markup (inc/shop-archive.php,
   vistara_variable_cart_control) and exists only to be the thing
   the badge hangs off: it shrink-wraps the button on every
   surface, so `top/right: -6px` is the button's top-right corner
   by construction rather than by arithmetic.

   What it replaces: the badge used to be positioned against
   `.vl-cartctl`, whose shape is not the same on every surface. On
   a catalogue card that control fills the column and merely
   CENTRES its button, so `right: -6px` (~3121) pinned the badge to
   the card's edge — the owner's screenshot of a teal "1" floating
   beside, not on, the Retatrutide trolley in the related-products
   row. A `@media (max-width: 640px)` block (~6066) had patched the
   phone case with `left: calc(50% + 8px)` plus one `top` for cards
   and another for tiles; those four numbers assume a particular
   button width and said nothing about desktop.

   (0,3,1) here beats both that block's (0,3,0) tile rule and its
   (0,2,1) card rule, so the patch is overridden rather than
   removed — home.css is append-only, and the rules above still
   match a markup shape that no longer exists. */
html .vl-cartctl__trigger {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
html .vl-cartctl--variable .vl-cartctl__trigger .vl-cartctl__held {
  top: -6px;
  right: -6px;
  left: auto;
}

/* ---- 6. The size popover: Adding…, and Bulk Order -----------
   Both rows come from vistara_variable_cart_control(). */

/* "Adding…" is in the markup on every in-stock row and shown only
   while that row's own request is in flight (the `is-adding`
   class is added and removed by the delegated handler in
   inc/shop-archive.php). Hidden by default, so a row that never
   gets tapped never mentions it. */
.vl-cartctl__adding {
  display: none;
  align-items: center;
  color: #6B7A83;
  font-size: .82rem;
  font-style: italic;
  white-space: nowrap;
}
.vl-cartctl__size.is-adding .vl-cartctl__adding { display: inline-flex; }

/* …and the stepper stands down while it shows, or the row would
   say "Adding…" beside a quantity that has already moved and the
   word would be describing nothing.

   !important, and it is the one place in this section that cannot
   be avoided: the rule revealing that stepper is
   `.vl-sizepick .vl-cartctl--variable .vl-cartctl__size.is-active
   .vl-cartctl__sizestep { display: flex }` (~4564) at (0,4,0),
   and the row this has to reach is inside `.vl-cartctl__size` —
   there is no honest way to a fifth class here. The rowcart glyph
   goes with it, for the same reason. */
.vl-cartctl__size.is-adding .vl-cartctl__sizestep,
.vl-cartctl__size.is-adding .vl-cartctl__rowcart { display: none !important; }

/* Bulk Order. Styled as a size row so the list reads as one set
   of choices, ruled off above so it is plainly not a size. It is
   an <a>, so it also needs the text treatment a <button> gets for
   free. */
.vl-cartctl__size--bulk {
  border-top: 1px solid #E3E9EC;
  margin-top: .15rem;
  padding-top: .35rem;
  text-decoration: none;
  justify-content: center;
}
.vl-cartctl__pick--bulk {
  color: #21609D;
  font-weight: 600;
  cursor: pointer;
}
.vl-cartctl__size--bulk:hover .vl-cartctl__pick--bulk {
  background: #F4F8FC;
  color: #1B5185;
}

/* ---- 7. The homepage CTA drops clear of the tiles -----------
   Owner asked for ~30px. It is also the fix for a hit-area bug he
   reported separately, which is why the two are one change.

   `.home .vl-section__cta` is `position: relative; z-index: 1`
   (~3382) and was pulled up by `margin-top: -19px` (~5965) into
   the bottom 31px of the Best Sellers buttons — measured at
   1440px: the buttons ran to y=608, the CTA block began at y=577.
   The retired card reveal had given every tile an `opacity`,
   making it a stacking context at `z-index: auto`, so the whole
   tile — cart button included — painted UNDER that block. The
   result was a button whose top few pixels answered a click and
   whose middle did not, which is exactly what "only the icon
   centre registers" looks like from the outside.

   Both causes are gone: the reveal is retired (no stacking
   context, so `.vl-cartctl { z-index: 2 }` reaches the surface
   again) and the block no longer overlaps anything. Belt and
   braces, deliberately — either alone would fix the symptom, and
   the overlap would still be a trap for the next change.

   +30px at both widths: -15px becomes 15px (~5404), and the
   >=1201px -19px becomes 11px (~5965). (0,2,1) against (0,2,0). */
html .home .vl-section__cta { margin-top: 15px; }
@media (min-width: 1201px) {
  html .home .vl-section__cta { margin-top: 11px; }
}

/* ---- 8. Hero button geometry -------------------------------
   The hero's "Explore Our Collection" is the same destination as
   the Best Sellers CTA beneath it (/peptides/), and the two
   disagreed about what a primary button looks like: 246px in the
   hero against 300px below on desktop, and on a phone the hero
   pair stretched to the full 338px column — a black bar, which is
   the owner's "it doesn't look like a button".

   300px on both, at both widths, so the page has one primary
   button shape.

   BOTH hero buttons are narrowed on the phone, not only the one
   named. They are stacked and touching; narrowing one of a pair
   to 300px and leaving the other at 338px would trade a bar for a
   mismatch. Flagged in the report as a judgement call. */
.home .vl-hero__actions .vl-btn--solid { min-width: 300px; }
@media (max-width: 767px) {
  .home .vl-hero__actions { align-items: center; }
  .home .vl-hero__actions .vl-btn--solid,
  .home .vl-hero__actions .vl-btn--ghost {
    width: 300px;
    max-width: 100%;
    min-width: 0;
  }
}

/* ---- 9. The stack product page's image ---------------------
   Owner: the composite "sits too high up… it needs to be
   basically horizontally aligned with the Quantity stepper".

   +10%, first. The image fills its gallery column, so it is grown
   past that column rather than inside it and the overflow is
   split evenly with a negative inline margin — otherwise 110% of
   a 562px column would run its right edge exactly onto the
   summary column at x=669. Split, it clears by 28px a side.
   !important on both, because the rule being beaten is this
   file's own `width: 100% !important` (~6229). */
.woocommerce div.product.entry.product_cat-stacks div.images img:not(.zoomImg) {
  width: 110% !important;
  max-width: 110% !important;
  margin-inline: -5%;
}

/* …and level with the buy panel.

   The two columns are grid siblings and `div.summary` holds the
   whole description as well as the buy controls, so it is ~2027px
   tall on desktop. `align-self: center` would therefore centre
   the image against the DESCRIPTION and leave it far below the
   fold — the reason this is a measured offset and not a
   centring keyword.

   The offsets centre the image on the block from the product
   title to the bottom of the buy panel. Measured at 1440px on all
   eight stack pages, which fall into exactly two groups:

     · 266px — the six stacks that sell sizes.
     · 208px — Limitless and GH Axis, which are simple products
       with no size row, so their buy panel is 58px shorter.

   The condition is not a guess about which products those are:
   vistara_buybox_render() prints `role="radiogroup"` on the pills
   IF AND ONLY IF there are sizes, so `:not([role])` IS "this
   product has no size row". The same test already keys the
   `.vl-buybox__pills:not([role])` rule further up this file.

   Reset below 900px, where the gallery stacks above the summary
   and an offset would only push the page down.

   A structurally exact alternative exists and was not taken:
   moving `.vl-single__desc` out of `div.summary` would shrink
   that column to the buy panel and let `align-self: center` do
   this with no numbers at all. It changes the layout of every
   product page in the shop, which is more than this asked for.
   Noted for Fable. */
@media (min-width: 901px) {
  .woocommerce div.product.entry.product_cat-stacks .woocommerce-product-gallery__wrapper {
    margin-top: 266px;
  }
  .woocommerce div.product.entry.product_cat-stacks:has(.vl-buybox__pills:not([role])) .woocommerce-product-gallery__wrapper {
    margin-top: 208px;
  }
}

/* ---- 6b. Bulk Order is not an addable size ------------------
   Caught in the rendered popover, not by reading the file: the
   new row picked up the "+" affordance that marks a size you can
   tap to add. It comes from
   `.vl-cartctl--variable .vl-cartctl__size:not(.is-active)
   :not(.vl-cartctl__size--soldout) .vl-cartctl__pick::after
   { content: "+" }` (~4482), whose two negations the Bulk Order
   row satisfies — it is neither active nor sold out — so the row
   read as "add one Bulk Order to the cart", which is the one
   thing it does not do.

   !important, following the precedent set for the same pseudo at
   ~3889: the rule being beaten is (0,4,0) on a `::after`, and the
   only honest selector for this row is (0,2,1).

   The pick also centres itself. That same block gives an
   un-added row `display: flex; justify-content: space-between;
   width: 100%` so the label and the plus sit at opposite ends —
   with no plus, space-between leaves a lone label ranged left
   while the divider above spans the panel. */
.vl-cartctl__size--bulk .vl-cartctl__pick--bulk::after { content: none !important; }
.vl-cartctl__size--bulk .vl-cartctl__pick--bulk { justify-content: center; }

/* ---- 6c. …and the stepper actually stands down --------------
   CORRECTION to 6, kept rather than edited in place because this
   file is append-only and the wrong reasoning is worth reading
   next to the right one.

   6 hides the stepper with `.vl-cartctl__size.is-adding
   .vl-cartctl__sizestep { display: none !important }` at (0,2,0)
   and its comment names `.vl-sizepick …` (~4564) as the rule it
   beats. That is the wrong rule. The one that actually governs a
   catalogue popover row is
   `.vl-cartctl--variable .vl-cartctl__size.is-active
   .vl-cartctl__sizestep { display: inline-flex !important }`
   (~4461) — (0,4,0) and ALSO !important, so !important on both
   sides settles nothing and specificity decides. The 6 rule lost,
   and the row rendered "− 1 +  Adding…", the stepper and the word
   side by side, which is the one arrangement neither of them
   means anything in.

   Found by rendering it and reading the cascade in the live page,
   not by re-reading the file. Two selectors, because there are
   two competing rules and each has to be outranked on its own
   terms:

     · (0,4,1) beats ~4461's (0,4,0) — the catalogue popover, on
       tiles, on cards, and in the related-products row.
     · (0,5,1) beats ~4564's (0,5,0) — the single-product size
       picker. Dormant today (VISTARA_BUYBOX_ALL is true, so the
       buy panel replaces `.vl-sizepick` everywhere), and covered
       anyway so that turning the panel off cannot resurrect this
       bug on a page nobody would think to retest.
   ------------------------------------------------------------ */
html .vl-cartctl--variable .vl-cartctl__size.is-adding .vl-cartctl__sizestep,
html .vl-cartctl--variable .vl-cartctl__size.is-adding .vl-cartctl__rowcart,
html .vl-sizepick .vl-cartctl--variable .vl-cartctl__size.is-adding .vl-cartctl__sizestep,
html .vl-sizepick .vl-cartctl--variable .vl-cartctl__size.is-adding .vl-cartctl__rowcart {
  display: none !important;
}

/* ---- 8b. …and the hero buttons actually narrow --------------
   CORRECTION to 8, appended rather than edited in place for the
   same reason as 6c.

   `width: 300px` did nothing on a phone. `.vl-hero__actions` is a
   `flex-wrap: wrap` ROW (not the column it looks like — the two
   buttons are on separate lines because each is too wide to share
   one), and its buttons carry `flex-grow: 1`. A flex item that is
   allowed to grow takes the whole line whatever its `width` says,
   so both buttons stayed at the full 338px column and the change
   was invisible.

   `flex: 0 0 auto` is the missing half: it stops the growing, and
   `width` becomes the used width. Measured before and after in
   the rendered page rather than reasoned about — the computed
   `width` read 338.203px with `flex-grow: 1` still in force.

   `justify-content` is already `center` on the container, so
   neither button needs centring once it stops filling the line.
   The `align-items: center` set in 8 is harmless but does nothing
   here (cross-axis on a row), and is left alone rather than
   removed, since this file is append-only. */
@media (max-width: 767px) {
  .home .vl-hero__actions .vl-btn--solid,
  .home .vl-hero__actions .vl-btn--ghost {
    flex: 0 0 auto;
  }
}

/* ---- 3b. …and the stacks gap holds on a phone too ----------
   CORRECTION to 3, appended rather than edited in place.

   `html .vl-stacks { row-gap: 2.75rem }` in 3 wins at desktop
   widths (measured: 44px at 1440px) but not below them. Two
   mobile rules set the SHORTHAND with a bang —
   `.vl-bestsellers { gap: 1.4rem .9rem !important }` (~3430 and
   ~3518) — and a shorthand sets row-gap along with column-gap, so
   a plain `row-gap` loses to it however specific it is. /stacks/
   carries `.vl-bestsellers` (it renders through the same
   shortcode), so it was inheriting the homepage row's 22.4px.

   Measured, not assumed: 22.4px of gap against ~5px of hover
   overshoot means there was no overlap on a phone even before
   this — one column, and the zoom never reached the control
   above. So this is the owner's "more vertical padding" being
   applied at both widths rather than a second bug being fixed.

   !important because the rule being beaten carries one. row-gap
   only, so the .9rem column gap those rules set is untouched —
   irrelevant on a one-column phone layout, but /stacks/ is two
   and three across further up and must keep it. */
html .vl-stacks { row-gap: 2.75rem !important; }

/* ============================================================
   PHASE C — !IMPORTANT AUDIT, AMENDED

   The audit at the head of this section ends "Nothing else below
   uses !important", and three corrections appended after it (3b,
   6b, 6c) now do. That sentence is wrong as it stands and this
   file cannot be edited in place, so the complete list lives
   here. Nine declarations, each beating a rule that itself
   carries a bang:

     1. #2  `height: 296px`            beats ~2399 (257px)
     2. #3  `row-gap: 2.5rem`          beats ~2823 (1rem)
     3. #3  `row-gap: 2rem` (<=640px)  beats ~2823 (1rem)
     4. #3b `row-gap: 2.75rem`         beats ~3430/~3518
                                       (`gap` shorthand, 1.4rem)
     5. #6  `display: none` on the adding row's stepper —
            SUPERSEDED by 8 below and now doing nothing. Left in
            place because removing it would mean editing above
            this line; it is (0,2,0) and loses to the rule 8
            beats, so it cannot affect anything either way.
     6. #6b `content: none`            beats ~4482 (the "+" pseudo)
     7. #6c `display: none`            beats ~4461 (0,4,0) and
                                       ~4564 (0,5,0)
     8. #9  `width: 110%`              beats ~6229 (100%)
     9. #9  `max-width: 110%`          beats ~6229 (100%)

   Nothing in Phase C uses !important against a rule that does
   not have one. The four rules the head of the section claims
   are the whole list were the whole list when it was written;
   3b, 6b and 6c were all found by rendering the result and
   reading the live cascade, which is the only way any of the
   three would have been found at all.
   ============================================================ */

/* ============================================================
   PHASE C — OWNER REDLINES  (29 Aug)

   Four notes from the owner's review of Phase C on desktop and
   phone. Appended at EOF like everything else in this file, and
   for the same reason: a rule written earlier loses to the
   Phase C section above at equal specificity, so anything that
   has to beat Phase C has to come after it. Nothing above this
   line was edited.

     R1. the vial is CLIPPED at the new size — show all of it,
     R2. the stack page's image comes back down 6%,
     R3. a simple product's control is never a stepper.

   !IMPORTANT AUDIT for this section — two declarations, both
   beating a rule that itself carries a bang, plus one that is
   deliberately defensive and says so:

     · R2 `width: 103.4%` / `max-width: 103.4%` beat this file's
       own `width: 100% !important` (~6229), same as Phase C #9
       did.
     · R3 `display: inline-flex` on the simple button beats
       `.vl-tile__actions .vl-cartctl.is-active
       .vl-cartctl__btn[data-action="add"] { display: none
       !important }` (~3659), (0,4,0) against (0,5,1).
     · R3's `display: none` on a simple stepper needs no bang and
       does not carry one.

   R1 wins on specificity and source order alone.
   ============================================================ */

/* ---- R1. The whole vial, cap to base ------------------------
   Owner: the vials are cut off on /peptides/, on the category
   archives and in the related-products row. The homepage is
   fine, and it is fine because Phase C #2 scaled its crop window
   (188 -> 216) along with its image. The `ul.products` window
   was not scaled: the image went 257 -> 296 and the window it is
   seen through stayed 191px at -37/-29 (~3647), which is a
   window sized for the OLD image.

   Measured before touching anything, on the rendered page rather
   than from the arithmetic, because the arithmetic here has a
   trap in it (below). Clearance from the window edge to the
   vial's own alpha bounds, negative meaning clipped:

     · related-products row, 1440px, 221px columns:
       top -0.4 to -1.2, bottom -9.5 to -10.1 — the cap IS being
       shaved, which is exactly what the owner reported.
     · /peptides/, 1440px, 242px columns: bottom -25.
     · /peptides/, 375px, 162px columns: bottom -21.

   THE TRAP. Three things decide where the vial lands, and only
   one of them is in this file's control:

     1. The image box is `height: 296px !important` with
        `width: auto !important; max-width: 100%` (~1800). On a
        column WIDER than 222px the box is 222x296 and the
        drawn vial is 202px tall. On a NARROWER one — every
        phone, 162px — `max-width` wins, the box is 162x296, and
        `object-fit: contain` shrinks the drawn image to 216px,
        so the vial is only 147px tall. The +15% never reached a
        phone at all; the column width has always capped it
        there. Left alone: making it reach would mean letting the
        image overflow its column, which is a bigger change than
        this was, and the owner asked about clipping, not size.
     2. `object-position` is `center bottom` (~1090), so which
        way that shrink falls is DOWNWARD.
     3. The image is an INLINE element, so its top edge is set by
        the line box, not by `margin-top` alone. That is why the
        same -37px produced a 37px offset on /peptides/ and a
        52.5px one in the related-products row — measured, both.
        Any fixed pixel window is wrong on one of the two.

   The fix removes 3 and neutralises 1:

     · `display: block` on the image. Its top edge is then
       exactly `margin-top` below the window's, on every surface,
       and the offset stops depending on font metrics. Horizontal
       centring, which the inline layout was doing for free, is
       restored with `margin-inline: auto`.
     · `object-position: 50% 50%`. The vial occupies 17.3%-85.5%
        of the image, so its centre is at 51.4% of it — half a
        percent off the middle. Centre the IMAGE and the VIAL is
        centred too, to within 1.2px, WHATEVER the column width
        does to the drawn size. That is what makes one pair of
        numbers correct at 162px and at 242px alike.

   Then the numbers are just the biggest vial plus air: 202px
   tall at most, 8px top and bottom, so a 218px window; and
   -43px to put the window's middle on the image's middle
   (296/2 - 218/2 = 39, plus the 4px the vial's centre sits below
   the image's). 296 - 43 - 35 = 218, so the margin box still
   matches the window exactly the way the old -37/-29 did.

   Cards get 27px taller. The row gap Phase C #3 set is
   untouched, so the space BETWEEN rows reads as it did.

   Scoped to `ul.products` only. The homepage and /stacks/ tiles
   are `.vl-tile` and are not touched by anything here — the
   homepage crop is already correct and must not be disturbed.
   (0,4,4) against the (0,4,3) rules it beats; no !important,
   because none of them carries one.
   ------------------------------------------------------------ */
html .woocommerce ul.products li.product .woocommerce-loop-image-link,
html .woocommerce ul.products li.product a.woocommerce-LoopProduct-link {
  height: 218px;
}
html .woocommerce ul.products li.product .woocommerce-loop-image-link img,
html .woocommerce ul.products li.product a.woocommerce-LoopProduct-link img {
  display: block;
  margin-left: auto;
  margin-right: auto;
  margin-top: -43px;
  margin-bottom: -35px;
  object-position: 50% 50%;
}

/* ---- R2. The stack page's image comes down 6% ---------------
   Owner redlined the size only; the vertical centring Phase C #9
   added is kept. 110% x 0.94 = 103.4%, so the image ends up 3.4%
   larger than it was before Phase C rather than 10%.

   The overflow is still split evenly with a negative inline
   margin, for the reason #9 gives: the image is grown past its
   own column, and unsplit it would run at the summary column.
   Measured at 1440px on this build: the column is 561.8px, so
   the image goes 618 -> 580.9 and its right edge moves from
   28px clear of the summary (x=669) to 46px clear.

   Re-measured rather than assumed, since #9's offsets were
   measured against the old size: the image is 5/3, so 6% off the
   width takes 22.3px off the height, and the top has to come
   down half of that — 11.1px — to leave the CENTRE where it was.
   266 -> 277 and 208 -> 219. Verified after the change on both
   groups: image centre against the centre of the block from the
   product title to the foot of the buy panel, 0.5px on the sized
   stacks and 0.4px on the two simple ones.

   The `:not([role])` test and the 901px floor are #9's and are
   restated here only because these are new values for the same
   two declarations. !important on the width pair for the same
   reason #9 needed it: the rule underneath is this file's own
   `width: 100% !important` (~6229).
   ------------------------------------------------------------ */
.woocommerce div.product.entry.product_cat-stacks div.images img:not(.zoomImg) {
  width: 103.4% !important;
  max-width: 103.4% !important;
  margin-inline: -1.7%;
}
@media (min-width: 901px) {
  .woocommerce div.product.entry.product_cat-stacks .woocommerce-product-gallery__wrapper {
    margin-top: 277px;
  }
  .woocommerce div.product.entry.product_cat-stacks:has(.vl-buybox__pills:not([role])) .woocommerce-product-gallery__wrapper {
    margin-top: 219px;
  }
}

/* ---- R3. A simple product is never a stepper ----------------
   Owner, verbatim: "get rid of the steppers, when someone taps
   the add to cart icon in catalogue view for a product without
   options just increase the cart quantity by 1" — and, sent
   separately, "no more steppers in related products view
   either".

   So on every catalogue surface — homepage tiles, /peptides/,
   the category archives, the related-products row and /stacks/ —
   a simple product's control is permanently the trolley button
   with a count badge. The stepper markup is gone from
   vistara_loop_cart_control() (inc/shop-archive.php) and the
   badge is rendered server-side from the cart, so a refresh
   agrees with what the tap painted.

   Two rules in this file swapped that button for a stepper the
   moment anything was held, and both key on `is-active`:

     · `.vl-cartctl.is-active .vl-cartctl__btn { display: none }`
       (~2094), (0,2,0) and no bang.
     · `.vl-tile__actions .vl-cartctl.is-active
       .vl-cartctl__btn[data-action="add"] { display: none
       !important }` (~3659), (0,4,0) WITH one.

   `is-active` is kept on the control rather than dropped: it is
   also what `.vl-tile:has(.vl-cartctl.is-active)
   .vl-tile__actions { opacity: 1 }` (~3275) reads to keep a
   holding tile's controls on show through the hover fade. So the
   two display rules are overridden here instead, on the new
   `.vl-cartctl--simple` class, which leaves the variable
   control's identical pair of rules working exactly as before.

   (0,6,3) and (0,5,1) against (0,2,0) and (0,4,0). The
   !important is for the second of those, which has one.
   ------------------------------------------------------------ */
html .woocommerce ul.products li.product .vl-cartctl--simple.is-active .vl-cartctl__btn[data-action="add"],
html .vl-tile .vl-tile__actions .vl-cartctl--simple.is-active .vl-cartctl__btn[data-action="add"] {
  display: inline-flex !important;
}

/* …and no simple control may show a stepper, whatever the markup
   says.

   Defensive, deliberately, and this is the one case where that
   earns its place: the theme editor is the only write path to
   live and it deploys ONE FILE AT A TIME. If home.css lands
   before inc/shop-archive.php, the old PHP is still rendering a
   stepper into every in-cart simple control while the rule above
   has already stopped hiding the button — which would put both
   controls on the card at once. This makes that window harmless
   in either deploy order.

   (0,2,1) against `.vl-cartctl.is-active .vl-cartctl__stepper
   { display: inline-flex }` (~2095), (0,2,0). No !important:
   nothing that reveals a stepper carries one. */
html .vl-cartctl--simple .vl-cartctl__stepper { display: none; }

/* ---- R4. The count badge does not take taps -----------------
   Found by hit-testing the rendered button rather than by
   reading the file: `elementFromPoint` swept across a 42px
   trolley on /peptides/ returns the BADGE, not the button, at
   the top-right — 1 of the 29 sampled points inside the circle.

   The badge is a `<span>` inside `.vl-cartctl__trigger` and a
   SIBLING of the button, so the delegated handler's
   `closest('[data-action]')` finds nothing from it and the tap
   is simply swallowed. It sits at `top/right: -6px` on a 20px
   square, which overlaps the button's corner by 14px each way.

   That corner used to be live on a simple product, because a
   simple product had no badge until now. The brief for this
   round says the hit area stays the full pill, so the badge
   gives its taps up: it is a readout, and there is nothing on it
   to click. Applies to the variable control's badge too, which
   has had the same dead corner since it was introduced and where
   nobody had thought to look.
   ------------------------------------------------------------ */
html .vl-cartctl__held { pointer-events: none; }

/* ---- R5. The two count badges centre their number alike ------
   Owner screenshot: the number sits dead centre on the simple
   control's badge and visibly left-of-centre on the variable
   one. Measured on /peptides/ at 1440, both badges forced
   visible and set to "1", comparing the centre of a Range over
   the text node against the centre of the badge box:

     simple    box 20x20  dx  0.00  dy -0.25  display: block
     variable  box 20x20  dx -1.86  dy -0.25  display: flex

   Same box, same padding, same font, same line-height — the
   geometry and typography were already identical. The whole
   difference is `display`. Two rules force the VARIABLE badge to
   `inline-flex` so it can be revealed over the
   `display: none !important` at ~4400 (one at ~6379 for all
   widths, one at ~5102 under 640px); the simple badge is never
   given a display at all and stays the plain inline `<span>` the
   markup ships.

   On the block badge `line-height: 20px` and `text-align: center`
   centre the digit. On the flex one they cannot: the text
   becomes an anonymous flex item shrink-wrapped to the glyph, so
   `text-align` has no slack to work with and the item is placed
   by `justify-content`, which is still `normal` — i.e. packed to
   the start. Hence 1.86px adrift, and hence left rather than
   right.

   Fix the cause rather than nudging the position: give the badge
   the two flex alignment properties it never had. They are inert
   on a non-flex box, so the simple badge — the reference — is
   untouched by design, and the variable one lands on top of it.

   AUDIT. Nothing in this file sets `justify-content` or
   `align-items` on `.vl-cartctl__held`; the eight rules that do
   match it set display, position, box metrics, colour, type and
   pointer-events. So this adds properties rather than overriding
   any, needs no `!important` (no competing declaration carries
   one) and would in fact win from anywhere in the file. It sits
   at the end because that is where this file's rules go.

   `html` prefix only to match the surrounding sections' (0,1,1).
   Applied to the shared class, not `--variable`, so the simple
   badge stays correct if it is ever given a flex display too.
   ------------------------------------------------------------ */
html .vl-cartctl__held {
  align-items: center;
  justify-content: center;
}

/* ---- R4. Stack gallery anchored to the trust rows -----------
   Owner, this round: "move the product image up so the bottom of
   the product image is in horizontal alignment with the 'Ships
   From Canada Within 24 Hours'" row. Measured at 1440px: the
   image bottom sat 157.7px below that row on the sized stacks
   and 99.7px on the simple two, so 277px and 219px both correct
   to the SAME 119px -- which makes sense, because the trust rows
   end above the point where the two layouts start to differ.
   One value now serves both, so the :has() split from R2/#9 is
   redundant here; it is restated at the same value rather than
   removed, because this file is append-only and a later reader
   should find the pair still agreeing. Same selectors, same
   901px floor as the rules these beat (~7556/7559). */
@media (min-width: 901px) {
  .woocommerce div.product.entry.product_cat-stacks .woocommerce-product-gallery__wrapper {
    margin-top: 119px;
  }
  .woocommerce div.product.entry.product_cat-stacks:has(.vl-buybox__pills:not([role])) .woocommerce-product-gallery__wrapper {
    margin-top: 119px;
  }
}


/* ============================================================
   PHASE D — HEADER & NAVIGATION RESTRUCTURE          (Aug 29)
   ============================================================
   Wordmark left (smaller), primary menu centred, and a new
   right-hand cluster: language flags, an expanding search, and
   the cart that replaces the retired floating corner button.

   WHAT THIS SECTION HAS TO BEAT, and why it can

   1. WORDMARK SIZE. The only font-size on the desktop wordmark
      is `clamp(1.25rem, 1rem + .8vw, 1.85rem)` in style.css
      (~549), at (0,2,0). home.css is enqueued with style.css as
      a dependency, so it is always the later sheet: an identical
      (0,2,0) selector here wins on order, and no !important is
      needed. On mobile the competing rule is the 24px pair in
      the <=640px block at ~4163, whose longer selector is
      (0,3,0); the same pair is restated below so the tie is
      broken by order there too.
      Nothing else in either file sets a font-size on .site-title.

   2. THE CART BADGE. `.vl-cart__count` (~594) positions and
      colours the badge for a 58px corner button: teal, 22px,
      top/right -5px. The badge markup is unchanged -- it has to
      be, because WooCommerce's fragment refresh replaces it with
      exactly what vistara_cart_count_html() prints -- so those
      rules still apply and are overridden here at (0,2,0)
      through the header's own wrapper.
      DELIBERATELY NO `display` DECLARATION. `.vl-cart__count.is-empty
      { display: none }` (~599) is also (0,2,0); a `display` set
      from here would come later, beat it, and leave a "0" badge
      on an empty cart. Centring is done the way that rule already
      does it, with line-height and text-align.

   3. THE TOAST. `.vl-cart__toast` (~4943) pins it beside the
      corner button with `right: calc(100% + 10px)` and a
      translate. The element keeps that class for the card
      styling and takes `.vl-hdr__toast` for position, both at
      (0,1,0) and (0,2,0) for the .is-in pair -- later file, later
      in file, so both win.

   4. NOTHING ELSE IN THIS FILE MATCHES `.vl-hdr*`, `.vl-drawer*`,
      `.vl-cartextras` or `.vl-bacnudge`. Those are new
      namespaces; every rule for them below is a first
      declaration, not an override.

   !IMPORTANT AUDIT: this section adds exactly three, all on the
   header container padding at <=640px (see MOBILE BAR), and only
   because `#masthead .site-container { padding-inline: var(--vl-edge)
   !important }` (~1384) already carries one and cannot be
   out-specified without matching it. Everything else here wins on
   cascade order alone.

   AGE GATE: nothing added here animates on its own. The search
   is a width transition on a class the visitor sets by clicking,
   and the cart pulse is a class set by a cart response, so there
   is no entrance animation for `vl-gate-pending` to hold. The
   existing hold list (hero art, marquee, ticker) is unchanged.
   ------------------------------------------------------------ */

/* ---- 1. Wordmark, a step down --------------------------------
   Term by term from the old clamp: 1.25 -> 1.05rem floor,
   .8vw -> .62vw slope, 1.85 -> 1.55rem cap. 27.5px becomes 22.5px
   at 1440 and 24.2px becomes 20.0px at 1025, so it reads smaller
   at every desktop width rather than only at the ends. */
.site-branding .site-title,
.site-title-wrap .site-title {
  font-size: clamp(1.05rem, .85rem + .62vw, 1.55rem);
}

/* ---- 2. Centred menu, generous item spacing ------------------
   Kadence gives the row `grid-template-columns: 1fr auto 1fr` as
   soon as the centre column holds anything, so the menu is
   genuinely centred on the page rather than centred on what is
   left over. The only thing missing is air between the items:
   the anchors ship at 10.56px of padding all round, which is the
   tight spacing of a right-aligned menu.

   Padding rather than gap, so the hover and focus target grows
   with the space instead of leaving dead ground between items. */
@media (min-width: 1025px) {
  .site-header .header-navigation .menu-item > a,
  #masthead .header-navigation .menu-item > a {
    padding-left: 1.15rem;
    padding-right: 1.15rem;
  }
  /* The dropdown panels are a fixed 220px list; their own padding
     is set at ~6001 and must not inherit the widened top-level
     value, or every sub-item indents by another 18px. */
  .site-header ul.sub-menu li a,
  #masthead ul.sub-menu li a {
    padding-left: 18px;
    padding-right: 18px;
  }
}

/* ---- 3. The right-hand cluster -------------------------------- */
.vl-hdr {
  display: flex;
  align-items: center;
  gap: 1.1rem;
}

/* Language flags. The markup is whatever menu is assigned to the
   `vistara-language` location: on live that is TranslatePress's
   own switcher items, which render as anchors carrying an
   <img class="trp-flag-image">. Both forms are handled --
   a flat two-item list, and the current-language-plus-submenu
   form, which is flattened by the sub-menu rules below. */
.vl-hdr__langmenu {
  list-style: none;
  display: flex;
  align-items: center;
  gap: .55rem;
  margin: 0;
  padding: 0;
}
.vl-hdr__langmenu li { margin: 0; padding: 0; }
.vl-hdr__langmenu a {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  font-size: .82rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: #233642;
  text-decoration: none;
  line-height: 1;
}
.vl-hdr__langmenu a:hover { color: #21609D; }
.vl-hdr__langmenu .current-menu-item > a { font-weight: 700; }
/* TranslatePress renders its flags as images; size them here so
   the row is the same height whichever switcher form is used. */
.vl-hdr__langmenu img,
.vl-hdr__langmenu img.trp-flag-image {
  width: 20px;
  height: 14px;
  object-fit: cover;
  display: block;
}
/* If the switcher is added as "current language + others", the
   others arrive in a sub-menu. Flatten it: the owner asked for
   two flags side by side, not a dropdown. `position: static`
   undoes the absolute placement Kadence gives header sub-menus. */
.vl-hdr__langmenu .sub-menu {
  position: static;
  display: flex;
  align-items: center;
  gap: .55rem;
  list-style: none;
  margin: 0 0 0 .55rem;
  padding: 0;
  background: none;
  border: 0;
  box-shadow: none;
  min-width: 0;
  width: auto;
  opacity: 1;
  visibility: visible;
  transform: none;
}

/* ---- 4. Expanding search -------------------------------------
   The field is OVERLAID rather than inserted. Laid out inline it
   would widen the right-hand grid column, and because a `1fr`
   track cannot shrink below its content the centred menu would
   slide sideways every time the magnifier was pressed -- about
   100px at 1025, where the column has the least slack. Absolute
   placement takes the field out of flow entirely, so the header
   does not move at all.

   It opens leftwards over the language flags, which is why it
   carries the header's own white: there is nothing else in that
   space, and a transparent field over the flags would be
   unreadable. */
/* Deliberately NOT `position: relative`. The field and the results
   panel are both positioned against the header section above, so
   that the field can measure itself against the whole column;
   making this a containing block would shrink both to the 40px
   magnifier. `z-index` still applies -- it works on a positioned
   ancestor, and the section is one. */
.vl-hdr__search { display: flex; align-items: center; }

.vl-hdr__searchtoggle,
.vl-hdr__cart {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  background: none;
  color: #233642;
  cursor: pointer;
  border-radius: 999px;
  text-decoration: none;
  transition: color .2s ease, background-color .2s ease;
}
.vl-hdr__searchtoggle:hover,
.vl-hdr__cart:hover { color: #21609D; background: #F4F8FC; }
.vl-hdr__searchtoggle:focus-visible,
.vl-hdr__cart:focus-visible { outline: 2px solid #233642; outline-offset: 2px; }

/* The field is measured against the HEADER COLUMN, not against a
   fixed pixel width.

   A flat `width: 260px` was wrong at the narrow end: at 1025 the
   open field ran from 672 to 872 while the menu's last item ended
   at 687, so "Contact Us" and the search box overlapped by 15px --
   and the /fr/ tree makes that worse, because "Contactez-nous"
   is wider than "Contact Us" and the menu grows into the same
   space. A number that has to be re-checked against every future
   label is a number that will eventually be wrong.

   So the form is positioned against the right-hand header SECTION
   -- the `1fr` grid column that begins exactly where the centred
   menu ends. `left: 0` IS the menu's right edge, so the field
   cannot reach the menu at any width or in any language: when the
   column is narrow the field is narrow. `right` reserves the
   trailing icons, `max-width` stops it sprawling on a wide screen,
   and `margin-left: auto` keeps it hard against the magnifier so
   it grows leftwards out of it.

   `max-width` is what animates, since `width` is now derived. */
#main-header .site-header-main-section-right {
  position: relative;
  /* Toggle + gap + cart + a little air: the run of things the field
     must stop short of. Named so the arithmetic stays readable if
     the cluster is ever re-spaced. */
  --vl-hdr-trail: calc(40px + 1.1rem + 40px + 4px);
}
.vl-hdr__searchform {
  position: absolute;
  top: 50%;
  left: 0;
  right: var(--vl-hdr-trail, 102px);
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  margin-left: auto;
  max-width: 0;
  overflow: hidden;
  /* `visibility` and not `display`: a transition needs something
     to animate, and a hidden-but-focusable field in the header is
     how a keyboard visitor ends up typing into a box they cannot
     see. `visibility: hidden` removes it from the tab order and
     from the accessibility tree, and still animates. */
  visibility: hidden;
  background: #FFFFFF;
  border-bottom: 1px solid var(--vl-rule, #DBDBDB);
  transition: max-width .26s cubic-bezier(.22,.61,.36,1), visibility .26s;
}
.vl-hdr__search.is-open .vl-hdr__searchform { max-width: 260px; visibility: visible; }

.vl-hdr__searchinput {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  background: none;
  padding: .45rem .2rem .45rem .5rem;
  font-family: var(--vl-font);
  font-size: .95rem;
  color: #233642;
}
.vl-hdr__searchinput:focus { outline: none; }
.vl-hdr__searchclose {
  flex: 0 0 auto;
  border: 0;
  background: none;
  color: #8A8A8A;
  font-size: 1.25rem;
  line-height: 1;
  padding: 0 .35rem;
  cursor: pointer;
}
.vl-hdr__searchclose:hover { color: #233642; }

/* Results. Anchored to the header section, like the field: its
   right edge is the page gutter, which is where the cart icon
   ends, so the panel hangs under the cluster whatever the field
   is doing. `top: 100%` is the bottom of the header row. */
.vl-hdr__results {
  position: absolute;
  top: calc(100% - 6px);
  right: 0;
  z-index: 950;
  width: min(360px, calc(100vw - 3rem));
  max-height: min(60vh, 460px);
  overflow-y: auto;
  background: #fff;
  border: 1px solid var(--vl-rule, #DBDBDB);
  border-radius: 8px;
  box-shadow: 0 14px 38px rgba(43,62,81,.16);
  padding: 6px 0;
}
.vl-hdr__results[hidden] { display: none; }
.vl-hdr__resultlist { list-style: none; margin: 0; padding: 0; }
.vl-hdr__result { margin: 0; }
.vl-hdr__resultlink {
  display: flex;
  align-items: center;
  gap: .7rem;
  padding: .55rem .9rem;
  text-decoration: none;
  color: #233642;
}
.vl-hdr__resultlink:hover,
.vl-hdr__resultlink.is-active { background: var(--vl-paper, #F6F7F6); }
.vl-hdr__resultthumb { flex: 0 0 auto; }
.vl-hdr__resultthumb img {
  width: 42px; height: 42px;
  object-fit: contain;
  display: block;
  background: #fff;
}
.vl-hdr__resulttext { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.vl-hdr__resultname {
  font-size: .9rem; font-weight: 600; line-height: 1.25; color: #233642;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.vl-hdr__resultprice { font-size: .84rem; color: #21609D; font-weight: 700; }
.vl-hdr__noresults { margin: 0; padding: .9rem; color: #666; font-size: .9rem; text-align: center; }

/* ---- 5. Header cart + badge ----------------------------------
   The badge element is the corner cart's, unchanged, because the
   fragment refresh replaces it with byte-identical markup. Only
   its size, colour and offsets move. Red rather than the corner
   button's teal, per the plan's "red dot style": the header is a
   quiet row of dark glyphs and the count has to be the one thing
   in it that carries. */
.vl-hdr__cart { position: relative; }
.vl-hdr__cart .vl-cart__count,
.vl-hdr__cart .vl-hdr__badge,
.vl-drawer__cart .vl-hdr__badge {
  top: 1px;
  right: 1px;
  min-width: 17px;
  height: 17px;
  padding: 0 5px;
  border-radius: 999px;
  background: #C0392B;
  color: #fff;
  font-size: .68rem;
  font-weight: 700;
  line-height: 17px;
  text-align: center;
  /* No `display` here: `.vl-cart__count.is-empty { display: none }`
     at ~599 is the same (0,2,0) and comes earlier, so setting one
     would show a "0" on an empty cart. */
}

/* A short nudge when something lands in the cart. The badge has
   its own `is-bumped` scale (~600); this is the icon around it. */
.vl-hdr__cart.is-pulsing { animation: vl-hdrcart-pulse .5s cubic-bezier(.22,.61,.36,1); }
@keyframes vl-hdrcart-pulse {
  0%   { transform: scale(1); }
  38%  { transform: scale(1.18); }
  100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .vl-hdr__cart.is-pulsing { animation: none; }
  .vl-hdr__searchform { transition: none; }
}

/* ---- 6. Toast, repinned --------------------------------------
   It used to hang off a button that never moved. The cart icon
   now sits in the header on a desktop and in the bar on a phone,
   and a header that scrolls away would take an anchored toast
   with it. Fixed to the top-right corner it is under the cart
   icon at the top of a page and still on screen further down.
   Below the age gate's 99999 by three orders of magnitude, so a
   first visit cannot have one slide out behind the dialog. */
.vl-hdr__toast {
  position: fixed;
  top: 88px;
  right: 22px;
  z-index: 940;
  transform: translateX(12px);
}
.vl-hdr__toast.is-in { transform: translateX(0); }
@media (max-width: 640px) {
  .vl-hdr__toast { top: 72px; right: 12px; max-width: calc(100vw - 24px); }
}
@media (prefers-reduced-motion: reduce) {
  .vl-hdr__toast,
  .vl-hdr__toast.is-in { transform: none; }
}

/* ---- 7. Mobile bar -------------------------------------------
   Kadence's mobile right-hand section is a flex row; the cart
   sits in it ahead of the hamburger. Both are 44px targets. */
@media (max-width: 1024px) {
  #mobile-header .site-header-section-right { align-items: center; gap: .15rem; }
  .vl-hdr__cart--mobile { width: 44px; height: 44px; }
}

/* The wordmark, the cart and the hamburger have to share 375px.
   24px of wordmark plus two 44px targets and the 3.2rem gutters
   left the last letter of "LΛBS" touching the trolley, so the
   type comes down a step and the gutter narrows to the 1.1rem
   the rest of the phone layout uses.

   Both !important, and neither is optional: the padding rule at
   ~1384 sets `padding-inline: var(--vl-edge) !important` on these
   exact containers, so nothing without one can reach them. The
   third is on the branding reset at ~1388, same reason. */
@media (max-width: 640px) {
  .mobile-site-branding .site-title,
  .mobile-site-branding .site-title-wrap .site-title { font-size: 20px; }

  #masthead .site-container,
  .site-header .site-container,
  #masthead .site-header-row-container-inner,
  .site-header .site-header-row-container-inner { padding-inline: 1.1rem !important; }
  .site-branding, .site-header-main-section-left { padding-left: 0 !important; margin-left: 0 !important; }
  .vl-hdr__cart--mobile { margin-right: 0 !important; }
}

/* ---- 8. Drawer tail ------------------------------------------
   Flags, then the cart, under the flat menu. The menu itself is
   flat because the menu ASSIGNED to the mobile location is flat --
   no CSS here hides a submenu, because there are none to hide.

   The tail is a SIBLING of the drawer's <nav>, because Kadence's
   mobile-navigation item prints the whole nav at priority 10 and
   this hooks the same action at 20. That item's wrapper is
   `display: flex` in a row, so the tail landed BESIDE the menu in
   a 72px column hanging off its right edge -- measured, x=277 in a
   338px drawer. Stacking the wrapper is what puts it underneath.

   Scoped to `#mobile-drawer`: the same class names the mobile
   header row would use if the navigation were ever placed there,
   and that layout should stay a row. */
#mobile-drawer .site-header-item-mobile-navigation {
  display: block;
}

/* Colours come from the drawer, not from the page.
   The drawer panel is Kadence's dark popup (#090C10, white text),
   so the header cluster's #233642 on white was very nearly
   invisible in it, and the hairline rule read as a bright scar
   across the panel. `inherit` and a translucent white follow the
   panel whatever it is set to. */
#mobile-drawer .vl-drawer__tail {
  /* No rule of its own. The drawer menu already draws a hairline under
     its last item, and a second one 33px below it read as a double
     border; the space is the separation. */
  margin-top: 1.5rem;
  display: flex;
  flex-direction: column;
  gap: .5rem;
  align-items: flex-start;
  color: inherit;
}
#mobile-drawer .vl-hdr__lang--drawer .vl-hdr__langmenu { gap: 1.4rem; }
/* 44px, like every other target in the drawer: two-letter links are the
   smallest thing on this panel and the easiest to miss with a thumb. */
#mobile-drawer .vl-hdr__lang--drawer .vl-hdr__langmenu a {
  font-size: 1.1rem;
  min-height: 44px;
  /* Wide as well as tall. "EN" measured 28px across, which is a 28x53
     target -- fine for a mouse, thin for a thumb. The label stays
     left-aligned inside the wider box, so the flags do not move. */
  min-width: 44px;
  padding: 0;
  color: inherit;
}
#mobile-drawer .vl-hdr__lang--drawer .vl-hdr__langmenu a:hover { opacity: .7; }
#mobile-drawer .vl-hdr__lang--drawer .vl-hdr__langmenu img { width: 26px; height: 18px; }

#mobile-drawer .vl-drawer__cart {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: .7rem;
  min-height: 48px;
  color: inherit;
  text-decoration: none;
  font-size: 1.32rem;
}
#mobile-drawer .vl-drawer__cart:hover { opacity: .7; }
/* The badge rides on the glyph, not the end of the label, so it
   reads as belonging to the trolley the way it does in the bar. */
#mobile-drawer .vl-drawer__cart .vl-hdr__badge {
  position: absolute; top: 4px; left: 13px; right: auto;
}

/* ---- 9. Cart page: water prompt + checkout -------------------
   New namespace; nothing above matches either class. The button
   is the buy box's language deliberately: same #21609D, same
   999px radius, same 48px, same weight, so "Proceed to Checkout"
   and "Add to Cart" read as the same control doing the next
   step. */
.vl-cartextras {
  margin-top: 2.2rem;
  display: flex;
  flex-direction: column;
  gap: 1.4rem;
  /* The cart page's content container runs the full width of the window and
     the Cart block insets its own two columns from inside it; content appended
     after the block gets neither, so the tinted prompt ran edge to edge under a
     cart that did not. `--vl-edge` is the value that inset is measured in
     (3.2rem, 1.15rem on a phone), so this lines the panel up with the line
     items at every width rather than at one tested one. */
  padding-inline: var(--vl-edge, 3.2rem);
}
.vl-cartextras.is-hidden,
.vl-bacnudge.is-hidden { display: none; }

.vl-bacnudge {
  position: relative;
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1rem 2.4rem 1rem 1rem;
  border: 1px solid var(--vl-rule, #DBDBDB);
  border-radius: 10px;
  background: var(--vl-paper, #F6F7F6);
}
.vl-bacnudge__img { flex: 0 0 auto; }
.vl-bacnudge__img img {
  width: 56px; height: 56px; object-fit: contain; display: block; background: #fff;
}
.vl-bacnudge__text { flex: 1 1 auto; min-width: 0; }
.vl-bacnudge__head { margin: 0 0 .2rem; font-weight: 700; color: #233642; font-size: 1rem; }
.vl-bacnudge__body { margin: 0; color: #666; font-size: .9rem; line-height: 1.45; }
.vl-bacnudge__add {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px;
  padding: .6rem 1.3rem;
  border: 0; border-radius: 999px;
  background: #21609D; color: #fff;
  font-family: var(--vl-font); font-size: .95rem; font-weight: 500; letter-spacing: .01em;
  line-height: 1.2;
  cursor: pointer;
  transition: background .2s ease;
}
.vl-bacnudge__add:hover { background: #1B5185; }
.vl-bacnudge__add.is-busy { opacity: .55; cursor: wait; }
.vl-bacnudge__add .woocommerce-Price-amount { color: inherit; }
.vl-bacnudge__dismiss {
  position: absolute; top: 6px; right: 8px;
  border: 0; background: none; cursor: pointer;
  color: #A8A8A8; font-size: 1.2rem; line-height: 1;
  padding: .25rem .35rem;
}
.vl-bacnudge__dismiss:hover { color: #233642; }

.vl-cartextras__checkout {
  display: inline-flex; align-items: center; justify-content: center;
  align-self: center;
  width: min(420px, 100%);
  min-height: 48px;
  padding: .7rem 1.6rem;
  border-radius: 999px;
  background: #21609D; color: #fff;
  font-family: var(--vl-font); font-size: 1rem; font-weight: 500; letter-spacing: .01em;
  line-height: 1.2;
  text-decoration: none;
  transition: background .2s ease;
}
.vl-cartextras__checkout:hover { background: #1B5185; color: #fff; }
.vl-cartextras__checkout:focus-visible { outline: 2px solid #233642; outline-offset: 2px; }

@media (max-width: 640px) {
  /* `align-items: flex-start` once the panel wraps: centred, the vial
     floated to the middle of a four-line paragraph and left the heading
     hanging over nothing. Topped, it sits beside the first line. */
  .vl-bacnudge { flex-wrap: wrap; align-items: flex-start; padding: .9rem 2.2rem .9rem .9rem; gap: .8rem; }
  .vl-bacnudge__text { flex: 1 1 60%; }
  .vl-bacnudge__add { flex: 1 1 100%; }
  .vl-cartextras__checkout { width: 100%; }
}

/* ---- 10. Empty cart: shop button under the message ------------
   The button block that replaces "New in store" in the cart
   page's own content. Centred on the block's own alignment, and
   given the ghost treatment the homepage CTA uses so it does not
   compete with the checkout button further down the same page. */
.wp-block-woocommerce-empty-cart-block .vl-emptycart-cta {
  display: flex;
  justify-content: center;
  margin: 0 0 2.6rem;
}
.wp-block-woocommerce-empty-cart-block .vl-emptycart-cta a {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 48px;
  padding: .7rem 1.9rem;
  border: 1px solid #000; border-radius: 999px;
  background: transparent; color: #000;
  font-family: var(--vl-font); font-size: 1rem; font-weight: 500; letter-spacing: .01em;
  line-height: 1.2;
  text-decoration: none;
  transition: background .2s ease, color .2s ease, border-color .2s ease;
}
.wp-block-woocommerce-empty-cart-block .vl-emptycart-cta a:hover {
  background: #21609D; border-color: #21609D; color: #fff;
}


/* ============================================================
   PHASE D — REDLINES: MINI-CART, DRAWER, BAC CARD    (Aug 29)
   ============================================================
   Owner redlines on the Phase D header. Seven changes; five of
   them need CSS:

     1/2. the trolley opens a MINI-CART instead of navigating --
          a dropdown under the trolley on a desktop, a slide-out
          sheet on a phone;
       4. the mobile drawer gains a search field and a cart row
          ABOVE the menu, and the EN/FR flags move inward to
          line up with the menu items;
       6. the bacteriostatic-water card moves ABOVE the cart
          table so it is in the first viewport;
       7. the toast stands down while the panel is open (that
          one is script, not CSS -- see inc/header.php).

   WHAT THIS SECTION HAS TO BEAT, and why it can
   ------------------------------------------------------------
   A. `.vl-minicart`, `.vl-minicart__scrim`, `.vl-minicart__panel`,
      `.vl-drawer__head`, `.vl-drawer__search*`, `.vl-cart__foot`,
      `.vl-cart__emptybox`, `.vl-cart__emptylink` and
      `.vl-cartextras--top` are NEW NAMES. Checked against both
      sheets: zero matches for each in home.css and style.css
      before this block. Every rule for them here is a first
      declaration, not an override, and needs no weight at all.

   B. THE PANEL'S INNARDS are deliberately NOT new. The markup
      comes from vistara_cart_panel_html(), which the retired
      corner cart already had styled at ~603-631 and ~826-936:
      `.vl-cart__head`, `.vl-cart__close`, `.vl-cart__panelinner`,
      `.vl-cart__items`, `.vl-cart__item`, `.vl-cart__thumb`,
      `.vl-cart__meta`, `.vl-cart__name`, `.vl-cart__price`,
      `.vl-cart__qtyctl`, `.vl-cart__step`, `.vl-cart__qtynum`,
      `.vl-cart__remove`, `.vl-cart__subtotal`,
      `.vl-cart__checkout`, `.vl-cart__viewcart`. All of those are
      inherited as-is and none is touched here.
      The ONE the panel must not inherit is `.vl-cart__panel`
      (~603), which positions the card `bottom: 72px` off a fixed
      corner button that no longer exists. It is not overridden --
      the class is simply not applied; the new shell is
      `.vl-minicart__panel`.

   C. `.vl-cart__panelinner` (~617) is `padding: 1.15rem;
      overflow-y: auto`, i.e. the whole body scrolls as one. The
      sheet needs the subtotal and CHECKOUT pinned while only the
      ITEMS scroll, so three declarations are re-set on it here.
      Reached at (0,2,0) through `.vl-minicart`, against the
      original's (0,1,0): it wins on specificity, not on file
      order, so it is safe wherever this block ends up.

   D. `#mobile-drawer .vl-drawer__cart` (~8185) styled that row as
      an ANCHOR. It is a <button> now -- it opens a panel, it
      never navigates -- so the UA button defaults (background,
      border, padding, font-family, text-align) are reset here.
      Same selector, later in the file, so order carries it; the
      badge offset at ~8199 is restated because the row gained a
      left inset it has to follow (see the comment there).

   E. `.vl-hdr__langmenu` (~7823) sets `padding: 0`. The drawer's
      copy takes a left inset here through
      `#mobile-drawer .vl-hdr__lang--drawer .vl-hdr__langmenu`,
      which is (1,3,0) against (0,1,0). Specificity, not order.

   F. `.vl-cartextras` (~8208) sets `margin-top: 2.2rem` for a
      block that lives BELOW the cart. The prepended copy carries
      `.vl-cartextras--top` as well, and the pair is (0,2,0)
      against the original's (0,1,0). Specificity again.
      `.vl-cartextras.is-hidden` (~8221) still matches both
      wrappers, which is what hides them on an empty cart.

   !IMPORTANT AUDIT: this section adds EXACTLY ONE !important
   declaration, and it is in part 9 -- the desktop single-product
   image width, added in the third redline pass.

   Parts 1-8 add none, and none is needed: nothing they have to
   beat carries one. The `!important` rules already in this file
   are on product-image widths, the bestseller grid and the header
   container padding, and only the first of those stands in the
   way of anything here.

   Part 9 is the exception because the rule it corrects --
   `width: 506px !important` at ~2417 -- is itself important, and
   no amount of specificity lets a normal declaration beat an
   important one. It is kept to the minimum: one property, inside
   one media query, on a selector that cannot match a stack page.
   Part 9's own header sets out the arithmetic and the proof.

   Z-INDEX, and the age gate
   ------------------------------------------------------------
   `.vl-gate` is 99999 (~312) and Kadence's own #mobile-drawer is
   also 99999. The sheet sits at 99989 and its scrim at 99988 --
   under BOTH, deliberately:
     - a first visit cannot slide a cart out over the age dialog;
     - the nav drawer is closed by the script before the sheet
       opens (see mcCloseDrawer()), so the sheet never needs to
       out-stack it, and if that ever failed the drawer would
       stay on top rather than trapping the visitor under it.
   The desktop dropdown is 960: above the search results panel
   (950, ~8000) and the toast (940, ~8091), below everything
   modal. Nothing here animates on page load, so `vl-gate-pending`
   still has nothing new to hold.
   ------------------------------------------------------------ */

/* ---- 1. Mini-cart: the shell -------------------------------- */
.vl-minicart[hidden] { display: none; }

/* The wrapper is a container and nothing else -- no position, no
   stacking context -- so the scrim and the panel can sit in two
   different position schemes at two different breakpoints without
   fighting a parent. */
.vl-minicart__scrim {
  position: fixed;
  inset: 0;
  z-index: 99988;
  background: rgba(9, 12, 16, .45);
  opacity: 0;
  transition: opacity .24s ease;
}
.vl-minicart.is-open .vl-minicart__scrim { opacity: 1; }

.vl-minicart__panel {
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1px solid var(--vl-rule, #DBDBDB);
  box-shadow: 0 18px 46px rgba(0, 0, 0, .20);
  overflow: hidden;
}

/* The body scrolls in two parts: the item list, and nothing else.
   See note C above -- this replaces `.vl-cart__panelinner`'s
   single scrolling box so the subtotal and CHECKOUT stay put. */
.vl-minicart .vl-cart__panelinner {
  display: flex;
  flex-direction: column;
  /* `min-height: 0` because a flex item's default `min-height: auto`
     refuses to shrink below its content -- without it the list does
     not scroll, the panel grows, and the sheet's footer is pushed
     off the bottom of the screen. */
  min-height: 0;
  flex: 1 1 auto;
  overflow: hidden;
}
.vl-minicart .vl-cart__items {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.vl-minicart .vl-cart__foot { flex: 0 0 auto; }
.vl-minicart .vl-cart__head { flex: 0 0 auto; }

/* The panel's three <button> kinds, on FOCUS.
   ------------------------------------------------------------
   Kadence's global.min.css carries a bare
   `button:focus { background: var(--global-palette-btn-bg-hover) }`
   at (0,1,1). Every button in this panel is styled by a single
   class -- `.vl-cart__close`, `.vl-cart__step`, `.vl-cart__remove`
   -- which is (0,1,0), so all three lose their background the
   moment they take focus and fill with #2B3E51 navy.

   That was invisible while the panel was dormant: nothing focused
   these buttons. The mini-cart focuses the close button on open
   (it is a dialog, and Escape has to have something to return
   from), and clicking a stepper leaves it focused, so a navy
   square appeared on the × of every sheet and under every + and −
   the shopper pressed. Measured with a rule walk in the page --
   `button:focus` from global.min.css#8 was the winning rule.

   Beaten at (0,2,1), which is one class more than Kadence's rule
   and does not depend on file order. The colour is restated
   rather than left to `inherit` because Kadence sets the button
   text to white in the same breath.

   The visible focus RING is moved to `:focus-visible`, so a
   keyboard visitor still gets an indicator and a mouse click that
   happens to leave focus behind does not paint one. */
.vl-minicart .vl-cart__close:focus,
.vl-minicart .vl-cart__step:focus,
.vl-minicart .vl-cart__remove:focus {
  background: none;
  color: #233642;
  box-shadow: none;
}
/* The steppers keep their own white pill and hairline (~929). */
.vl-minicart .vl-cart__step:focus { background: #fff; }
.vl-minicart .vl-cart__close:focus-visible,
.vl-minicart .vl-cart__step:focus-visible,
.vl-minicart .vl-cart__remove:focus-visible {
  outline: 2px solid #233642;
  outline-offset: 2px;
}

/* Empty cart: a short card with a way out of it, not a tall one
   with a sentence floating in the middle. */
.vl-cart__emptybox {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .9rem;
  padding: .6rem 0 .4rem;
}
.vl-cart__emptylink {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: .6rem 1.4rem;
  border: 1px solid #000;
  border-radius: 999px;
  background: transparent;
  color: #000;
  font-family: var(--vl-font);
  font-size: .95rem;
  font-weight: 500;
  line-height: 1.2;
  text-decoration: none;
  transition: background .2s ease, color .2s ease, border-color .2s ease;
}
.vl-cart__emptylink:hover {
  background: #21609D;
  border-color: #21609D;
  color: #fff;
}

/* ---- 2. Desktop: a dropdown under the trolley ---------------
   Placed in VIEWPORT coordinates by the script, which measures the
   trolley and writes `top`/`right` inline on open and again on
   each scroll frame while open.

   `fixed`, NOT `absolute`, because `#masthead` is
   `position: sticky; top: 0` (Kadence, z-index 500). Measured at
   1440x900: scrolled to y=1200, the header rect was still 0..80
   and the trolley still at viewport y 20..60. A panel placed in
   page coordinates would therefore scroll up and away from a
   trolley that had not moved.

   This is the one place the mini-cart does NOT follow
   `.vl-hdr__results` (~7996), which is absolute inside the header
   itself and rides the sticky element for free. The panel cannot
   live inside the header: it has to be one element serving both
   breakpoints, and the desktop header is `display: none` on a
   phone. See the note on vistara_mini_cart() in inc/header.php. */
@media (min-width: 1025px) {
  .vl-minicart__scrim { display: none; }

  .vl-minicart__panel {
    position: fixed;
    z-index: 960;
    width: min(380px, calc(100vw - 2rem));
    max-height: min(70vh, 560px);
    border-radius: 10px;
    transform: translateY(-6px);
    opacity: 0;
    transition: opacity .18s ease, transform .18s cubic-bezier(.22,.61,.36,1);
  }
  .vl-minicart.is-open .vl-minicart__panel {
    transform: translateY(0);
    opacity: 1;
  }
}

/* ---- 3. Mobile: a sheet from the right ----------------------
   Same presentation family as Kadence's nav drawer, which is what
   the owner asked for: a right-hand sheet over a dimmed page, so
   the cart and the menu feel like two faces of one control rather
   than two different mechanisms. */
@media (max-width: 1024px) {
  .vl-minicart__panel {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: auto;
    z-index: 99989;
    width: min(400px, 92vw);
    max-height: none;
    border-radius: 0;
    border-width: 0 0 0 1px;
    transform: translateX(100%);
    transition: transform .26s cubic-bezier(.22,.61,.36,1);
  }
  .vl-minicart.is-open .vl-minicart__panel { transform: translateX(0); }

  /* The head is the sheet's only close affordance once the page
     behind it is covered, so it gets a real target rather than the
     1.5rem glyph the corner panel could get away with. */
  .vl-minicart .vl-cart__close {
    min-width: 44px;
    min-height: 44px;
    font-size: 1.9rem;
  }
  /* Room for a phone's home indicator under the checkout button. */
  .vl-minicart .vl-cart__foot { padding-bottom: env(safe-area-inset-bottom, 0px); }

  /* Stop the item list handing its scroll to the page underneath.
     The sheet covers the whole screen, so a flick that runs past the
     end of the list would otherwise carry on scrolling the product
     page behind it, and the shopper closes the cart to find
     themselves somewhere else. Measured: with the sheet open the
     document still scrolled freely.

     `contain` keeps the gesture in the list WITHOUT locking the
     document, which is the deliberate half of this. The age gate and
     Kadence's drawer each set their own scroll lock on <html>/<body>;
     a third one interleaving with those is how a page ends up
     permanently unscrollable, and that is not a risk worth taking on
     a live shop for a nicety. See the open question in the report. */
  .vl-minicart .vl-cart__items,
  .vl-minicart__panel { overscroll-behavior: contain; }
}

@media (prefers-reduced-motion: reduce) {
  .vl-minicart__panel,
  .vl-minicart.is-open .vl-minicart__panel { transition: none; transform: none; }
  .vl-minicart__scrim { transition: none; }
}

/* ---- 4. Drawer head: search, then cart ----------------------
   Printed above the menu by vistara_drawer_head() on
   `kadence_mobile_navigation` at priority 5. It inherits the
   drawer's own column: `.site-header-item-mobile-navigation` runs
   63px to 349.5px at 375px wide, which is exactly the box the menu
   items' hairlines run across, so a full-width field here lines up
   with those rules without being told to. */
#mobile-drawer .vl-drawer__head {
  display: flex;
  flex-direction: column;
  gap: .35rem;
  margin-bottom: 1.1rem;
}

#mobile-drawer .vl-drawer__search {
  display: flex;
  align-items: center;
  gap: .25rem;
  border: 1px solid rgba(255, 255, 255, .22);
  border-radius: 8px;
  background: rgba(255, 255, 255, .08);
  padding-right: .2rem;
}
#mobile-drawer .vl-drawer__search:focus-within {
  border-color: rgba(255, 255, 255, .5);
  background: rgba(255, 255, 255, .12);
}
#mobile-drawer .vl-drawer__searchinput {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 46px;
  border: 0;
  background: none;
  color: #fff;
  font-family: var(--vl-font);
  /* 16px exactly, and in px rather than rem. Anything smaller makes
     iOS Safari zoom the page on focus, which on a 375px screen
     leaves the drawer wider than the window and scrolled sideways. */
  font-size: 16px;
  padding: 0 .2rem 0 .8rem;
}
#mobile-drawer .vl-drawer__searchinput::placeholder { color: rgba(255, 255, 255, .55); }
#mobile-drawer .vl-drawer__searchinput:focus { outline: none; }
/* The clear "x" WebKit adds to type=search is drawn dark, and on a
   near-black field it is a smudge. */
#mobile-drawer .vl-drawer__searchinput::-webkit-search-cancel-button { filter: invert(1); opacity: .6; }
#mobile-drawer .vl-drawer__searchgo {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  background: none;
  color: #fff;
  cursor: pointer;
  border-radius: 6px;
}
#mobile-drawer .vl-drawer__searchgo:hover { background: rgba(255, 255, 255, .12); }

/* NO CART ROW HERE (removed Aug 29, second redline pass).
   The first pass put a trolley row between this search field and the
   menu, and this block carried the rules that made a <button> look
   like the drawer's other rows plus a restated badge offset. The
   owner removed the row -- the bar trolley beside the hamburger opens
   the same sheet -- so those rules went with it rather than being
   left to match nothing.

   `#mobile-drawer .vl-drawer__cart` (~8185) and its badge rule
   (~8199) are still in this file, above, from the original Phase D
   drawer tail. They are left exactly as they are: this sheet is
   append-only, they now match nothing, and they cost nothing. Note D
   in this section's header describes rules that no longer apply and
   is superseded by this paragraph. */

/* ---- 5. Drawer tail: EN / FR, moved inward ------------------
   Kadence gives every drawer menu anchor `padding-left: 10.56px`
   inside a box that starts at 63px, so the menu TEXT begins at
   73.56px while the flags began at 63px -- measured, a 10.56px
   step the owner could see. .66rem is 10.56px at this root size,
   and it is put on the LIST rather than on each anchor so the
   1.4rem gap between EN and FR is not widened as a side effect. */
#mobile-drawer .vl-hdr__lang--drawer .vl-hdr__langmenu {
  padding-left: .66rem;
}

/* ---- 6. Cart page: the water card, above the cart -----------
   Same card, prepended instead of appended. It only needs its
   margins turned around: it now has the page title above it and
   the Cart block below it, where before it had the block above
   and the checkout button below. */
.vl-cartextras.vl-cartextras--top {
  margin-top: 1.2rem;
  margin-bottom: 1.6rem;
}
@media (max-width: 640px) {
  /* Tighter still on a phone, where the whole point of the move is
     that the card and the first line item share the first screen. */
  .vl-cartextras.vl-cartextras--top {
    margin-top: .8rem;
    margin-bottom: 1.1rem;
  }
}

/* ---- 7. Search: make the field focusable AT CLICK TIME -------
   Redline 3. The magnifier has to focus the field inside the same
   user gesture, because iOS raises the keyboard only for a focus()
   that happens in a trusted event -- and the previous build's
   requestAnimationFrame focus is a new task, so on a phone the
   field opened and the keyboard did not.

   THE ACTUAL BLOCKER WAS THIS TRANSITION, NOT THE SCRIPT.

   `.vl-hdr__searchform` (~7965) transitions `visibility` over
   .26s alongside `max-width`. CSS animates visibility discretely
   with one special case: for any progress STRICTLY between 0 and
   1 the value is `visible`. At progress exactly 0 -- which is
   where a style recalculation forced in the same tick as the class
   change lands -- it is still `hidden`, and a hidden element
   cannot take focus. Measured on a fresh load: `is-open` was on
   the wrapper, and getComputedStyle(form).visibility still read
   `hidden` with focus sitting on the magnifier.

   So the duration is taken off `visibility` and replaced with a
   DELAY, in opposite directions:

     - closed (base rule): `visibility 0s linear .26s` -- when the
       class is removed, the field stays visible for the whole
       width animation and only then disappears, which is the
       behaviour the .26s duration was there to give;
     - open (.is-open): `visibility 0s linear 0s` -- when the class
       is added, it becomes visible in that same recalculation, so
       the synchronous focus() in openSearch() succeeds and the
       keyboard comes up.

   `max-width` still animates at .26s in both directions, so the
   field opens and closes exactly as it did.

   WEIGHT: the base rule is (0,1,0), the same as ~7965, and wins on
   file order. The open rule is (0,2,0), the same as ~7967, and
   wins on file order too -- that rule declares no transition of
   its own, so nothing is being contradicted, only added to.

   The reduced-motion pair at ~8074 sets `transition: none` on
   `.vl-hdr__searchform` at (0,1,0). The (0,2,0) rule below would
   out-specify it whatever the order, so BOTH selectors are
   restated in a reduced-motion block here. Without that, this
   section would have quietly re-enabled an animation for the
   visitors who asked not to have one. */
.vl-hdr__searchform {
  transition: max-width .26s cubic-bezier(.22,.61,.36,1), visibility 0s linear .26s;
}
.vl-hdr__search.is-open .vl-hdr__searchform {
  transition: max-width .26s cubic-bezier(.22,.61,.36,1), visibility 0s linear 0s;
}
@media (prefers-reduced-motion: reduce) {
  .vl-hdr__searchform,
  .vl-hdr__search.is-open .vl-hdr__searchform { transition: none; }
}

/* ---- 8. Homepage best-sellers, 40px closer to the headline ---
   Owner, Aug 29: on a phone the six tiles sit too far under the
   "Best Sellers" heading; bring them up by 40px.

   MEASURED FIRST, at 375x812 on /:
     - heading bottom            1039.2
     - grid top                  1132.2
     - gap                       93px  =  24px of the tint
       section's own padding-bottom + 69px of grid margin-top.

   The 69px is the knob: `.home .vl-bestsellers { margin-top: 69px }`
   inside the existing `@media (max-width: 640px)` block, which a
   rule walk in the page confirmed as the winning declaration at
   this width. 69 - 40 = 29, leaving a 53px gap -- still a clear
   separation between a 40px headline and the first tile, so the
   full 40px was taken rather than a reduced amount. No overlap is
   possible: the tint section's 24px padding-bottom is untouched
   and the grid's margin stays positive.

   SCOPE. Only the <=640px value moves. `.home .vl-bestsellers`
   at 84px (all widths) and `.home .vl-bestsellers--6` at 58px
   (>=1201px) are left exactly as they are, so desktop and the
   641-1200 mid range are unchanged -- which is what "mobile view"
   asked for, and 640px is the breakpoint this spacing was already
   tuned at rather than a new one invented for the change.

   WEIGHT: `.home .vl-bestsellers` is (0,2,0), identical to the
   rule it replaces, in an identical media query. It wins on file
   order alone -- this block is at the end of the sheet -- and adds
   no !important. */
@media (max-width: 640px) {
  .home .vl-bestsellers { margin-top: 29px; }
}

/* ---- 9. Single product image, 20% smaller on DESKTOP ---------
   Owner, Aug 29 (third redline, with a MOTS-c screenshot): the vial
   on a single product page is too big on a desktop. INDIVIDUAL
   PEPTIDES ONLY -- the stacks keep their own treatment exactly.

   MEASURED FIRST, at 1440x900 on /product/ghk-cu-100mg/:
     - gallery column   51.2 -> 613.0   (561.8 wide)
     - .wp-post-image   51.2 -> 557.2   (506 wide, 674.7 tall)
     - summary column  669.0 -> 1388.8

   WHAT THIS BEATS
   `.woocommerce div.product.entry div.images img` and
   `.woocommerce div.product div.images img.wp-post-image` (~2415-2420)
   set `width: 506px !important`, both at (0,4,3). An !important is
   unavoidable here -- a plain declaration cannot touch an important
   one whatever its specificity -- so this is the ONE !important this
   whole redline section adds, and it is added only because the rule
   being corrected already carries one.

   The selectors below are the same two with `:not(.product_cat-stacks)`
   inserted on the product element, which takes them to (0,5,3) --
   `:not()` contributes its argument's specificity. Later in the file
   as well, so they win on either count.

   WHY THE STACKS CANNOT BE CAUGHT BY THIS, twice over:
     1. `:not(.product_cat-stacks)` means these selectors do not match
        on a stack page at all; and
     2. even if they did, the stack rule at ~6226,
        `.woocommerce div.product.entry.product_cat-stacks div.images
        img:not(.zoomImg)`, is (0,6,3) !important -- a class higher
        than this block -- so it would still win. The stack composite
        keeps `width: 100%`, `aspect-ratio: 5/3`, `object-fit: cover`
        and the 119px gallery offset at ~7705, all untouched.

   405px was 506 x 0.8 (owner's 29 Aug -20%). On 30 Aug, with the new
   vial cutouts in, the owner asked for +25% -- and 405 x 1.25 is 506.25,
   so the value returns to the original 506 to the pixel. Kept as this
   block rather than deleted so the centring and the stacks guard stay.

   CENTRED, which is new. At 506 the image filled all but 55.8px of
   its 561.8px column, so left-aligned read as centred. At 405 it
   would have left 156.8px of slack on one side and pulled the vial
   away from the summary column beside it. `margin-inline: auto` is
   what the <=900px tier (~2427) already does for the same reason,
   so this is the existing answer applied one tier up rather than a
   new idea.

   SCOPE: `min-width: 901px` only. The 324px tier at ~2427 and the
   259px tier at ~5063 are untouched, and they had to be excluded
   explicitly because this block sits after both. The 900/901
   boundary is left in better shape than it was found: the step used
   to be 324 -> 506, and is now 324 -> 405. */
@media (min-width: 901px) {
  .woocommerce div.product.entry:not(.product_cat-stacks) div.images img,
  .woocommerce div.product:not(.product_cat-stacks) div.images img.wp-post-image {
    width: 506px !important;
    margin-inline: auto;
  }
}

/* ============================================================
   BATCH 3 — TILE VIALS +8%
   Owner, 29 Aug: the product images on the tiles are too small.

   SCOPE, and it differs per surface because the request did:
     · /peptides/ and every category archive — PHONES ONLY.
     · /stacks/                              — PHONES ONLY.
     · Homepage Best Sellers                 — EVERY WIDTH.
   Product PAGES are not touched; the desktop catalogue is not
   touched. `max-width: 640px` is this site's phone tier and is
   the same boundary the grids themselves switch on (the 2-col
   rule at ~3517 and the 1-col stacks rule at ~6205).

   WHY THIS IS A WIDTH CHANGE AND NOT A HEIGHT ONE. The +15%
   pass above (#2) scaled `height`, which is right on desktop
   /peptides/ where 296px of height is what binds. On a phone it
   does nothing at all: measured at 390px, the tile column is
   169.4px and the image is `object-fit: contain` inside a
   169.4 x 296 box, so the vial is fitted to the WIDTH and the
   remaining 70px of height is empty letterbox. Growing the
   height there adds letterbox and not one pixel of vial. The
   clamp that actually binds is `max-width: 100%`, so that is
   what moves.

   NOT `transform: scale()`, which was the first thing tried.
   Three rules already own `transform` on these images --
   `.vl-tile:hover .vl-tile__img img` (~345),
   `.woocommerce ul.products li.product:hover img` (~1720), and
   the `transform: none` resets under `prefers-reduced-motion`
   (~351, ~1737) and `@media (hover: none)` (~4614). A base
   scale would be REPLACED, not composed, by every one of them:
   on a phone, where `hover: none` matches and a tap can latch
   `:hover`, the vial would visibly shrink back to 100% the
   moment it was touched. Sizing the box leaves all six rules
   doing exactly what they do today.

   FULL-CAP FIT — measured, not assumed, because the +15% pass
   is on record for having had to be careful about it. Source
   artwork, from the files themselves:
     · vial cutouts (600x800): art occupies 41.5% of the width
       and 68.3% of the height, with ~29% transparent padding
       each side and 17.1% above the cap.
     · stack composites (600x600): art spans y 309-570, so 69px
       of white sits above the caps INSIDE the visible crop.
   At +8% every surface still clips transparent padding only --
   the worked numbers are on each rule below.
   ============================================================ */

/* ---- /peptides/ and category archives, phones -------------
   `width` is not set here: `.woocommerce ul.products li.product
   img { width: auto !important }` (~1090) already computes it
   from the 296px height and the 3:4 intrinsic ratio, which
   wants 222px and is then clamped to the 169.4px column. Moving
   the clamp alone is enough, and it means this rule needs no
   `!important` to answer one -- it is not fighting that
   declaration, it is changing the limit that declaration hits.

   169.4 x 1.08 = 182.9. The vial then renders 182.9 x 243.9
   inside a 296px box, so 26.1px of letterbox remains above and
   below it. In the 218px window (margin-top -43) the cap lands
   at y=24.8 and the base at y=191.8 -- both inside, with 25px
   of headroom at the top. Was 30.7/185.4.

   Centred by negative margins rather than `auto`: the image is
   now WIDER than its container, and over-constrained `auto`
   margins collapse to the start edge, which would push the vial
   8% off to the right. -4% each side re-centres it exactly.
   `html …` prefix, matching the +15% rule it sits beside, so
   this is (0,4,4) against the (0,3,3) margin rule at ~1033. */
@media (max-width: 640px) {
  html .woocommerce ul.products li.product img,
  html .woocommerce ul.products li.product .attachment-woocommerce_thumbnail {
    max-width: 108%;
    margin-inline: -4%;
  }
}

/* ---- Homepage Best Sellers, every width -------------------
   Desktop as well as phones, at the owner's follow-up.

   `width` IS restated here, because `.vl-tile__img img { width:
   100% }` (~496) sets it explicitly and raising `max-width`
   alone would leave the image at 100% of the column. Both move
   together so the two can never disagree.

   The 216px window and the -41/-34 margins from #2 are left
   exactly as they are, so the tile does not change height, the
   row cannot grow, and nothing can collide with the section
   below it. The extra width is clipped by the window's own
   `overflow: hidden`, and what it clips is transparent padding:

     390px  column 169.4 -> 182.9. Cap y=24.4, base y=191.2 in a
            216px window (was 30.4 / 184.8).
     1440px column 196.3 -> 212.0. Cap y=11.7, base y=205.0 in
            the same window (was 18.6 / 197.5). This is the
            tightest case on the site and still clears the frame
            by 11px above and 11px below.
   Horizontally the art sits 29%-71% of the box, so at 1440 it
   spans x=53.5 to x=142.5 inside a 196px window -- the 7.8px
   trimmed from each edge is empty.

   (0,3,1) via `:not(.vl-stacks)`, which is load-bearing for the
   same reason it is in #2: /stacks/ renders through the same
   shortcode and carries `.vl-bestsellers` too, and its images
   are a `cover` crop that must not be given a `contain` box. */
.vl-bestsellers:not(.vl-stacks) .vl-tile__img img {
  width: 135%;
  max-width: 135%;
  margin-inline: -17.5%;
}

/* ---- /stacks/ tiles, phones -------------------------------
   The composites are `width: 100%`, `aspect-ratio: 5/3`,
   `object-fit: cover`, anchored `50% 100%` -- so the frame
   height is derived from the width and BOTH scale together.
   The visible slice is therefore the lower 60% of the square at
   any width, and widening the image by 8% changes what is shown
   not at all: it enlarges the vials and the tile by 8% and
   crops exactly the same whitespace it crops today. The 69px of
   white above the caps is untouched, which is why this surface
   needs no clipping arithmetic of its own.

   One column at this tier (~6205), and the grid carries 5vw of
   side padding, so the 4% that hangs off each edge lands in the
   gutter: 351 -> 379 inside a 390px viewport. No horizontal
   scroll, and no neighbour to collide with.

   (0,2,1), beating `.vl-stacks .vl-tile__img img { width: 100% }`
   (~6190) on source order at equal specificity -- the same way
   that rule beats the 253px `contain` box at ~2503. */
@media (max-width: 640px) {
  .vl-stacks .vl-tile__img img {
    width: 108%;
    max-width: 108%;
    margin-inline: -4%;
  }
}

/* ============================================================
   BATCH 3 — STACK PAGE: WHAT THE VIALS COST SEPARATELY
   The struck-through total the /stacks/ tiles have always shown
   (`.vl-tile__was`, ~3024), now on the stack's own product page.
   Markup comes from vistara_stack_single_price_html() in
   inc/catalog.php; the figure is computed from the component
   prices, never typed.

   WHAT THIS BEATS. Kadence sets `.woocommerce div.product p.price
   del { opacity: .5 }` (0,3,2) and WooCommerce sets the same at
   .7 — both would render the total as washed-out black rather
   than the grey the tiles use. Adding the class takes the
   selector to (0,4,2), and home.css loads after both sheets, so
   it wins on specificity and source order alike. No !important:
   neither rule carries one.

   Opacity is reset to 1 deliberately. Leaving it at .5 and
   setting a colour would light-grey the grey, and the figure has
   to stay legible — it is the number the saving is measured
   against, not decoration.

   The real price is the dominant half: the total is dropped to
   .78em and greyed, and the price beside it takes the weight.
   `:has()` scopes that to prices that actually carry a struck
   total, so every other product page is untouched — the same
   construction `:has(.vl-buybox) form.cart` already uses in this
   file. `>` matters: it selects the price's OWN amount and not
   the one inside the <del>.
   ============================================================ */
.woocommerce div.product p.price del.vl-stack__was {
  opacity: 1;
  color: #9BA7AE;
  font-size: .78em;
  font-weight: 400;
  margin-right: .1rem;
  text-decoration: line-through;
}

.woocommerce div.product p.price:has(del.vl-stack__was) > .woocommerce-Price-amount {
  font-weight: 700;
}

/* ============================================================
   CLIENT REVIEWS — five stars ON the name line
   Owner's redline 30 Aug: "Z.C. ★★★★★", one line, not a row of
   its own. The stars are now a <span> inside `.vl-review__name`
   (inc/homepage.php), so this styles an inline box.

   Must beat `.vl-review__stars { font-size: .85rem }` further up
   (itself beating the 1.05rem base rule) and the `margin: 0` at
   ~1610. Same specificity (0,1,0) in all four places, so source
   order settles it and no !important is needed.

   Size is pinned to .72rem: identical to `.vl-review__name`, now
   its parent, which is what "a little 5 stars by the name" asks
   for. The star row was reading as the loudest thing in the card
   at .85rem.

   font-weight is restated because the name is 700 and the stars
   would otherwise inherit it — bold glyphs, heavier than the row
   they sit on. margin-left tops up the real space the markup
   carries; the space is there so the line reads "Z.C. ★★★★★" when
   it is copied or announced, the margin so the gap is a chosen
   number rather than whatever the font makes of U+0020 after a
   full stop.

   Colour is #239B97 — sampled from `.vl-review__text`, the words
   of the review itself. Restated here rather than left to the
   base rule so a later appended section cannot quietly drift it.
   NOT the "Why Vistara Labs?" heading, which is #21609D: the two
   were assumed to match and do not.

   Deliberately not gold. The card is a two-colour object (teal
   text, near-black name) and a third hue would be the only
   saturated accent on an otherwise plain page.
   ============================================================ */
.vl-review__stars {
  color: #239B97;
  font-size: .72rem;
  letter-spacing: .14em;
  font-weight: 400;
  margin-left: .15em;
  white-space: nowrap;
}

/* ============================================================
   CLIENT REVIEWS — the "Read more.." fold
   The renderer cuts at the fold and wraps the tail in
   `.vl-review__rest`; both halves are always in the markup, so
   the review is complete without JavaScript. The ellipsis for a
   cut that landed mid-sentence is at the end of this file.

   `is-clipped` is put on the TRACK by the carousel script — the
   same idiom as `is-fading` — so a fold only exists while the
   script that can open it is running. A cut review with no way to
   expand it would be worse than a tall card.

   `inline`, not `initial`: the span continues a sentence and has
   to flow with the words before it.
   ============================================================ */
.vl-review__rest { display: inline; }
.vl-reviews__track.is-clipped .vl-review:not(.is-open) .vl-review__rest {
  display: none;
}

/* ============================================================
   HOMEPAGE HERO — misty mountain backdrop
   A flat tint painted through the mountain range as an alpha
   mask, on a pseudo-element behind the hero copy.

   641px AND UP ONLY. The phone treatment was deleted on the
   owner's instruction, 30 Aug; see the @supports block below for
   what that leaves behind and what it deliberately does not.

   KEEPING THIS OUT OF THE LCP — read before resizing the file.
   Left to itself the range becomes the Largest Contentful Paint:
   it is wider than anything else in the hero, so it beats the
   logo, and decoration then defines the metric while being
   fetched at low priority (the logo carries fetchpriority=high).

   Chrome records min(displayed area, INTRINSIC area) for an image
   paint, so the intrinsic size of the file is the control. The
   wide panorama is cut below 84,100px — the hero logo's smallest
   painted area, which it takes at 768-1024px where the layout has
   stacked — so the logo outranks the backdrop at every width.
   Measured across 360/390/430/641/768/900/1024/1200/1440/1920:
   LCP is the logo at all ten. Enlarging the file past that area
   puts the backdrop back in the running in the 768-1199px band.

   Upscaling costs nothing visible here: the artwork is a soft,
   low-contrast silhouette and the mask is only an alpha ramp, so
   520px of source stretched across 1920 is indistinguishable.

   Three things that do NOT work, so they are not worth retrying:
   masks are LCP candidates like any other image; pseudo-element
   backgrounds are attributed to the host element; and the
   low-entropy exclusion scales with painted area, which at 641px
   would demand a sub-600-byte file.

   The artwork is monochrome — measured chroma spread is under
   two levels — so a single tint through an alpha mask is
   indistinguishable from the photograph. #C9D3DD is its darkest
   pixel, and the mask was normalised over the artwork's real
   luminance range (211-255).

   The gradient does double duty: it is both the tint and the
   contrast guard. Alpha ramps from 0 partway down to full at the
   bottom, so it multiplies against the mask and fades the range
   out before it reaches the title, subtitle and button. Nothing
   here affects layout, so there is no shift.

   OPACITY, 30 Aug: the owner asked for +50%, then +50% again —
   2.25x the alpha the Phase C build shipped. That is applied per
   POINT along the ramp, not to the end stop alone. Multiplying
   the stops in place would have been almost nothing (.92 x 2.25
   clamps straight back to 1, a 9% change); what the eye reads as
   "how present the range is" is the alpha through the middle of
   the ramp, so the stop POSITION is what moves. The ramp reaches
   full opacity where 2.25x the old alpha first hits 1, and holds
   it from there:
     0 @42%  ->  1 @70.0%   (.92 x 2.25 = 2.07, clamps)
   The start point is untouched, so the guard over the copy is
   exactly where it was. The phone ramp this paragraph also used
   to describe (0 @78% -> 1 @85.1%) went with the phone block.

   `.home` scopes it to the homepage — `.vl-hero` is reused by
   other templates. `.vl-hero__inner` is lifted to z-index 1
   because the pseudo is positioned and would otherwise paint
   over the copy; it is the section's only child, so nothing else
   needs raising. A negative z-index would have avoided that, but
   `.vl-hero` is not a stacking context, so the layer would have
   escaped behind an ancestor's background instead.

   Anchored `bottom`: the range ends on the hero's bottom edge.
   The source was cropped to the mountain bases for this reason —
   dead whitespace baked into the file would push the range up by
   an amount that varies with viewport width. Desktop overrides
   the anchor; see the offset in the min-width block below.
   ============================================================ */
.home .vl-hero { position: relative; }
.home .vl-hero__inner { position: relative; z-index: 1; }

.home .vl-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center bottom;
          mask-position: center bottom;
  -webkit-mask-size: 100% auto;
          mask-size: 100% auto;
}

/*
 * The tint is declared only where masking is supported, and the
 * one breakpoint that has it carries its own tint AND mask
 * together. Both matter: an unmasked gradient would wash the full
 * width of the hero's lower half in flat grey, so no rule may
 * ever leave one without the other.
 *
 * PHONES HAVE NO BACKDROP, owner 30 Aug. The <=640 block was
 * deleted outright rather than turned down, which is exactly what
 * pairing the tint with the mask bought: nothing below 641px
 * matches, and there is no orphaned gradient left behind to wash
 * the hero grey. The base rule above still applies there, but with
 * no mask-image and no background it paints nothing, and it is
 * absolutely positioned so it cannot affect layout either.
 *
 * hero-mountains-mobile.webp stays in the media library and is now
 * referenced by nothing. It was never preloaded and no PHP names
 * it, so this rule was the only thing that fetched it.
 */
@supports ((mask-image: linear-gradient(#000, #000)) or (-webkit-mask-image: linear-gradient(#000, #000))) {

  /* ----------------------------------------------------------
     Tablet and desktop -- the whole of the treatment. Delete this
     one block and the backdrop is gone site-wide, with nothing
     left to unpick.
     ---------------------------------------------------------- */
  @media (min-width: 641px) {
    .home .vl-hero::before {
      /*
       * Clears the subtitle, which ends at 38.4% of hero height on a wide
       * screen. Between 641 and ~1000 the hero stacks and the subtitle drops
       * to 73-78%, past where this ramp reaches full — what holds the tint off
       * the copy there is the mask, whose range sits lower still. Measured
       * white behind the subtitle at 641, 900, 1200 and 1440. Anything that
       * raises the mask must re-check that band.
       */
      background: linear-gradient(to bottom, rgba(201, 211, 221, 0) 42%, rgba(201, 211, 221, 1) 70%);
      /* -2: the original file carried ~30 bottom rows of near-zero haze
         (mean alpha 0.015-0.05), so its bottom edge LOOKED like a white gap
         above the trust strip however it was anchored. The -2 export crops
         that tail (116 -> 85 rows) and ramps the base alpha up so the ridge
         visibly lands on the strip. 520x85 = 44,200px, still under the
         84,100 LCP exclusion ceiling. */
      -webkit-mask-image: url(../../uploads/2026/08/hero-mountains-wide-2.webp);
              mask-image: url(../../uploads/2026/08/hero-mountains-wide-2.webp);

      /*
       * Dropped below the hero's own bottom edge, owner 30 Aug.
       *
       * 2.4rem is not a guess: it is `.vl-trustbar { margin-top }`
       * (~1752), the whole of the gap between the hero and the
       * strip, so that term alone lands the range flush on the top
       * of the trust bar at every desktop width. The 130px is the
       * further drop asked for on top of it — 60px on the first
       * pass and another 70px on the second, both by eye, so the
       * number is a preference and not a derivation.
       *
       * The pseudo stays at `inset: 0`, so the hero's box clips
       * whatever falls past its bottom edge. That crop is the
       * point: the range runs under the fold of the section rather
       * than painting across the trust strip's words. Extending the
       * pseudo downwards instead would put tint over that text.
       *
       * WHY THE min(), 30 Aug, third pass.
       *
       * A constant shift crops a constant number of pixels, but the
       * range's HEIGHT scales with viewport width — it is
       * `mask-size: 100% auto` over a 520x116 file, so the range is
       * 0.22308 x viewport tall. A fixed crop therefore eats a much
       * bigger share of a short range, and at 168.4px it stops being
       * a matter of degree: the crop equals the whole range at
       * 168.4 / 0.22308 = 755px, so a constant-only rule would show
       * NOTHING from 641 to 755 and 16% of a range at 900. The min()
       * is now load-bearing rather than a refinement.
       *
       * So below the crossover the whole shift scales with the width
       * instead of the drop being constant. 14.03vw is the value
       * that keeps the visible fraction pinned at its 1200 figure —
       * derived the same way the 8.2vw it replaces was, as the crop
       * fraction at 1200 times the range's own coefficient:
       *   crop@1200 = 168.4 / (0.22308 x 1200) = 0.62908
       *   shift     = 0.62908 x 0.22308 x W    = 0.14033 W
       * 14.03vw at 1200 is 168.36px against 2.4rem + 130px = 168.4px.
       * That 0.04px is the rounding to two decimals: it is under a
       * device pixel at any DPR, so the branches still meet at 1200
       * with nothing visible to jump on resize. Below it the ridge
       * stays continuous and proportional at 37.1% of its height,
       * which is what tablets render; above it the constant takes
       * over and more of the range survives — 47.6% at 1440, 60.7%
       * at 1920.
       *
       * The rem term is why this is min() and not a plain clamp on
       * vw: the crossover has to track the trust bar's own margin,
       * not a number typed twice. Note vw counts the scrollbar, so a
       * classic-scrollbar desktop crosses over ~15px of layout width
       * early and sub-1200 shifts land ~2px lower. Immaterial here.
       *
       * The gradient is deliberately NOT moved with it. It is
       * anchored to the hero and guards the copy above; shifting
       * it down would drag full opacity up toward the subtitle.
       * Lowering the mask can only move the range further from the
       * copy, so the contrast measured for the 60px drop is a floor
       * for this one.
       */
      /* Plain bottom, no shift. The owner's "lower it by X" rounds were
         chasing one thing (30 Aug, with a screenshot): the PHOTO'S OWN
         bottom edge touching the trust strip. Every shifted variant slid
         the art down INSIDE the hero and cropped its base off below the
         frame while the white band under the frame stayed. Anchoring the
         uncropped bottom edge here, plus zeroing the strip's top margin
         (below), is that state -- and it retires the vw arithmetic. */
      -webkit-mask-position: center bottom;
              mask-position: center bottom;
    }
  }
}

/* ============================================================
   CLIENT REVIEWS — the ellipsis on a mid-sentence fold
   Owner 30 Aug: the fold may now cut inside a sentence when the
   first one is longer than the character budget on its own, and
   a cut like that has to look cut. C.Y. is the only review it
   reaches today ("...replied within the…"); Z.C. still folds on
   its own "guys!!" and carries no `is-cut`, so nothing is added
   after a sentence that already ended.

   GENERATED CONTENT ON PURPOSE, not a character in the markup.
   The "…" must not exist in the text the toggle reveals: put it
   in the DOM and "Read more.." either shows it mid-sentence or
   has to strip it back out, and `head . tail` stops being the
   stored review byte for byte. As a `::after` there is nothing
   to remove — the rule simply stops matching once the card is
   open, and matches again on collapse. It is also outside
   textContent, so a copied review is the customer's words only.

   `::after` on the paragraph rather than on `.vl-review__rest`:
   the tail span is `display: none` while collapsed, and a
   pseudo-element of a display:none box does not render. The
   paragraph's own ::after sits after that empty box, which puts
   it immediately after the last visible word.

   Scoped to `.is-clipped ... :not(.is-open)` to match the fold
   rule above exactly — no script, no clipping, no ellipsis, so
   the review never reads as truncated when it is shown whole.

   Nothing else in this file styles `.vl-review__text::after`.
   The seven earlier `.vl-review__text` blocks — 143, 818, 912,
   982, 1464, 1607 and 1641 — are font, colour and margin on the
   element itself, so this pseudo-element has no rule anywhere to
   lose a cascade fight with, at any position in the file.

   `\2026` rather than a literal ellipsis, the same choice as the
   bullet at ~4835: the escape decodes to U+2026 whatever encoding
   the stylesheet is served as, and this file carries no @charset.
   ============================================================ */
.vl-reviews__track.is-clipped .vl-review:not(.is-open) .vl-review__text.is-cut::after {
  content: "\2026";
}

/* ============================================================
   CART REMOVAL — INSTANT FEEDBACK               (Aug 30)
   ============================================================
   Three small rules, all of them serving one behaviour: a line
   the shopper has removed leaves the screen on the tap, and the
   figures that cannot follow until the server answers say so.

   The scripts are inc/cart-page.php (the Cart block's rows) and
   inc/header.php (the mini-cart panel); both carry the reasoning
   for WHY the page gets ahead of the server. This file only has
   to explain what these selectors must beat.

   1. THE CART BLOCK'S ROW, and why `!important`.

      The row is hidden by an ATTRIBUTE because React rewrites the
      row's class from its own props on every re-render and would
      wipe a class added from outside — see the note in
      inc/cart-page.php. That attribute selector is (0,1,1), and
      the rule it has to beat is the block's own

          .wc-block-cart table.wc-block-cart-items
          .wc-block-cart-items__row { display: grid }

      at (0,3,1), from a stylesheet WooCommerce enqueues and whose
      order relative to this file is not ours to set. There is no
      selector available here that wins on specificity without
      naming the block's internals three levels deep and breaking
      the next time they change, so this is one of the few places
      the flag is the honest answer.

      Nothing else in this file styles `[data-vl-removing]`; the
      attribute was introduced with this section.

   2. THE ORDER SUMMARY while a removal is in flight. Dimmed, not
      recalculated: shipping thresholds and coupon minimums are
      decided server-side and a figure invented in the browser is
      a figure that can be wrong. The transition sits on the
      element rather than inside the body-class rule so it fades
      both ways.

      The SUMMARY, not the whole totals column. Proceed to Checkout
      is in that column as well, and on a phone it is the button
      pinned to the bottom of the screen — a checkout button that
      greys out for a second every time a line is removed reads as
      the shop refusing to take the order. What is briefly out of
      date is the figures, not the way forward.

   3. THE ITEMS TABLE once every row has gone. The block swaps the
      whole cart for its empty state when the response lands; for
      the second before that, this is what stops the shopper
      looking at a header row over nothing. `.vl-cartextras` needs
      no rule here — the script's own count reaches zero on the
      tap and `.is-hidden` (~8221) already hides both panels.
   ============================================================ */
.wc-block-cart-items__row[data-vl-removing] { display: none !important; }

.wp-block-woocommerce-cart-order-summary-block { transition: opacity .12s linear; }
body.vl-cart-settling .wp-block-woocommerce-cart-order-summary-block { opacity: .55; }

body.vl-cart-emptying .wp-block-woocommerce-cart-items-block { display: none; }

/* ------------------------------------------------------------
   The mini-cart panel, same two states.

   `is-settling` dims the SUBTOTAL for the length of the round
   trip, for the reason above: it is the server's figure and is
   not recomputed here. Only the subtotal — CHECKOUT sits directly
   under it and is the one thing in this panel that must never
   look unavailable, for the same reason the cart page's own
   submit button is left alone. The rows are left alone too; they
   are what the shopper just acted on, and dimming them would read
   as the tap not having registered.

   `is-emptying` is the last row having gone. The panel's real
   empty state (the message and the way back to the shop) comes
   with the response and replaces this; until then the foot is
   hidden rather than left offering a subtotal for a list holding
   nothing.

   Both are set on `#vl-cart-panelinner`, which is the element the
   `#vl-cart-panelinner` fragment replaces wholesale — so neither
   class can survive a refresh it should not survive.
   ------------------------------------------------------------ */
.vl-cart__panelinner .vl-cart__subtotal { transition: opacity .12s linear; }
.vl-cart__panelinner.is-settling .vl-cart__subtotal { opacity: .45; }
.vl-cart__panelinner.is-emptying .vl-cart__foot { display: none; }

/* ============================================================
   CART PAGE — QUANTITY IN FLIGHT                (Aug 30, r2)
   ============================================================
   The companion to the block above, for the +/− steppers rather
   than the trash control. Set from inc/cart-page.php, which
   carries the measurement of what actually lags.

   ONE PROPERTY, ONE ELEMENT: the line total, dimmed while the
   figure on it is out of date. The number in the stepper is NOT
   dimmed — the block repaints it on the tap, it is the thing
   under the shopper's thumb, and greying it would read as the tap
   not having registered. The order summary is covered by
   `body.vl-cart-settling` (previous section), which the same
   script now also sets for a quantity change.

   NO `!important` HERE, unlike the row-hide above. That one had
   to beat the block's own `display: grid` at (0,3,1); this sets
   `opacity`, and nothing in cart.css or this file declares
   opacity on `.wc-block-cart-item__total` at any specificity —
   checked. `[data-vl-qty]` is an attribute rather than a class
   for the same React reason as `[data-vl-removing]`.
   ============================================================ */
.wc-block-cart-item__total { transition: opacity .12s linear; }
.wc-block-cart-items__row[data-vl-qty] .wc-block-cart-item__total { opacity: .45; }

/* ============================================================
   STACK PRODUCT PAGES — BIGGER COMPOSITE, LOWER ON PHONES
   Owner, 30 Aug, after the -v7 composites replaced the art:
   desktop stack image +20%; on phones +15% and the whole block
   under the breadcrumb pushed down so the page breathes.

   Same selector as the (0,6,3) !important stack rule at ~6226
   and later in the file, so these win on source order at equal
   specificity. Width over transform for the same reason as the
   +8% tiles: six earlier rules own `transform` on product
   imagery and a scale would be silently replaced; a wider box
   with the overhang split by margin-inline stays centred and
   participates in layout. `aspect-ratio: 5/3` on the base rule
   scales height with width, so both grow together.

   Non-stack product pages cannot match: `.product_cat-stacks`
   is in every selector. The 641-900px tier is untouched.
   ============================================================ */
@media (min-width: 901px) {
  .woocommerce div.product.entry.product_cat-stacks div.images img:not(.zoomImg) {
    width: 120% !important;
    max-width: 120% !important;
    margin-inline: -10%;
  }
}
@media (max-width: 640px) {
  .woocommerce div.product.entry.product_cat-stacks div.images img:not(.zoomImg) {
    width: 115% !important;
    max-width: 115% !important;
    margin-inline: -7.5%;
  }
  /* The gap under the breadcrumb. On the outer gallery, not the
     wrapper — the wrapper carries its own offset arithmetic for
     the crop (~7556) and adding here keeps that untouched. */
  .woocommerce div.product.entry.product_cat-stacks .woocommerce-product-gallery {
    margin-top: 48px;
  }
}

/* The other half of closing the gap: the strip's own 2.4rem top margin
   was the white band between the hero's bottom edge (where the range now
   ends, uncropped) and the strip. Desktop band only -- phones have no
   backdrop and keep their spacing. */
@media (min-width: 641px) {
  .home .vl-trustbar { margin-top: 0; }
}

/* ============================================================
   BULK ORDER PAGE (/bulk-order/)                    30 Aug
   ------------------------------------------------------------
   Appended at EOF per the file's convention, but for once this
   block beats NOTHING: `.vl-bulkpage*` are new classes and no
   earlier rule matches them. It is here rather than beside
   `.vl-contactpage` (~2708) only so that a concurrent append
   cannot collide with an insertion in the middle of the file.

   The page deliberately borrows two things instead of restating
   them: `.vl-section` for its gutters and max-width, and
   `.vl-btn .vl-btn--solid` for the download button -- which is
   why there is no colour, border or hover here. The button is
   the site's primary button, and it must keep tracking it.
   ============================================================ */
.vl-bulkpage { text-align: center; }

/* The same measure the Contact page holds its copy to (~2708),
   so the two pages -- which the owner asked to look alike --
   wrap at the same width instead of merely both being centred. */
.vl-bulkpage p {
  max-width: 620px;
  margin-left: auto;
  margin-right: auto;
  text-align: center;
}

/* Air either side of the button, and none of the paragraph
   spacing it would otherwise inherit from `p`. */
.vl-bulkpage__cta { margin: 2.25rem auto 1.75rem; }

/* min-width matches `.vl-section__cta .vl-btn` (~78), which is
   what every other standalone call to action on this site is
   sized to. Without it the button shrink-wraps its label and
   reads as a text link that happens to be black. */
.vl-bulkpage__cta .vl-btn { min-width: 300px; }

.vl-bulkpage__note { margin-top: 1.5rem; }

/* The address is the page's second action after the download,
   so it is a link and looks like one. */
.vl-bulkpage__email { margin-top: .35rem; font-weight: 600; }
.vl-bulkpage__email a { text-decoration: underline; }

/* Phones: 300px min-width plus `.vl-btn`'s 26px side padding
   overflows a 375px viewport once the section's 5vw gutters are
   taken off, so the button stops being a fixed width and becomes
   a full-width tap target instead -- which is what it should
   have been on a phone regardless. Most of this shop's traffic
   is here. */
@media (max-width: 640px) {
  .vl-bulkpage__cta .vl-btn {
    min-width: 0;
    display: block;
    width: 100%;
  }
}

/* ============================================================
   CART PAGE — FIRST PAINT                       (Aug 30, r3)
   ============================================================
   The server prints the cart's line items immediately BEFORE the
   Cart block so they are on screen in the first paint instead of
   after ~1MB of React (inc/cart-page.php carries the live
   measurements, and the two measured reasons the markup sits
   outside the block rather than inside it). Two rules make the
   handover clean; the script in that file does the rest.

   DRIVEN BY THE SIBLING, NOT BY A CLASS. Both rules match only
   while `.vl-cartsnap` is still in the document, so removing that
   one element is the whole swap — there is no state to unset and
   nothing for the block to overwrite. That matters here: the
   container's `class` is React's, rewritten at mount (measured —
   a class put on it by PHP was gone by the time the block had
   painted), so a rule keyed to a class on the container would
   have stopped applying at exactly the wrong moment.

   1. WHILE THE SNAPSHOT IS UP, the block's own children are
      suppressed. That is what stops the shopper seeing the grey
      skeleton bars UNDER a perfectly good cart — and the skeleton
      is the thing being complained about, so it must not merely
      be beaten to the punch, it has to be absent.

      NOT `display: none`. The block measures its container to
      choose a layout (`is-large` above 700px, down to `is-mobile`
      — its own thresholds, read out of the bundle) via a resize
      observer. A display:none subtree measures 0, so the block
      would mount believing it is on a phone and then re-lay-out
      the moment it was revealed. Out of flow and invisible at
      full width, it measures correctly and lands right first
      time.

   2. THE SNAPSHOT'S HOST is a CSS CONTAINER, and that is the
      whole of its styling. The block's stack-on-a-phone layout is
      a container query — cart.css declares `container-type:
      inline-size` on `.wp-block-woocommerce-cart` and then
      `@container (max-width: 699px)`. Outside that element there
      is no container to query, so those rules silently did
      nothing and the snapshot sat side-by-side at 390px (items
      254px, totals 137px, overlapping). Declaring the containment
      here hands the block's own responsive CSS the measurement it
      is asking for.

      The gutter is the same one the block gets at ~2724, so the
      two occupy the same box and nothing shifts sideways at the
      swap. Everything else the snapshot needs it already has:
      it wears the block's class names, so the block's stylesheet
      draws it, and "identical to what replaces it" is a property
      of the CSS rather than something kept in step by hand.

   `~` rather than `+`: the water prompt is prepended to this same
   content, so the two are siblings but not always adjacent.
   ============================================================ */
.vl-cartsnap {
  container-type: inline-size;
  width: 100%;
  padding-inline: var(--vl-edge);
  box-sizing: border-box;
}

.vl-cartsnap ~ .wp-block-woocommerce-cart { position: relative; }
.vl-cartsnap ~ .wp-block-woocommerce-cart > * {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  width: 100%;
  visibility: hidden;
  pointer-events: none;
}

/* ============================================================
   CLASSIC CHECKOUT — FIRST-PAINT VEIL           (Aug 30, r4)
   ============================================================
   WooCommerce's own checkout.js blocks the payment box and the
   order review with jQuery.blockUI the instant the page loads —
   `overlayCSS: { background: '#fff', opacity: 0.6 }` — and holds
   that veil for one `update_order_review` round trip. The content
   under it is the server's own render and is correct throughout;
   see inc/cart-page.php for the full call chain and the timings.

   This hides the veil for that first, redundant update only. The
   class comes off on the first `updated_checkout`, so every later
   update — country, postcode, shipping method, coupon, the ones
   that really do move the figures — veils exactly as it does now.

   OPACITY, NOT `display: none`. The overlay is what stops a click
   reaching Place Order mid-update. Removing it would change what
   the checkout DOES, which a display fix does not get to do; made
   transparent it still swallows the click, so behaviour is
   identical and only the wash is gone.

   `!important` is unavoidable: blockUI animates the overlay's
   opacity as an INLINE style, and no selector out-specifies that.

   Scoped to the two containers core names in its own block() call,
   so a blockUI overlay anywhere else on the page — the coupon
   form, the place-order submit — is untouched. Nothing else in
   this file styles `.blockUI`.
   ============================================================ */
html.vl-ck-firstpaint .woocommerce-checkout-payment > .blockUI.blockOverlay,
html.vl-ck-firstpaint .woocommerce-checkout-review-order-table > .blockUI.blockOverlay {
  opacity: 0 !important;
}

/* ============================================================
   HERO HEADLINE — INTER (owner, 30 Aug)
   "Premium Canadian Peptides" set in Inter Bold, stacked as
   Premium / Canadian Peptides via a <br> in the page markup
   (page 72). Self-hosted woff2 in the theme's fonts/ dir —
   no third-party font request; latin subset only, ~24KB each,
   swap so text never blocks on the font. Medium (500) is
   loaded alongside bold for the owner's "bold / medium" spec,
   available if the subtitle follows later.
   ============================================================ */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(fonts/inter-500.woff2) format('woff2');
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(fonts/inter-700.woff2) format('woff2');
}
.vl-hero__title {
  font-family: 'Inter', -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;
  font-weight: 700;
}

/* ============================================================
   HERO BACKDROP — THE OWNER'S PHOTO (30 Aug, second art swap)
   Replaces the masked-tint silhouette wholesale: the new artwork
   is a full picture that fades to white at its own edges, so it
   sits as a plain background, bottom-anchored — its bottom edge
   stays flush on the trust strip (whose top margin is already 0
   at this band). mask-image: none retires the silhouette
   machinery; the white ramp keeps the headline band clean where
   the picture's sky rises behind it.
   -2: 1600x533. The 500px export blurred (owner, 30 Aug) — this
   photo has real ridgelines, unlike the haze the LCP-exclusion
   sizing was tuned for. At this size the image IS an LCP
   candidate, so a front-page preload (inc/display.php) makes it
   paint with the hero rather than after it: the metric may name
   the backdrop, but it names an early paint. Phones: no backdrop.
   ============================================================ */
@media (min-width: 641px) {
  .home .vl-hero::before {
    -webkit-mask-image: none;
            mask-image: none;
    /* The flat top veil fades the whole picture toward the page's
       white — a background layer can't take opacity, so the veil is the
       same thing said in layers. 0.2 on 30 Aug, then an additional 20%
       the same evening = 0.4. The 18%→60% ramp under it still clears
       the headline band on top of that. */
    background:
      linear-gradient(rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0.4)),
      linear-gradient(#fff 18%, rgba(255, 255, 255, 0) 60%),
      url(../../uploads/2026/08/hero-mountains-photo-2.webp) center bottom / 100% auto no-repeat;
  }
}

/* ============================================================
   10-PACK PILL CAPTION — "10mg × 10 | SAVE 20%"  (30 Aug)

   WHAT THIS HAS TO BEAT: nothing, and that is deliberate.
   `.vl-buybox__pillsave` is a new class with no earlier rule
   anywhere in this file, so it is appended here only because
   that is where new sections go — not to win a cascade fight.
   The one inherited value it does override is the white
   `color` the SELECTED pill sets on its subtree at ~6564
   (`.vl-buybox__pill:has(.vl-buybox__radio:checked)`); a
   direct rule beats an inherited one regardless of order, and
   the checked-state rule below is more specific than that one
   in any case. Position in the file is not load-bearing here.

   THE SCALE IS BORROWED, not invented. This is the same
   caption treatment `.vl-buybox__pillsold` (~6579) already
   uses inside a pill — .7rem, 700, uppercase, .08em tracking —
   because the shop should have one way of putting a small
   word next to a size, not two.

   TEAL, not red (owner, 30 Aug). #158081 is the site's own
   accent (see ~5610, --vl-teal-dark); a red discount flash is
   the look of every other peptide shop and explicitly not
   this one. It lightens to #7FD1D2 on the filled slate of a
   selected pill, where #158081 would be unreadable — 7.2:1
   against #233642, against 4.9:1 for the teal on white.

   The separator is a CSS ::before so the accessible name of
   the pill stays "10mg × 10 Save 20%" and a screen reader
   does not read out a vertical bar. `.vl-buybox__pills` has
   `flex-wrap: wrap` (~6521) and the pill is `inline-flex` with
   `gap: .5rem` (~6523), so the caption wraps with its pill and
   never on its own; `white-space: nowrap` keeps "SAVE 20%"
   from breaking across two lines at 375px.
   ============================================================ */
.vl-buybox__pillsave {
  font-size: .7rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: #158081;
  white-space: nowrap;
}
.vl-buybox__pillsave::before {
  content: "|";
  margin-right: .5rem;
  font-weight: 400;
  letter-spacing: 0;
  color: #C9D6DE;
}
.vl-buybox__pill:has(.vl-buybox__radio:checked) .vl-buybox__pillsave { color: #7FD1D2; }
.vl-buybox__pill:has(.vl-buybox__radio:checked) .vl-buybox__pillsave::before {
  color: rgba(255, 255, 255, .35);
}
/* A pack cannot be sold out today -- add-ten-packs.php only builds one over an
   in-stock size -- but if one ever is, the caption goes grey with the rest of
   the pill rather than advertising a saving on something nobody can buy. */
.vl-buybox__pill--soldout .vl-buybox__pillsave { color: #9BA7AE; }

/* The 10-pack lead-time line: one quiet sentence under the pills,
   present only while a pack is the selected size. Muted to the
   note grey the buybox already uses for small print; no icon, no
   border — the owner asked for a notice, not a banner. */
.vl-buybox__leadtime {
  margin: .35rem 0 0;
  font-size: .82rem;
  color: #8a94a0;
}

/* ============================================================
   HERO HEADLINE — PLAYFAIR DISPLAY (owner, 30 Aug, superseding
   the Inter pass from earlier today)
   Three stacked words — Premium / Canadian / Peptides — with
   "Canadian" in the site teal, set in Playfair Display Bold at
   the owner's spec: 700 / 0.92 line-height / -0.02em tracking.
   Self-hosted woff2 beside the Inter files; swap, latin subset.
   The Inter block above still loads its files — the subtitle or
   another element may yet take them, and they are 24KB each.
   ============================================================ */
@font-face {
  font-family: 'Playfair Display';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(fonts/playfair-600.woff2) format('woff2');
}
/* 700 stays registered: the browser would otherwise synthesise a
   fake bold if anything ever asks for it. */
@font-face {
  font-family: 'Playfair Display';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(fonts/playfair-700.woff2) format('woff2');
}
/* .vl-hero-scoped to (0,2,0): `.vl-hero .vl-hero__title` (~1491) also
   declares a weight, and a bare class here would lose to it however late
   this block sits. Same for the accent below. */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(fonts/inter-600.woff2) format('woff2');
}
/* Inter VARIABLE, for the hero headline only.
   The three static faces are 500/600/700, so a request for anything
   between them snaps to the nearest cut — which is why "8% lighter than
   500" (owner, 31 Aug) needed a real variable axis rather than another
   static file. Declared under its own family name so the static faces
   keep serving every other Inter rule on the site untouched, and so a
   future weight tweak is a one-number change with no new asset. */
@font-face {
  font-family: 'Inter Var';
  font-style: normal;
  font-weight: 100 900;
  /* `optional`, NOT `swap` (owner, 31 Aug: the headline "bugs out and is
     bigger for a split second, then assumes its actual size"). Measured at
     the hero's 36.48px/460, Inter Var sets "Premium Canadian Peptides" at
     397px where the system fallback sets it at 445px — 12% wider. With
     `swap` the browser painted that 12% first and reflowed once the webfont
     arrived, which is the flash. `optional` never swaps: the font is either
     ready before first paint or sits out that page load, so the headline
     cannot change size after it is on screen. The preload added in
     inc/display.php is what makes "ready before first paint" the normal
     case, including the age-gate load where the hero renders behind the
     overlay. */
  font-display: optional;
  src: url(fonts/inter-var.woff2) format('woff2-variations');
}
/* Metric-matched stand-in for the rare load where `optional` declines to use
   Inter Var. size-adjust is the measured 397/446, so the fallback sets the
   headline at the width Inter Var would have: the hero holds its size even
   when the webfont never arrives at all. */
@font-face {
  font-family: 'Inter Fallback';
  font-style: normal;
  font-weight: 100 900;
  src: local('Arial'), local('Helvetica Neue'), local('Segoe UI'), local('Roboto');
  size-adjust: 89%;
  line-gap-override: 0%;
}
.vl-hero .vl-hero__title {
  /* THE KEEPER (owner, 30 Aug, after a compare page of all three faces):
     Inter — the first pass — settled at Medium after 700 and 600 both
     read too thick at the enlarged desktop size. Then 500 -> 460
     (owner, 31 Aug: -8%, "will feel more premium with a bit less
     weight"). Applied at both breakpoints: it is one headline, and a
     weight that changed across the fold would read as a defect.
     'Inter Var' first, then the metric-matched 'Inter Fallback'.
     Static 'Inter' is deliberately NOT in this one stack: measured at the
     hero size it sets the line at 472px against Inter Var's 397px, a worse
     mismatch than the system face, and it carries font-display:swap of its
     own — so it could reintroduce exactly the resize flash this stack now
     exists to prevent. 'Inter Fallback' is local()-only, so it is present
     instantly and never swaps. Every other Inter rule on the site still
     uses the static faces untouched. */
  font-family: 'Inter Var', 'Inter Fallback', -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;
  font-weight: 460;
  letter-spacing: -0.02em;
  line-height: 0.92;
  color: #2B3E51;
}
.vl-hero .vl-hero__title .vl-hero__accent {
  color: #239B97;
}

/* Hero headline +25% on desktop (owner, 30 Aug). The winning size is
   the (0,1,0) clamp at ~1671 — clamp(1.9rem, 1rem + 2.4vw, 3.08rem) —
   and this is that formula times 1.25 exactly, at this block's (0,2,0)
   so it wins where it applies. Desktop tier only; phones and tablets
   keep the base clamp. The three hard-broken words cannot wrap, so the
   larger size costs no layout risk. */
@media (min-width: 901px) {
  .vl-hero .vl-hero__title {
    /* x1.25 twice, then -5% (owner, 30 Aug, three rounds) = 1.4844x base. */
    font-size: clamp(2.821rem, 1.4844rem + 3.5625vw, 4.5719rem);
    /* Opened from the 0.92 base (owner, 30 Aug): the three-line desktop
       lockup wants air between the lines — "Vistara means limitless
       expanse". Phones keep 0.92; their two-line lockup reads best tight. */
    line-height: 1.12;
  }
}

/* The hero's settled voice, carried through the rest of the type
   (owner, 30 Aug, third revision): Inter Medium on the homepage
   section headings — Best Sellers, Client Reviews, Why Vistara
   Labs?, Resources — AND on every page's own headline sitewide.
   .vl-pagehead__title is the one class every major page's title
   wears (peptides, stacks, contact, FAQ, calculator, bulk order,
   policy — both the mark and plain header patterns), so the two
   selectors below are the whole of "everywhere". */
.vl-section__title,
.vl-pagehead__title {
  font-family: 'Inter', -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;
  font-weight: 500;
}

/* Phones read the headline in two lines — Premium / Canadian Peptides
   (owner, 30 Aug). Hiding the classed break joins the last two words;
   the space in the markup before "Peptides" is what separates them
   here and collapses invisibly at line-start on desktop. */
@media (max-width: 640px) {
  .vl-hero__br3 { display: none; }
}

/* Hero headline +20% on phones (owner, 30 Aug). The base clamp
   (~1671) floors at 1.9rem on narrow screens; this is that formula
   times 1.2 exactly, so phones ride the same curve a fifth larger.
   Desktop keeps its own 1.5625x rule above. */
@media (max-width: 640px) {
  .vl-hero .vl-hero__title {
    font-size: clamp(2.28rem, 1.2rem + 2.88vw, 3.696rem);
  }
}

/* ============================================================
   HERO COMPOSITION, DESKTOP (owner, 30 Aug evening): the copy
   block — headline, subline, Shop All Peptides — walks 100px in
   from the left gutter, and the logo art walks 50px the OTHER
   way (its 20% right padding less 50px) at 10% smaller again
   (378px -> 340px; the 378 cap at ~1434 was itself "10% smaller",
   so the caps are a history, and the LAST one is the size).
   Desktop tier only: below 901px the hero stacks centered and
   a sideways shove would just un-center it.
   ============================================================ */
@media (min-width: 901px) {
  /* 100px in, then 40px back out (owner, third pass) = 60px. */
  .home .vl-hero__copy { margin-left: 60px; }
  .home .vl-hero__art { padding-right: calc(20% - 50px); }
  /* margin-top 36px, MEASURED at 1440w: the wordmark inside the logo art
     ends at 904/1024 of the image (the rest is transparent padding), and
     36px puts that line level with the subline's baseline band. The copy
     column's type scales with vw while the art is a fixed 340px, so the
     alignment is exact at desktop widths near 1440 and drifts a few px
     at the extremes — re-measure before changing, don't eyeball. */
  .home .vl-hero__art img { max-width: min(100%, 340px); margin-top: 36px; }
}

/* ============================================================
   RESEARCH NOTES — the /research-notes/ hub, its seven area
   pages, and the homepage "Explore Research by Area of
   Interest" band. Markup in inc/research.php.

   WHAT THIS HAS TO BEAT, and why every selector is doubled.

   Kadence styles the page body through `.entry-content` —
   `.entry-content p`, `.entry-content img`, `.entry-content
   ul/ol` — all at specificity (0,1,1). A single class is
   (0,1,0) and LOSES to those regardless of where it sits in
   this file, so the first pass of this section was silently
   overruled: every <p> took Kadence's 32px bottom margin
   instead of the tight spacing below, and every hero vial took
   `.entry-content img { max-width: min(100%, 720px) }` instead
   of its own cap — which at 375px drew two 338px vials in a
   338px row, hanging 155px off each side of the screen.

   So every rule here is scoped under `.vl-rs`, giving (0,2,0),
   which beats (0,1,1) cleanly. That is this file's own
   established answer to Kadence — see "HERO COPY SPACING"
   (~1483) and "RECONSTITUTION LISTS" (~1495), which document
   the identical fight and solve it the identical way. No
   !important is needed anywhere in this section, and none is
   used.

   `.vl-rs` is therefore on the root of BOTH surfaces: the
   shortcode wrapper on the eight pages, and the homepage
   band's own <section class="vl-rs vl-rs-home">. That is why
   the band needs no second copy of the custom properties.

   POSITION IN THE FILE IS NOT LOAD-BEARING. Every class is
   under the `vl-rs-` prefix, which appears nowhere earlier in
   this file. This block is at the end because that is where
   new sections go, not because it is winning an order fight —
   it could be moved, and a later append cannot outrank it by
   position either.

   THE ONE CLASS DELIBERATELY REUSED is `.vl-section__title`,
   on the homepage band's heading. That band has to read as a
   sibling of Best Sellers and Client Reviews, and that class
   is where the site's Inter 500 section voice lives (~9915).
   `.vl-rs .vl-rs-home__title` only trims its bottom margin,
   because this band has a support line under the heading where
   the others go straight into content.

   `.vl-rs-btn` is a NEW button rather than a `.vl-btn`
   variant: `.vl-btn` (~28) is italic, uppercase and black, and
   the owner's mockups draw teal, sentence-case, non-italic
   buttons. Reusing it would have meant unpicking four
   inherited declarations per state.

   PALETTE, per the brief: navy #2B3E51 headings, teal #239B97
   accents (#158081 where it sits on tint and needs contrast),
   #E4E9EE hairline borders, #F8FAFB band tint. No shadows —
   the site does not use them.

   Content is already full-bleed on this theme
   (`.content-container` is max-width:none, padding-inline:0 at
   ~879), so every section sets its own 1.6rem gutter to match
   the site's and caps its inner column at 1280px.
   ============================================================ */

.vl-rs {
  --vl-rs-navy: #2B3E51;
  --vl-rs-teal: #239B97;
  --vl-rs-teal-dk: #158081;
  --vl-rs-line: #E4E9EE;
  --vl-rs-tint: #F8FAFB;
  --vl-rs-muted: #5A6B7A;
  --vl-rs-inner: 1280px;
  --vl-rs-gutter: 1.6rem;
  color: var(--vl-rs-navy);
}

/* ---- Shared: eyebrow, section shell, headings ---- */
.vl-rs .vl-rs-eyebrow {
  margin: 0 0 .55rem;
  color: var(--vl-rs-teal);
  font-size: 0.792rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  line-height: 1.3;
}

.vl-rs .vl-rs-sec {
  max-width: var(--vl-rs-inner);
  margin-inline: auto;
  padding: 2.6rem var(--vl-rs-gutter) 0;
}

/* Inter 500 — the same voice .vl-section__title carries sitewide (~9915).
   Named explicitly rather than by joining that selector list, so a change to
   the homepage headings cannot silently restyle eight pages. */
.vl-rs .vl-rs-sec__title,
.vl-rs .vl-rs-hero__title,
.vl-rs .vl-rs-ahero__title,
.vl-rs .vl-rs-band__title,
.vl-rs .vl-rs-path__head {
  font-family: 'Inter', -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;
  font-weight: 500;
  letter-spacing: -0.02em;
  color: var(--vl-rs-navy);
}

.vl-rs .vl-rs-sec__title {
  font-size: clamp(1.45rem, 1.15rem + .95vw, 1.95rem);
  line-height: 1.2;
  margin: 0 0 1.4rem;
}
.vl-rs .vl-rs-sec__sub {
  margin: -.9rem 0 1.6rem;
  color: var(--vl-rs-muted);
  font-size: 1.056rem;
  line-height: 1.5;
}

/* Heading left, "View all articles" right; they stack rather than collide
   once the row is too narrow to hold both. */
.vl-rs .vl-rs-sec__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: .5rem 1.5rem;
}

.vl-rs .vl-rs-viewall,
.vl-rs .vl-rs-link {
  color: var(--vl-rs-teal-dk);
  font-size: 1.012rem;
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
}
.vl-rs .vl-rs-viewall:hover,
.vl-rs .vl-rs-link:hover { color: var(--vl-rs-navy); text-decoration: underline; }

.vl-rs .vl-rs-icon { display: block; }

/* ---- Buttons ---- */
.vl-rs .vl-rs-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  padding: 14px 26px;
  border-radius: 4px;
  border: 1px solid transparent;
  font-size: 1.045rem;
  font-weight: 500;
  line-height: 1.25;
  text-align: center;
  text-decoration: none;
  transition: background .18s ease, color .18s ease, border-color .18s ease;
}
.vl-rs .vl-rs-btn--solid { background: var(--vl-rs-teal-dk); border-color: var(--vl-rs-teal-dk); color: #fff; }
.vl-rs .vl-rs-btn--solid:hover { background: #0F6768; border-color: #0F6768; color: #fff; }
.vl-rs .vl-rs-btn--dark { background: #101418; border-color: #101418; color: #fff; }
.vl-rs .vl-rs-btn--dark:hover { background: var(--vl-rs-navy); border-color: var(--vl-rs-navy); color: #fff; }
.vl-rs .vl-rs-btn--outline { background: #fff; border-color: var(--vl-rs-line); color: var(--vl-rs-navy); }
.vl-rs .vl-rs-btn--outline:hover { border-color: var(--vl-rs-teal); color: var(--vl-rs-teal-dk); }


/* ============================================================
   HUB HERO
   ============================================================ */
.vl-rs .vl-rs-hero { position: relative; padding: 2.4rem 0 1.2rem; }
.vl-rs .vl-rs-hero__inner {
  position: relative;
  z-index: 1;
  max-width: var(--vl-rs-inner);
  margin-inline: auto;
  padding-inline: var(--vl-rs-gutter);
  display: grid;
  /* Round two (owner, 30 Aug): five vials, ~40% bigger again, feet FLUSH
     with the search bar's bottom border. The copy keeps the page's left
     line via the padding calc; the art runs to the right viewport edge. */
  grid-template-columns: minmax(0, .55fr) minmax(0, 1.45fr);
  gap: 2rem;
  align-items: end;
}
@media (min-width: 901px) {
  .vl-rs .vl-rs-hero { overflow: hidden; }
  .vl-rs .vl-rs-hero__inner {
    max-width: none;
    padding-left: max(var(--vl-rs-gutter), calc((100vw - var(--vl-rs-inner)) / 2 + var(--vl-rs-gutter)));
    padding-right: 1.25rem;
  }
  .vl-rs .vl-rs-hero__copy { max-width: 34rem; }
}
.vl-rs .vl-rs-hero__title {
  font-size: clamp(2rem, 1.25rem + 2.6vw, 3.35rem);
  line-height: 1.1;
  margin: 0 0 1.1rem;
}
.vl-rs .vl-rs-hero__sub {
  margin: 0 0 1.6rem;
  color: var(--vl-rs-muted);
  font-size: 1.122rem;
  line-height: 1.6;
}
.vl-rs .vl-rs-hero__art { text-align: right; } /* four vials hug the right edge (owner, 30 Aug) */
/* The cap here is the rule that `.entry-content img` was overruling.
   Four vials now share the row (owner, 30 Aug: BPC-157, Retatrutide,
   KLOW, 5-Amino-1MQ), so each takes a quarter of it, inline. */
.vl-rs .vl-rs-hero__vial {
  max-width: min(23.4%, 300px); /* +20%, owner 30 Aug */
  display: inline-block;
  vertical-align: bottom;
}
.vl-rs .vl-rs-hero__vial img { width: 100%; height: auto; display: block; }
.vl-rs .vl-rs-hero__artwrap { position: relative; }
.vl-rs .vl-rs-hero__step { display: none; }
@media (min-width: 901px) {
  /* Raised off the section edge (owner, 31 Aug): feet on the mountains,
     the faded reflection running down toward the white border below the
     photo instead of being cropped at the edge. translateY, NOT margin:
     a % margin resolves against the CONTAINER width; translateY against
     the vial itself (the canvas keeps 15.33% reflection space under the
     glass foot). */
  .vl-rs .vl-rs-hero__vial { transform: translateY(8.5%); }
  /* Two deliberate lines — "Explore by Area" / "of Interest." (owner,
     31 Aug, superseding the one-line pass the same day). */
  .vl-rs .vl-rs-hero__copy { max-width: none; }
  /* Catalogue-style hover: the vial swells like a shop tile. The scale
     COMPOSES with the ledge translate or hovering would drop the vial
     back onto the baseline. */
  .vl-rs .vl-rs-hero__vial { transition: transform .25s ease; }
  .vl-rs .vl-rs-hero__vial:hover { transform: translateY(8.5%) scale(1.06); }
}
/* Shoulder to shoulder (owner, 31 Aug: less horizontal spacing). */
.vl-rs .vl-rs-hero__vial + .vl-rs-hero__vial { margin-left: -4.5%; }

/*
 * The mountain range behind the hero.
 *
 * Same photo the front page uses (~9766) under a different selector, so the
 * `.home .vl-hero` rules are untouched and either treatment can change without
 * the other. Decorative background on a pseudo-element at `inset: 0`, so it is
 * outside layout entirely and cannot shift anything.
 *
 * `100% auto`, bottom-anchored, exactly as the front page sizes it. `cover`
 * was tried first and is wrong here: the box is much taller than it is wide,
 * so cover scaled a 1600x533 panorama until a single ridge filled the hero.
 *
 * 901px up only. On a phone the hero stacks, the art sits under the copy, and
 * a range painted behind that is grey haze behind text — the same conclusion
 * the front page reached when its phone backdrop was deleted (~9240). The
 * white veil holds the headline band clean where the sky rises.
 */
@media (min-width: 901px) {
  .vl-rs .vl-rs-hero::before {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    background:
      linear-gradient(rgba(255, 255, 255, .45), rgba(255, 255, 255, .45)),
      linear-gradient(#fff 14%, rgba(255, 255, 255, 0) 55%),
      url(../../uploads/2026/08/hero-mountains-photo-2.webp) center bottom / 100% auto no-repeat;
  }
}

/* ---- Search field ---- */
.vl-rs .vl-rs-search {
  display: flex;
  align-items: center;
  gap: .7rem;
  max-width: 430px;
  padding: .85rem 1rem;
  background: #fff;
  border: 1px solid var(--vl-rs-line);
  border-radius: 6px;
}
.vl-rs .vl-rs-search:focus-within { border-color: var(--vl-rs-teal); }
.vl-rs .vl-rs-search__icon { color: var(--vl-rs-muted); flex: 0 0 auto; }
.vl-rs .vl-rs-search__input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  padding: 0;
  background: transparent;
  font-size: 1.045rem;
  color: var(--vl-rs-navy);
}
.vl-rs .vl-rs-search__input:focus { outline: none; }
/*
 * The mockup draws the field with no button. The button is still here and
 * still focusable — clipped rather than display:none — so Enter submits, a
 * keyboard reaches it, and a screen reader announces it. Same clip idiom core
 * uses for .screen-reader-text.
 */
.vl-rs .vl-rs-search__go {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}


/* ============================================================
   AREA CARDS — landing grid and homepage band share one card.

   Flex, not grid, ON PURPOSE. The owner's layout is 4 then 3,
   with the short row CENTRED. A 4-column grid leaves the last
   row left-aligned with a hole on the right; centring it needs
   per-item column-start arithmetic that breaks the moment the
   number of areas changes. `flex-wrap` with
   `justify-content: center` centres any short row for free,
   whatever the count.
   ============================================================ */
.vl-rs .vl-rs-cards {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 1.25rem;
}
.vl-rs .vl-rs-card {
  flex: 0 1 calc(25% - .94rem);   /* 4 up: 4 cards, 3 gaps */
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: 1.5rem 1.35rem 1.3rem;
  background: #fff;
  border: 1px solid var(--vl-rs-line);
  border-radius: 6px;
  text-decoration: none;
  color: inherit;
  transition: border-color .18s ease;
}
.vl-rs .vl-rs-card:hover { border-color: var(--vl-rs-teal); }
.vl-rs .vl-rs-card__icon { color: var(--vl-rs-teal); margin-bottom: .9rem; }
.vl-rs .vl-rs-card__title {
  margin: 0 0 .75rem;
  font-size: 1.122rem;
  font-weight: 600;
  line-height: 1.3;
  color: var(--vl-rs-navy);
}
.vl-rs .vl-rs-card__desc {
  margin: 0 0 1.1rem;
  color: var(--vl-rs-muted);
  font-size: 0.968rem;
  line-height: 1.55;
}
/* Pushed to the card's foot so every "Explore →" in a row sits on one
   baseline however tall the descriptions above them are. */
.vl-rs .vl-rs-card__go {
  margin-top: auto;
  color: var(--vl-rs-teal-dk);
  font-size: 0.968rem;
  font-weight: 500;
}
.vl-rs .vl-rs-card:hover .vl-rs-card__go { text-decoration: underline; }


/* ============================================================
   ARTICLE CARDS
   ============================================================ */
.vl-rs .vl-rs-arts {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.25rem;
}
.vl-rs .vl-rs-art {
  display: flex;
  gap: .75rem;
  align-items: flex-start;
  padding: 1.35rem 1.25rem;
  background: #fff;
  border: 1px solid var(--vl-rs-line);
  border-radius: 6px;
}
.vl-rs .vl-rs-art__text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.vl-rs .vl-rs-art__meta {
  margin: 0 0 .5rem;
  color: var(--vl-rs-muted);
  font-size: 0.88rem;
}
.vl-rs .vl-rs-art__dot { margin: 0 .3rem; }
.vl-rs .vl-rs-art__title { margin: 0 0 .6rem; font-size: 1.122rem; font-weight: 600; line-height: 1.32; }
.vl-rs .vl-rs-art__title a { color: var(--vl-rs-navy); text-decoration: none; }
.vl-rs .vl-rs-art__title a:hover { color: var(--vl-rs-teal-dk); }
.vl-rs .vl-rs-art__ex {
  margin: 0 0 1rem;
  color: var(--vl-rs-muted);
  font-size: 0.946rem;
  line-height: 1.55;
}
.vl-rs .vl-rs-art__more { margin-top: auto; color: var(--vl-rs-teal-dk); font-size: 0.946rem; font-weight: 500; text-decoration: none; }
.vl-rs .vl-rs-art__more:hover { text-decoration: underline; }
/* The vial is decoration beside the text, so it never takes more than a
   third of the card and shrinks before the words do. */
.vl-rs .vl-rs-art__thumb { flex: 0 0 44%; max-width: 275px; align-self: center; } /* 2.5x, owner 30 Aug */
.vl-rs .vl-rs-art__img { width: 100%; max-width: 100%; height: auto; display: block; }


/* ============================================================
   AREA PAGE — breadcrumb, hero, chips
   ============================================================ */
.vl-rs .vl-rs-crumbs {
  max-width: var(--vl-rs-inner);
  margin-inline: auto;
  padding: 1.4rem var(--vl-rs-gutter) 0;
  font-size: 0.924rem;
  color: var(--vl-rs-muted);
}
.vl-rs .vl-rs-crumbs a { color: #21609D; text-decoration: none; }
.vl-rs .vl-rs-crumbs a:hover { text-decoration: underline; }
.vl-rs .vl-rs-crumbs__sep { margin: 0 .4rem; color: #9AA7B2; }
.vl-rs .vl-rs-crumbs__here { color: var(--vl-rs-navy); }

.vl-rs .vl-rs-ahero { position: relative; padding: 1rem 0 .6rem; }
.vl-rs .vl-rs-ahero__inner {
  position: relative;
  z-index: 1;
  max-width: var(--vl-rs-inner);
  margin-inline: auto;
  padding-inline: var(--vl-rs-gutter);
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr);
  gap: 2rem;
  align-items: center;
}
/* Icon and title in one lockup, as in every area mockup. */
.vl-rs .vl-rs-ahero__head { display: flex; align-items: center; gap: 1rem; margin-bottom: 1rem; }
.vl-rs .vl-rs-ahero__icon { color: var(--vl-rs-teal); flex: 0 0 auto; }
.vl-rs .vl-rs-ahero__title {
  margin: 0;
  font-size: clamp(1.85rem, 1.1rem + 2.4vw, 2.95rem);
  line-height: 1.08;
}
.vl-rs .vl-rs-ahero__intro {
  margin: 0 0 1.3rem;
  max-width: 34em;
  color: var(--vl-rs-muted);
  font-size: 1.1rem;
  line-height: 1.6;
}
.vl-rs .vl-rs-ahero__art {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: .5rem;
}
.vl-rs .vl-rs-ahero__vial { max-width: min(48%, 210px); height: auto; display: block; }
/* The area heroes carry the Vistara Labs logo now (owner, 30 Aug; -20% same day). */
.vl-rs .vl-rs-ahero__logo { max-width: min(62%, 272px); height: auto; display: block; margin-inline: auto; }

@media (min-width: 901px) {
  .vl-rs .vl-rs-ahero::before {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    background:
      linear-gradient(rgba(255, 255, 255, .5), rgba(255, 255, 255, .5)),
      linear-gradient(#fff 12%, rgba(255, 255, 255, 0) 52%),
      url(../../uploads/2026/08/hero-mountains-photo-2.webp) center bottom / 100% auto no-repeat;
  }
}

/*
 * Chips are jump links, not filters — there is no JavaScript on these pages.
 * "All" carries `.is-active` as the resting state because the page always
 * shows everything; the others scroll to a section.
 */
.vl-rs .vl-rs-chips { display: flex; flex-wrap: wrap; gap: .55rem; }
.vl-rs .vl-rs-chip {
  display: inline-block;
  padding: .5rem 1.05rem;
  border: 1px solid var(--vl-rs-line);
  border-radius: 999px;
  background: #fff;
  color: var(--vl-rs-navy);
  font-size: 0.924rem;
  line-height: 1.3;
  text-decoration: none;
  transition: border-color .18s ease, color .18s ease;
}
.vl-rs .vl-rs-chip:hover { border-color: var(--vl-rs-teal); color: var(--vl-rs-teal-dk); }
.vl-rs .vl-rs-chip.is-active {
  background: var(--vl-rs-teal-dk);
  border-color: var(--vl-rs-teal-dk);
  color: #fff;
}
/* The chips are in-page anchors, so without this a jump lands the target
   underneath the sticky header. */
.vl-rs [id^="vl-rs-"],
.vl-rs[id^="vl-rs-"] { scroll-margin-top: 100px; }


/* ============================================================
   FEATURED COMPOUNDS
   ============================================================ */
.vl-rs .vl-rs-cpds {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 1.25rem;
}
.vl-rs .vl-rs-cpd {
  position: relative;
  flex: 0 1 calc(25% - .94rem);
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 0;
  padding: 1.4rem 1.1rem 1.3rem;
  background: #fff;
  border: 1px solid var(--vl-rs-line);
  border-radius: 6px;
  text-align: center;
}
.vl-rs .vl-rs-cpd__badge {
  position: absolute;
  top: .85rem;
  left: .85rem;
  padding: .22rem .6rem;
  border-radius: 4px;
  font-size: 0.748rem;
  font-weight: 600;
  letter-spacing: .02em;
  line-height: 1.4;
}
.vl-rs .vl-rs-cpd__badge--primary { background: var(--vl-rs-teal-dk); color: #fff; }
.vl-rs .vl-rs-cpd__badge--related { background: #fff; border: 1px solid var(--vl-rs-line); color: var(--vl-rs-muted); }
.vl-rs .vl-rs-cpd__img { display: block; margin: .6rem 0 .9rem; }
.vl-rs .vl-rs-cpd__img img { max-width: min(100%, 207px); height: auto; display: block; margin-inline: auto; } /* +20% then +15%, owner 30-31 Aug */
.vl-rs .vl-rs-cpd__name { margin: 0 0 .4rem; font-size: 1.122rem; font-weight: 600; line-height: 1.3; }
.vl-rs .vl-rs-cpd__name a { color: var(--vl-rs-navy); text-decoration: none; }
.vl-rs .vl-rs-cpd__name a:hover { color: var(--vl-rs-teal-dk); }
.vl-rs .vl-rs-cpd__desc { margin: 0 0 .6rem; color: var(--vl-rs-muted); font-size: 0.924rem; line-height: 1.5; }
/*
 * The price is not in the mockup. It is here because these are product cards
 * on a page whose whole job is to move a reader toward the catalogue, and a
 * product card with no price makes the reader click to find out. It is drawn
 * by vistara_research_price(), which wraps the catalogue's own
 * vistara_purchasable_price_range() helper, so a card here and a tile on the
 * homepage can never quote different figures.
 */
.vl-rs .vl-rs-cpd__price { margin: 0 0 .9rem; font-size: 1.045rem; font-weight: 600; color: var(--vl-rs-navy); }
.vl-rs .vl-rs-cpd__price del { color: var(--vl-rs-muted); font-weight: 400; margin-right: .35rem; }
.vl-rs .vl-rs-cpd__go {
  margin-top: auto;
  color: var(--vl-rs-teal-dk);
  font-size: 0.968rem;
  font-weight: 500;
  text-decoration: none;
}
.vl-rs .vl-rs-cpd__go:hover { text-decoration: underline; }


/* ---- Injury Recovery's extra two-up band ---- */
.vl-rs .vl-rs-extra__eyebrow { margin-bottom: 1rem; }
.vl-rs .vl-rs-extra__grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.25rem; }
.vl-rs .vl-rs-xc {
  display: flex;
  gap: 1rem;
  padding: 1.5rem 1.4rem;
  background: #fff;
  border: 1px solid var(--vl-rs-line);
  border-radius: 6px;
}
.vl-rs .vl-rs-xc__body { flex: 1 1 auto; min-width: 0; }
.vl-rs .vl-rs-xc__title { margin: 0 0 .7rem; font-size: 1.21rem; font-weight: 600; line-height: 1.3; }
.vl-rs .vl-rs-xc__lede { margin: 0 0 .9rem; color: var(--vl-rs-muted); font-size: 0.968rem; line-height: 1.55; }
.vl-rs .vl-rs-xc__lablabel { margin: 0 0 .3rem; color: var(--vl-rs-teal-dk); font-size: 0.946rem; font-weight: 600; }
.vl-rs .vl-rs-xc__lab { margin: 0; color: var(--vl-rs-muted); font-size: 0.924rem; line-height: 1.55; }
.vl-rs .vl-rs-xc__side {
  flex: 0 0 30%;
  max-width: 150px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .7rem;
  text-align: center;
}
.vl-rs .vl-rs-xc__img img { max-width: 100%; height: auto; display: block; }


/* ============================================================
   CROSS-LINK BAND, BOTTOM CTA PAIR, TRUST STRIP
   ============================================================ */
.vl-rs .vl-rs-path {
  max-width: var(--vl-rs-inner);
  margin: 2.6rem auto 0;
  padding-inline: var(--vl-rs-gutter);
}
.vl-rs .vl-rs-path__inner {
  display: grid;
  grid-template-columns: auto minmax(0, 1.4fr) minmax(0, .8fr) auto;
  gap: 1.5rem;
  align-items: center;
  padding: 1.5rem 1.6rem;
  background: var(--vl-rs-tint);
  border: 1px solid var(--vl-rs-line);
  border-radius: 6px;
}
.vl-rs .vl-rs-path__icon { color: var(--vl-rs-teal); }
.vl-rs .vl-rs-path__head { margin: 0 0 .25rem; font-size: 1.188rem; }
.vl-rs .vl-rs-path__title { margin: 0 0 .35rem; color: var(--vl-rs-teal-dk); font-size: 1.078rem; font-weight: 600; }
.vl-rs .vl-rs-path__body { margin: 0; color: var(--vl-rs-muted); font-size: 0.946rem; line-height: 1.55; }
.vl-rs .vl-rs-path__keyhead { margin: 0 0 .4rem; font-size: 0.902rem; font-weight: 600; color: var(--vl-rs-navy); }
.vl-rs .vl-rs-path__list { margin: 0; padding-left: 1.1rem; color: var(--vl-rs-muted); font-size: 0.946rem; line-height: 1.7; }
.vl-rs .vl-rs-path__list li { margin: 0; }

.vl-rs .vl-rs-ctapair { margin-top: 2.6rem; padding: 0 var(--vl-rs-gutter); }
.vl-rs .vl-rs-ctapair__inner {
  max-width: var(--vl-rs-inner);
  margin-inline: auto;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 1rem;
  padding: 1.6rem;
  background: var(--vl-rs-tint);
}
.vl-rs .vl-rs-ctapair .vl-rs-btn { min-width: 270px; }

.vl-rs .vl-rs-band { margin-top: 2.6rem; padding: 0 var(--vl-rs-gutter); }
.vl-rs .vl-rs-band__inner {
  max-width: var(--vl-rs-inner);
  margin-inline: auto;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 1.5rem;
  align-items: center;
  padding: 1.5rem 1.8rem;
  background: var(--vl-rs-tint);
}
.vl-rs .vl-rs-band__icon { color: var(--vl-rs-teal); }
.vl-rs .vl-rs-band__title { margin: 0 0 .3rem; font-size: 1.32rem; }
.vl-rs .vl-rs-band__sub { margin: 0; color: var(--vl-rs-muted); font-size: 0.99rem; line-height: 1.5; }
.vl-rs .vl-rs-band__actions { display: flex; flex-wrap: wrap; gap: .8rem; }

.vl-rs .vl-rs-trust { margin-top: 2.6rem; padding: 0 var(--vl-rs-gutter) 1rem; }
.vl-rs .vl-rs-trust__inner {
  max-width: var(--vl-rs-inner);
  margin-inline: auto;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1.25rem;
  padding: 1.5rem 0;
  background: var(--vl-rs-tint);
}
.vl-rs .vl-rs-trust__item {
  display: flex;
  align-items: center;
  gap: .85rem;
  padding-inline: 1.4rem;
}
/* Hairline dividers between the four claims, as in the mockups — a left
   border on every item but the first, so nothing is added to the DOM and
   the rule survives the columns collapsing (see the mobile block). */
.vl-rs .vl-rs-trust__item + .vl-rs-trust__item { border-left: 1px solid var(--vl-rs-line); }
.vl-rs .vl-rs-trust__icon { color: var(--vl-rs-navy); flex: 0 0 auto; }
.vl-rs .vl-rs-trust__text { display: block; min-width: 0; }
.vl-rs .vl-rs-trust__head { display: block; font-size: 0.946rem; font-weight: 600; color: var(--vl-rs-navy); }
.vl-rs .vl-rs-trust__sub { display: block; color: var(--vl-rs-muted); font-size: 0.88rem; line-height: 1.45; }


/* ============================================================
   HOMEPAGE BAND
   Between Best Sellers and Client Reviews. Full-bleed tint:
   the front page's content container is already max-width:none
   with no inline padding (~879), so a plain background reaches
   both edges without a negative-margin trick.

   The section carries `.vl-rs` as well as `.vl-rs-home`, which
   is what gives every rule above its (0,2,0) and lets the band
   share the card component and the custom properties with the
   eight pages instead of restating them.
   ============================================================ */
.vl-rs.vl-rs-home {
  background: var(--vl-rs-tint);
  padding: 3rem 0 3.2rem;
  margin-block: 1.5rem;
}
.vl-rs .vl-rs-home__inner {
  max-width: var(--vl-rs-inner);
  margin-inline: auto;
  padding-inline: var(--vl-rs-gutter);
  text-align: center;
}
/* .vl-section__title carries the Inter 500 voice and the 2.5rem bottom margin
   the homepage sections use; this band has a support line under the heading,
   so only the margin is trimmed. (0,2,0) beats both that rule and any
   `.entry-content h2` Kadence applies, so no !important is needed. */
.vl-rs .vl-rs-home__title { margin: 0 0 .6rem; }
.vl-rs .vl-rs-home__sub {
  margin: 0 auto 2rem;
  max-width: 44em;
  color: var(--vl-rs-muted);
  font-size: 1.078rem;
  line-height: 1.55;
}
.vl-rs .vl-rs-home__cta { margin-top: 2rem; }
.vl-rs .vl-rs-home .vl-rs-link { font-size: 1.078rem; }
/* The cards are centred as a block but read left-aligned inside. */
.vl-rs .vl-rs-cards--home { text-align: left; }
.vl-rs .vl-rs-card--home { padding: 1.25rem 1.15rem 1.1rem; }
.vl-rs .vl-rs-card--home .vl-rs-card__title { font-size: 1.056rem; margin-bottom: .55rem; }
.vl-rs .vl-rs-card--home .vl-rs-card__desc { font-size: 0.902rem; margin-bottom: .9rem; }


/* ============================================================
   RESPONSIVE
   Site breakpoints: <=640 phones, <=900 and <=1200 mid,
   >=1201 desktop. Card counts follow the catalogue's own ladder
   (4 up, 3 up at <=1200, 2 up at <=640) with an extra 2-up step
   at <=900 so a tablet never shows three cramped product cards.

   NOTHING HERE MAY SCROLL HORIZONTALLY: every flex basis is a
   percentage minus its share of the gap, and every grid track
   is minmax(0, 1fr) so a long word cannot push a column past
   its share. Measured at 375, 390, 640, 768, 900, 1200 and
   1440 — see the verification notes in the handover.
   ============================================================ */

@media (max-width: 1200px) {
  .vl-rs .vl-rs-card,
  .vl-rs .vl-rs-cpd { flex-basis: calc(33.333% - .84rem); }
  .vl-rs .vl-rs-path__inner { grid-template-columns: auto minmax(0, 1fr); }
  .vl-rs .vl-rs-path__keys,
  .vl-rs .vl-rs-path__cta { grid-column: 2; }
}

@media (max-width: 900px) {
  .vl-rs .vl-rs-hero__inner,
  .vl-rs .vl-rs-ahero__inner { grid-template-columns: minmax(0, 1fr); gap: 1.5rem; }
  /* Vials drop BELOW the copy on a narrow screen, per the brief — they are
     decoration, and pushing the headline down behind them costs the fold. */
  .vl-rs .vl-rs-hero__art,
  .vl-rs .vl-rs-ahero__art { order: 2; }
  /* Owner-dialled mobile strip (30-31 Aug): flex so the COLOURED vials
     (KLOW, 5-Amino-1MQ) lead, +15% size, snap points, and a right-edge
     fade so it reads as a carousel with more to swipe. */
  .vl-rs .vl-rs-hero__art {
    display: flex;
    gap: .6rem;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    scroll-snap-type: x proximity;
    padding-bottom: .4rem;
    -webkit-mask-image: linear-gradient(to right, #000 88%, transparent);
            mask-image: linear-gradient(to right, #000 88%, transparent);
  }
  .vl-rs .vl-rs-hero__art::-webkit-scrollbar { display: none; }
  .vl-rs .vl-rs-hero__vial { max-width: none; width: 52vw; flex: 0 0 auto; scroll-snap-align: start; }
  .vl-rs .vl-rs-hero__vial--3 { order: -2; }
  .vl-rs .vl-rs-hero__vial--4 { order: -1; }
  /* Carousel steppers UNDER the strip (owner, 31 Aug round two — the
     overlaid circles fouled the vials): two bare teal long arrows,
     centred beneath, swipe still works. */
  .vl-rs .vl-rs-hero__artwrap { display: flex; flex-wrap: wrap; justify-content: center; column-gap: 2.6rem; }
  .vl-rs .vl-rs-hero__artwrap .vl-rs-hero__art { order: 1; flex: 0 0 100%; }
  .vl-rs .vl-rs-hero__step {
    display: inline-block;
    order: 2;
    border: 0;
    background: none;
    color: #1b7f7b;
    font-size: 1.7rem;
    line-height: 1;
    padding: .2rem .4rem 0;
    cursor: pointer;
  }
  .vl-rs .vl-rs-ahero__vial { max-width: min(40%, 170px); }
  /* No logo on phones (owner, 31 Aug): Featured Peptides moves up. */
  .vl-rs .vl-rs-ahero__art { display: none; }

  .vl-rs .vl-rs-card,
  .vl-rs .vl-rs-cpd { flex-basis: calc(50% - .63rem); }
  .vl-rs .vl-rs-arts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .vl-rs .vl-rs-extra__grid { grid-template-columns: minmax(0, 1fr); }

  .vl-rs .vl-rs-band__inner { grid-template-columns: auto minmax(0, 1fr); }
  .vl-rs .vl-rs-band__actions { grid-column: 1 / -1; }
  .vl-rs .vl-rs-trust__inner { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 1.4rem; }
  /* With two columns the every-item-but-the-first border would draw a line
     down the left edge of the second row's first cell. Reset, then redraw it
     on the even cells only. */
  .vl-rs .vl-rs-trust__item + .vl-rs-trust__item { border-left: 0; }
  .vl-rs .vl-rs-trust__item:nth-child(even) { border-left: 1px solid var(--vl-rs-line); }
}

@media (max-width: 640px) {
  .vl-rs { --vl-rs-gutter: 1.15rem; }

  .vl-rs .vl-rs-sec { padding-top: 2rem; }
  .vl-rs .vl-rs-hero { padding-top: 1.6rem; }
  .vl-rs .vl-rs-hero__sub br,
  .vl-rs .vl-rs-hero__title br { display: none; }

  /* The icon sits above the title on a phone: side by side, a 62px icon and
     a three-word title leave the headline about eleven characters wide. */
  .vl-rs .vl-rs-ahero__head { flex-direction: column; align-items: flex-start; gap: .6rem; }
  .vl-rs .vl-rs-ahero__icon svg { width: 46px; height: 46px; }

  /* 2-up compact, both grids, per the brief. */
  .vl-rs .vl-rs-card,
  .vl-rs .vl-rs-cpd { flex-basis: calc(50% - .4rem); }
  .vl-rs .vl-rs-cards,
  .vl-rs .vl-rs-cpds { gap: .8rem; }
  .vl-rs .vl-rs-card { padding: 1.1rem .95rem 1rem; }
  .vl-rs .vl-rs-card__icon svg { width: 28px; height: 28px; }
  .vl-rs .vl-rs-card__title { font-size: 0.99rem; }
  .vl-rs .vl-rs-card__desc { font-size: 0.858rem; margin-bottom: .8rem; }
  .vl-rs .vl-rs-card__go { font-size: 0.902rem; }
  .vl-rs .vl-rs-cpd { padding: 1.1rem .8rem 1rem; }
  .vl-rs .vl-rs-cpd__img img { max-width: min(100%, 150px); } /* +20% then +15%, owner 30-31 Aug */
  .vl-rs .vl-rs-cpd__name { font-size: 1.012rem; }
  .vl-rs .vl-rs-cpd__desc { font-size: 0.858rem; }

  /* Articles read as a single column on a phone; two would leave the excerpt
     about four words wide. */
  .vl-rs .vl-rs-arts { grid-template-columns: minmax(0, 1fr); }
  .vl-rs .vl-rs-art__thumb { flex-basis: 30%; max-width: 88px; }

  .vl-rs .vl-rs-chips { gap: .45rem; }
  .vl-rs .vl-rs-chip { padding: .42rem .8rem; font-size: 0.858rem; }

  .vl-rs .vl-rs-path__inner,
  .vl-rs .vl-rs-band__inner { grid-template-columns: minmax(0, 1fr); padding: 1.3rem 1.2rem; }
  .vl-rs .vl-rs-path__keys,
  .vl-rs .vl-rs-path__cta,
  .vl-rs .vl-rs-band__actions { grid-column: auto; }
  .vl-rs .vl-rs-band__actions .vl-rs-btn,
  .vl-rs .vl-rs-ctapair .vl-rs-btn { flex: 1 1 100%; min-width: 0; }
  .vl-rs .vl-rs-ctapair__inner { padding: 1.2rem; }

  .vl-rs .vl-rs-xc { flex-direction: column; }
  .vl-rs .vl-rs-xc__side { flex-basis: auto; max-width: 130px; align-self: center; }

  .vl-rs .vl-rs-trust__inner { padding: 1.2rem 0; }
  .vl-rs .vl-rs-trust__item { padding-inline: .9rem; gap: .6rem; }
  .vl-rs .vl-rs-trust__icon svg { width: 24px; height: 24px; }
  .vl-rs .vl-rs-trust__head { font-size: 0.88rem; }
  .vl-rs .vl-rs-trust__sub { font-size: 0.814rem; }

  .vl-rs.vl-rs-home { padding: 2.2rem 0 2.4rem; }
  .vl-rs .vl-rs-home__sub { font-size: 0.99rem; margin-bottom: 1.5rem; }
  .vl-rs .vl-rs-card--home { padding: 1rem .9rem .95rem; }
}

/* ============================================================
   IN-ARTICLE VIAL FIGURE  (owner, 30 Aug)
   Injected by vistara_research_post_figure() after the first
   paragraph of every filed article. Floats beside the text on
   desktop; full-width and centered on phones. Scoped under
   .entry-content at (0,2,0), the same fight-and-win as .vl-rs.
   ============================================================ */
.entry-content .vl-rs-postfig {
  float: right;
  width: min(38%, 300px);
  margin: .4rem 0 1.2rem 2rem;
  text-align: center;
}
.entry-content .vl-rs-postfig img { max-width: 100%; height: auto; display: block; margin-inline: auto; }
.entry-content .vl-rs-postfig figcaption { font-size: .935rem; color: #8a94a0; margin-top: .35rem; }
.entry-content .vl-rs-postfig figcaption a { color: #1b7f7b; text-decoration: none; font-weight: 500; }
.entry-content .vl-rs-postfig figcaption a:hover { text-decoration: underline; }
@media (max-width: 640px) {
  .entry-content .vl-rs-postfig { float: none; width: min(72%, 260px); margin: .8rem auto 1.2rem; }
}

/* Article headline in the section-title blue (owner, 30 Aug) and the
   research-area badge above the opening paragraph. */
.single-post h1.entry-title { color: #21609D; }
.entry-content .vl-rs-areabadge {
  margin: 0 0 1rem;
  font-size: .82rem;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
}
.entry-content .vl-rs-areabadge a { color: #1b7f7b; text-decoration: none; }
.entry-content .vl-rs-areabadge a:hover { text-decoration: underline; }

/* ============================================================
   REVIEW CARDS — name snug under the text  (owner, 30 Aug)
   The text block was flex:1, stretching to fill the card and
   pushing the name/stars row to the floor; equal-height cards
   made the gap read as dead air mid-card. The stretch comes
   off and the row follows the text at a normal paragraph gap;
   whatever height the strip's tallest card imposes now falls
   BELOW the stars, where empty space reads as padding.
   ============================================================ */
.vl-review__text { flex: 0 0 auto; margin-bottom: .55rem; }

/* ============================================================
   RESOURCES — restyled to the research trust-strip theme
   (owner, 30 Aug: screenshot-2 format and colour scheme, same
   three links). The MARKUP is untouched — same classes, same
   content on page 72 local and live — so the whole conversion
   is these overrides winning at EOF over the black band at ~195.
   Icons ride ::before as data-URI SVGs, teal stroke, matching
   the vl-rs icon voice, keyed by position.
   ============================================================ */
.vl-resources { background: #fff; padding: .4rem 5vw 3rem; margin-top: 0; }
.vl-resources__title { margin-bottom: 1.8rem; }
.vl-resources__grid {
  max-width: 1240px;
  margin-inline: auto;
  background: #F8FAFB;
  border-radius: 14px;
  padding: 1.9rem 1.6rem;
  gap: 2.2rem;
}
.vl-resources__item { position: relative; padding-left: 2.5rem; }
.vl-resources__item + .vl-resources__item { border-left: 1px solid #E3E9EE; padding-left: 2.9rem; }
.vl-resources__item::before {
  content: "";
  position: absolute;
  left: .35rem;
  top: .15rem;
  width: 26px;
  height: 26px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23239B97' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='9.3' r='5.6'/%3E%3Cpath d='m8.5 14.1-1.6 7.1 5.1-2.6 5.1 2.6-1.6-7.1'/%3E%3C/svg%3E") center / contain no-repeat;
}
.vl-resources__item + .vl-resources__item::before { left: .75rem; }
.vl-resources__item:nth-child(2)::before { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23239B97' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 2.7 4.9 5.5v6.1c0 4.5 3 8 7.1 9.5 4.1-1.5 7.1-5 7.1-9.5V5.5L12 2.7Z'/%3E%3Cpath d='M12 8.5v6.2M8.9 11.6h6.2'/%3E%3C/svg%3E"); }
.vl-resources__item:nth-child(3)::before { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23239B97' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 5.6h16v10.6H9.4L4 20.4Z'/%3E%3Cpath d='M8 9.4h8M8 12.4h5'/%3E%3C/svg%3E"); }
.vl-resources__item h3 { color: #2B3E51; font-size: 1.08rem; font-weight: 700; margin: 0 0 .45rem; }
.vl-resources__item p { color: #5B6B7B; font-size: .97rem; line-height: 1.55; margin: 0 0 .8rem; }
.vl-resources__item a { color: #1b7f7b; font-weight: 600; text-decoration: none; font-size: .97rem; }
.vl-resources__item a::after { content: " \2192"; }
.vl-resources__item a:hover { text-decoration: underline; }
@media (max-width: 700px) {
  .vl-resources__grid { grid-template-columns: 1fr; gap: 1.6rem; padding: 1.4rem 1.2rem; }
  .vl-resources__item + .vl-resources__item { border-left: 0; border-top: 1px solid #E3E9EE; padding-left: 2.5rem; padding-top: 1.4rem; }
  .vl-resources__item + .vl-resources__item::before { left: .35rem; top: 1.55rem; }
}

/* ============================================================
   MOBILE ARTICLE CARDS — image-first  (owner-dialled, 30 Aug)
   80% of traffic is phones: the vial moves ABOVE the headline
   at ~55% of the screen, catalogue-adjacent, and the card
   reads as a shop tile instead of text with a stamp.
   ============================================================ */
@media (max-width: 640px) {
  .vl-rs .vl-rs-art { flex-direction: column; }
  .vl-rs .vl-rs-art__thumb {
    order: -1;
    flex: 0 0 auto;
    width: 55%;
    max-width: 210px;
    align-self: center;
    margin-bottom: .5rem;
  }
}

/* ============================================================
   REVIEW CARDS, round two  (owner, 30 Aug)
   Every card shows the same ~120-char budget before its fold,
   so four text lines is the ceiling — and the FLOOR too:
   min-height pins the name row to one line across every card
   instead of it jumping to wherever a short review ended.
   (min-, not height: an expanded "Read more" still grows.)
   The body's bottom padding drops with the dead air.
   ============================================================ */
.vl-review__text { min-height: 6.1rem; }
.vl-review__body { padding-bottom: 1rem; }
@media (max-width: 900px) {
  .vl-review__text { min-height: 5.3rem; }
}

/* ============================================================
   ALL RESEARCH ARTICLES + SITE SEARCH RESULTS  (owner, 30 Aug)
   The articles page reuses the area hero one column wide and
   lets the card grid wrap. Search results get the same card
   language: Kadence's ratio-box thumbnails were exploding the
   vial art to column width — the ratio box is retired and the
   image capped, so a search lands somewhere that looks like
   the rest of the shop.
   ============================================================ */
.vl-rs-allarts .vl-rs-ahero__inner { grid-template-columns: minmax(0, 1fr); }
.vl-rs-allarts .vl-rs-ahero__copy { max-width: 44rem; }
.vl-rs-allarts .vl-rs-search { margin-top: 1rem; max-width: 34rem; }
.vl-rs-allarts .vl-rs-arts--all { flex-wrap: wrap; }
.vl-rs-allarts .vl-rs-arts--all .vl-rs-art { flex: 0 1 calc(33.333% - .9rem); }
@media (max-width: 900px) {
  .vl-rs-allarts .vl-rs-arts--all .vl-rs-art { flex-basis: calc(50% - .7rem); }
}
@media (max-width: 640px) {
  .vl-rs-allarts .vl-rs-arts--all .vl-rs-art { flex-basis: 100%; }
}

/* Gutters on the search results page — applies to every ?s= search
   (owner, 31 Aug). Kadence's site-container runs the results grid to the
   viewport edges here, unlike every other archive on the site; the cards
   were touching both sides. Both the no-results body and the results body
   are covered, and the width is capped so the grid does not sprawl on very
   wide screens. */
/* Specificity note: the site-wide reset at ~883 zeroes this padding
   through `#inner-wrap .content-container` — an ID, (1,1,0). Two classes
   lose to that however late they sit, so these carry the same ID and add
   the body class, giving (1,2,0). Same fight, same fix, as the .vl-rs
   scoping documented elsewhere in this file. */
.search-results #inner-wrap .content-container,
.search-no-results #inner-wrap .content-container {
  max-width: 1240px;
  margin-inline: auto;
  padding-inline: clamp(1rem, 4vw, 2.5rem);
  box-sizing: border-box;
}
.search-results .page-title,
.search-no-results .page-title { margin-bottom: 1.2rem; }
@media (max-width: 640px) {
  .search-results #inner-wrap .content-container,
  .search-no-results #inner-wrap .content-container { padding-inline: 1rem; }
}

.search-results #primary article.entry {
  border: 1px solid #E3E9EE;
  border-radius: 12px;
  background: #fff;
  padding: 1.1rem 1.1rem 1.3rem;
}
.search-results #primary .post-thumbnail { display: block; text-align: center; margin-bottom: .6rem; }
.search-results #primary [class*="kadence-thumbnail-ratio"] { padding-bottom: 0 !important; height: auto; }
.search-results #primary .post-thumbnail-inner { position: static !important; }
.search-results #primary .post-thumbnail img {
  position: static !important;
  width: auto !important;
  height: auto !important;
  max-width: min(60%, 170px) !important;
  max-height: 230px;
  object-fit: contain;
  margin-inline: auto;
}
.search-results #primary .entry-title { font-size: 1.15rem; }
.search-results #primary .entry-title a { color: #21609D; }

/* ============================================================
   HUB ARTICLE CAROUSEL  (owner, 30 Aug)
   Twelve latest notes in a scroll-snap strip — swipe on touch,
   arrows for pointers, three-up card width kept from the grid.
   ============================================================ */
.vl-rs-carousel { position: relative; }
.vl-rs .vl-rs-arts--carousel {
  flex-wrap: nowrap;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding-bottom: .4rem;
}
.vl-rs .vl-rs-arts--carousel::-webkit-scrollbar { display: none; }
.vl-rs .vl-rs-arts--carousel .vl-rs-art { flex: 0 0 calc(33.333% - .9rem); scroll-snap-align: start; }
.vl-rs-carousel__btn {
  position: absolute;
  top: 42%;
  z-index: 2;
  width: 42px; height: 42px;
  border-radius: 50%;
  border: 1px solid #E3E9EE;
  background: #fff;
  color: #1b7f7b;
  font-size: 1.15rem;
  cursor: pointer;
  box-shadow: 0 2px 10px rgba(43, 62, 81, .08);
}
.vl-rs-carousel__btn--prev { left: -10px; }
.vl-rs-carousel__btn--next { right: -10px; }
.vl-rs-carousel__btn:hover { background: #F2F7F7; }
@media (max-width: 900px) {
  .vl-rs .vl-rs-arts--carousel .vl-rs-art { flex: 0 0 78%; }
  .vl-rs-carousel__btn { display: none; }
}

/* Resources tighter under the leaf on DESKTOP only (owner, 30 Aug —
   round two; mobile is right already). */
@media (min-width: 901px) {
  .vl-resources { margin-top: -5.8rem; }
  .vl-resources__title { margin-bottom: 1.2rem; }
}

/* ============================================================
   ARTICLE REFERENCES  (owner, 31 Aug)
   Deliberately plain: a numbered list, the study's own title as
   the link, then design/size in muted type. It should read like
   a reference section, not a marketing panel.
   ============================================================ */
.entry-content .vl-rs-refs {
  margin: 2.6rem 0 0;
  padding: 1.6rem 1.5rem 1.2rem;
  background: #F8FAFB;
  border: 1px solid #E3E9EE;
  border-radius: 12px;
}
.entry-content .vl-rs-refs__title {
  margin: 0 0 .5rem;
  font-family: 'Inter', -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;
  font-weight: 500;
  font-size: 1.35rem;
  color: #21609D;
}
.entry-content .vl-rs-refs__context { margin: 0 0 1.1rem; color: #4A5A6A; font-size: .97rem; line-height: 1.6; }
.entry-content .vl-rs-refs__list { margin: 0; padding-left: 1.2rem; }
.entry-content .vl-rs-refs__list li { margin: 0 0 1rem; padding: 0; }
.entry-content .vl-rs-ref__title { display: block; color: #1b7f7b; font-weight: 600; text-decoration: none; line-height: 1.4; }
.entry-content .vl-rs-ref__title:hover { text-decoration: underline; }
.entry-content .vl-rs-ref__meta { display: block; color: #6B7A88; font-size: .88rem; margin-top: .15rem; }
.entry-content .vl-rs-ref__note { display: block; color: #4A5A6A; font-size: .9rem; margin-top: .25rem; line-height: 1.5; }
.entry-content .vl-rs-refs__foot { margin: 1.2rem 0 0; color: #8a94a0; font-size: .84rem; line-height: 1.55; }
@media (max-width: 640px) {
  .entry-content .vl-rs-refs { padding: 1.2rem 1rem 1rem; }
}

/* ============================================================
   STACK PATHWAY LINES
   ============================================================ */
/* The "Compound -> Pathway" heading on each stack takes the same teal as the
   short description above it, so the two read as one voice (owner, 31 Aug).
   The bullet terms under each arrow stay body colour. */
.vl-single__desc .vl-pathway strong,
.entry-content .vl-pathway strong {
  color: #239B97;
}

/* ============================================================
   EMAIL CAPTURE — footer form and checkout card
   ============================================================ */
/* Both are rendered hidden and revealed by script: the site is served from full
   page cache, so "already subscribed" can only be decided in the browser. */
.vl-capture {
  background: #F6F7F6;
  border-top: 1px solid #E4E7E5;
  padding: 2.6rem 1.25rem;
}
.vl-capture__inner { max-width: 560px; margin-inline: auto; text-align: center; }
.vl-capture__title {
  margin: 0 0 .35rem; color: #239B97;
  font-size: 1.15rem; font-weight: 600; letter-spacing: -0.01em;
}
.vl-capture__body { margin: 0 0 1.1rem; color: #2B3E51; font-size: .95rem; line-height: 1.5; }
.vl-capture__form { display: flex; gap: .5rem; justify-content: center; flex-wrap: wrap; }
/* The script hides the form once a code has been issued. `display: flex` above
   beats the UA sheet's `[hidden] { display: none }`, so without this the field
   and button stayed on screen underneath the code that had just replaced them. */
.vl-capture__form[hidden] { display: none; }
.vl-capture__form input[type="email"] {
  flex: 1 1 260px; max-width: 320px;
  padding: .7rem .9rem; font-size: 1rem;
  border: 1px solid #CFD6D2; border-radius: 8px; background: #fff; box-sizing: border-box;
}
.vl-capture__form input[type="email"]:focus {
  outline: none; border-color: #239B97; box-shadow: 0 0 0 3px rgba(35,155,151,.15);
}
.vl-capture__submit {
  padding: .7rem 1.3rem; border: none; border-radius: 8px; cursor: pointer;
  background: #239B97; color: #fff; font-size: 1rem; font-weight: 600;
  transition: background .15s ease;
}
.vl-capture__submit:hover { background: #1d837f; }
.vl-capture__submit:disabled { opacity: .6; cursor: default; }
.vl-capture__done { margin: .4rem 0 0; color: #2B3E51; font-size: 1.05rem; font-weight: 600; }
.vl-capture__code {
  display: inline-block; margin-left: .4rem; padding: .15rem .55rem;
  background: #fff; border: 1px dashed #239B97; border-radius: 6px;
  color: #239B97; letter-spacing: .06em;
}
.vl-capture__fine { margin: .8rem 0 0; color: #8a94a0; font-size: .8rem; }
.vl-capture__sent { display: block; margin-top: .3rem; color: #8a94a0; font-size: .82rem; font-weight: 400; }
/* Shown instead of a code when the address has ordered before. */
.vl-capture__msg  { margin: .4rem 0 0; color: #2B3E51; font-size: .95rem; line-height: 1.5; }
.vl-capture__submit.is-busy { opacity: .75; }

/* ---- The checkout instance --------------------------------------
   A card in a column of cards, not the footer's full-bleed band.

   `margin-inline: var(--vl-edge)` is the same gutter `.vl-cartextras`
   uses for the bacteriostatic-water card immediately above it, so the
   two line up at every width instead of this one running out to the
   window edge (owner, 31 Aug). Vertical padding is roughly half the
   footer's: in the footer this block IS the section break and can
   afford the air; at checkout it is one step in a purchase and every
   pixel it takes pushes the pay button further down the page.

   NOTE: these declarations were live but inert until 31 Aug. An
   unmatched closing brace, left behind when the corner pill was
   removed, sat directly above this rule. CSS folds a stray closing
   brace into the NEXT rule's selector, so it and the selector below
   parsed as one invalid selector and this entire block was discarded
   -- which is why the checkout box rendered with the footer's padding
   and no border. The brace is gone. Do not add one above this comment,
   and keep brace characters out of comments here so a brace-balance
   check over this file stays meaningful. */
.vl-capture--checkout {
  border: 1px solid #E4E7E5;
  border-radius: 10px;
  padding: 1.15rem 1.25rem;
  margin: 0 var(--vl-edge, 3.2rem) 1.4rem;
}
.vl-capture--checkout .vl-capture__title { font-size: 1.05rem; }
.vl-capture--checkout .vl-capture__body  { margin-bottom: .8rem; font-size: .9rem; }
.vl-capture--checkout .vl-capture__fine  { margin-top: .6rem; }

/* Wide screens: the pitch on the left, the field on the right, so the card
   is one band rather than four stacked rows pushing the pay button down the
   page (owner, 31 Aug). Only at checkout -- the footer band has the whole
   width to itself and reads better centred. */
@media (min-width: 900px) {
  .vl-capture--checkout .vl-capture__inner {
    max-width: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 2.5rem;
    text-align: left;
  }
  .vl-capture--checkout .vl-capture__copy   { flex: 1 1 auto; min-width: 0; }
  .vl-capture--checkout .vl-capture__action { flex: 0 0 auto; }
  .vl-capture--checkout .vl-capture__body   { margin-bottom: 0; }
  .vl-capture--checkout .vl-capture__form   { justify-content: flex-end; flex-wrap: nowrap; }
  .vl-capture--checkout .vl-capture__form input[type="email"] { flex: 0 1 300px; }
  /* nowrap above takes the row's spare width off the button first, which
     broke "Get my code" across two lines. */
  .vl-capture--checkout .vl-capture__submit { white-space: nowrap; }
  /* The confirmation replaces the form in the same column, so it keeps the
     column's alignment rather than reverting to the centred default. */
  .vl-capture--checkout .vl-capture__done,
  .vl-capture--checkout .vl-capture__msg,
  .vl-capture--checkout .vl-capture__fine { text-align: right; }
  .vl-capture--checkout .vl-capture__msg  { max-width: 34ch; }
}

/* ============================================================
   SECOND OFFERS BANNER — mid-homepage
   ============================================================ */
/* Sits under the "Shop All Peptides" button that closes Best Sellers, and
   renders the same PAIR the header does: `.vl-marquee` shows below 701px,
   `.vl-ticker` from 701px up. Spacing therefore belongs on the wrapper, not
   on either component, since which one is visible depends on the viewport.

   The marquee override is reached at (0,4,0) deliberately. Two existing rules
   style a marquee sitting in homepage content --
   `.home .entry-content .vl-marquee` at (0,3,0) and `.home .vl-marquee` at
   (0,2,0) -- and they were written for the mid-page banner this replaces. A
   plain `.vl-banner2 .vl-marquee` is only (0,2,0) and loses to the first of
   them wherever it sits in the file, so the homepage context is named here
   rather than left to file order. */
.vl-banner2 { margin: 1.7rem 0 2.1rem; }
.home .entry-content .vl-banner2 .vl-marquee { margin: 0; }
.vl-banner2 .vl-ticker { margin: 0; }

/* ============================================================
   FOOTER — CARD SCHEME MARKS
   ============================================================ */
/* Visa / Mastercard / Amex, beside the Interac mark (owner, 31 Aug).

   Source order is cards-then-Interac, so the whole thing is one
   flex-direction: row on a desktop puts the cards on the LEFT, column once
   the footer stacks puts them ABOVE — which is where they were asked for in
   each case, with no `order` juggling.

   SIZING, and why the selector is as heavy as it is. The footer marks have
   been resized many times, and the rule that survived is
   `.vl-footer .vl-footer__pay img` at (0,3,0) carrying `!important` --
   it matches ANY image in this block, so a new one is pinned to the Interac
   mark's 113px unless it is beaten on both counts. `img.vl-footer__cards`
   takes it to (0,3,1) and restates `!important`. */

/* ONE HEIGHT FOR EVERY MARK.

   The Interac mark was 113x52 while a single card in the strip was about
   50x34, so it towered over them (owner, 31 Aug: they should all be the same
   size, and Interac should not be the biggest). They are different shapes, so
   "same size" is matched HEIGHT -- the way payment marks are normally set.
   Widths below are that shared height times each image's own aspect:
   the strip is 4.55:1, the Interac mark 2.17:1.

     desktop  44px tall -> cards 200px, Interac  95px
     stacked  42px tall -> cards 191px, Interac  91px
     phone    39px tall -> cards 177px, Interac  85px */
.vl-footer .vl-footer__pay img.vl-footer__cards {
  max-width: 200px !important;
  width: 200px;
  height: auto;
  margin-inline: 0;
}
.vl-footer .vl-footer__pay img.vl-footer__interac {
  max-width: 95px !important;
  width: 95px;
  height: auto;
}

@media (min-width: 1025px) {
  .vl-footer .vl-footer__pay {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: .9rem;

    /* The 30% nudge above (~line 4075) exists to drop ONE 113px mark onto the
       footer's midline, because the middle grid column sits left of it. Two
       marks are 309px wide, and pushed 98px right they ran under the Research
       Use Disclaimer. Cleared, so the pair centres in its own column and the
       whole group moves left, clear of the legal text (owner, 31 Aug). */
    transform: none;
  }
}

/* Stacked footer: cards above the Interac mark, under the wordmark. The extra
   gap is deliberate -- two marks stacked with the old single-mark spacing read
   as one crowded block (owner, 31 Aug: give it a bit more room here). */
@media (max-width: 1024px) {
  .vl-footer .vl-footer__pay {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1.15rem;
    padding-top: 1.6rem;
  }
  .vl-footer .vl-footer__pay img.vl-footer__cards {
    max-width: 191px !important;
    width: 191px;
    margin-inline: auto;
  }
  .vl-footer .vl-footer__pay img.vl-footer__interac {
    max-width: 91px !important;
    width: 91px;
    margin-inline: auto;
  }
}

@media (max-width: 640px) {
  .vl-footer .vl-footer__pay img.vl-footer__cards {
    max-width: 177px !important;
    width: 177px;
  }
  .vl-footer .vl-footer__pay img.vl-footer__interac {
    max-width: 85px !important;
    width: 85px;
  }
  .vl-footer .vl-footer__pay { gap: 1.25rem; }
}
