/* ==========================================================================
   APEX PURE PEPTIDES — brand stylesheet
   --------------------------------------------------------------------------
   Install:
     1. Save as  wp-content/themes/blocksy-child/apex.css
     2. Enqueue it from the child theme's functions.php (see build notes)
     3. Wrap every Elementor HTML widget's content in <div class="apex"> … </div>

   Everything here is scoped under .apex so it cannot leak into Elementor's
   editor UI, the WooCommerce checkout, the admin bar, or Blocksy's own
   header/footer. The only exceptions are the clearly-marked WooCommerce
   bridge and COA sections at the bottom, which must reach Woo's own markup.

   Fonts: Sora is NOT loaded here. Register it in Blocksy > Customizer >
   Typography, or self-host with OMGF. Do not load it from fonts.googleapis.com
   — serving Google Fonts from Google's CDN has been ruled a GDPR breach in
   the EU, which matters for an EU storefront.
   ========================================================================== */


/* ── 1. BRAND TOKENS ─────────────────────────────────────────────────────── */

:root {
  --apex-bg-white:     #ffffff;
  --apex-bg-light:     #f8f9fa;
  --apex-border:       #eaeaea;

  --apex-orange:       #e8601c;
  --apex-orange-hover: #cf5517;
  --apex-orange-fade:  #fdf2ed;
  --apex-orange-grad:  linear-gradient(90deg, #e8601c 0%, #f7a05a 100%);

  --apex-text-dark:    #111827;
  --apex-text-gray:    #4b5563;
  --apex-text-muted:   #9ca3af;

  --apex-green:        #10b981;
  --apex-green-bg:     #d1fae5;

  --apex-shadow-sm:    0 1px 2px 0 rgba(0,0,0,.05);
  --apex-shadow-md:    0 4px 6px -1px rgba(0,0,0,.05), 0 2px 4px -1px rgba(0,0,0,.03);
  --apex-shadow-lg:    0 10px 15px -3px rgba(0,0,0,.05), 0 4px 6px -2px rgba(0,0,0,.03);

  --apex-radius-sm:    6px;
  --apex-radius-md:    12px;
  --apex-radius-lg:    20px;

  --apex-font:         'Sora', system-ui, -apple-system, sans-serif;

  /* Horizontal page gutter. A variable rather than a literal because the hero
     badge is absolutely positioned on mobile and has to line up with the
     padded content — one value, so they cannot drift apart. */
  --apex-gutter:       24px;

  /* Height of Blocksy's transparent header. Tune this to match your header
     so the hero content clears it instead of sitting underneath. */
  --apex-header-h:     140px;

}


/* ── 2. SCOPED RESET ─────────────────────────────────────────────────────── */

/* :where() keeps specificity at 0, so any single class below overrides it
   and you never end up in an !important war with Elementor. */

.apex,
.apex *,
.apex *::before,
.apex *::after { box-sizing: border-box; }

.apex :where(h1, h2, h3, h4, h5, h6, p, ul, ol, figure, blockquote) {
  margin: 0;
  padding: 0;
}

.apex :where(ul, ol) { list-style: none; }

.apex {
  /* Blocksy sets font-family directly on h1–h6 via var(--theme-font-family).
     A direct element rule beats an inherited value, so setting font-family
     on this wrapper alone leaves every heading in the theme font. Rebinding
     the variable here makes Blocksy's own rules resolve to Sora instead of
     starting an !important war with them. Same technique as #offcanvas. */
  --theme-font-family: var(--apex-font);

  font-family: var(--apex-font);
  font-size: 16px;
  line-height: 1.6;
  color: var(--apex-text-dark);
  -webkit-font-smoothing: antialiased;
}

.apex :where(a) {
  color: inherit;
  text-decoration: none;
  transition: all .2s ease;
}

.apex :where(img) {
  max-width: 100%;
  height: auto;
  display: block;
}


/* ── 3. LAYOUT ───────────────────────────────────────────────────────────── */

/* padding-inline, never the `padding` shorthand.

   .hero-inner carries BOTH classes (class="container hero-inner"), so a
   shorthand here resets that element's vertical padding too — and whichever
   rule sits later in the file wins, which is not something you want decided
   by source order. Horizontal spacing is this rule's job; vertical spacing
   belongs to whatever the element also is. */
.apex .container {
  max-width: 1280px;
  margin: 0 auto;
  padding-inline: var(--apex-gutter);
  width: 100%;
}

/* Full-bleed escape hatch.

   You should NOT need this — set the Elementor container to Full Width with
   zero padding and the section already spans the viewport. It is here only for
   sections nested inside a constrained parent you can't change.

   This replaces the old  width:100vw + margin-left:-50vw  approach, which
   overflowed by the scrollbar's width and forced overflow-x:hidden on <body>
   as a patch. `overflow: clip` contains the bleed without creating a scroll
   container, so position:sticky children keep working. */

.apex-bleed-root { overflow-x: clip; }

.apex .fullbleed {
  width: auto;
  margin-inline: calc(50% - 50vw);
}

/* Un-box an Elementor container that holds an Apex section.
 *
 * Symptom: a section in a "Boxed" container sits ~865px wide and inset, while
 * a section in a Full Width container spans the viewport — so two sections of
 * the same page don't line up.
 *
 * There are THREE things holding that inner box in, and missing any one of
 * them leaves the width unchanged. Measured on the live site:
 *
 *   1. Blocksy, not Elementor, sets an explicit width:
 *        .ct-elementor-default-template .e-con > .e-con-inner {
 *          width: var(--theme-block-width, var(--theme-container-width)); }
 *      An explicit width is why clearing max-width alone does nothing. This
 *      is also why Full Width containers are unaffected — they render no
 *      .e-con-inner at all, so the rule has nothing to match.
 *
 *   2. Elementor caps it:  max-width: var(--content-width)  →  min(100%,1290px)
 *
 *   3. Elementor centres it with  margin: 0 auto . Auto inline margins
 *      suppress cross-axis stretch in a column flex container, so the box
 *      shrink-wraps even once width and max-width are dealt with.
 *
 * Padding is cleared through the custom properties rather than padding-inline,
 * because Elementor reads --padding-inline-start/end back out in its own
 * rules; overriding the resolved property gets recomputed away.
 *
 * Scoped to :has(.apex) so ordinary Elementor containers elsewhere keep their
 * boxed behaviour. Setting the container to Full Width with zero padding in
 * the editor is still the tidier fix — this is the safety net that stops a
 * mis-set container from silently breaking the layout. */

.e-con-boxed:has(.apex) {
  --padding-inline-start: 0px;
  --padding-inline-end: 0px;
}

.e-con-boxed:has(.apex) > .e-con-inner {
  width: 100%;
  max-width: none;
  margin-inline: 0;
}


/* ── 4. BUTTONS ──────────────────────────────────────────────────────────── */

.apex .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 16px 36px;
  border-radius: 100px;
  font-family: inherit;
  font-weight: 600;
  font-size: 15px;
  /* text-align matters as well as justify-content: the latter centres the
     line box, but a label that WRAPS — "Live QR Code Verification" does at
     mobile widths — falls back to left-aligned on its second line without
     this. line-height 1 would also let those two lines touch. */
  text-align: center;
  line-height: 1.25;
  border: none;
  cursor: pointer;
  transition: all .2s ease;
}

.apex .btn-primary {
  background: var(--apex-orange);
  color: #fff;
  box-shadow: var(--apex-shadow-sm);
}
.apex .btn-primary:hover {
  background: var(--apex-orange-hover);
  color: #fff;
  transform: translateY(-1px);
}

.apex .btn-outline {
  background: rgba(255,255,255,.12);
  color: #fff;
  border: 1.5px solid rgba(255,255,255,.6);
  backdrop-filter: blur(4px);
}
.apex .btn-outline:hover {
  background: rgba(255,255,255,.22);
  border-color: #fff;
  color: #fff;
}

.apex .btn-gradient {
  background: var(--apex-orange-grad);
  color: #fff;
  font-size: 16px;
  padding: 18px 48px;
  box-shadow: var(--apex-shadow-sm);
}
.apex .btn-gradient:hover {
  color: #fff;
  transform: translateY(-1px);
  box-shadow: var(--apex-shadow-md);
}

/* Keyboard focus — was missing entirely before. */
.apex a:focus-visible,
.apex button:focus-visible,
.apex input:focus-visible {
  outline: 2px solid var(--apex-orange);
  outline-offset: 3px;
}


/* ── 5. TICKER ───────────────────────────────────────────────────────────── */

/* Prefer Blocksy's header Top Row for this. Kept for use as an HTML widget. */

.apex .ticker {
  background: var(--apex-orange);
  color: #fff;
  font-size: 13px;
  font-weight: 500;
  text-align: center;
  padding: 10px 24px;
}


/* ── 6. HERO ─────────────────────────────────────────────────────────────── */

/* NOTE: self-host this image in the Media Library. It currently points at a
   Shopify CDN that will 404 the day that store closes. */

/* The photo lives on ::after, not on .hero itself, so it can be animated with
   `transform` — which the compositor handles on the GPU. Animating
   background-position or background-size instead would repaint a full-screen
   image every frame and stutter badly on a phone.

   Layer order:  ::after (photo, z 0)  <  ::before (scrim, z 1)  <  .hero-inner (z 2) */

.apex .hero {
  position: relative;
  min-height: 92vh;
  display: flex;
  align-items: center;
  overflow: hidden;
}

.apex .hero::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  /* Root-relative, NOT an absolute https://appeptides.com/… URL.
   *
   * A CSS url() beginning with / resolves against the site root, not against
   * this stylesheet's folder, so it lands on /wp-content/uploads/… correctly
   * while surviving any future domain change untouched. Hardcoding the domain
   * is what left http:// srcset values stranded during the move off the
   * hostingersite.com staging address.
   *
   * This was previously hotlinked from i.postimg.cc — a free image host with
   * no uptime commitment, serving the single largest visual on the homepage of
   * a live store. Now served from the media library. */
  background-image: url('/wp-content/uploads/2026/08/BPC10-hero.jpeg');
  background-size: cover;
  /* The vial sits just right of centre and the microscope right of that, so
     anchoring right keeps both in frame as the viewport narrows. The scrim on
     ::before darkens the left, where the headline sits — which is also the
     emptiest part of the photo. Same composition as the 5 MG image this
     replaced, so neither value needed changing. */
  background-position: right center;
  /* One pass, alternating, so it never snaps back to the start. Lower this
     number to speed the drift up, raise it to slow down. Below ~12s it stops
     reading as atmosphere and starts drawing attention to itself. */
  animation: apex-hero-drift 20s ease-in-out infinite alternate;
  will-change: transform;
}

/* Scale stays above 1 and the translate stays well inside the resulting
   overhang, so no edge of the photo is ever exposed. At scale 1.04 there is
   2% of bleed per side; the pan only travels 1%. */
@keyframes apex-hero-drift {
  from { transform: scale(1.04) translate3d(0, 0, 0); }
  to   { transform: scale(1.10) translate3d(-1%, -0.6%, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .apex .hero::after {
    animation: none;
    transform: scale(1.04);
  }
}

.apex .hero::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg,
    rgba(0,0,0,.55) 0%,
    rgba(0,0,0,.25) 60%,
    rgba(0,0,0,.05) 100%);
  z-index: 1;
}

.apex .hero-inner {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  width: 100%;
  padding-top: calc(var(--apex-header-h) + 20px);
  padding-bottom: 100px;
  /* No side gutter on desktop — the hero copy aligns flush to the container
     edge, level with the section headings below it.

     This used to happen by accident: the old `padding` shorthand zeroed the
     sides as a side effect of setting the vertical values, and beat
     .container only because it sat later in the file. Declared explicitly now
     so it survives any reordering. The mobile block puts the gutter back. */
  padding-inline: 0;
}

.apex .hero-content { max-width: 620px; }

.apex .hero-badge {
  display: inline-block;
  background: rgba(232, 96, 28, .38);
  color: var(--apex-orange);
  border: 1px solid rgba(232, 96, 28, .55);
  padding: 6px 14px;
  border-radius: 100px;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1px;
  margin-bottom: 24px;
}

.apex .hero h1 {
  font-size: clamp(40px, 5vw, 68px);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -1.5px;
  color: #fff;
  margin-bottom: 20px;
}

.apex .hero p {
  font-size: 18px;
  line-height: 1.7;
  color: rgba(255,255,255,.82);
  max-width: 500px;
  margin-bottom: 36px;
}

.apex .hero-actions {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
}


/* ── 7. FEATURES STRIP ───────────────────────────────────────────────────── */

.apex .features {
  background: var(--apex-bg-light);
  border-top: 1px solid var(--apex-border);
  border-bottom: 1px solid var(--apex-border);
  padding: 40px 0;
}

.apex .features-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 24px;
}

.apex .feature-item {
  display: flex;
  align-items: center;
  gap: 16px;
}

.apex .feature-icon {
  width: 48px;
  height: 48px;
  flex-shrink: 0;
  background: #fff;
  border: 1px solid rgba(232,96,28,.25);
  border-radius: 50%;
  color: var(--apex-orange);
  display: flex;
  align-items: center;
  justify-content: center;
}
.apex .feature-icon svg { width: 20px; height: 20px; }

.apex .feature-text h4 {
  font-size: 15px;
  font-weight: 700;
  margin-bottom: 4px;
}
.apex .feature-text p {
  font-size: 13px;
  line-height: 1.5;
  color: var(--apex-text-gray);
}


/* ── 8. SECTION HEADINGS ─────────────────────────────────────────────────── */

.apex .section-eyebrow {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1.5px;
  color: var(--apex-text-gray);
  margin-bottom: 14px;
}
.apex .section-eyebrow::before {
  content: '';
  width: 30px;
  height: 2px;
  background: var(--apex-orange);
  flex-shrink: 0;
}

.apex .section-title {
  font-size: clamp(28px, 4vw, 42px);
  font-weight: 700;
  letter-spacing: -.5px;
  line-height: 1.15;
  margin-bottom: 48px;
}

.apex .section-header { margin-bottom: 48px; }
.apex .section-header h2 {
  font-size: clamp(28px, 4vw, 42px);
  font-weight: 700;
  letter-spacing: -.5px;
  line-height: 1.15;
}

/* .apex-legal is listed here too. Those pages are plain post content rather
   than shortcode output, so they carry no .apex wrapper — without this the
   gradient span in their headings would render as flat body text. */
.apex .gradient-text,
.apex-legal .gradient-text {
  background: var(--apex-orange-grad);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: var(--apex-orange); /* fallback if background-clip is unsupported */
}


/* ── 9. PRODUCTS GRID ────────────────────────────────────────────────────── */

/* Markup for these cards is emitted by the [apex_products] shortcode, so
   prices and stock stay live. Keep the class names in sync with that PHP. */

.apex .products-section {
  padding: 80px 0;
  background: var(--apex-bg-white);
}

.apex .products-grid {
  display: grid;
  /* Column count comes from --apex-cols so [apex_products columns="3"] can set
     it inline. Do NOT let the shortcode write grid-template-columns directly:
     an inline style beats every media query below, so the grid would keep its
     desktop column count on a phone. A custom property has no such problem —
     the breakpoints override the whole declaration and win normally. */
  grid-template-columns: repeat(var(--apex-cols, 4), 1fr);
  gap: 24px;
}

.apex .product-card {
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-md);
  padding: 20px;
  transition: all .2s ease;
}
.apex .product-card:hover {
  border-color: var(--apex-orange);
  box-shadow: var(--apex-shadow-lg);
  transform: translateY(-3px);
}

.apex .product-image {
  position: relative;
  overflow: hidden;
  aspect-ratio: 1 / 1;
  background: var(--apex-bg-light);
  border-radius: var(--apex-radius-sm);
  margin-bottom: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.apex .product-image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform .3s ease;
}
.apex .product-card:hover .product-image img { transform: scale(1.05); }

.apex .product-badge {
  position: absolute;
  top: 12px;
  left: 12px;
  z-index: 1;
  background: #fff;
  color: var(--apex-text-dark);
  padding: 4px 10px;
  border-radius: 100px;
  font-size: 11px;
  font-weight: 600;
  box-shadow: var(--apex-shadow-sm);
}
/* Use these instead of inline style="color:#10b981" in the PHP. */
.apex .product-badge--stock { color: var(--apex-green); }
.apex .product-badge--out   { color: var(--apex-text-muted); }

/* Coming soon — solid rather than a coloured word on white, so it reads as a
   state rather than as another stock label. Used by the homepage grid; the
   catalog cards use the full-image overlay in section 19 instead. */
.apex .product-badge--soon {
  background: var(--apex-text-dark);
  color: #fff;
}

.apex .product-title {
  font-size: 18px;
  font-weight: 600;
  margin-bottom: 8px;
}

/* The shortcode wraps the title in a link to the product page. There is no
   blanket `.apex a` reset in this file, so without this the title picks up
   Blocksy's link colour and underline. Scoped to the title only — the rest of
   the file assumes links elsewhere are styled deliberately. */
.apex .product-title a {
  color: inherit;
  text-decoration: none;
}
.apex .product-card:hover .product-title a { color: var(--apex-orange); }

.apex .product-meta {
  font-size: 13px;
  color: var(--apex-text-gray);
  margin-bottom: 20px;
}

.apex .product-bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: auto;
}

.apex .product-price {
  font-size: 18px;
  font-weight: 700;
  color: var(--apex-orange);
}
/* Woo wraps prices in <span class="woocommerce-Price-amount"> — inherit. */
.apex .product-price .woocommerce-Price-amount { color: inherit; font: inherit; }
.apex .product-price del { opacity: .5; font-weight: 500; margin-right: 6px; }

.apex .add-to-cart {
  width: 36px;
  height: 36px;
  flex-shrink: 0;
  border-radius: var(--apex-radius-sm);
  background: var(--apex-bg-light);
  border: 1px solid var(--apex-border);
  color: var(--apex-text-dark);
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
}
.apex .product-card:hover .add-to-cart {
  background: var(--apex-orange);
  border-color: var(--apex-orange);
  color: #fff;
}
/* Woo's ajax states */
.apex .add-to-cart.loading { opacity: .6; pointer-events: none; }
.apex .add-to-cart.added::after { content: '✓'; }


/* ── 10. TRANSPARENCY / COA MOCKUP ───────────────────────────────────────── */

.apex .transparency {
  background: var(--apex-bg-light);
  border-top: 1px solid var(--apex-border);
  border-bottom: 1px solid var(--apex-border);
  padding: 80px 0;
}

.apex .transparency-inner {
  display: flex;
  align-items: center;
  gap: 80px;
}

.apex .coa-mockup {
  flex: 1;
  background: #fff;
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-lg);
  padding: 40px;
  box-shadow: var(--apex-shadow-lg);
}

.apex .coa-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  border-bottom: 2px solid var(--apex-border);
  padding-bottom: 16px;
  margin-bottom: 24px;
}
.apex .coa-title { font-size: 16px; font-weight: 700; }
.apex .coa-status {
  font-size: 14px;
  font-weight: 600;
  color: var(--apex-green);
  background: var(--apex-green-bg);
  padding: 4px 12px;
  border-radius: 100px;
  white-space: nowrap;
}

.apex .coa-data p {
  font-size: 14px;
  color: var(--apex-text-gray);
  margin-bottom: 12px;
}
.apex .coa-data strong {
  display: inline-block;
  width: 140px;
  color: var(--apex-text-dark);
}

.apex .qr-box {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-top: 30px;
  padding: 16px;
  background: var(--apex-orange-fade);
  border: 1px dashed var(--apex-orange);
  border-radius: var(--apex-radius-sm);
}

/* The box is an <a> wrapping the whole thing, so the QR and the copy are one
   target. Dashed border goes solid on hover — a small signal that this is
   clickable, since a QR normally isn't. */
.apex a.qr-box {
  transition: background .2s ease, border-color .2s ease, border-style .2s ease;
}
.apex a.qr-box:hover {
  background: #fbe7db;
  border-style: solid;
}
.apex a.qr-box:hover .qr-text strong {
  text-decoration: underline;
}
/* QR thumbnail.

   The padding is not decoration — it synthesises the "quiet zone" the QR
   spec requires (4 modules of white around the code). The supplied file has
   almost none, and scanners fail without it.

   object-fit: contain because the source is 118x113, not square — stretching
   a QR distorts the modules and can stop it scanning altogether. */
.apex .qr-icon {
  flex-shrink: 0;
  width: 76px;
  height: 76px;
  padding: 6px;
  background: #fff;
  border-radius: var(--apex-radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
}
.apex .qr-icon img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}
.apex .qr-text strong { display: block; font-size: 14px; color: var(--apex-orange); }
.apex .qr-text span  { font-size: 12px; color: var(--apex-text-gray); }

.apex .transparency-text { flex: 1; }
.apex .transparency-text > p {
  font-size: 16px;
  line-height: 1.7;
  color: var(--apex-text-gray);
  margin-bottom: 32px;
}

.apex .list-item {
  display: flex;
  gap: 16px;
  margin-bottom: 24px;
}
.apex .list-num {
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--apex-orange-grad);
  color: #fff;
  font-size: 14px;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
}
.apex .list-text h4 { font-size: 16px; font-weight: 600; margin-bottom: 4px; }
.apex .list-text p  { font-size: 14px; line-height: 1.6; color: var(--apex-text-gray); }


/* ── 11. WHY CHOOSE APEX ─────────────────────────────────────────────────── */

.apex .why-choose-us {
  background: var(--apex-bg-light);
  border-top: 1px solid var(--apex-border);
  border-bottom: 1px solid var(--apex-border);
  padding: 100px 0;
}

.apex .why-choose-inner {
  display: grid;
  grid-template-columns: 1fr 1.3fr;
  gap: 60px;
  align-items: stretch;
}

.apex .why-left {
  display: flex;
  flex-direction: column;
}
.apex .why-left h2 {
  font-size: clamp(32px, 4vw, 46px);
  font-weight: 700;
  letter-spacing: -1px;
  line-height: 1.1;
  margin-bottom: 16px;
}
/* :not(.section-eyebrow) is load-bearing.
 *
 * The eyebrow is also a <p> directly inside .why-left, so a bare
 * `.apex .why-left > p` matched it too — and at (0,2,1) it outranks
 * `.apex .section-eyebrow` at (0,2,0), which pushed "APEX STANDARD" to 16px
 * with a 32px margin while every other eyebrow on the page stayed at 11px.
 * This rule is for the intro paragraph only; excluding the eyebrow lets it
 * fall back to the shared style in section 8. */
.apex .why-left > p:not(.section-eyebrow) {
  font-size: 16px;
  line-height: 1.6;
  color: var(--apex-text-gray);
  margin-bottom: 32px;
}
.apex .why-left > p:not(.section-eyebrow) strong { color: var(--apex-text-dark); font-weight: 600; }

.apex .why-image-wrapper {
  position: relative;
  flex: 1 1 0%;
  min-height: 300px;
  width: 93%; /* intentional inset — widen to 100% if you want a flush edge */
  background: #fff;
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-lg);
  box-shadow: var(--apex-shadow-sm);
  overflow: hidden;
}
.apex .why-image-wrapper img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: left;
}

.apex .why-right {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px;
  height: 100%;
}

.apex .why-card {
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-md);
  padding: 32px;
  box-shadow: var(--apex-shadow-sm);
  transition: box-shadow .2s ease;
}
.apex .why-card:hover { box-shadow: var(--apex-shadow-md); }
.apex .why-card h4 {
  font-size: 18px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--apex-orange);
  margin-bottom: 12px;
}
.apex .why-card p {
  font-size: 14px;
  line-height: 1.6;
  color: var(--apex-text-gray);
}
.apex .why-card p strong { color: var(--apex-text-dark); font-weight: 600; }


/* ── 12. ACADEMIC DISCOUNT ───────────────────────────────────────────────── */

.apex .academic-split { padding: 100px 0; background: var(--apex-bg-white); }

.apex .academic-split-inner {
  display: flex;
  align-items: center;
  gap: 60px;
}

.apex .academic-image { flex: 1; }
.apex .academic-image img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: 24px;
  box-shadow: var(--apex-shadow-sm);
}

/* Left-aligned by default — this is what the inline styles on the old markup
   were forcing. Add .academic-content--center for the centred variant. */
.apex .academic-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
}
.apex .academic-content--center {
  align-items: center;
  text-align: center;
}

.apex .academic-content h2 {
  font-size: clamp(32px, 4vw, 46px);
  font-weight: 700;
  letter-spacing: -.5px;
  line-height: 1.15;
  margin-bottom: 16px;
}
.apex .academic-content > p {
  font-size: 16px;
  line-height: 1.6;
  color: var(--apex-text-gray);
  max-width: 480px;
  margin-bottom: 32px;
}

.apex .acad-badge {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--apex-orange-fade);
  color: var(--apex-orange);
  border: 1px solid rgba(232,96,28,.15);
  padding: 6px 18px;
  border-radius: 100px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .5px;
  margin-bottom: 24px;
}

.apex .acad-stats {
  display: flex;
  width: 100%;
  background: #fff;
  border: 1px solid var(--apex-border);
  border-radius: 16px;
  padding: 28px 0;
  margin-bottom: 16px;
}
.apex .acad-stat-item {
  flex: 1;
  text-align: center;
  border-right: 1px solid var(--apex-border);
}
.apex .acad-stat-item:last-child { border-right: none; }
.apex .acad-stat-num {
  font-size: 32px;
  font-weight: 700;
  line-height: 1;
  color: var(--apex-orange);
  margin-bottom: 4px;
}
.apex .acad-stat-label { font-size: 13px; color: var(--apex-text-gray); }

.apex .acad-steps {
  display: flex;
  gap: 12px;
  width: 100%;
  margin-bottom: 32px;
}
.apex .acad-step {
  flex: 1;
  background: #fdfdfd;
  border: 1px solid var(--apex-border);
  border-radius: 12px;
  padding: 20px 12px;
}
.apex .acad-step-num {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .5px;
  color: var(--apex-orange);
  margin-bottom: 8px;
}
.apex .acad-step-text { font-size: 13px; line-height: 1.4; color: var(--apex-text-gray); }

.apex .acad-footnote {
  font-size: 12px;
  color: var(--apex-text-muted);
  margin-top: 16px;
}


/* ── 13. COA DATABASE + BATCH TABLES ─────────────────────────────────────── */

/* NOT scoped to .apex — this markup is emitted by apex_render_batch_table()
   into the WooCommerce product tab, which lives outside your wrapper. */

.apex-coa-table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--apex-font);
  font-size: 14px;
  margin-bottom: 32px;
}
.apex-coa-table th {
  text-align: left;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--apex-text-gray);
  background: var(--apex-bg-light);
  padding: 12px 16px;
  border-bottom: 2px solid var(--apex-border);
  white-space: nowrap;
}
.apex-coa-table td {
  padding: 14px 16px;
  border-bottom: 1px solid var(--apex-border);
  color: var(--apex-text-gray);
}
.apex-coa-table td:first-child {
  font-weight: 600;
  color: var(--apex-text-dark);
  font-variant-numeric: tabular-nums;
}
.apex-coa-table tbody tr:hover { background: var(--apex-orange-fade); }
.apex-coa-table a {
  color: var(--apex-orange);
  font-weight: 600;
  text-decoration: none;
}
.apex-coa-table a:hover { text-decoration: underline; }

/* Tables must scroll inside themselves on mobile, never the page. */
.apex-coa-group,
.woocommerce-Tabs-panel--apex_coa { overflow-x: auto; }

.apex-coa-filter {
  width: 100%;
  max-width: 420px;
  font-family: var(--apex-font);
  font-size: 15px;
  padding: 14px 18px;
  border: 1px solid var(--apex-border);
  border-radius: 100px;
  margin-bottom: 32px;
  background: #fff;
}
.apex-coa-filter:focus {
  outline: none;
  border-color: var(--apex-orange);
  box-shadow: 0 0 0 3px rgba(232,96,28,.12);
}

.apex-coa-group { margin-bottom: 40px; }
.apex-coa-group h3 {
  font-family: var(--apex-font);
  font-size: 20px;
  font-weight: 700;
  color: var(--apex-text-dark);
  margin-bottom: 16px;
}
.apex-coa-group[hidden] { display: none; }


/* ── 14. WOOCOMMERCE BRAND BRIDGE ────────────────────────────────────────── */

/* Pulls Woo's default buttons and sale badges onto the Apex palette so the
   shop, cart, and checkout don't look like a different website. Deliberately
   minimal — let Blocksy handle layout, only override colour here. */

/* Blocksy's button colours, set at the source.
 *
 * The .woocommerce-scoped rules below only reach markup inside a .woocommerce
 * wrapper. The header cart dropdown is NOT inside one — its "Return to shop"
 * button sits in .ct-cart-content in the header — so it stayed Blocksy blue
 * (#2872fa) while everything around it was orange. That dropdown opens on
 * cart hover, which is why it looked like the cart button had a blue hover.
 *
 * Rather than add another scoped selector for that one button, set the two
 * variables every Blocksy button reads. This also covers the buttons on cart,
 * checkout and my-account, which are still unstyled.
 *
 * apex.css loads after Blocksy's stylesheets, so an equal-specificity :root
 * here wins on source order — no !important needed.
 *
 * Broader option, not taken: setting --theme-palette-color-1 and -2 directly
 * would fix these AND content links, focus rings and every other stock-blue
 * element in one go. That is a bigger blast radius than a cart fix warrants,
 * so it belongs in Customizer > Colors as a deliberate decision. */
:root {
  --theme-button-background-initial-color: var(--apex-orange);
  --theme-button-background-hover-color:   var(--apex-orange-hover);
  --theme-button-text-initial-color:       #ffffff;
  --theme-button-text-hover-color:         #ffffff;
}

.woocommerce .button,
.woocommerce button.button,
.woocommerce input.button,
.woocommerce a.button,
.woocommerce #respond input#submit,
.woocommerce .button.alt {
  background: var(--apex-orange);
  color: #fff;
  border-radius: 100px;
  font-family: var(--apex-font);
  font-weight: 600;
}
.woocommerce .button:hover,
.woocommerce button.button:hover,
.woocommerce input.button:hover,
.woocommerce a.button:hover,
.woocommerce #respond input#submit:hover,
.woocommerce .button.alt:hover {
  background: var(--apex-orange-hover);
  color: #fff;
}

.woocommerce span.onsale {
  background: var(--apex-orange);
  color: #fff;
}

.woocommerce div.product p.price,
.woocommerce div.product span.price {
  color: var(--apex-orange);
  font-weight: 700;
}


/* ── 15. BLOCKSY HEADER ──────────────────────────────────────────────────── */

/* Makes Blocksy's header match the original hand-built one. Not scoped to
   .apex — the header is theme markup and sits outside your wrapper.

   Selectors are taken from the live site: the header is
   <header id="header" class="ct-header" data-id="type-1"> with rows marked
   [data-row="middle"] and elements marked [data-id="logo|menu|cart"].

   Colours deliberately use currentColor so they follow whatever Blocksy sets
   for the transparent vs scrolled state — set those in the Customizer, not
   here, and this styling works in both without duplication. */

/* Cart as a pill-outline button, matching the old .header-cart */
.ct-header [data-id="cart"] .ct-cart-item {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 24px;
  border: 1.5px solid color-mix(in srgb, currentColor 55%, transparent);
  border-radius: 100px;
  font-family: var(--apex-font);
  font-size: 14px;
  font-weight: 600;
  line-height: 1;
  transition: background .3s ease, border-color .3s ease;
}
.ct-header [data-id="cart"] .ct-cart-item:hover {
  background: color-mix(in srgb, currentColor 15%, transparent);
  border-color: currentColor;
}

/* Cart icon hover.
 *
 * Blocksy's rule is:
 *   .ct-cart-item:hover svg { fill: var(--theme-icon-hover-color,
 *                                       var(--theme-palette-color-2)); }
 *
 * --theme-icon-hover-color ships EMPTY, so it falls through to palette
 * colour 2 — the stock blue. Setting the variable is what fixes it; setting
 * `fill` on the svg would work too but has to beat Blocksy's own selector,
 * and this way the fallback simply never fires. */
.ct-header [data-id="cart"] .ct-cart-item {
  --theme-icon-hover-color: var(--apex-orange);
}

/* Cart count bubble.
 *
 * Blocksy paints this with palette colour 1 (#2872fa) from generated CSS
 * rather than a stylesheet rule, so there is nothing to override by
 * specificity — declare the colours outright. White text is kept explicitly:
 * orange at this size needs the contrast, and inheriting would give it the
 * dark header text colour. */
.ct-header [data-id="cart"] .ct-dynamic-count-cart {
  background-color: var(--apex-orange);
  color: #fff;
}


/* ── Mini-cart dropdown ─────────────────────────────────────────────────── */

/* Blocksy ships this panel dark (#29333c) with white text, which is the one
   dark surface on an otherwise white site. Rebuilt as a light card matching
   the product cards: white, 1px border, 12px radius, large shadow.

   Structure Blocksy/Woo emit, for reference when editing:
     .ct-cart-content
       ul.woocommerce-mini-cart
         li.mini_cart_item   > a.remove, a.ct-media-container > img, .product-data
       p.woocommerce-mini-cart__total
       p.woocommerce-mini-cart__buttons > a.button, a.button.checkout   */

.ct-header .ct-cart-content {
  width: 380px;
  padding: 0;
  background: #fff;
  color: var(--apex-text-dark);
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-md);
  box-shadow: var(--apex-shadow-lg);
  overflow: hidden;
}

.ct-cart-content .woocommerce-mini-cart {
  margin: 0;
  padding: 4px 20px;
  list-style: none;
  /* Cap the list, not the panel — the subtotal and buttons must stay visible
     with a full cart rather than being scrolled out of reach. */
  max-height: 320px;
  overflow-y: auto;
}

/* Explicit column placement: Woo emits the remove link FIRST in the source,
   before the image, so relying on source order would put the bin icon in the
   image slot. */
.ct-cart-content .mini_cart_item {
  display: grid;
  grid-template-columns: 56px 1fr auto;
  gap: 14px;
  align-items: center;
  margin: 0;
  padding: 16px 0;
  border-bottom: 1px solid var(--apex-border);
}
.ct-cart-content .mini_cart_item:last-child { border-bottom: 0; }
.ct-cart-content .mini_cart_item .ct-media-container { grid-column: 1; display: block; }
.ct-cart-content .mini_cart_item .product-data { grid-column: 2; min-width: 0; }
.ct-cart-content .mini_cart_item .remove { grid-column: 3; }

.ct-cart-content .mini_cart_item img {
  display: block;
  width: 56px;
  height: 56px;
  border-radius: var(--apex-radius-sm);
  background: var(--apex-bg-light);
  object-fit: cover;
}

.ct-cart-content .product-title {
  display: block;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.35;
  color: var(--apex-text-dark);
  margin-bottom: 2px;
}
.ct-cart-content .product-title:hover { color: var(--apex-orange); }

.ct-cart-content .quantity,
.ct-cart-content .quantity .amount {
  font-size: 13px;
  color: var(--apex-text-gray);
}


/* ── Mini-cart quantity stepper ─────────────────────────────────────────── */

/* Markup from the woocommerce_widget_cart_item_quantity filter in the
   apex-products snippet. */

.ct-cart-content .apex-mini-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 8px;
}

.ct-cart-content .apex-mini-qty {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  border: 1px solid var(--apex-border);
  border-radius: 100px;
  overflow: hidden;
  background: #fff;
}

.ct-cart-content .apex-qty-btn {
  width: 26px;
  height: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: none;
  font-family: inherit;
  font-size: 15px;
  line-height: 1;
  color: var(--apex-text-gray);
  cursor: pointer;
  transition: background .15s ease, color .15s ease;
}
.ct-cart-content .apex-qty-btn:hover:not(:disabled) {
  background: var(--apex-orange-fade);
  color: var(--apex-orange);
}
/* Disabled at qty 1 — removal belongs to the bin, so stepping to zero would
   make an item vanish on a click that meant "one fewer". */
.ct-cart-content .apex-qty-btn:disabled {
  opacity: .35;
  cursor: default;
}

.ct-cart-content .apex-qty-val {
  min-width: 24px;
  text-align: center;
  font-size: 13px;
  font-weight: 600;
  color: var(--apex-text-dark);
  font-variant-numeric: tabular-nums;
}

/* Dimmed and click-blocked while the request is in flight, so a double-click
   cannot queue two updates against the same line. */
.ct-cart-content .apex-mini-qty.is-busy {
  opacity: .55;
  pointer-events: none;
}

.ct-cart-content .apex-mini-line {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 14px;
  font-weight: 700;
  color: var(--apex-text-dark);
  white-space: nowrap;
}
.ct-cart-content .apex-mini-line .amount { color: inherit; font: inherit; }

.ct-cart-content .apex-mini-save {
  padding: 2px 7px;
  border-radius: 100px;
  background: #d1fae5;
  color: #065f46;
  font-size: 11px;
  font-weight: 700;
}

/* position: static is the fix, not a margin.
 *
 * Blocksy sets .remove to position:absolute with right:-8px, deliberately
 * hanging it outside the row — which put it 7px past the panel's own border.
 * Because it was absolutely positioned, the grid-column above had no effect
 * on it. Returning it to normal flow lets the grid place it in column 3, and
 * the list's 20px side padding then gives it a proper margin from the edge. */
.ct-cart-content .mini_cart_item .remove {
  position: static;
  grid-column: 3;
}

.ct-cart-content .remove {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: transparent;
  color: var(--apex-text-muted);
}
.ct-cart-content .remove:hover {
  background: var(--apex-orange-fade);
  color: var(--apex-orange);
}
/* fill, not color — the bin is an inline SVG path. */
.ct-cart-content .remove svg { fill: currentColor; }

.ct-cart-content .woocommerce-mini-cart__total {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: 0;
  padding: 14px 20px;
  border-top: 1px solid var(--apex-border);
  background: var(--apex-bg-light);
}
.ct-cart-content .woocommerce-mini-cart__total strong {
  font-size: 13px;
  font-weight: 600;
  color: var(--apex-text-gray);
}
.ct-cart-content .woocommerce-mini-cart__total .amount {
  font-size: 18px;
  font-weight: 700;
  color: var(--apex-text-dark);
}

.ct-cart-content .woocommerce-mini-cart__buttons {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin: 0;
  padding: 14px 20px 18px;
}
.ct-cart-content .woocommerce-mini-cart__buttons .button {
  margin: 0;
  padding: 11px 10px;
  border-radius: var(--apex-radius-sm);
  font-size: 13px;
  font-weight: 600;
  text-align: center;
  text-transform: none;
}

/* Deliberate hierarchy: both buttons were solid orange, which gives the eye
   nowhere to land. View cart is the secondary action and goes outline;
   Checkout keeps the fill. */
.ct-cart-content .woocommerce-mini-cart__buttons .button:not(.checkout) {
  background: #fff;
  color: var(--apex-text-dark);
  border: 1px solid var(--apex-border);
}
.ct-cart-content .woocommerce-mini-cart__buttons .button:not(.checkout):hover {
  background: #fff;
  border-color: var(--apex-orange);
  color: var(--apex-orange);
}
.ct-cart-content .woocommerce-mini-cart__buttons .button.checkout {
  background: var(--apex-orange);
  border: 1px solid var(--apex-orange);
  color: #fff;
}
.ct-cart-content .woocommerce-mini-cart__buttons .button.checkout:hover {
  background: var(--apex-orange-hover);
  border-color: var(--apex-orange-hover);
  color: #fff;
}

/* Empty state */
.ct-cart-content .cart-empty,
.ct-cart-content .wc-empty-cart-message {
  margin: 0;
  padding: 28px 20px;
  background: transparent;
  color: var(--apex-text-gray);
  font-size: 14px;
  text-align: center;
}
.ct-cart-content .return-to-shop { padding: 0 20px 20px; text-align: center; }

/* Nav spacing and weight, matching the old .nav-menu */
.ct-header [data-id="menu"] > ul.menu {
  gap: 32px;
}
.ct-header [data-id="menu"] .ct-menu-link {
  font-family: var(--apex-font);
  font-size: 15px;
  font-weight: 500;
}

/* Hover and current-page colour.
 *
 * Blocksy ships #1559ed (its palette colour 2) for both, which is the stock
 * blue and reads as un-themed next to the orange everywhere else. Hover and
 * "you are here" both resolve from --theme-link-hover-color, so rebinding
 * that one variable fixes both at once.
 *
 * Rebinding the variable rather than setting `color` on the links matters:
 * Blocksy applies it through several selectors (menu link, current item,
 * ancestor-of-current, submenu), and overriding each one by hand is how you
 * end up chasing a state that was missed. Change the source, not the leaves.
 *
 * Equivalent Customizer route, if you would rather keep it out of CSS:
 * Customizer > Header > Menu > Font Colour > Hover/Active. */
.ct-header [data-id="menu"] {
  --theme-link-hover-color: var(--apex-orange);
}

/* The transparent state keeps its own initial colour (white over the hero),
   but the hover still wants to be orange — set here too because the
   transparent-row block below rebinds these variables wholesale. */
.ct-header [data-transparent-row="yes"] [data-id="menu"] {
  --theme-link-hover-color: var(--apex-orange);
}

/* Logo sizing: nothing here on purpose.

   Set it in Customizer > Header > Logo > Logo Height. Blocksy applies that
   height to the <img> itself, so any height/max-height declared here fights
   it. Two ways that has already gone wrong on this site:

     max-height: 100px  ->  silently capped the Customizer value, so raising
                            the slider appeared to do nothing
     height: auto       ->  cancelled the Customizer value entirely, so the
                            logo rendered at its natural 432px

   If the logo ever needs constraining, do it in the Customizer, not here. */

/* Ticker row: match .ticker exactly. Set the orange background in the
   Customizer (Header > Top Row > background) — this only fixes the type. */
.ct-header [data-row="top"] {
  font-family: var(--apex-font);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: .01em;
}


/* ── Mobile offcanvas menu ───────────────────────────────────────────────

   The slide-in panel behind the hamburger. Its nav is [data-id="mobile-menu"],
   NOT [data-id="menu"] — which is why the desktop nav rules above never
   reached it and it was rendering in the system font.

   Setting Blocksy's variables on #offcanvas rather than styling each element,
   for the same reason as the header: they cascade to the links, icons, and
   submenu toggles from one place.

   This does not fix the menu CONTENTS. If Cart, Checkout, My account, or Home
   appear here, no menu is assigned to the mobile location and Blocksy is
   auto-listing every page — fix that in Customizer > Menus, not with CSS. */

#offcanvas {
  --theme-font-family: var(--apex-font);
  --theme-link-initial-color: #ffffff;
  --theme-link-hover-color: var(--apex-orange);
  --theme-icon-color: #ffffff;
}

#offcanvas .ct-panel-inner {
  background: rgba(17, 24, 39, .98);
}

#offcanvas [data-id="mobile-menu"] .ct-menu-link {
  font-family: var(--apex-font);
  font-size: 17px;
  font-weight: 500;
  letter-spacing: -.01em;
  /* Generous vertical padding so each row clears the 44px minimum tap target
     — Blocksy's default 5px is fine with a mouse and cramped with a thumb. */
  padding: 17px 0;
  border-bottom: 1px solid rgba(255, 255, 255, .08);
  transition: color .2s ease;
}

#offcanvas [data-id="mobile-menu"] > ul > li:last-child .ct-menu-link {
  border-bottom: none;
}

#offcanvas .ct-toggle-close {
  color: #ffffff;
  opacity: .7;
  transition: opacity .2s ease;
}
#offcanvas .ct-toggle-close:hover {
  opacity: 1;
}

/* Note: the cart lives in the mobile header row, not in this panel.

   If you ever move it into the offcanvas drawer, it will need widening — the
   panel carries class="ct-panel ct-header", so the compact pill written for
   the desktop header matches in here too and looks cramped at full width. */


/* Hide Blocksy's page-title block on the homepage.

   Blocksy renders  div.hero-section > article > header.entry-header >
   h1.page-title  above the content, which printed a 45px "Home" heading
   and pushed the hero down. The tidier fix is Blocksy's own Page Title
   setting (editor > Meta Boxes panel, or Customizer > Page Title); this
   rule guarantees the result either way and is harmless if both are used.

   Scoped so ordinary inner pages keep their titles — only pages that build
   their own heading are covered:

     body.home              the homepage hero supplies the H1
     :has(.apex-catalog)    the catalog shortcode supplies "Apex Pure Peptides"
     body.single-product    the product template supplies the product name

   :has() is used for the catalog rather than a page-id class so the rule
   follows the shortcode. Move it to another page, or rebuild the Catalog page
   and get a new ID, and this keeps working with nothing to update.

   On single products this also removes the .hero-section that held Blocksy's
   duplicate breadcrumb — the .ct-breadcrumbs rule in section 18 becomes
   redundant, but is left in place as belt and braces.

   The tidier fix per page is Blocksy's own Page Title setting (editor > Meta
   Boxes panel, or Customizer > Page Title). This rule guarantees the result
   either way and is harmless if both are used. */
body.home .hero-section,
body.single-product .hero-section,
body.woocommerce-cart .hero-section,
body.woocommerce-checkout .hero-section,
body:has(.apex-catalog) .hero-section,
body:has(.apex-coa-db) .hero-section,
body:has(.apex-tracking) .hero-section,
body:has(.apex-legal) .hero-section {
  display: none;
}


/* Transparent header: positioning is Blocksy's, colour is ours.

   Blocksy's transparent header lifts the desktop wrapper out of flow
   (data-transparent on [data-device="desktop"]) but it does NOT recolour the
   type — the menu keeps Blocksy's default #3A4F66, which is invisible-ish
   against the hero photo. That has to be set here.

   Done by overriding Blocksy's own custom properties rather than setting
   `color` directly. Two reasons:

     - the variables cascade, so the menu, cart, and icons all follow from one
       declaration instead of a list of selectors that has to be kept in sync
     - it is scoped to [data-transparent-row="yes"], so any row that is NOT
       transparent — inner pages, and the scrolled state once that exists —
       keeps Blocksy's configured colours automatically

   Do not add `color: #fff` rules alongside these. The variables are what
   Blocksy reads; a hardcoded colour would win over them and break the
   non-transparent states. */

.ct-header [data-transparent-row="yes"] {
  --theme-link-initial-color: #ffffff;
  --theme-link-hover-color: rgba(255, 255, 255, .75);
  --theme-text-color: #ffffff;
  --theme-icon-color: #ffffff;
}


/* Sticky header — the scrolled state.

   apex.js adds `apex-stuck` to <body> once the hero has scrolled out of view,
   and swaps the white wordmark for the dark one. Everything below is purely
   how that state looks; the script holds no styling of its own.

   Reproduces the original: header scrolls away with the page, then drops back
   in as a solid white bar. Because these rules also override the transparent
   colour variables above, the menu and cart flip to dark type automatically —
   that is the payoff for setting Blocksy's variables rather than hardcoding
   `color` earlier. */

/* Scroll sentinel created by apex.js.

   Absolutely positioned and hidden, so it occupies no layout. Its height
   matches the header's, and the header sticks at the moment it leaves the
   viewport. Do not give this a background, border, or margin. */
.apex-scroll-sentinel {
  position: absolute;
  top: 0;
  left: 0;
  width: 1px;
  pointer-events: none;
  visibility: hidden;
}

/* Division of labour — read this before changing anything here.

   BLOCKSY owns the sticky behaviour: it wraps the row in
   .ct-sticky-container > [data-sticky="fixed:shrink"], fixes it on scroll,
   reserves a placeholder so content doesn't jump, and swaps the white
   wordmark for the dark one. Keep Sticky switched ON in the Customizer —
   turning it off is what stops the logo switching.

   THIS FILE owns only the text colour. That is necessary because Blocksy
   leaves data-transparent-row="yes" on the row even while it is stuck, so
   the white type set above would otherwise stay white on the white bar and
   the menu would vanish.

   apex.js exists purely to add `apex-stuck` to <body> at the right scroll
   point. It sets no styling and does not position anything.

   Do NOT add position/background/box-shadow rules here. Blocksy is already
   positioning this element; a second fixed element on top of it is where
   doubled headers and scroll jitter come from. Style the stuck bar's
   background in Customizer > Header > Middle Row > Sticky. */

body.apex-stuck .ct-header [data-transparent-row="yes"] {
  --theme-link-initial-color: var(--apex-text-gray);
  --theme-link-hover-color: var(--apex-orange);
  --theme-text-color: var(--apex-text-dark);
  --theme-icon-color: var(--apex-text-dark);
}

/* The logo keeps its Customizer height when stuck — no rule needed.

   There was a `max-height` here that shrank it on scroll. Removed rather than
   set to 100px to match: hardcoding the Customizer value in a second place
   means the two silently drift apart the next time the slider moves. If you
   later want the bar to tighten on scroll, add the max-height back here and
   accept that it has to be kept in step by hand. */



/* Close the white gap above the hero.

   Blocksy pads .ct-container-full by 60px, which is correct for a normal page
   with a title, but wrong here: the transparent header floats over the hero,
   so the hero has to start at the very top of the page or a white band shows
   above it. The hero supplies its own top spacing via --apex-header-h, so no
   padding is needed from the theme. */
body.home .ct-container-full {
  padding-top: 0;
}


/* ── 16. LEGAL DISCLAIMER ────────────────────────────────────────────────── */

/* Drop this into a Blocksy footer HTML widget. Keep the Research Use Only
   wording consistent with the product pages. */

.apex .disclaimer {
  background: #1f2937;
  border-left: 4px solid var(--apex-orange);
  border-radius: var(--apex-radius-sm);
  padding: 20px;
  margin-bottom: 30px;
}
.apex .disclaimer p {
  font-size: 12px;
  line-height: 1.6;
  color: #9ca3af;
}
.apex .disclaimer strong { color: #fff; }


/* ── 16. RESPONSIVE ──────────────────────────────────────────────────────── */

@media (max-width: 1024px) {
  .apex .features-grid  { grid-template-columns: repeat(2, 1fr); }
  .apex .products-grid  { grid-template-columns: repeat(2, 1fr); }

  .apex .transparency-inner   { flex-direction: column; }
  .apex .academic-split-inner { flex-direction: column; }
  .apex .academic-image   { order: 2; width: 100%; }
  .apex .academic-content { order: 1; width: 100%; }

  .apex .why-choose-inner   { grid-template-columns: 1fr; gap: 40px; }
  .apex .why-image-wrapper  { width: 100%; min-height: 400px; }
}

@media (max-width: 768px) {
  /* Hero: image on top, copy anchored to the bottom over a vertical scrim. */
  .apex .hero {
    min-height: 85svh;
    align-items: flex-end;
  }
  /* Framing lives on the layer that carries the photo. */
  .apex .hero::after {
    background-position: center top;
  }
  .apex .hero::before {
    background: linear-gradient(180deg,
      rgba(0,0,0,0)   0%,
      rgba(0,0,0,.15) 40%,
      rgba(0,0,0,.65) 65%,
      rgba(0,0,0,.80) 100%);
  }
  /* The hero content is bottom-aligned, so this bottom padding is what lifts
     the headline and buttons up the image. Increase it to raise them further,
     decrease to drop them. Longhand, so it cannot wipe out .container's
     horizontal gutter. */
  .apex .hero-inner {
    /* Stretched to the full height of the hero so the badge below can be
       positioned against the TOP of the image. Content still sits at the
       bottom — that's this element's own align-items: flex-end. */
    align-self: stretch;
    align-items: flex-end;
    padding-top: 40px;
    padding-bottom: 45px;
    /* Gutter restored here — desktop sets padding-inline: 0, and without this
       the headline would run to the screen edge on a phone. */
    padding-inline: var(--apex-gutter);
  }
  .apex .hero-content { max-width: 100%; width: 100%; }
  .apex .hero h1      { font-size: 36px; letter-spacing: -1px; margin-bottom: 12px; }

  /* Hide the hero's body copy on mobile, but NOT the badge — which is also a
     <p>. Without :not(), this selector is 0,2,1 and beats the badge's own
     0,2,0 rule below regardless of source order, so the badge never shows. */
  .apex .hero p:not(.hero-badge) { display: none; }

  /* Badge pinned near the top of the hero rather than sitting above the
     headline, which is where it would land in normal flow with the content
     bottom-aligned. Measured from the top of the page — the transparent
     header floats over this, so 120px clears it. Change that one number to
     move it up or down. */
  .apex .hero p.hero-badge {
    display: inline-block;
    position: absolute;
    top: 170px;
    /* Centred horizontally. translateX(-50%) pulls it back by half its own
       width, which is what actually centres it — `left: 50%` alone would put
       its left EDGE at the midpoint. */
    left: 50%;
    transform: translateX(-50%);
    margin-bottom: 0;
    z-index: 3;

    font-size: 15px;
    line-height: 1.4;
    padding: 9px 20px;

    /* An explicit width, NOT max-width, and deliberately no text-wrap: balance.

       `balance` on an inline-block is free to shrink the box to find an even
       fit — it settled on 188px here and produced three lines, despite
       max-width being 270px. An explicit width takes that choice away.

       Measured at 15px / 700 / 1px tracking, border-box, 42px of padding
       and border:
         "Research Peptides"        164px  ->  box must be at least 206px
         "Premium Quality Research" 243px  ->  box must stay under    285px

         250px  ->  208px of text area
                    "Premium Quality"  (154px) fits, + "Research" (243px) does not
                    "Research Peptides" (164px) fits on line two

       Change the font size and all three figures move, so re-measure rather
       than nudging this by eye. If it ever lands wrong, the deterministic fix
       is a <br> in the markup, hidden on desktop. */
    width: 250px;
    max-width: calc(100% - (var(--apex-gutter) * 2));
    text-align: center;
  }

  /* Hero buttons sit side by side and share the row.
     The old file set .btn{width:100%} in a later media block, which silently
     overrode flex:1 and stacked them — fixed by scoping each rule. */
  .apex .hero-actions { flex-direction: row; gap: 12px; }
  .apex .hero-actions .btn { flex: 1; width: auto; padding: 14px 20px; font-size: 14px; }

  /* Everywhere else, buttons go full width. */
  .apex .btn:not(.hero-actions .btn) { width: 100%; }

  .apex .features-grid { grid-template-columns: repeat(2, 1fr); gap: 20px; }
  .apex .products-grid { grid-template-columns: repeat(2, 1fr); }

  .apex .coa-mockup      { padding: 24px; }
  .apex .coa-header      { flex-direction: column; align-items: flex-start; gap: 8px; }
  .apex .coa-data strong { display: block; width: auto; margin-bottom: 2px; }

  .apex .transparency,
  .apex .products-section { padding: 60px 0; }
  .apex .why-choose-us,
  .apex .academic-split   { padding: 60px 0; }

  .apex .why-right         { grid-template-columns: 1fr; }
  .apex .why-image-wrapper { min-height: 300px; }

  .apex .acad-steps { flex-direction: column; }
  .apex .acad-stats { flex-wrap: wrap; }

  /* Headings sat 48px clear of their own content, which on a narrow screen
     reads as drift rather than breathing room. */
  .apex .section-title,
  .apex .section-header { margin-bottom: 28px; }

  .apex .transparency-inner   { gap: 40px; }
  .apex .academic-split-inner { gap: 32px; }
  .apex .list-item            { margin-bottom: 18px; }
  .apex .why-card             { padding: 24px; }
}

/* Small phones.

   Products and features stay TWO across, as in the original design. An earlier
   version of this file dropped them to one column here, which made the
   products section 2171px tall on a 375px screen — four full-width cards you
   had to scroll past to reach anything else.

   Two-up at 375px leaves ~151px per card, so the card's internals are scaled
   down to suit rather than the grid being widened. */

@media (max-width: 480px) {
  .apex .products-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 14px;
  }
  .apex .features-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 16px 12px;
  }

  .apex .product-card   { padding: 12px; }
  .apex .product-image  { margin-bottom: 14px; }
  .apex .product-title  { font-size: 15px; margin-bottom: 4px; }
  .apex .product-meta   { font-size: 12px; margin-bottom: 14px; }
  .apex .product-price  { font-size: 16px; }
  .apex .add-to-cart    { width: 32px; height: 32px; }
  .apex .product-badge  { font-size: 10px; padding: 3px 8px; top: 8px; left: 8px; }

  .apex .feature-item    { gap: 12px; }
  .apex .feature-icon    { width: 40px; height: 40px; }
  .apex .feature-icon svg { width: 17px; height: 17px; }
  .apex .feature-text h4 { font-size: 13px; }
  .apex .feature-text p  { font-size: 12px; }

  /* Narrower gutter. Set on the variable so .container and the absolutely
     positioned hero badge both follow it. */
  .apex { --apex-gutter: 18px; }
}


/* ── 17. MOTION + PRINT ──────────────────────────────────────────────────── */

/* .apex-legal is listed alongside .apex. The legal, About, FAQ and affiliate
   pages are plain post content and carry no .apex wrapper, so a block scoped
   only to .apex would leave every card hover and button transition on those
   pages running for someone who has asked the OS for less motion. */

@media (prefers-reduced-motion: reduce) {
  .apex *,
  .apex *::before,
  .apex *::after,
  .apex-legal *,
  .apex-legal *::before,
  .apex-legal *::after,
  .apex-ruo,
  .apex-ruo *,
  .apex-ruo *::before,
  .apex-ruo *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

@media print {
  .apex-coa-table { font-size: 11px; }
  .apex-coa-table a::after { content: ' (' attr(href) ')'; font-weight: 400; }
}


/* ── 18. SINGLE PRODUCT PAGE ─────────────────────────────────────────────── */

/* Markup comes from the apex-single-product WPCode snippet. Class names are
   shared with that PHP — rename in one place and you must rename in both.

   Woo's own wrappers (div.product, .summary, .woocommerce-product-gallery)
   are styled here too, so this section is NOT purely .apex-scoped: it has to
   reach markup WooCommerce emits. Same exception the bridge in section 14
   makes, for the same reason. */

/* ── Breadcrumb ─────────────────────────────────────────────────────────── */

/* Blocksy renders its own breadcrumb into .hero-section > .entry-header on
   single products, which duplicates the bar below. Not scoped to .apex — it
   has to reach Blocksy's markup, and it is deliberately narrow: only the nav
   is hidden, and only on single products. Its wrappers carry no padding, so
   they collapse to zero height rather than leaving a gap.
   The alternative is switching breadcrumbs off in Customizer > Breadcrumbs,
   which would also remove them from pages where they are wanted. */
.single-product .ct-breadcrumbs { display: none; }

.apex-single .apex-breadcrumb {
  background: var(--apex-bg-light);
  border-bottom: 1px solid var(--apex-border);
  padding-block: 16px;
}

.apex-single .apex-breadcrumb-inner {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  font-size: 13px;
  color: var(--apex-text-gray);
}
.apex-single .apex-breadcrumb-inner a { color: var(--apex-text-gray); }
.apex-single .apex-breadcrumb-inner a:hover { color: var(--apex-orange); }
.apex-single .apex-breadcrumb-sep { color: var(--apex-border); }
.apex-single .apex-breadcrumb-current { color: var(--apex-text-dark); font-weight: 500; }


/* ── Two-column layout ──────────────────────────────────────────────────── */

/* IMPORTANT — the grid parent is NOT div.product.
 *
 * Stock WooCommerce makes the gallery and .summary direct children of
 * div.product, but Blocksy inserts its own .product-entry-wrapper between
 * them. A grid on div.product therefore produces one column containing that
 * wrapper, and the two columns never form at any viewport width.
 *
 * The second selector keeps this working on stock Woo markup where no wrapper
 * exists — :has() lets us grid whichever element is the real parent.
 *
 * Blocksy also pins .is-width-constrained to var(--theme-container-width),
 * about 865px, which is what made the page look narrow. Overridden below; at
 * (0,2,0) this beats Blocksy's (0,1,0) with no !important needed. */

.apex-single div.product {
  display: block;
  max-width: none;
  margin: 0;
  padding: 0;
}

/* The image column is capped rather than a free 1fr. At 1fr 1fr the gallery
   grew to ~584px on a wide screen, which dwarfed the buying controls next to
   it. minmax(0, 460px) fixes the image at a sensible size and gives every
   extra pixel to the summary. The minmax(0, …) form on both tracks stops a
   long unbreakable string in the summary from forcing the grid wider. */
.apex-single .product-entry-wrapper,
.apex-single div.product:not(:has(.product-entry-wrapper)) {
  display: grid;
  grid-template-columns: minmax(0, 460px) minmax(0, 1fr);
  /* auto 1fr is what keeps the COA badge pinned under the gallery.
   *
   * The summary spans both rows, and a spanning item's height is distributed
   * across EVERY row it spans — so with two auto rows the summary inflated
   * row 1 as well. The gallery is ~460px but row 1 measured 650px, dropping
   * the badge 214px below the image, and opening an accordion pushed it a
   * further 184px because row 1 grew again.
   *
   * Sizing row 1 to its content pins it to the gallery, and 1fr sends every
   * extra pixel the summary needs into row 2's slack BELOW the badge. */
  grid-template-rows: auto 1fr;
  gap: 24px 64px;
  align-items: start;
  width: 100%;
  max-width: 1280px;
  margin-inline: auto;
  padding: 48px 24px 60px;
}

/* Explicit placement, because the left column is two separate grid items —
   the gallery and the COA badge box — with the summary spanning both rows.
   Without this the badge box lands in column 2 and pushes the summary into
   a third row. */
.apex-single .woocommerce-product-gallery {
  grid-column: 1;
  grid-row: 1;
  float: none;
  width: 100% !important;
  margin: 0;
}

/* Both document boxes live in ONE grid item, not two.
 *
 * Giving the MSDS box its own row would mean grid-template-rows: auto auto
 * auto 1fr, and then a product with no COA — or no MSDS — leaves an empty
 * track whose row gaps still apply, dropping the remaining box 48px below the
 * gallery instead of 24px. Stacking them inside a flex wrapper makes the pair
 * a single item that collapses cleanly to whichever boxes actually render, and
 * leaves grid-template-rows: auto 1fr untouched. */

.apex-single .apex-doc-stack {
  grid-column: 1;
  grid-row: 2;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.apex-single .summary {
  grid-column: 2;
  grid-row: 1 / span 2;
}

/* No position:sticky on the gallery.
 *
 * The mockup sticks the whole left column, but here the gallery and the badge
 * box are independent grid items — sticking only the gallery leaves the badge
 * box scrolling out from under it, which reads as broken. Sticking the column
 * as a unit needs a real wrapper around both, i.e. a template override rather
 * than hooks. Revisit if that override ever happens. */

/* Blocksy replaces Woo's gallery markup as well as the summary. The elements
   that actually exist are .ct-product-gallery-container and .ct-media-container
   — there is no .woocommerce-product-gallery__wrapper and no
   .flex-control-thumbs, so styling those does nothing at all. */

.apex-single .ct-product-gallery-container,
.apex-single .ct-media-container {
  display: block;
  width: 100%;
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-lg);
  overflow: hidden;
  background: var(--apex-bg-light);
}

/* !important is load-bearing: Blocksy writes aspect-ratio:3/4 as an INLINE
   style on the img, from its product image ratio setting, and nothing in a
   stylesheet outranks an inline declaration without it. The source images are
   square, so 3/4 was cropping the top and bottom off every vial.

   The tidier fix is Customizer > WooCommerce > Product images > ratio = 1:1,
   which removes the inline style; this rule then becomes redundant but
   harmless. */
.apex-single .woocommerce-product-gallery img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1 / 1 !important;
  object-fit: cover;
}

.apex-single .ct-product-gallery-thumbs,
.apex-single .flex-control-thumbs {
  display: flex;
  gap: 10px;
  margin: 12px 0 0;
  padding: 0;
  list-style: none;
}
.apex-single .ct-product-gallery-thumbs li,
.apex-single .flex-control-thumbs li { width: 72px; flex: 0 0 auto; }
.apex-single .ct-product-gallery-thumbs img,
.apex-single .flex-control-thumbs img {
  border: 2px solid var(--apex-border);
  border-radius: var(--apex-radius-sm);
  cursor: pointer;
  transition: border-color .2s ease;
}
.apex-single .ct-product-gallery-thumbs img:hover,
.apex-single .flex-control-thumbs img:hover,
.apex-single .flex-control-thumbs img.flex-active { border-color: var(--apex-orange); }


/* ── Blocksy's own summary elements ─────────────────────────────────────── */

/* Blocksy Companion builds the summary with its own element list
   (.entry-summary-items) rather than running Woo's
   woocommerce_single_product_summary hooks. That means remove_action() on
   Woo's price and meta callbacks has no effect — Blocksy renders its own,
   which is where the SECOND, orange price came from.
   The price is replaced by .apex-price-row (which carries the bulk preview),
   and the category by .apex-cat-label above the title.

   Cleaner alternative: Customizer > WooCommerce > Single Product, and switch
   Price and Meta off in the elements list. This rule is the safety net. */

.apex-single .summary > p.price,
.apex-single .summary > .product_meta,
.apex-single .summary > .ct-product-divider { display: none; }

.apex-single .summary { margin: 0 !important; width: 100% !important; float: none; }


/* ── COA badge box (left column, under the gallery) ─────────────────────── */

/* COA and MSDS share every visual rule below — they are the same component
   with a different icon, label and target. Listed as two selectors rather than
   one shared class because the COA markup predates the MSDS box and renaming
   its classes would silently break any rule elsewhere that still targets
   .apex-coa-badge. */

.apex-single .apex-coa-badge,
.apex-single .apex-msds-badge {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  /* nowrap, not wrap.
   *
   * With flex-wrap: wrap the browser decides line breaks from each item's
   * HYPOTHETICAL width before any shrinking happens — so giving the text block
   * flex: 1 1 auto does NOT stop the button dropping to a second line, which is
   * exactly what the MSDS box did while the shorter COA box stayed on one row.
   * Forbidding the wrap and letting the text column shrink instead keeps both
   * boxes to a single row, which is the whole point of them being a pair. */
  flex-wrap: nowrap;
  background: var(--apex-orange-fade);
  border: 1px dashed var(--apex-orange);
  border-radius: var(--apex-radius-md);
  padding: 16px 20px;
}
/* min-width: 0 overrides the flex default of min-width: auto, which refuses to
   shrink an item below its content — without it "nowrap" would just overflow
   the box instead of wrapping the text inside it. */
.apex-single .apex-coa-badge-left {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 1 1 auto;
  min-width: 0;
}
.apex-single .apex-coa-badge-action { flex: 0 0 auto; }
.apex-single .apex-coa-badge-icon { width: 28px; height: 28px; flex-shrink: 0; color: var(--apex-orange); }
.apex-single .apex-coa-badge-icon svg { width: 100%; height: 100%; }
.apex-single .apex-coa-badge-title { font-size: 14px; font-weight: 700; color: var(--apex-text-dark); }
.apex-single .apex-coa-badge-sub { font-size: 12px; color: var(--apex-text-gray); margin-top: 2px; }
.apex-single .apex-coa-badge-action {
  background: var(--apex-orange);
  color: #fff;
  padding: 9px 18px;
  border-radius: var(--apex-radius-sm);
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
}
.apex-single .apex-coa-badge-action:hover { background: var(--apex-orange-hover); color: #fff; }


/* ── MSDS variant ───────────────────────────────────────────────────────── */

/* Same palette, inverted weight. Two identical orange-filled boxes stacked on
   top of each other read as one heavy block and neither gets looked at; the
   COA is the one customers came to check, so it keeps the filled treatment and
   the MSDS sits back as the quieter, outlined sibling.
   Solid border rather than dashed for the same reason — the dashes are the
   COA's signature, and repeating them is what made the pair monotone. */

.apex-single .apex-msds-badge {
  background: #fff;
  border-style: solid;
  border-color: color-mix(in srgb, var(--apex-orange) 35%, transparent);
}

/* 8px/17px, not 9px/18px: this button gains a 1px border the filled COA button
   does not have, so the padding drops by 1px on each side to keep the two the
   same height. Without it the MSDS button is 2px taller and the pair looks
   misaligned. */
.apex-single .apex-msds-badge .apex-coa-badge-action {
  background: #fff;
  color: var(--apex-orange);
  border: 1px solid var(--apex-orange);
  padding: 8px 17px;
}
.apex-single .apex-msds-badge .apex-coa-badge-action:hover {
  background: var(--apex-orange);
  color: #fff;
}

/* Below 560px the wrap comes BACK, and deliberately.
 *
 * The desktop nowrap keeps each box to one row by letting the text column
 * shrink. That works down to about the tablet width; at 390px the column is
 * only 327px and shrinking squeezed both the title and the subtitle onto three
 * lines each, taking the COA box to 161px. Letting the button drop below the
 * text instead keeps every line intact and both boxes at 132px.
 *
 * flex: 1 1 100% on the text column forces the break cleanly rather than
 * leaving it to whatever the content width happens to be. */

@media (max-width: 560px) {
  .apex-single .apex-coa-badge,
  .apex-single .apex-msds-badge { flex-wrap: wrap; }
  .apex-single .apex-coa-badge-left { flex: 1 1 100%; }

  /* Centre the button on its own row.
   *
   * margin-inline: auto rather than justify-content: center on the parent —
   * the parent's justify-content also governs the first row, and the box uses
   * space-between there. Auto margins on the flex item itself centre it within
   * the free space of its own line and leave the row above alone. */
  .apex-single .apex-coa-badge-action { margin-inline: auto; }

  /* Icon stays BESIDE the text, and the whole group is nudged left so the TEXT
   * lands on the box's centre line rather than the group as a whole.
   *
   * The icon and its gap occupy 40px on the left (28px + 12px), so centring
   * the group put the title and subtitle 20px right of centre while the button
   * below them sat exactly centred. Shifting the group by half that width —
   * 20px — cancels it: measured titleOffset and subOffset are now 0 on both
   * boxes, matching the button.
   *
   * transform, NOT padding-right: 40px. The padding version centres the text
   * just as well but steals 40px of width, which pushed the COA's longer
   * subtitle onto a second line and took that box to 174px while the MSDS
   * stayed at 132px. A transform moves the group without changing what it has
   * to lay out in, so nothing rewraps and both boxes stay 132px.
   *
   * 15px, not the 20px that centres the text exactly. The full 20px left the
   * COA icon only 6px from the box's left edge — its text is the wider of the
   * two, so the same shift pushes its icon further out. Backing off to 15px
   * gives that icon 11px of clearance and costs 5px of centring: measured,
   * title and subtitle sit 5px right of the box centre on both boxes. The MSDS
   * icon clears by 36px either way.
   *
   * To go back to a true single centre line for every element, replace this
   * rule with flex-direction: column; gap: 8px — icon on its own row above the
   * text, at a cost of 36px of height per box. */
  .apex-single .apex-coa-badge-left {
    justify-content: center;
    text-align: center;
    transform: translateX(-15px);
  }
}


/* ── Summary head ───────────────────────────────────────────────────────── */

.apex-single .apex-cat-label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1.5px;
  color: var(--apex-orange);
  margin-bottom: 10px;
}
.apex-single .apex-cat-label::before {
  content: '';
  width: 20px;
  height: 2px;
  background: var(--apex-orange);
  flex-shrink: 0;
}
.apex-single .apex-cat-label a { color: inherit; }

/* The stock pill is emitted inside the H1 by the the_title filter, so the
   heading is the row. One element behaves identically at both breakpoints —
   a float would have worked on desktop but is ignored on mobile, where the
   summary becomes display:contents and its children become grid items. */
.apex-single .product_title {
  display: flex;
  align-items: center;
  /* NOT space-between. That distributes every flex item, and the name and the
     strength are separate items unless wrapped — it pulled "10mg" away from
     "BPC-157" and stranded it mid-heading. margin-left:auto on the badge pins
     it right without touching anything to its left, whatever precedes it. */
  justify-content: flex-start;
  gap: 16px;
  flex-wrap: wrap;
  font-size: clamp(28px, 3.4vw, 38px);
  font-weight: 700;
  letter-spacing: -.5px;
  line-height: 1.1;
  margin-bottom: 6px;
}

/* Name + strength as one flex item, so the gradient stays inline against the
   compound name. Emitted by the the_title filter. */
.apex-single .product_title .apex-title-text {
  flex: 1 1 auto;
  min-width: 0;
}

/* Reset the heading's type off the pill — it would otherwise inherit 38px
   and the -.5px tracking. */
.apex-single .product_title .apex-stock-badge {
  flex-shrink: 0;
  margin-left: auto;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
}
.apex-single .product_title .apex-stock-badge--soon {
  background: var(--apex-text-dark);
  color: #fff;
}

.apex-single .apex-subtitle {
  font-size: 15px;
  color: var(--apex-text-gray);
  margin-bottom: 20px;
}


/* ── Rating + stock ─────────────────────────────────────────────────────── */

.apex-single .apex-rating-row {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 20px;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--apex-border);
}
.apex-single .apex-rating-text { font-size: 13px; color: var(--apex-text-gray); }
.apex-single .apex-rating-text a { color: var(--apex-orange); font-weight: 600; }
.apex-single .star-rating { color: var(--apex-orange); }

.apex-single .apex-stock-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
  background: #d1fae5;
  color: #065f46;
  padding: 4px 12px;
  border-radius: 100px;
  font-size: 12px;
  font-weight: 600;
}
.apex-single .apex-stock-dot {
  width: 7px;
  height: 7px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--apex-green);
  /* Slow pulse. Deliberately unhurried at 2.2s — a fast blink reads as an
     error indicator, this should read as "live". Opacity and transform only,
     both compositor-friendly, so it costs nothing to run continuously. */
  animation: apex-stock-pulse 2.2s ease-in-out infinite;
}

@keyframes apex-stock-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: .3; transform: scale(.8); }
}

/* Anything that animates forever has to honour the reduced-motion setting —
   for some people a persistent pulse is a genuine problem, not a flourish.
   Section 17 covers this globally, repeated here so the rule survives if this
   section is ever lifted out on its own. */
@media (prefers-reduced-motion: reduce) {
  .apex-single .apex-stock-dot { animation: none; }
}
.apex-single .apex-stock-badge--out { background: #f3f4f6; color: var(--apex-text-muted); }


/* ── Price ──────────────────────────────────────────────────────────────── */

.apex-single .apex-price-row { margin-bottom: 24px; }
.apex-single .apex-price-main { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }

.apex-single .apex-price-was {
  font-size: 22px;
  font-weight: 600;
  color: var(--apex-text-muted);
  text-decoration: line-through;
}

.apex-single .apex-price-now { font-size: 34px; font-weight: 700; color: var(--apex-text-dark); }

/* Gradient only while a bulk tier is active, so the discounted figure is the
   thing that draws the eye. */
.apex-single .apex-price-now.is-discounted {
  background: linear-gradient(90deg, var(--apex-orange) 0%, #f7a05a 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

.apex-single .apex-price-note { font-size: 13px; color: var(--apex-text-gray); margin-top: 4px; }


/* ── Bulk tiers ─────────────────────────────────────────────────────────── */

.apex-single .apex-bulk { margin-bottom: 24px; }
.apex-single .apex-bulk-label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  margin-bottom: 10px;
}
.apex-single .apex-bulk-options { display: flex; gap: 10px; flex-wrap: wrap; }

.apex-single .apex-bulk-btn {
  padding: 10px 18px;
  border: 2px solid var(--apex-border);
  border-radius: var(--apex-radius-sm);
  background: #fff;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--apex-text-gray);
  cursor: pointer;
  transition: all .2s ease;
}
.apex-single .apex-bulk-btn:hover { border-color: var(--apex-orange); color: var(--apex-orange); }
.apex-single .apex-bulk-btn.is-active {
  border-color: var(--apex-orange);
  color: var(--apex-orange);
  background: var(--apex-orange-fade);
}
.apex-single .apex-bulk-save { color: var(--apex-green); font-size: 11px; margin-left: 4px; }


/* ── Quantity + add to cart ─────────────────────────────────────────────── */

.apex-single form.cart {
  display: flex;
  gap: 12px;
  align-items: center;
  flex-wrap: wrap;
  margin-bottom: 32px;
}

.apex-single form.cart .quantity {
  display: flex;
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-sm);
  overflow: hidden;
}
/* Width must clear Blocksy's arrow padding — same trap as the cart page, but
 * worse here because the control is taller.
 *
 * Blocksy's type-2 quantity overlays absolutely positioned − / + arrows and
 * pads the input to keep the digit clear of them. That padding scales with
 * height: 31px each side at the cart's 42px tall control, 42px each side at
 * this 52px one. At width:72px the padding totalled 84px inside an 84px box,
 * leaving EXACTLY 0px for the number — it was not off-centre, it was gone.
 *
 * 128px is the practical ceiling: the wrapper settles at 130px including its
 * borders, so anything larger is clamped. That leaves 44px of clear space. */
.apex-single form.cart .quantity input.qty {
  width: 128px;
  height: 52px;
  border: 0;
  text-align: center;
  font-family: inherit;
  font-size: 16px;
  font-weight: 600;
  color: var(--apex-text-dark);
  background: #fff;
}

.apex-single form.cart .single_add_to_cart_button {
  flex: 1 1 200px;
  height: 52px;
  border: 0;
  border-radius: var(--apex-radius-sm);
  background: linear-gradient(90deg, var(--apex-orange) 0%, #f7a05a 100%);
  color: #fff;
  font-family: inherit;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: .3px;
  cursor: pointer;
  transition: opacity .2s ease, transform .15s ease;
}
.apex-single form.cart .single_add_to_cart_button:hover { opacity: .9; transform: translateY(-1px); }

/* Hide Woo's "80 in stock" line.
 *
 * Availability is already stated by the pill on the title row, and an exact
 * remaining count is information a customer does not need and a competitor
 * does. Hidden rather than filtered out: Blocksy renders this from its own
 * summary builder, so remove_action / woocommerce_get_stock_html do not
 * reliably reach it — the same reason the price and meta removals failed.
 * The green pill is untouched. */
.apex-single .ct-product-add-to-cart > p.stock { display: none; }


/* ── Coming soon notice ─────────────────────────────────────────────────── */

/* Stands in for the add-to-cart form on a non-purchasable product. Dashed
   border rather than solid, matching the COA badge box — it reads as a
   placeholder for something that will be there later, not as an error. */
.apex-single .apex-coming-soon-notice {
  margin-bottom: 32px;
  padding: 20px 24px;
  background: var(--apex-bg-light);
  border: 1px dashed var(--apex-border);
  border-radius: var(--apex-radius-md);
}
.apex-single .apex-coming-soon-title {
  font-size: 13px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1.4px;
  color: var(--apex-text-dark);
  margin-bottom: 6px;
}
.apex-single .apex-coming-soon-notice p:last-child {
  font-size: 14px;
  line-height: 1.6;
  color: var(--apex-text-gray);
}


/* ── Trust row ──────────────────────────────────────────────────────────── */

.apex-single .apex-trust-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  margin-bottom: 32px;
}
.apex-single .apex-trust-item {
  background: var(--apex-bg-light);
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-md);
  padding: 14px 12px;
  text-align: center;
}
.apex-single .apex-trust-text {
  display: block;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.4;
  color: var(--apex-text-dark);
}

/* Icons are masked SVG rather than emoji. Emoji render at wildly different
   sizes and colours per platform and look like a mistake next to a tuned
   type scale — and they cannot take the brand colour. */
.apex-single .apex-trust-icon {
  display: block;
  width: 20px;
  height: 20px;
  margin: 0 auto 6px;
  background: var(--apex-orange);
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
  -webkit-mask-size: contain;
          mask-size: contain;
}
.apex-single .apex-icon-flask {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 2v7l-6.5 11A2 2 0 0 0 4.24 23h15.52a2 2 0 0 0 1.74-3L15 9V2'/%3E%3Cpath d='M8.5 2h7'/%3E%3Cpath d='M10 14h4'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 2v7l-6.5 11A2 2 0 0 0 4.24 23h15.52a2 2 0 0 0 1.74-3L15 9V2'/%3E%3Cpath d='M8.5 2h7'/%3E%3Cpath d='M10 14h4'/%3E%3C/svg%3E");
}
.apex-single .apex-icon-box {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z'/%3E%3Cpolyline points='3.27 6.96 12 12.01 20.73 6.96'/%3E%3Cline x1='12' y1='22.08' x2='12' y2='12'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z'/%3E%3Cpolyline points='3.27 6.96 12 12.01 20.73 6.96'/%3E%3Cline x1='12' y1='22.08' x2='12' y2='12'/%3E%3C/svg%3E");
}
.apex-single .apex-icon-doc {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z'/%3E%3Cpolyline points='14 2 14 8 20 8'/%3E%3Cline x1='16' y1='13' x2='8' y2='13'/%3E%3Cline x1='16' y1='17' x2='8' y2='17'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z'/%3E%3Cpolyline points='14 2 14 8 20 8'/%3E%3Cline x1='16' y1='13' x2='8' y2='13'/%3E%3Cline x1='16' y1='17' x2='8' y2='17'/%3E%3C/svg%3E");
}


/* ── Accordions ─────────────────────────────────────────────────────────── */

.apex-single .apex-accordion { border-top: 1px solid var(--apex-border); }
.apex-single .apex-accordion-item { border-bottom: 1px solid var(--apex-border); }

.apex-single .apex-accordion-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  width: 100%;
  padding: 20px 0;
  border: 0;
  background: none;
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  color: var(--apex-text-dark);
  text-align: left;
  cursor: pointer;
}
.apex-single .apex-accordion-header:hover { color: var(--apex-orange); }

/* Two bars rather than a "+" glyph, so the cross animates into a minus and
   the baseline doesn't shift between fonts. */
.apex-single .apex-accordion-icon {
  position: relative;
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}
.apex-single .apex-accordion-icon::before,
.apex-single .apex-accordion-icon::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 2px;
  margin-top: -1px;
  background: currentColor;
  transition: transform .25s ease;
}
.apex-single .apex-accordion-icon::after { transform: rotate(90deg); }
.apex-single .apex-accordion-header[aria-expanded="true"] .apex-accordion-icon::after { transform: rotate(0deg); }

.apex-single .apex-accordion-body { padding-bottom: 20px; }
.apex-single .apex-accordion-body p,
.apex-single .apex-accordion-body li {
  font-size: 14px;
  line-height: 1.7;
  color: var(--apex-text-gray);
}
.apex-single .apex-accordion-body p { margin-bottom: 10px; }
.apex-single .apex-accordion-body ul { list-style: disc; padding-left: 20px; }
.apex-single .apex-accordion-body li { margin-bottom: 4px; }

.apex-single .apex-spec-table { width: 100%; border-collapse: collapse; }
.apex-single .apex-spec-table th,
.apex-single .apex-spec-table td {
  padding: 10px 0;
  border-bottom: 1px solid var(--apex-border);
  font-size: 14px;
  text-align: left;
  vertical-align: top;
}
.apex-single .apex-spec-table th { width: 42%; color: var(--apex-text-gray); font-weight: 600; }
.apex-single .apex-spec-table td { color: var(--apex-text-dark); }
.apex-single .apex-spec-table tr:last-child th,
.apex-single .apex-spec-table tr:last-child td { border-bottom: 0; }


/* ── Transparency / batch verification ──────────────────────────────────── */

.apex-single .apex-transparency {
  background: var(--apex-bg-light);
  border-top: 1px solid var(--apex-border);
  border-bottom: 1px solid var(--apex-border);
  padding: 60px 0;
}
.apex-single .apex-transparency-inner {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 60px;
  align-items: center;
}
.apex-single .apex-transparency h2 {
  font-size: clamp(26px, 3vw, 32px);
  font-weight: 700;
  letter-spacing: -.3px;
  line-height: 1.2;
  margin-bottom: 16px;
}
.apex-single .apex-transparency-copy > p {
  font-size: 15px;
  line-height: 1.7;
  color: var(--apex-text-gray);
  margin-bottom: 28px;
}

.apex-single .apex-batch-verify {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  background: #fff;
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-md);
  padding: 20px 24px;
}
.apex-single .apex-batch-label {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--apex-text-gray);
  margin-bottom: 6px;
}
.apex-single .apex-batch-num {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 20px;
  font-weight: 700;
  color: var(--apex-text-dark);
}

.apex-single .apex-coa-card {
  background: #fff;
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-lg);
  box-shadow: var(--apex-shadow-lg);
  padding: 32px;
}
.apex-single .apex-coa-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  border-bottom: 2px solid var(--apex-border);
  padding-bottom: 14px;
  margin-bottom: 20px;
}
.apex-single .apex-coa-card-title { font-size: 15px; font-weight: 700; }
.apex-single .apex-coa-passed {
  background: #d1fae5;
  color: #065f46;
  padding: 4px 12px;
  border-radius: 100px;
  font-size: 12px;
  font-weight: 700;
  white-space: nowrap;
}
.apex-single .apex-coa-row {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding: 10px 0;
  border-bottom: 1px solid var(--apex-border);
  font-size: 14px;
}
.apex-single .apex-coa-row:last-of-type { border-bottom: 0; }
.apex-single .apex-coa-row-label { color: var(--apex-text-gray); font-weight: 600; }
.apex-single .apex-coa-row-val { color: var(--apex-text-dark); font-weight: 500; text-align: right; }

.apex-single .apex-coa-qr {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 20px;
  padding: 14px 16px;
  background: var(--apex-orange-fade);
  border: 1px dashed var(--apex-orange);
  border-radius: var(--apex-radius-sm);
}
.apex-single .apex-coa-qr:hover { background: #fbe6db; }
.apex-single .apex-coa-qr-icon { width: 26px; height: 26px; flex-shrink: 0; color: var(--apex-orange); }
.apex-single .apex-coa-qr-icon svg { width: 100%; height: 100%; }
.apex-single .apex-coa-qr-text strong { display: block; font-size: 14px; font-weight: 700; color: var(--apex-orange); }
.apex-single .apex-coa-qr-text span { font-size: 12px; color: var(--apex-text-gray); }


/* ── FAQ ────────────────────────────────────────────────────────────────── */

.apex-single .apex-faq { padding: 60px 0; }
.apex-single .apex-faq h2,
.apex-single .apex-reviews h2 {
  font-size: clamp(26px, 3vw, 32px);
  font-weight: 700;
  letter-spacing: -.3px;
}
.apex-single .apex-faq-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px;
  margin-top: 40px;
}
.apex-single .apex-faq-item {
  background: var(--apex-bg-light);
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-md);
  padding: 24px;
}
.apex-single .apex-faq-q { font-size: 15px; font-weight: 600; margin-bottom: 10px; }
.apex-single .apex-faq-a { font-size: 14px; line-height: 1.7; color: var(--apex-text-gray); }


/* ── Reviews ────────────────────────────────────────────────────────────── */

.apex-single .apex-reviews {
  background: var(--apex-bg-light);
  border-top: 1px solid var(--apex-border);
  padding: 60px 0;
}
.apex-single .apex-reviews-head {
  display: flex;
  align-items: center;
  gap: 40px;
  flex-wrap: wrap;
  margin: 32px 0 40px;
}
.apex-single .apex-reviews-score { text-align: center; }
.apex-single .apex-reviews-num { font-size: 52px; font-weight: 700; line-height: 1; }
.apex-single .apex-reviews-count { font-size: 13px; color: var(--apex-text-gray); }

.apex-single .apex-reviews-bars { flex: 1 1 260px; }
.apex-single .apex-review-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 8px;
  font-size: 13px;
  color: var(--apex-text-gray);
}
.apex-single .apex-review-bar-label { width: 34px; flex-shrink: 0; text-align: right; }
.apex-single .apex-review-bar-track {
  flex: 1;
  height: 6px;
  background: var(--apex-border);
  border-radius: 3px;
  overflow: hidden;
}
.apex-single .apex-review-bar-fill {
  display: block;
  height: 100%;
  border-radius: 3px;
  background: linear-gradient(90deg, var(--apex-orange) 0%, #f7a05a 100%);
}
.apex-single .apex-review-bar-pct { width: 34px; flex-shrink: 0; }
.apex-single .apex-reviews-empty { margin-top: 24px; color: var(--apex-text-gray); }
.apex-single .apex-reviews-woo { margin-top: 32px; }


/* ── Related products ───────────────────────────────────────────────────── */

/* Woo emits ul.products here, not .products-grid, so section 9's card rules
   don't reach it. Match the geometry rather than rewriting Woo's loop. */
.apex-single .related { padding: 60px 0; }
.apex-single .related > h2 {
  font-size: clamp(26px, 3vw, 32px);
  font-weight: 700;
  letter-spacing: -.3px;
  margin-bottom: 40px;
}
.apex-single .related ul.products {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 20px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.apex-single .related ul.products li.product {
  width: auto;
  float: none;
  margin: 0;
  background: #fff;
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-md);
  overflow: hidden;
  transition: all .2s ease;
}
.apex-single .related ul.products li.product:hover {
  border-color: var(--apex-orange);
  box-shadow: var(--apex-shadow-lg);
  transform: translateY(-3px);
}
.apex-single .related ul.products img { width: 100%; height: auto; margin: 0; }
.apex-single .related ul.products .woocommerce-loop-product__title {
  margin: 0;
  padding: 14px 16px 4px;
  font-size: 15px;
  font-weight: 600;
}
.apex-single .related ul.products .price {
  display: block;
  padding: 0 16px 14px;
  font-size: 16px;
  font-weight: 700;
  color: var(--apex-orange);
}
.apex-single .related ul.products .button { margin: 0 16px 16px; }


/* ── Responsive ─────────────────────────────────────────────────────────── */

@media (max-width: 1024px) {
  /* Collapse to one column. The explicit grid-column / grid-row placements
     above must be unset too, or the summary stays pinned to a column 2 that
     no longer exists and the badge box overlaps it. */
  .apex-single .product-entry-wrapper,
  .apex-single div.product:not(:has(.product-entry-wrapper)) {
    grid-template-columns: 1fr;
    /* Clear the desktop `auto 1fr`. In one column with a dozen auto-placed
       items, a 1fr second track would stretch whichever item landed in it and
       leave a gap under the second block. */
    grid-template-rows: none;
    /* gap: 0 because display:contents below promotes every summary child to a
       grid item — a 32px gap would then apply between all fourteen of them,
       on top of the margins they already carry. Spacing stays with the
       elements themselves. */
    gap: 0;
    padding-top: 32px;
  }

  /* Reading order on a phone: name, then image, then proof, then buying.
   *
   * The title lives inside .summary, so ordering the wrapper's children could
   * never reach it — .summary moved as one block. display:contents dissolves
   * that box so its children become grid items in their own right and can be
   * ordered individually. No markup change, no duplicated heading.
   *
   * Everything gets an explicit order; anything unlisted defaults to 0 and
   * would jump to the top. */
  .apex-single .summary { display: contents; }

  /* This reset is REQUIRED for the ordering below to do anything.
   *
   * `order` only affects AUTO-placed grid items. The desktop rules pin the
   * gallery to grid-row 1 and the COA badge to grid-row 2, and an explicitly
   * placed item ignores `order` completely — so without this the gallery sat
   * in row 1 regardless of being given order: 4, and the reorder silently did
   * nothing while every computed value looked correct. */
  .apex-single .woocommerce-product-gallery,
  .apex-single .apex-doc-stack,
  .apex-single .summary {
    grid-column: auto;
    grid-row: auto;
  }

  .apex-single .apex-cat-label { order: 1; }
  .apex-single .product_title  { order: 2; }
  .apex-single .apex-subtitle  { order: 3; }
  .apex-single .woocommerce-product-gallery { order: 4; }
  .apex-single .apex-doc-stack { order: 5; margin-bottom: 24px; }
  .apex-single .apex-rating-row { order: 6; }
  .apex-single .apex-price-row  { order: 7; }
  .apex-single .apex-bulk       { order: 8; }
  .apex-single .summary > p.price,
  .apex-single .woocommerce-product-details__short-description,
  .apex-single .ct-product-divider { order: 9; }
  .apex-single .ct-product-add-to-cart,
  .apex-single form.cart,
  .apex-single .apex-coming-soon-notice { order: 10; }
  .apex-single .apex-trust-row { order: 11; }
  .apex-single .apex-accordion { order: 12; }
  .apex-single .product_meta   { order: 13; }

  /* Without a cap the image would span the full single-column width and
     dominate the page on a tablet — the original complaint, one breakpoint
     down. */
  .apex-single .woocommerce-product-gallery {
    max-width: 460px;
    margin: 4px auto 18px;
  }

  /* Buying controls: centred and bigger.
   *
   * .ct-cart-actions is Blocksy's flex row holding the quantity and the
   * button. It sized itself to 240px inside a 327px column and sat left,
   * which read as misaligned against the centred gallery above it.
   *
   * The button carries flex: 1 1 100% from Blocksy, so it always takes its
   * own row — that is wanted here, it just needs the row to be full width.
   * The quantity then centres above it. */
  .apex-single .ct-cart-actions {
    width: 100%;
    justify-content: center;
    align-items: center;
    gap: 14px;
  }

  /* The wrapper needs the width, not just the input. Blocksy pins .quantity
     to 100px on mobile, so widening only the input left it clamped at 98px.
     height: auto matters too — the wrapper was 40px tall around a 52px input,
     and with overflow:hidden on it that clipped the control. */
  .apex-single form.cart .quantity {
    width: 150px;
    height: auto;
  }
  .apex-single form.cart .quantity input.qty {
    width: 100%;
    height: 58px;
    font-size: 17px;
  }

  .apex-single form.cart .single_add_to_cart_button {
    height: 58px;
    font-size: 16px;
  }

  .apex-single .apex-transparency-inner,
  .apex-single .apex-faq-grid { grid-template-columns: 1fr; }
  .apex-single .related ul.products { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 600px) {
  .apex-single .apex-trust-row { grid-template-columns: 1fr 1fr; }
  .apex-single form.cart .single_add_to_cart_button { flex-basis: 100%; }
  .apex-single .apex-price-now { font-size: 26px; }
  .apex-single .apex-coa-card { padding: 22px; }
  .apex-single .apex-reviews-head { flex-direction: column; align-items: flex-start; gap: 20px; }
}


/* ── 19. CATALOG PAGE ────────────────────────────────────────────────────── */

/* Markup comes from [apex_catalog] in the apex-products WPCode snippet.
 *
 * CLASS COLLISION — read before editing.
 *
 * The catalog design reuses .product-card, .product-image, .product-meta and
 * .product-price, but styles them differently from the homepage cards in
 * section 9: edge-to-edge image, no card padding, dark price, a category
 * label and a "View →" affordance instead of the round add-to-cart button.
 *
 * Every rule below is therefore written as `.apex .apex-catalog …` (0,3,0),
 * which outranks section 9's `.apex .product-card` (0,2,0) outright rather
 * than relying on source order. Keep that three-class shape when adding
 * rules here, or a section 9 change will silently reach into this page. */


/* ── Hero ───────────────────────────────────────────────────────────────── */

/* Top spacing.
 *
 * Three separate paddings stacked above this hero and pushed it 118px further
 * down than it needed to be, measured at 1900px wide:
 *
 *   Blocksy   .ct-container-full   padding-top 60px
 *   Elementor .e-con / .e-con-inner  ~ 58px of block padding
 *   here      .shop-hero             48px
 *
 * The first two are cleared below so this rule is the only thing controlling
 * the gap — change the 32px and nothing else fights it.
 *
 * Do NOT add calc(var(--apex-header-h) …) here the way .hero-inner does. That
 * compensation exists because the homepage header floats transparently over
 * the hero with nothing reserving space. On this page Blocksy reserves a
 * placeholder for the fixed header, so the content area already begins at its
 * bottom edge (measured: 105px, with the logo ending at 90px). Adding the
 * header height again double-counts it and made the gap larger, not smaller. */
.apex .apex-catalog .shop-hero {
  padding: 32px 0 28px;
  border-bottom: 1px solid var(--apex-border);
}

/* Clear the theme and page-builder padding above the hero. Scoped with :has()
   so it follows the shortcode rather than a page ID, and so no other page
   loses its normal content padding. */
body:has(.apex-catalog) .ct-container-full { padding-top: 0; }

.e-con:has(.apex-catalog) { --padding-block-start: 0px; }
.e-con:has(.apex-catalog) > .e-con-inner { padding-block-start: 0; }

.apex .apex-catalog .shop-hero-inner {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
}

.apex .apex-catalog .shop-hero h1 {
  font-size: clamp(30px, 4vw, 42px);
  font-weight: 700;
  letter-spacing: -.5px;
  line-height: 1.15;
}

.apex .apex-catalog .shop-hero-search { display: flex; align-items: center; gap: 12px; }

.apex .apex-catalog .search-input {
  width: 240px;
  padding: 10px 20px;
  border: 1px solid var(--apex-border);
  border-radius: 100px;
  background: #fff;
  font-family: inherit;
  font-size: 14px;
  outline: none;
  transition: border-color .2s ease;
}
.apex .apex-catalog .search-input:focus { border-color: var(--apex-orange); }


/* ── Filter bar ─────────────────────────────────────────────────────────── */

/* Not sticky, by request. It scrolls away with the rest of the page.
   To bring it back: position: sticky with top: var(--apex-header-h) and a
   z-index above the cards — the offset has to clear Blocksy's fixed header
   or the bar hides underneath it. */
.apex .apex-catalog .filter-bar {
  background: var(--apex-bg-white);
  border-bottom: 1px solid var(--apex-border);
  padding-block: 14px;
}

.apex .apex-catalog .filter-bar-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

.apex .apex-catalog .filter-tabs { display: flex; gap: 8px; flex-wrap: wrap; }

.apex .apex-catalog .filter-tab {
  padding: 8px 18px;
  border: 1px solid var(--apex-border);
  border-radius: 100px;
  background: #fff;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--apex-text-gray);
  cursor: pointer;
  transition: all .2s ease;
}
.apex .apex-catalog .filter-tab:hover { border-color: var(--apex-orange); color: var(--apex-orange); }
.apex .apex-catalog .filter-tab.active {
  background: var(--apex-orange);
  border-color: var(--apex-orange);
  color: #fff;
}

.apex .apex-catalog .sort-select {
  padding: 8px 16px;
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-sm);
  background: #fff;
  font-family: inherit;
  font-size: 13px;
  color: var(--apex-text-gray);
  cursor: pointer;
  outline: none;
}


/* ── Grid ───────────────────────────────────────────────────────────────── */

.apex .apex-catalog .shop-main { padding: 40px 0 60px; }

.apex .apex-catalog .products-grid {
  display: grid;
  grid-template-columns: repeat(var(--apex-cols, 4), 1fr);
  gap: 24px;
}

/* The whole card is one link here, unlike section 9 where only the image and
   title are. Fewer tab stops, and a much bigger tap target on mobile. */
.apex .apex-catalog .product-card {
  display: flex;
  flex-direction: column;
  padding: 0;
  background: #fff;
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-md);
  overflow: hidden;
  color: inherit;
  text-decoration: none;
  transition: all .2s ease;
}
.apex .apex-catalog .product-card:hover {
  border-color: var(--apex-orange);
  box-shadow: var(--apex-shadow-lg);
  transform: translateY(-3px);
}
.apex .apex-catalog .product-card[hidden] { display: none; }

.apex .apex-catalog .product-image {
  position: relative;
  aspect-ratio: 1 / 1;
  margin: 0;
  border-radius: 0;
  background: var(--apex-bg-light);
  overflow: hidden;
}
.apex .apex-catalog .product-image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform .3s ease;
}
.apex .apex-catalog .product-card:hover .product-image img { transform: scale(1.05); }

.apex .apex-catalog .product-badges {
  position: absolute;
  top: 12px;
  left: 12px;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
}
.apex .apex-catalog .badge {
  padding: 3px 10px;
  border-radius: 100px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .3px;
}
.apex .apex-catalog .badge-instock    { background: #d1fae5; color: #065f46; }
.apex .apex-catalog .badge-lowstock   { background: #fef3c7; color: #92400e; }
.apex .apex-catalog .badge-outofstock { background: #f3f4f6; color: var(--apex-text-muted); }
.apex .apex-catalog .badge-bestseller { background: var(--apex-orange); color: #fff; }
.apex .apex-catalog .badge-new        { background: #dbeafe; color: #1e40af; }


/* ── Coming soon ────────────────────────────────────────────────────────── */

/* Set per product by the "Coming soon" checkbox in Product data > General.
   The same flag also blocks purchase server-side — see apex_is_coming_soon()
   in the snippet. This is only the visual half. */

.apex .apex-catalog .product-card.is-coming-soon .product-image img {
  /* scale() is not decoration. filter: blur() samples beyond the element's
     edges, leaving a soft translucent border where the background shows
     through; overscaling pushes that fringe outside the clip box. */
  filter: blur(7px) saturate(.55);
  transform: scale(1.1);
}

/* Cancel the hover zoom — the image is not the point on these cards, and
   animating a blurred image just draws attention to the blur. */
.apex .apex-catalog .product-card.is-coming-soon:hover .product-image img {
  transform: scale(1.1);
}

/* Two lines: the state, then the reassurance. A single run of uppercase text
   at this tracking is hard to scan, and "back soon" is the part that keeps
   someone from writing the product off. */
.apex .apex-catalog .coming-soon-overlay {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 8px;
  background: rgba(255, 255, 255, .45);
  color: var(--apex-text-dark);
  text-align: center;
}
.apex .apex-catalog .coming-soon-overlay strong {
  font-size: 13px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1.6px;
  line-height: 1.2;
}
.apex .apex-catalog .coming-soon-overlay span {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 1.2px;
  color: var(--apex-text-gray);
}

/* The price stays legible but recedes — it is information, not an offer. */
.apex .apex-catalog .product-card.is-coming-soon .product-price { color: var(--apex-text-gray); }

/* Not clickable, so it must not behave as though it is. The snippet emits a
   <div> rather than an <a> for these, and this strips the remaining hover
   affordances the shared .product-card rule provides — the lift and the
   orange border read as "this responds to you" even with no link behind it. */
.apex .apex-catalog .product-card.is-coming-soon { cursor: default; }
.apex .apex-catalog .product-card.is-coming-soon:hover {
  border-color: var(--apex-border);
  box-shadow: none;
  transform: none;
}

.apex .apex-catalog .product-body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: 16px 20px 20px;
}

.apex .apex-catalog .product-category {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .8px;
  color: var(--apex-orange);
  margin-bottom: 6px;
}

.apex .apex-catalog .product-name {
  font-size: 17px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--apex-text-dark);
  margin-bottom: 4px;
}

.apex .apex-catalog .product-meta {
  font-size: 12px;
  color: var(--apex-text-gray);
  margin-bottom: 16px;
}

.apex .apex-catalog .product-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: auto;
}

/* Dark, not orange — on this page the "View →" carries the accent colour, and
   two orange elements in one row fight each other. */
.apex .apex-catalog .product-price {
  font-size: 18px;
  font-weight: 700;
  color: var(--apex-text-dark);
}
.apex .apex-catalog .product-price .woocommerce-Price-amount { color: inherit; font: inherit; }
.apex .apex-catalog .product-price del { opacity: .5; font-weight: 500; margin-right: 6px; }

.apex .apex-catalog .view-btn {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 13px;
  font-weight: 600;
  color: var(--apex-orange);
  transition: gap .2s ease;
}
.apex .apex-catalog .product-card:hover .view-btn { gap: 8px; }

.apex .apex-catalog .empty-state {
  padding: 48px 0;
  text-align: center;
  color: var(--apex-text-gray);
}


/* ── Support strip ──────────────────────────────────────────────────────── */

.apex .apex-catalog .support-strip {
  background: var(--apex-bg-light);
  border-top: 1px solid var(--apex-border);
  padding: 56px 0;
  text-align: center;
}
.apex .apex-catalog .support-strip h3 { font-size: 24px; font-weight: 700; margin-bottom: 10px; }
.apex .apex-catalog .support-strip p { font-size: 15px; color: var(--apex-text-gray); margin-bottom: 24px; }

.apex .apex-catalog .btn-support {
  display: inline-block;
  padding: 14px 32px;
  border-radius: 100px;
  background: var(--apex-orange);
  color: #fff;
  font-size: 15px;
  font-weight: 600;
  transition: background .2s ease;
}
.apex .apex-catalog .btn-support:hover { background: var(--apex-orange-hover); color: #fff; }


/* ── Responsive ─────────────────────────────────────────────────────────── */

@media (max-width: 1024px) {
  .apex .apex-catalog .products-grid { grid-template-columns: repeat(3, 1fr); }
}

@media (max-width: 860px) {
  .apex .apex-catalog .shop-hero-inner { flex-direction: column; align-items: flex-start; gap: 16px; }
  .apex .apex-catalog .shop-hero-search,
  .apex .apex-catalog .search-input { width: 100%; }
  .apex .apex-catalog .products-grid { grid-template-columns: repeat(2, 1fr); gap: 16px; }
}

@media (max-width: 600px) {
  .apex .apex-catalog .products-grid { gap: 12px; }
  .apex .apex-catalog .product-body { padding: 12px 14px 16px; }
  .apex .apex-catalog .product-name { font-size: 14px; }
  .apex .apex-catalog .product-price { font-size: 15px; }
  .apex .apex-catalog .filter-tab { font-size: 12px; padding: 6px 14px; }
  .apex .apex-catalog .filter-tabs { gap: 6px; }
}


/* ── 21. CART PAGE ───────────────────────────────────────────────────────── */

/* Scoped to body.woocommerce-cart rather than an .apex wrapper.
 *
 * The cart comes from [woocommerce_cart], so there is no wrapper element to
 * hang .apex on without a template override or an unbalanced open/close pair
 * across two hooks. The body class WooCommerce already sets is a stable
 * anchor and needs no PHP at all. Sections 14 and 18 make the same exception
 * for the same reason.
 *
 * Woo's markup, for reference:
 *   form.woocommerce-cart-form > table.shop_table.cart
 *     tr.cart_item > td.product-thumbnail | .product-name | .product-quantity
 *                    | .product-subtotal | .product-remove
 *     td.actions > .coupon + button[name=update_cart]
 *   .cart_totals > table > tr.cart-subtotal, tr.order-total
 *   .wc-proceed-to-checkout > a.checkout-button                                */


/* ── Page shell ─────────────────────────────────────────────────────────── */

/* The .apex wrapper trips the un-boxing rule in section 3, so the Elementor
   container spans the viewport. That is what lets the hero and support bands
   run edge to edge — but it also means Woo's own output needs constraining
   back to the page gutter, which .container does for the other blocks. */
.apex-cart .woocommerce {
  max-width: 1280px;
  margin-inline: auto;
  padding: 0 24px 56px;
}

/* Do NOT build a two-column grid here.
 *
 * Blocksy already wraps the form and the totals in .ct-woocommerce-cart-form
 * and makes that a grid — measured at 1600px: 833px + 417px with a 40px gap.
 * The form and .cart_totals are children of THAT wrapper, not siblings under
 * .woocommerce, so a grid on .woocommerce would produce one column and change
 * nothing. Same shape as .product-entry-wrapper on the single product page.
 *
 * All that is wanted is a narrower totals column and for it to stay in view
 * while a long item list scrolls. */
@media (min-width: 1000px) {
  body.woocommerce-cart .ct-woocommerce-cart-form {
    grid-template-columns: minmax(0, 1fr) 360px;
    align-items: start;
  }
  body.woocommerce-cart .cart_totals {
    position: sticky;
    top: 120px;
  }
}

/* Notices land inside Blocksy's grid; span them rather than letting them take
   a column. The step bar lives in the hero now, outside the grid entirely. */
body.woocommerce-cart .woocommerce-notices-wrapper {
  grid-column: 1 / -1;
}


/* ── Hero band ──────────────────────────────────────────────────────────── */

.apex-cart .apex-cart-hero {
  background: var(--apex-bg-light);
  border-bottom: 1px solid var(--apex-border);
  padding: 48px 0 40px;
  margin-bottom: 40px;
}
.apex-cart .apex-cart-hero h1 {
  font-size: clamp(30px, 4vw, 42px);
  font-weight: 700;
  letter-spacing: -.5px;
  line-height: 1.15;
}


/* ── Step indicator ─────────────────────────────────────────────────────── */

.apex-cart .apex-cart-steps {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 24px;
}
.apex-cart .apex-cart-step {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 600;
  color: var(--apex-text-dark);
}
.apex-cart .apex-cart-step-num {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--apex-orange);
  color: #fff;
  font-size: 12px;
}
.apex-cart .apex-cart-step.is-inactive { color: var(--apex-text-gray); opacity: .5; }
.apex-cart .apex-cart-step.is-inactive .apex-cart-step-num {
  background: var(--apex-border);
  color: var(--apex-text-gray);
}
.apex-cart .apex-cart-step-line {
  width: 40px;
  height: 2px;
  background: var(--apex-border);
}


/* ── Trust strip ────────────────────────────────────────────────────────── */

/* Sits below the cart and the coupon row, inside the page gutter. */
.apex-cart .apex-cart-trust {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  margin-bottom: 48px;
}

/* Compact: the tiles were 18px padding with a 12px icon gap, which made three
   short labels occupy a band as tall as a cart row. Tightened, with an orange
   border and a faint orange wash so they read as one connected group rather
   than three more white boxes on a white page. */
.apex-cart .apex-cart-trust-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  background: var(--apex-orange-fade);
  border: 1px solid color-mix(in srgb, var(--apex-orange) 30%, transparent);
  border-radius: var(--apex-radius-md);
  box-shadow: var(--apex-shadow-sm);
}
.apex-cart .apex-cart-trust-text strong {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: var(--apex-text-dark);
}
.apex-cart .apex-cart-trust-text span {
  font-size: 11px;
  color: var(--apex-text-gray);
}

/* Masked SVG rather than emoji — emoji render at a different size on every
   platform and cannot take the brand colour. */
.apex-cart .apex-cart-trust-icon {
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  background: var(--apex-orange);
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
  -webkit-mask-size: contain;
          mask-size: contain;
}
.apex-cart .apex-ct-icon--test {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 2v7l-6.5 11A2 2 0 0 0 4.24 23h15.52a2 2 0 0 0 1.74-3L15 9V2'/%3E%3Cpath d='M8.5 2h7'/%3E%3Cpath d='M10 14h4'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 2v7l-6.5 11A2 2 0 0 0 4.24 23h15.52a2 2 0 0 0 1.74-3L15 9V2'/%3E%3Cpath d='M8.5 2h7'/%3E%3Cpath d='M10 14h4'/%3E%3C/svg%3E");
}
.apex-cart .apex-ct-icon--ship {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z'/%3E%3Cpolyline points='3.27 6.96 12 12.01 20.73 6.96'/%3E%3Cline x1='12' y1='22.08' x2='12' y2='12'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z'/%3E%3Cpolyline points='3.27 6.96 12 12.01 20.73 6.96'/%3E%3Cline x1='12' y1='22.08' x2='12' y2='12'/%3E%3C/svg%3E");
}
.apex-cart .apex-ct-icon--free {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='1' y='3' width='15' height='13'/%3E%3Cpolygon points='16 8 20 8 23 11 23 16 16 16 16 8'/%3E%3Ccircle cx='5.5' cy='18.5' r='2.5'/%3E%3Ccircle cx='18.5' cy='18.5' r='2.5'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='1' y='3' width='15' height='13'/%3E%3Cpolygon points='16 8 20 8 23 11 23 16 16 16 16 8'/%3E%3Ccircle cx='5.5' cy='18.5' r='2.5'/%3E%3Ccircle cx='18.5' cy='18.5' r='2.5'/%3E%3C/svg%3E");
}
.apex-cart .apex-ct-icon--secure {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='11' width='18' height='11' rx='2'/%3E%3Cpath d='M7 11V7a5 5 0 0 1 10 0v4'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='11' width='18' height='11' rx='2'/%3E%3Cpath d='M7 11V7a5 5 0 0 1 10 0v4'/%3E%3C/svg%3E");
}


/* ── Support strip ──────────────────────────────────────────────────────── */

.apex-cart .apex-cart-support {
  background: var(--apex-bg-light);
  border-top: 1px solid var(--apex-border);
  padding: 48px 0;
  text-align: center;
}
.apex-cart .apex-cart-support h3 { font-size: 22px; font-weight: 700; margin-bottom: 8px; }
.apex-cart .apex-cart-support p { font-size: 14px; color: var(--apex-text-gray); margin-bottom: 20px; }
.apex-cart .apex-cart-support-btn {
  display: inline-block;
  padding: 12px 28px;
  border-radius: 100px;
  background: var(--apex-orange);
  color: #fff;
  font-size: 14px;
  font-weight: 600;
}
.apex-cart .apex-cart-support-btn:hover { background: var(--apex-orange-hover); color: #fff; }


/* ── Item table, rebuilt as rows ────────────────────────────────────────── */

/* The header row is dropped and each item becomes a card-like row. A four
   column table works on a spreadsheet, not on a phone, and Woo's responsive
   fallback stacks it into unlabelled fragments. */
body.woocommerce-cart table.shop_table.cart {
  width: 100%;
  border: 0;
  border-collapse: collapse;
  margin: 0;
}
body.woocommerce-cart table.shop_table.cart thead { display: none; }

body.woocommerce-cart tr.cart_item {
  display: grid;
  grid-template-columns: 88px minmax(0, 1fr) auto auto;
  gap: 18px;
  align-items: center;
  padding: 20px 0;
  border-bottom: 1px solid var(--apex-border);
}
body.woocommerce-cart tr.cart_item td {
  border: 0;
  padding: 0;
  background: none;
}

body.woocommerce-cart td.product-thumbnail { grid-column: 1; }
body.woocommerce-cart td.product-thumbnail img {
  display: block;
  width: 88px;
  height: 88px;
  border-radius: var(--apex-radius-sm);
  border: 1px solid var(--apex-border);
  background: var(--apex-bg-light);
  object-fit: cover;
}

body.woocommerce-cart td.product-name { grid-column: 2; font-size: 15px; }
body.woocommerce-cart td.product-name > a {
  display: block;
  font-size: 16px;
  font-weight: 600;
  color: var(--apex-text-dark);
  margin-bottom: 4px;
}
body.woocommerce-cart td.product-name > a:hover { color: var(--apex-orange); }

/* Woo prints the per-item meta (our "Bulk discount: −5%") as a dl in here. */
body.woocommerce-cart td.product-name dl.variation,
body.woocommerce-cart td.product-name .wc-item-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 8px;
  margin: 4px 0 0;
  padding: 0;
  font-size: 13px;
  color: var(--apex-text-gray);
}
body.woocommerce-cart td.product-name dt,
body.woocommerce-cart td.product-name dd { margin: 0; padding: 0; }
body.woocommerce-cart td.product-name dd p { margin: 0; }

body.woocommerce-cart td.product-quantity { grid-column: 3; }
body.woocommerce-cart td.product-quantity .quantity {
  display: flex;
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-sm);
  overflow: hidden;
}
/* Width has to clear Blocksy's own padding, not fight it.
 *
 * Blocksy's type-2 quantity overlays two absolutely positioned 22px arrows
 * (.ct-increase / .ct-decrease) and pads the input by ~31px each side to keep
 * the number clear of them. Its natural size is 89px wide inside a 100px
 * wrapper. Forcing width:64px left 62px of padding in a 64px box — about 2px
 * for the digit, which is why the number looked hidden and off-centre.
 * 104px gives the number a comfortable 42px between the arrows. */
body.woocommerce-cart td.product-quantity input.qty {
  width: 104px;
  height: 42px;
  border: 0;
  background: #fff;
  text-align: center;
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  color: var(--apex-text-dark);
}

body.woocommerce-cart td.product-subtotal {
  grid-column: 4;
  min-width: 90px;
  text-align: right;
  font-size: 16px;
  font-weight: 700;
  color: var(--apex-text-dark);
}

/* Do NOT un-hide .product-quantity below 1000px.
 *
 * Blocksy hides .product-quantity, .product-subtotal and .product-remove there
 * because it renders a complete replacement inside td.product-name:
 *
 *   .product-mobile-actions.ct-hidden-lg
 *     .quantity  ×  .amount  a.remove
 *
 * An earlier version of this file forced .product-quantity back on, having
 * read the hidden cell as a missing control. It was not missing — it had
 * moved, and forcing it back produced TWO quantity steppers on every mobile
 * row. Style Blocksy's mobile block instead; it already carries the price and
 * the remove link alongside. */

body.woocommerce-cart .product-mobile-actions {
  gap: 10px;
  margin-top: 12px;
}
body.woocommerce-cart .product-mobile-actions .amount {
  font-weight: 700;
  color: var(--apex-text-dark);
}
body.woocommerce-cart .product-mobile-actions .ct-product-multiply-symbol {
  color: var(--apex-text-muted);
}
body.woocommerce-cart .product-mobile-actions .remove {
  margin-left: auto;
  color: var(--apex-text-muted);
}
body.woocommerce-cart .product-mobile-actions .remove:hover { color: var(--apex-orange); }

/* Woo puts remove in its own cell; pull it onto the name row rather than
   giving it a fifth column that would squeeze everything else. */
body.woocommerce-cart td.product-remove {
  grid-column: 2;
  grid-row: 2;
  justify-self: start;
}
body.woocommerce-cart td.product-remove a.remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: transparent;
  color: var(--apex-text-muted);
  font-size: 18px;
  line-height: 1;
}
body.woocommerce-cart td.product-remove a.remove:hover {
  background: var(--apex-orange-fade);
  color: var(--apex-orange);
}


/* ── Coupon + update row ────────────────────────────────────────────────── */

body.woocommerce-cart td.actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding: 24px 0 0;
  border: 0;
}
body.woocommerce-cart td.actions .coupon {
  display: flex;
  gap: 8px;
  flex: 1 1 260px;
}
body.woocommerce-cart td.actions input#coupon_code {
  flex: 1 1 auto;
  min-width: 0;
  height: 44px;
  padding: 0 16px;
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-sm);
  background: #fff;
  font-family: inherit;
  font-size: 14px;
}
body.woocommerce-cart td.actions input#coupon_code:focus {
  outline: none;
  border-color: var(--apex-orange);
}

/* Both buttons here are secondary — the primary action is Proceed to
   checkout in the totals card. Solid orange on all three would flatten the
   hierarchy and slow the eye down. */
body.woocommerce-cart td.actions .button {
  height: 44px;
  padding: 0 20px;
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-sm);
  background: #fff;
  color: var(--apex-text-dark);
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
}
body.woocommerce-cart td.actions .button:hover:not(:disabled) {
  border-color: var(--apex-orange);
  background: #fff;
  color: var(--apex-orange);
}
body.woocommerce-cart td.actions .button:disabled { opacity: .45; }

/* Quantity changes commit themselves after a short pause (see the cart
   snippet), so the manual Update cart button is redundant. Hidden rather than
   removed from the markup: the auto-update works by clicking it, so it has to
   stay in the DOM. Apply coupon is unaffected. */
body.woocommerce-cart td.actions button[name="update_cart"] {
  display: none;
}

/* Brief dimming while the page reloads with the new quantity, so a slow
   connection does not look like nothing happened. */
body.woocommerce-cart form.apex-cart-updating {
  opacity: .55;
  pointer-events: none;
  transition: opacity .15s ease;
}


/* ── Totals card ────────────────────────────────────────────────────────── */

body.woocommerce-cart .cart_totals {
  padding: 24px;
  background: var(--apex-bg-light);
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-md);
}
body.woocommerce-cart .cart_totals h2 {
  font-size: 17px;
  font-weight: 700;
  margin-bottom: 16px;
}
body.woocommerce-cart .cart_totals table {
  width: 100%;
  border: 0;
  border-collapse: collapse;
  margin: 0;
}
body.woocommerce-cart .cart_totals tr { border: 0; }
body.woocommerce-cart .cart_totals th,
body.woocommerce-cart .cart_totals td {
  padding: 10px 0;
  border: 0;
  border-bottom: 1px solid var(--apex-border);
  background: none;
  font-size: 14px;
  vertical-align: top;
}
body.woocommerce-cart .cart_totals th {
  width: 45%;
  text-align: left;
  font-weight: 600;
  color: var(--apex-text-gray);
}
body.woocommerce-cart .cart_totals td { text-align: right; color: var(--apex-text-dark); }

body.woocommerce-cart .cart_totals tr.order-total th,
body.woocommerce-cart .cart_totals tr.order-total td {
  border-bottom: 0;
  padding-top: 14px;
  font-size: 18px;
  font-weight: 700;
  color: var(--apex-text-dark);
}

body.woocommerce-cart .wc-proceed-to-checkout { padding: 18px 0 0; }
body.woocommerce-cart a.checkout-button {
  display: block;
  width: 100%;
  height: 52px;
  line-height: 52px;
  padding: 0;
  border: 0;
  border-radius: var(--apex-radius-sm);
  background: linear-gradient(90deg, var(--apex-orange) 0%, #f7a05a 100%);
  color: #fff;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: .3px;
  text-align: center;
  transition: opacity .2s ease, transform .15s ease;
}
body.woocommerce-cart a.checkout-button:hover {
  opacity: .9;
  transform: translateY(-1px);
  color: #fff;
}


/* ── Trust strip ────────────────────────────────────────────────────────── */

body.woocommerce-cart .apex-cart-trust {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  margin-top: 18px;
}
body.woocommerce-cart .apex-cart-trust-item {
  padding: 14px 12px;
  background: #fff;
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-md);
  text-align: center;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.4;
  color: var(--apex-text-dark);
}
/* Masked SVG, not emoji — emoji render at different sizes per platform and
   cannot take the brand colour. Same approach as the product page. */
body.woocommerce-cart .apex-cart-trust-icon {
  display: block;
  width: 20px;
  height: 20px;
  margin: 0 auto 6px;
  background: var(--apex-orange);
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
  -webkit-mask-size: contain;
          mask-size: contain;
}


/* ── Empty state ────────────────────────────────────────────────────────── */

body.woocommerce-cart .cart-empty,
body.woocommerce-cart .wc-empty-cart-message {
  margin: 0 0 20px;
  padding: 0;
  background: none;
  border: 0;
  font-size: 16px;
  color: var(--apex-text-gray);
  text-align: center;
}
body.woocommerce-cart .return-to-shop { text-align: center; }


/* ── Responsive ─────────────────────────────────────────────────────────── */

/* ── Mobile ─────────────────────────────────────────────────────────────────
 *
 * The page measured 1731px tall on a 375px screen, with the hero and support
 * band alone accounting for 484px of it — mostly padding. Everything below
 * tightens vertical rhythm rather than shrinking text: the reading sizes stay,
 * the whitespace between blocks does not.
 * ------------------------------------------------------------------------ */

@media (max-width: 720px) {
  .apex-cart .woocommerce { padding: 0 16px 32px; }

  /* Hero: 48/40 padding plus a 40px margin was 128px of empty band before the
     first product. */
  .apex-cart .apex-cart-hero {
    padding: 26px 0 22px;
    margin-bottom: 24px;
  }
  .apex-cart .apex-cart-steps { margin-top: 16px; gap: 6px; }
  .apex-cart .apex-cart-step { font-size: 12px; gap: 6px; }
  .apex-cart .apex-cart-step-num { width: 20px; height: 20px; font-size: 11px; }
  .apex-cart .apex-cart-step-line { width: 16px; }

  /* Thumbnail and name side by side; Blocksy's .product-mobile-actions sits
     under the name and carries quantity, price and remove. Only two columns
     are needed — the other cells are hidden by the theme. */
  body.woocommerce-cart tr.cart_item {
    grid-template-columns: 64px minmax(0, 1fr);
    gap: 12px;
    padding: 14px 0;
  }
  body.woocommerce-cart td.product-thumbnail img { width: 64px; height: 64px; }
  body.woocommerce-cart td.product-name > a { font-size: 15px; }

  body.woocommerce-cart td.actions {
    flex-direction: column;
    align-items: stretch;
    padding-top: 18px;
  }

  /* flex: 1 1 260px on .coupon is a WIDTH basis in the desktop row. Once
     td.actions becomes a column the same 260px is read as a HEIGHT, which
     stretched the input to 197px tall. Reset the basis and stack the field
     above its button. */
  body.woocommerce-cart td.actions .coupon {
    flex: 0 0 auto;
    flex-direction: column;
    gap: 8px;
  }
  body.woocommerce-cart td.actions input#coupon_code {
    flex: 0 0 auto;
    width: 100%;
    height: 44px;
  }

  body.woocommerce-cart .cart_totals { padding: 18px; margin-top: 20px; }
  body.woocommerce-cart .cart_totals h2 { font-size: 16px; margin-bottom: 10px; }
  body.woocommerce-cart .cart_totals th,
  body.woocommerce-cart .cart_totals td { padding: 8px 0; }
  body.woocommerce-cart .wc-proceed-to-checkout { padding-top: 14px; }
  body.woocommerce-cart a.checkout-button { height: 48px; line-height: 48px; }

  /* One column, tighter than the desktop tiles — a full-width row holding one
     short label does not need 12px of vertical padding. */
  .apex-cart .apex-cart-trust {
    grid-template-columns: 1fr;
    gap: 8px;
    margin-bottom: 28px;
  }
  .apex-cart .apex-cart-trust-item { padding: 9px 12px; }

  .apex-cart .apex-cart-support { padding: 32px 0; }
  .apex-cart .apex-cart-support h3 { font-size: 19px; }
  .apex-cart .apex-cart-support p { margin-bottom: 16px; }
}


/* ── 22. COA DATABASE PAGE ───────────────────────────────────────────────── */

/* Markup from [apex_coa_database] in the apex-products snippet.
 *
 * Not to be confused with section 13, which styles .apex-coa-table — a
 * table-based per-product batch list emitted by apex_render_batch_table().
 * That function was never written, so section 13 is currently inert. Left in
 * place because a per-product batch history is still on the Phase 5 list;
 * this section is the standalone repository page, which is a different thing.
 *
 * The .apex wrapper trips the un-boxing rule in section 3, so the hero band
 * spans the viewport while .container holds the content to the page gutter —
 * same arrangement as the cart page. */


/* ── Hero ───────────────────────────────────────────────────────────────── */

.apex-coa-db .apex-coa-hero {
  background: var(--apex-bg-light);
  border-bottom: 1px solid var(--apex-border);
  padding: 48px 0 40px;
}
.apex-coa-db .apex-coa-hero h1 {
  font-size: clamp(28px, 4vw, 40px);
  font-weight: 700;
  letter-spacing: -.5px;
  line-height: 1.15;
  margin-bottom: 14px;
}
.apex-coa-db .apex-coa-intro {
  max-width: 640px;
  font-size: 15px;
  line-height: 1.7;
  color: var(--apex-text-gray);
  margin-bottom: 24px;
}


/* ── Search ─────────────────────────────────────────────────────────────── */

.apex-coa-db .apex-coa-search {
  position: relative;
  max-width: 560px;
}
.apex-coa-db .apex-coa-search-icon {
  position: absolute;
  top: 50%;
  left: 18px;
  width: 18px;
  height: 18px;
  transform: translateY(-50%);
  color: var(--apex-text-muted);
  pointer-events: none;
}
.apex-coa-db .apex-coa-search-icon svg { width: 100%; height: 100%; display: block; }

.apex-coa-db .apex-coa-search input {
  width: 100%;
  height: 52px;
  /* Left padding clears the icon; without it the caret starts under the glass. */
  padding: 0 20px 0 48px;
  border: 1px solid var(--apex-border);
  border-radius: 100px;
  background: #fff;
  font-family: inherit;
  font-size: 15px;
  color: var(--apex-text-dark);
  outline: none;
  transition: border-color .2s ease, box-shadow .2s ease;
}
.apex-coa-db .apex-coa-search input:focus {
  border-color: var(--apex-orange);
  box-shadow: 0 0 0 3px var(--apex-orange-fade);
}


/* ── List ───────────────────────────────────────────────────────────────── */

.apex-coa-db .apex-coa-list-section { padding: 40px 0 72px; }

.apex-coa-db .apex-coa-list {
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-md);
  overflow: hidden;
  background: #fff;
}

/* One column template shared by the header and every row.
 *
 * The last track MUST be a fixed width, not `auto`. The header and each row
 * are separate grids, so an auto track sizes to its own content in each —
 * measured at 86px in the header ("Document") against 133px in a row (the
 * button). That 47px difference came out of the fr tracks, shifting every
 * value left of its column heading by a growing amount across the table.
 *
 * 140px fits "View report" with room to spare. If the button label changes,
 * check this still clears it — a fixed track that is too narrow will wrap the
 * button instead of misaligning the table, which is at least obvious. */
.apex-coa-db .apex-coa-list-header,
.apex-coa-db .apex-coa-row {
  display: grid;
  grid-template-columns: minmax(0, 2.2fr) minmax(0, 1.4fr) minmax(0, 1fr) minmax(0, 1fr) 140px;
  gap: 16px;
  align-items: center;
}

.apex-coa-db .apex-coa-list-header {
  padding: 14px 20px;
  background: var(--apex-bg-light);
  border-bottom: 1px solid var(--apex-border);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--apex-text-gray);
}

.apex-coa-db .apex-coa-row {
  padding: 16px 20px;
  border-bottom: 1px solid var(--apex-border);
  transition: background .15s ease;
}
.apex-coa-db .apex-coa-row:last-child { border-bottom: 0; }
.apex-coa-db .apex-coa-row:hover { background: var(--apex-orange-fade); }
.apex-coa-db .apex-coa-row[hidden] { display: none; }

.apex-coa-db .apex-coa-col-product {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}
.apex-coa-db .apex-coa-file-icon {
  flex-shrink: 0;
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--apex-radius-sm);
  background: var(--apex-orange-fade);
  color: var(--apex-orange);
}
.apex-coa-db .apex-coa-file-icon svg { width: 17px; height: 17px; }

.apex-coa-db .apex-coa-product-text { min-width: 0; }
.apex-coa-db .apex-coa-name {
  display: block;
  font-size: 15px;
  font-weight: 600;
  color: var(--apex-text-dark);
  line-height: 1.3;
}
.apex-coa-db .apex-coa-name:hover { color: var(--apex-orange); }
.apex-coa-db .apex-coa-size {
  display: block;
  font-size: 12px;
  color: var(--apex-text-gray);
}

/* Monospace so batch codes align down the column and a transposed character
   is easy to spot when someone is checking a code against a vial. */
.apex-coa-db .apex-coa-batch {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px;
  font-weight: 600;
  color: var(--apex-text-dark);
}

.apex-coa-db .apex-coa-purity {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 100px;
  background: #d1fae5;
  color: #065f46;
  font-size: 12px;
  font-weight: 700;
}

.apex-coa-db .apex-coa-col-date { font-size: 13px; color: var(--apex-text-gray); }
.apex-coa-db .apex-coa-muted { color: var(--apex-text-muted); }

.apex-coa-db .apex-coa-btn {
  display: inline-block;
  padding: 9px 18px;
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-sm);
  background: #fff;
  font-size: 13px;
  font-weight: 600;
  color: var(--apex-text-dark);
  white-space: nowrap;
  transition: all .2s ease;
}
.apex-coa-db .apex-coa-btn:hover {
  background: var(--apex-orange);
  border-color: var(--apex-orange);
  color: #fff;
}

.apex-coa-db .apex-coa-empty,
.apex-coa-db .apex-coa-empty-all {
  padding: 40px 20px;
  text-align: center;
  color: var(--apex-text-gray);
  font-size: 15px;
}
.apex-coa-db .apex-coa-empty-all {
  border: 1px dashed var(--apex-border);
  border-radius: var(--apex-radius-md);
}


/* ── Mobile ─────────────────────────────────────────────────────────────── */

@media (max-width: 860px) {
  .apex-coa-db .apex-coa-hero { padding: 30px 0 26px; }
  .apex-coa-db .apex-coa-list-section { padding: 24px 0 48px; }

  /* The header row goes and each row becomes a card. data-label on the cells
     carries the column name, so the values stay identifiable without a header
     to read them against — a five-column table on a 375px screen is not a
     table anyone can use. */
  .apex-coa-db .apex-coa-list-header { display: none; }

  .apex-coa-db .apex-coa-row {
    grid-template-columns: 1fr auto;
    gap: 10px 14px;
    padding: 16px;
  }
  .apex-coa-db .apex-coa-col-product { grid-column: 1 / -1; }

  .apex-coa-db .apex-coa-col-batch,
  .apex-coa-db .apex-coa-col-purity,
  .apex-coa-db .apex-coa-col-date {
    display: flex;
    align-items: baseline;
    gap: 8px;
    grid-column: 1;
    font-size: 13px;
  }
  .apex-coa-db .apex-coa-col-batch::before,
  .apex-coa-db .apex-coa-col-purity::before,
  .apex-coa-db .apex-coa-col-date::before {
    content: attr(data-label) ':';
    flex-shrink: 0;
    min-width: 54px;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .6px;
    color: var(--apex-text-muted);
  }

  .apex-coa-db .apex-coa-col-action {
    grid-column: 2;
    grid-row: 2 / span 3;
    align-self: center;
  }
}

@media (max-width: 480px) {
  .apex-coa-db .apex-coa-row { grid-template-columns: 1fr; }
  .apex-coa-db .apex-coa-col-action { grid-column: 1; grid-row: auto; margin-top: 4px; }
  .apex-coa-db .apex-coa-btn { display: block; text-align: center; }
}


/* ── 23. ORDER TRACKING PAGE ─────────────────────────────────────────────── */

/* Wrapper from the do_shortcode_tag filter in the apex-cart snippet; the form
 * itself is WooCommerce's own [woocommerce_order_tracking] output.
 *
 * The hero reuses .apex-cart-hero and the footer reuses .apex-cart-support
 * from section 21 — same components, so the two pages cannot drift apart.
 * Only the form and the result view are new here.
 *
 * Woo's markup:
 *   form.track_order
 *     p                          intro sentence
 *     p.form-row.form-row-first  > label + input#orderid
 *     p.form-row.form-row-last   > label + input#order_email
 *     p.form-row                 > button.button
 *   after a successful lookup: ul.woocommerce-order-overview + order details */

.apex-tracking .apex-tracking-intro {
  max-width: 560px;
  font-size: 15px;
  line-height: 1.7;
  color: var(--apex-text-gray);
}

.apex-tracking .apex-tracking-body {
  padding-block: 40px 64px;
}


/* ── The lookup form ────────────────────────────────────────────────────── */

.apex-tracking form.track_order {
  max-width: 620px;
  margin: 0 auto;
  padding: 32px;
  background: #fff;
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-md);
  box-shadow: var(--apex-shadow-sm);
  /* Grid rather than Woo's float-based form-row-first / form-row-last, which
     is what .clear divs exist to prop up. Two fields side by side on desktop,
     stacked below 600px. */
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
}

/* Woo emits <div class="clear"> between the float rows. Harmless in a grid,
   but it would occupy a cell and knock the button into the wrong column. */
.apex-tracking form.track_order .clear { display: none; }

/* The intro sentence and the submit row span both columns. Woo gives the
   intro no class at all, hence the :first-child reach. */
.apex-tracking form.track_order > p:first-child {
  grid-column: 1 / -1;
  margin: 0;
  font-size: 14px;
  line-height: 1.6;
  color: var(--apex-text-gray);
}

.apex-tracking form.track_order .form-row { margin: 0; }
.apex-tracking form.track_order .form-row-first { grid-column: 1; }
.apex-tracking form.track_order .form-row-last  { grid-column: 2; }

/* The submit row carries no first/last class, so it is the one .form-row that
   is neither — target it by exclusion and let it span. */
.apex-tracking form.track_order .form-row:not(.form-row-first):not(.form-row-last) {
  grid-column: 1 / -1;
  margin-top: 4px;
}

.apex-tracking form.track_order label {
  display: block;
  margin-bottom: 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--apex-text-dark);
}

.apex-tracking form.track_order input.input-text {
  width: 100%;
  height: 48px;
  padding: 0 16px;
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-sm);
  background: #fff;
  font-family: inherit;
  font-size: 15px;
  color: var(--apex-text-dark);
  outline: none;
  transition: border-color .2s ease, box-shadow .2s ease;
}
.apex-tracking form.track_order input.input-text::placeholder {
  color: var(--apex-text-muted);
  font-size: 14px;
}
.apex-tracking form.track_order input.input-text:focus {
  border-color: var(--apex-orange);
  box-shadow: 0 0 0 3px var(--apex-orange-fade);
}

.apex-tracking form.track_order button.button {
  width: 100%;
  height: 52px;
  border: 0;
  border-radius: var(--apex-radius-sm);
  background: linear-gradient(90deg, var(--apex-orange) 0%, #f7a05a 100%);
  color: #fff;
  font-family: inherit;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: .3px;
  cursor: pointer;
  transition: opacity .2s ease, transform .15s ease;
}
.apex-tracking form.track_order button.button:hover {
  opacity: .9;
  transform: translateY(-1px);
  color: #fff;
}


/* ── Result view ────────────────────────────────────────────────────────── */

/* Shown after a successful lookup, in place of the form. */
.apex-tracking .woocommerce-order-overview {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 12px;
  margin: 0 0 28px;
  padding: 0;
  list-style: none;
}
.apex-tracking .woocommerce-order-overview li {
  padding: 14px 16px;
  background: var(--apex-bg-light);
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-md);
  font-size: 15px;
  font-weight: 700;
  color: var(--apex-text-dark);
  /* Woo puts the label in a <strong> INSIDE the li, after the label text —
     the whole li is "Order number: 1234". Splitting them visually needs the
     strong on its own line. */
}
.apex-tracking .woocommerce-order-overview li strong {
  display: block;
  margin-top: 2px;
  font-size: 15px;
}

.apex-tracking .woocommerce-order-details,
.apex-tracking .woocommerce-customer-details {
  margin-bottom: 28px;
}
.apex-tracking .woocommerce-order-details h2,
.apex-tracking .woocommerce-customer-details h2 {
  font-size: 18px;
  font-weight: 700;
  margin-bottom: 14px;
}
.apex-tracking table.shop_table {
  width: 100%;
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-md);
  border-collapse: separate;
  border-spacing: 0;
  overflow: hidden;
  background: #fff;
}
.apex-tracking table.shop_table th,
.apex-tracking table.shop_table td {
  padding: 12px 16px;
  border: 0;
  border-bottom: 1px solid var(--apex-border);
  font-size: 14px;
  text-align: left;
}
.apex-tracking table.shop_table th {
  background: var(--apex-bg-light);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .8px;
  color: var(--apex-text-gray);
}
.apex-tracking table.shop_table tr:last-child td { border-bottom: 0; }


/* ── Notices ────────────────────────────────────────────────────────────── */

/* Woo prints "Order not found" or a validation error as a .woocommerce-error
   or .woocommerce-info above the form. */
.apex-tracking .woocommerce-error,
.apex-tracking .woocommerce-info,
.apex-tracking .woocommerce-message {
  max-width: 620px;
  margin: 0 auto 20px;
  padding: 14px 18px;
  border-radius: var(--apex-radius-sm);
  list-style: none;
  font-size: 14px;
}
.apex-tracking .woocommerce-error {
  background: #fef2f2;
  border: 1px solid #fecaca;
  color: #991b1b;
}
.apex-tracking .woocommerce-info,
.apex-tracking .woocommerce-message {
  background: var(--apex-orange-fade);
  border: 1px solid color-mix(in srgb, var(--apex-orange) 30%, transparent);
  color: var(--apex-text-dark);
}


/* ── Mobile ─────────────────────────────────────────────────────────────── */

@media (max-width: 600px) {
  .apex-tracking .apex-tracking-body { padding-block: 24px 44px; }
  .apex-tracking form.track_order {
    grid-template-columns: 1fr;
    padding: 20px;
    gap: 14px;
  }
  .apex-tracking form.track_order .form-row-first,
  .apex-tracking form.track_order .form-row-last { grid-column: 1; }
  .apex-tracking table.shop_table th,
  .apex-tracking table.shop_table td { padding: 10px 12px; }
}


/* ── 24. FOOTER ──────────────────────────────────────────────────────────── */

/* Blocksy's footer builder markup, not .apex — same exception as sections 14,
 * 15, 18 and 21.
 *
 *   footer.ct-footer
 *     [data-row="top"]    > .ct-container > [data-column="…"] × n
 *     [data-row="middle"] > widget-area-1   (the research-use-only notice)
 *     [data-row="bottom"] > .ct-footer-copyright
 *
 * Colours are set by rebinding Blocksy's own variables rather than by
 * overriding each rule. Blocksy reads --theme-link-* in several places, and
 * chasing them individually is how one state gets missed — the same lesson as
 * the menu hover and the cart icon. */

.ct-footer {
  /* Body copy at pure white on navy is harsh at 14px and flattens the
     hierarchy: everything shouts equally. Muted grey for prose and links,
     white reserved for headings and hover. */
  --theme-text-color: #9ca3af;
  --theme-link-initial-color: #9ca3af;
  --theme-link-hover-color: var(--apex-orange);
}

/* Vertical rhythm.
 *
 * The padding lives on the INNER .ct-container, not the row, and Blocksy sets
 * it from --container-spacing in a rule at (0,3,1):
 *
 *   [data-footer*="type-1"] .ct-footer [data-row*="top"] > div {
 *     --container-spacing: 26px; --columns-gap: 23px; --widgets-gap: 16px; }
 *
 * An earlier version of this section set padding-block on [data-row], which
 * was silently inert — the row has no padding to override. The leading
 * [data-footer] here takes this to (0,4,0) so it actually lands.
 *
 * It also set --widgets-gap here, believing that variable produced the spacing
 * between widgets in a column. It does not produce a `gap`: Blocksy spends it
 * as margin-top on each widget after the first. That distinction cost two
 * rounds of wrong fixes — first widgets with no spacing at all, then spacing
 * of gap + margin stacked. The variable is gone; the column `gap` and the
 * margin reset further down are now the only things setting that spacing. */

[data-footer] .ct-footer [data-row="top"] > .ct-container {
  padding-block: 32px 24px;
}
[data-footer] .ct-footer [data-row="middle"] > .ct-container { padding-block: 11px; }
[data-footer] .ct-footer [data-row="bottom"] > .ct-container { padding-block: 12px; }

.ct-footer [data-row="middle"],
.ct-footer [data-row="bottom"] { border-top: 1px solid rgba(255, 255, 255, .08); }


/* ── Top row columns ────────────────────────────────────────────────────── */

/* The brand column is wider; the three link columns share what is left.
 *
 * NOT repeat(auto-fit, …) — auto-fit fills the container with as many tracks
 * as will fit regardless of how many children exist, and measured here it
 * produced seven empty 0px tracks after the real ones.
 *
 * This was then grid-auto-flow: column, which laid out correctly but left a
 * large void: the brand column ran to 485px while the tallest link list was
 * 200px, so roughly 285px of the bottom-right of the footer was empty.
 *
 * So: explicit placement. display: contents dissolves the brand column and
 * promotes its widgets into THIS grid, which lets the company-details block be
 * placed ACROSS the link columns on row 3 — into the space the link lists do
 * not use. The link columns are pinned to rows 1–2 so the brand column's third
 * row cannot push them down.
 *
 * Measured at 1440px: top row 535px -> 393px, footer 646px -> 504px.
 *
 * The company block is matched by CONTENT, :has(.apex-footer-company), not by
 * the block-N id WordPress assigns it. Those ids change whenever a widget is
 * deleted and re-created, which would silently undo this layout.
 *
 * This hardcodes THREE link columns. Adding a fourth means adding a track and
 * a placement line below — auto-flow no longer covers it. That is the price of
 * putting the company block where the empty space was. */

.ct-footer [data-row="top"] > .ct-container {
  display: grid;
  gap: 26px 32px;
  align-items: start;
}

@media (min-width: 901px) {
  .ct-footer [data-row="top"] > .ct-container {
    grid-auto-flow: row;
    grid-template-columns: minmax(0, 1.7fr) repeat(3, minmax(0, 1fr));
    row-gap: 18px;
  }

  /* [data-footer] prefix is load-bearing, not decoration.
   *
   * The column rule further down is `… > [data-column]`, which ties this at
   * (0,4,0) — and being LATER in the file, its display: flex then wins and
   * display: contents never applies. Measured: the company block computed
   * grid-column: 2 / -1 correctly but stayed at x=68 in the brand column,
   * because a flex item ignores grid placement. The prefix takes this to
   * (0,5,0) so it no longer depends on source order. */
  [data-footer] .ct-footer [data-row="top"] > .ct-container > [data-column="widget-area-2"] {
    display: contents;
  }
  [data-footer] .ct-footer [data-row="top"] > .ct-container > [data-column="widget-area-2"] > .ct-widget {
    grid-column: 1;
  }
  [data-footer] .ct-footer [data-row="top"] > .ct-container
    > [data-column="widget-area-2"] > .ct-widget:has(.apex-footer-company) {
    grid-column: 2 / -1;
    grid-row: 3;
  }

  [data-footer] .ct-footer [data-row="top"] > .ct-container > [data-column="widget-area-3"] { grid-column: 2; grid-row: 1 / span 2; }
  [data-footer] .ct-footer [data-row="top"] > .ct-container > [data-column="widget-area-4"] { grid-column: 3; grid-row: 1 / span 2; }
  [data-footer] .ct-footer [data-row="top"] > .ct-container > [data-column="widget-area-5"] { grid-column: 4; grid-row: 1 / span 2; }
}

/* Blocksy inserts an empty "ghost" column to pad the builder row. It has no
   content and would otherwise hold open a grid track. */
.ct-footer [data-column="ghost"] { display: none; }

/* Spacing BETWEEN widgets in a column.
 *
 * Blocksy makes each column a flex column but leaves gap at `normal` (zero),
 * and --widgets-gap turned out not to drive it. An earlier version of this
 * file zeroed margin-bottom on .ct-widget on the assumption that the gap was
 * handling it — which removed the only thing separating them, and stacked the
 * logo, description, company block and socials flush against each other.
 *
 * Set the gap on the column itself so it works however many widgets a column
 * holds. align-items: flex-start stops a widget stretching to the column
 * width and dragging its content with it.
 *
 * The margin reset below is not redundant with the gap — Blocksy puts a 12px
 * margin-top on each widget, which STACKS on the gap. With gap: 20px the
 * measured space between widgets was 32px, which is what made the brand column
 * read as airy. Zero the margin and the gap value is the real value.
 *
 * It needs the full [data-row] > .ct-container > [data-column] prefix to reach
 * (0,4,1); a shorter `.ct-footer .ct-widget` is (0,2,0) and loses. */

.ct-footer [data-row="top"] > .ct-container > [data-column] {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 18px;
}

.ct-footer [data-row="top"] > .ct-container > [data-column] > .ct-widget {
  margin-top: 0;
  margin-bottom: 0;
}


/* ── Brand column ───────────────────────────────────────────────────────── */

/* max-height as well as max-width: the wordmark is 4:3, so a width cap alone
   left it 108px tall — taller than the paragraph beside it and the single
   biggest block in the brand column. */
.ct-footer .wp-block-image img {
  width: auto;
  height: auto;
  max-width: 132px;
  max-height: 74px;
  object-fit: contain;
  object-position: left;
}

/* !important is needed on the colours here, reluctantly.
 *
 * These paragraphs carry the block editor's own colour classes
 * (has-text-color, has-palette-color-N-color) which set `color` directly at a
 * specificity this cannot reach without an absurd selector. Rebinding
 * --theme-text-color has no effect because nothing reads it once a literal
 * colour is set on the element. Size and spacing below need no override. */
.ct-footer .ct-widget p,
.ct-footer [data-column] p {
  color: #9ca3af !important;
  font-size: 14px;
  line-height: 1.7;
}
.ct-footer [data-column="widget-area-2"] p { max-width: 340px; }


/* ── Column headings ────────────────────────────────────────────────────── */

/* Matches the section eyebrows used elsewhere on the site: small, uppercase,
   tracked, white against the muted body text. */
.ct-footer .ct-widget .widget-title,
.ct-footer .ct-widget h1,
.ct-footer .ct-widget h2,
.ct-footer .ct-widget h3,
.ct-footer .ct-widget h4,
.ct-footer .ct-widget h5 {
  margin: 0 0 16px;
  /* Same reason as the paragraphs: the heading block carries a palette colour
     class, and its font size comes from a has-*-font-size class. */
  font-size: 12px !important;
  color: #ffffff !important;
  font-weight: 700;
  line-height: 1.3;
  text-transform: uppercase;
  letter-spacing: 1.2px;
}


/* ── Link lists ─────────────────────────────────────────────────────────── */

/* Blocksy sets --theme-link-initial-color ON the menu element itself, and a
   closer ancestor always wins for custom properties — so rebinding it on
   .ct-footer alone left the links at the theme's slate blue. Rebind here too. */
.ct-footer [data-id="menu"] {
  --theme-link-initial-color: #9ca3af;
  --theme-link-hover-color: var(--apex-orange);
}

/* Covers both a Blocksy menu element and a plain list block in a widget
   area, so it looks the same however the column was built. */
.ct-footer [data-id="menu"] > ul,
.ct-footer .ct-widget ul {
  display: flex;
  flex-direction: column;
  gap: 9px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.ct-footer [data-id="menu"] > ul > li,
.ct-footer .ct-widget ul li { margin: 0; }

.ct-footer [data-id="menu"] a,
.ct-footer .ct-widget ul a {
  font-size: 14px;
  font-weight: 400;
  line-height: 1.4;
  color: var(--theme-link-initial-color);
  transition: color .15s ease;
}
.ct-footer [data-id="menu"] a:hover,
.ct-footer .ct-widget ul a:hover { color: var(--apex-orange); }


/* ── Social icons ───────────────────────────────────────────────────────── */

.ct-footer .ct-socials-block { margin-top: 4px; }

.ct-footer .ct-social-box {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}
.ct-footer .ct-social-box a {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: rgba(255, 255, 255, .07);
  border: 1px solid rgba(255, 255, 255, .1);
  color: #ffffff;
  transition: all .18s ease;
}
.ct-footer .ct-social-box a:hover {
  background: var(--apex-orange);
  border-color: var(--apex-orange);
  transform: translateY(-2px);
}
.ct-footer .ct-social-box svg { width: 16px; height: 16px; fill: currentColor; }


/* ── Research-use-only notice ───────────────────────────────────────────── */

/* Legal small print: quiet, but the lead-in stays white so the statement is
   findable at a glance rather than dissolving into the paragraph. */
.ct-footer [data-row="middle"] p {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.65;
  color: #8b97a5 !important;
}
.ct-footer [data-row="middle"] strong { color: #ffffff !important; font-weight: 700; }


/* ── Company details block ──────────────────────────────────────────────── */

/* Registered-entity details, pasted as a Custom HTML block into a footer
   widget area — see legal-pages/9-footer-company-block.html.
   Set smaller than the surrounding footer copy: this is reference
   information someone looks for deliberately, not something to read. */

/* No margin-top: the column gap above already separates this from the widget
   before it, and an extra 18px on top of that gap was double spacing. */
.ct-footer .apex-footer-company { margin-top: 0; }

.ct-footer .apex-footer-company p {
  margin: 0 0 8px;
  font-size: 12.5px !important;
  line-height: 1.6;
  color: #8b97a5 !important;
}
.ct-footer .apex-footer-company p:last-child { margin-bottom: 0; }

.ct-footer .apex-footer-company-name {
  font-weight: 600;
  color: #ffffff !important;
}

.ct-footer .apex-footer-company a { color: #8b97a5; }
.ct-footer .apex-footer-company a:hover { color: var(--apex-orange); }

/* Inline row of legal links, separated by a rule rather than a bullet so it
   reads as a set of equals rather than a list with a leader. */
.ct-footer .apex-footer-company-links {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 14px;
  margin-top: 12px !important;
  padding-top: 12px;
  border-top: 1px solid rgba(255, 255, 255, .08);
}


/* ── Copyright ──────────────────────────────────────────────────────────── */

.ct-footer .ct-footer-copyright,
.ct-footer .ct-footer-copyright p {
  margin: 0;
  font-size: 13px;
  color: #8b97a5;
  text-align: center;
}
.ct-footer .ct-footer-copyright a { color: #8b97a5; }
.ct-footer .ct-footer-copyright a:hover { color: var(--apex-orange); }


/* ── Responsive ─────────────────────────────────────────────────────────── */

/* grid-auto-flow MUST be reset to row here.
 *
 * The desktop rule uses grid-auto-flow: column so each footer column gets a
 * track automatically. Overriding only grid-template-columns below does not
 * undo that — the named track becomes the first column and every remaining
 * child gets an IMPLICIT column instead of a new row.
 *
 * Measured at 375px before this reset: four 61.5px tracks side by side, every
 * link broken onto its own line, footer 1921px tall. It read as a layout bug
 * because it was one. */

/* THREE columns below 901px, not two.
 *
 * Two columns could not hold three link lists evenly: Shop and Company paired
 * on one row and Legal dropped alone onto the next, sitting under Shop with
 * Company stranded to its right. It read as one menu having wandered off,
 * because visually that is exactly what it was.
 *
 * Three equal tracks put all three lists on one row, tops aligned. At 390px
 * that is a 102px track, narrow enough that "Terms & Conditions" and "Returns
 * & Withdrawal" wrap to two lines — acceptable, and better than a column out
 * of line. At 768px they are 218px and nothing wraps.
 *
 * grid-auto-flow MUST be reset to row. The desktop rule sets it to row inside
 * its own media query, but grid-auto-columns from an earlier revision would
 * still give each child an IMPLICIT column rather than a new row. Measured at
 * 375px before that reset: four 61.5px tracks side by side, every link on its
 * own line, footer 1921px tall. */

@media (max-width: 900px) {
  .ct-footer [data-row="top"] > .ct-container {
    grid-auto-flow: row;
    grid-auto-columns: auto;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 30px 18px;
    align-items: start;
  }
  /* The brand block reads as an intro, so it spans rather than sitting in a
     third-width column with the link lists. */
  .ct-footer [data-column="widget-area-2"] { grid-column: 1 / -1; }
}

@media (max-width: 560px) {
  .ct-footer [data-row="top"] {
    padding-block: 36px 28px;
  }
  .ct-footer [data-column="widget-area-2"] p { max-width: none; }
  .ct-footer [data-row="middle"] p { font-size: 12px; }
}


/* ── 25. LEGAL PAGES ─────────────────────────────────────────────────────── */

/* Wrapper is <div class="apex-legal"> in the pasted page HTML — see the files
 * in legal-pages/. Deliberately NOT .apex: these pages are plain WordPress
 * content, not shortcode output, and none of the .apex layout resets apply.
 *
 * The point of this section is readability. A privacy policy set at the full
 * 1280px container is roughly 160 characters a line, which nobody reads —
 * they scroll to the bottom and click accept. Constraining the measure is the
 * difference between a page that can be read and one that is merely present. */

/* The block is centred, and the page supplies its own H1 inside it.
 *
 * WordPress's own page title is suppressed for these pages (see section 15) —
 * it sat left-aligned against Blocksy's container while the content column was
 * centred, and the two could not be reconciled without either un-centring the
 * text or widening the measure. Moving the heading into the content makes it
 * one block that aligns with itself, and lets the heading take the same
 * gradient treatment as every other page title on the site. */
.apex-legal {
  max-width: 760px;
  margin-inline: auto;
  padding-block: 8px 48px;
  font-size: 15px;
  line-height: 1.75;
  color: var(--apex-text-gray);
}

.apex-legal h1 {
  margin: 0 0 10px;
  font-size: clamp(28px, 4vw, 38px);
  font-weight: 700;
  letter-spacing: -.5px;
  line-height: 1.15;
  color: var(--apex-text-dark);
}

.apex-legal p { margin-bottom: 16px; }

.apex-legal h2 {
  margin: 40px 0 14px;
  font-size: 20px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--apex-text-dark);
}
.apex-legal h2:first-child { margin-top: 0; }

.apex-legal h3 {
  margin: 26px 0 8px;
  font-size: 16px;
  font-weight: 600;
  color: var(--apex-text-dark);
}

.apex-legal ul,
.apex-legal ol {
  margin: 0 0 16px;
  padding-left: 22px;
}
.apex-legal ul { list-style: disc; }
.apex-legal ol { list-style: decimal; }
.apex-legal li { margin-bottom: 7px; }

.apex-legal a {
  color: var(--apex-orange);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.apex-legal a:hover { color: var(--apex-orange-hover); }

.apex-legal strong { color: var(--apex-text-dark); font-weight: 600; }

.apex-legal code {
  padding: 2px 6px;
  border-radius: 4px;
  background: var(--apex-bg-light);
  border: 1px solid var(--apex-border);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px;
  color: var(--apex-text-dark);
}

/* Opening sentence, set slightly larger to give the page a way in. */
.apex-legal .apex-legal-lead {
  font-size: 17px;
  line-height: 1.65;
  color: var(--apex-text-dark);
  margin-bottom: 28px;
}

.apex-legal .apex-legal-updated {
  margin-bottom: 32px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--apex-border);
  font-size: 13px;
  color: var(--apex-text-muted);
}

/* The GDPR legal-basis line under each processing purpose. Subordinate to the
   paragraph it explains, so it is smaller and indented rather than another
   body paragraph competing for attention. */
.apex-legal .apex-legal-basis {
  margin: -8px 0 18px;
  padding-left: 14px;
  border-left: 2px solid var(--apex-border);
  font-size: 13px;
  color: var(--apex-text-muted);
}

/* Clauses that carry real weight — the research-use condition of sale, the
   sealed-goods exception. These are the two paragraphs a dispute turns on, so
   they should not read like the rest of the boilerplate. */
.apex-legal .apex-legal-callout {
  margin: 0 0 20px;
  padding: 18px 20px;
  background: var(--apex-orange-fade);
  border: 1px solid color-mix(in srgb, var(--apex-orange) 30%, transparent);
  border-radius: var(--apex-radius-md);
}
.apex-legal .apex-legal-callout p { margin-bottom: 10px; color: var(--apex-text-dark); }
.apex-legal .apex-legal-callout p:last-child { margin-bottom: 0; }

.apex-legal .apex-legal-block {
  padding: 16px 20px;
  background: var(--apex-bg-light);
  border-radius: var(--apex-radius-md);
  color: var(--apex-text-dark);
}

.apex-legal .apex-legal-table {
  width: 100%;
  margin-bottom: 20px;
  border-collapse: collapse;
}
.apex-legal .apex-legal-table th,
.apex-legal .apex-legal-table td {
  padding: 11px 0;
  border-bottom: 1px solid var(--apex-border);
  font-size: 14px;
  text-align: left;
  vertical-align: top;
}
.apex-legal .apex-legal-table th {
  width: 45%;
  font-weight: 600;
  color: var(--apex-text-gray);
}
.apex-legal .apex-legal-table td { color: var(--apex-text-dark); }

/* Monospace so the fill-in rules line up. */
.apex-legal .apex-legal-form {
  padding: 20px;
  background: var(--apex-bg-light);
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-md);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px;
  line-height: 1.9;
}
.apex-legal .apex-legal-form p { margin-bottom: 4px; }

@media (max-width: 600px) {
  .apex-legal { font-size: 14.5px; padding-block: 4px 36px; }
  .apex-legal h2 { font-size: 18px; margin-top: 30px; }
  .apex-legal .apex-legal-lead { font-size: 16px; }
  .apex-legal .apex-legal-table th { width: auto; display: block; padding-bottom: 2px; border: 0; }
  .apex-legal .apex-legal-table td { display: block; padding-top: 0; }
  .apex-legal .apex-legal-form { font-size: 12px; padding: 16px; }
}


/* ── 26. ABOUT + FAQ PAGE COMPONENTS ─────────────────────────────────────── */

/* Extra blocks used inside .apex-legal on the About and FAQ pages. Those two
 * reuse the legal wrapper for their typography, measure and gradient heading —
 * only the components below are new. */


/* ── Value cards (About) ────────────────────────────────────────────────── */

.apex-legal .apex-values {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
  margin: 26px 0 32px;
}

/* White card on the page's white background, lifted by a soft shadow rather
   than the flat grey fill it had before. The grey read as "disabled" next to
   the orange CTA. The border is kept, at lower contrast, so the card still has
   an edge on a screen where the shadow is barely visible. */

.apex-legal .apex-value,
.apex-legal .apex-step {
  padding: 22px 20px;
  background: #fff;
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-md);
  box-shadow: 0 1px 2px rgba(15, 23, 42, .04), 0 4px 12px rgba(15, 23, 42, .04);
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
.apex-legal .apex-value:hover,
.apex-legal .apex-step:hover {
  transform: translateY(-2px);
  border-color: #f0c9ac;
  box-shadow: 0 2px 4px rgba(15, 23, 42, .05), 0 10px 24px rgba(232, 96, 28, .10);
}

.apex-legal .apex-value h3 {
  margin: 0 0 8px;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -.01em;
  color: var(--apex-text-dark);
}
.apex-legal .apex-value p {
  margin: 0;
  font-size: 13.5px;
  line-height: 1.65;
  color: var(--apex-text-gray);
}

/* Headline figure for a card whose point IS the number — the commission and
   discount cards. Set on its own line at 30px so the offer is legible from
   across the page instead of hiding inside a 15px heading. Cards without a
   figure just omit the span and keep the plain heading. */

.apex-legal .apex-value-stat {
  display: block;
  margin-bottom: 2px;
  font-size: 30px;
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: -.03em;
  background: var(--apex-orange-grad);
  -webkit-background-clip: text;
          background-clip: text;
  -webkit-text-fill-color: transparent;
  color: var(--apex-orange); /* fallback if background-clip is unsupported */
}


/* ── Numbered steps (Affiliate) ─────────────────────────────────────────── */

/* Same card as .apex-value with a counter badge on top. The number comes from
 * a CSS counter rather than being typed into the HTML, so reordering or
 * inserting a step cannot leave the sequence reading 1, 2, 2, 4 — which is the
 * usual fate of hand-numbered markup someone edits a year later. */

.apex-legal .apex-steps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
  margin: 26px 0 32px;
  counter-reset: apex-step;
}

/* Card styling is shared with .apex-value above. Only the counter badge and
   the heading sizes are specific to a step. */

.apex-legal .apex-step { counter-increment: apex-step; }

.apex-legal .apex-step::before {
  content: counter(apex-step);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  margin-bottom: 14px;
  border-radius: 50%;
  /* Gradient, not flat orange — matches the gradient headings and the CTA.
     A background-image on a round element needs no background-clip trick
     because the number is the ELEMENT's text colour, sitting on top of it. */
  background: var(--apex-orange-grad);
  box-shadow: 0 3px 8px rgba(232, 96, 28, .28);
  color: #fff;
  font-size: 14px;
  font-weight: 700;
  line-height: 1;
}
.apex-legal .apex-step h3 {
  margin: 0 0 8px;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -.01em;
  color: var(--apex-text-dark);
}
.apex-legal .apex-step p {
  margin: 0;
  font-size: 13.5px;
  line-height: 1.65;
  color: var(--apex-text-gray);
}


/* ── Checklist (Affiliate) ──────────────────────────────────────────────── */

/* list-style: none plus a masked SVG tick, not a "✓" character. The glyph is
 * missing from some system font stacks and falls back to a tofu box; the mask
 * inherits the orange and stays crisp at any zoom. Same technique as the
 * product-page icons in section 18. */

.apex-legal ul.apex-checklist {
  list-style: none;
  margin: 20px 0 30px;
  padding: 0;
}
.apex-legal ul.apex-checklist li {
  position: relative;
  margin-bottom: 12px;
  padding-left: 30px;
  color: var(--apex-text-gray);
}
.apex-legal ul.apex-checklist li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 4px;
  width: 18px;
  height: 18px;
  /* The gradient shows THROUGH the mask, so the tick picks up the same orange
     ramp as the step badges and the CTA rather than sitting flat beside them. */
  background: var(--apex-orange-grad);
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
  -webkit-mask-size: contain;
          mask-size: contain;
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E");
}
.apex-legal ul.apex-checklist li strong { color: var(--apex-text-dark); }


/* ── FAQ accordion ──────────────────────────────────────────────────────── */

/* Native <details>/<summary>. No JavaScript, keyboard accessible and
 * announced correctly by screen readers for free — and, unlike a scripted
 * accordion, the browser's own find-on-page can still reach collapsed text in
 * engines that support it. */

.apex-legal .apex-faq-list {
  border-top: 1px solid var(--apex-border);
  margin-bottom: 34px;
}

.apex-legal .apex-faq-list details {
  border-bottom: 1px solid var(--apex-border);
}

.apex-legal .apex-faq-list summary {
  position: relative;
  padding: 15px 40px 15px 0;
  font-size: 15px;
  font-weight: 600;
  line-height: 1.45;
  color: var(--apex-text-dark);
  cursor: pointer;
  /* Removes the default disclosure triangle in every engine — Safari needs
     the -webkit- form, Firefox honours list-style. */
  list-style: none;
}
.apex-legal .apex-faq-list summary::-webkit-details-marker { display: none; }
.apex-legal .apex-faq-list summary:hover { color: var(--apex-orange); }
.apex-legal .apex-faq-list summary:focus-visible {
  outline: 2px solid var(--apex-orange);
  outline-offset: 3px;
  border-radius: var(--apex-radius-sm);
}

/* Chevron drawn from two borders, so it rotates cleanly and needs no icon
   font or SVG per row. */
.apex-legal .apex-faq-list summary::after {
  content: '';
  position: absolute;
  top: 50%;
  right: 6px;
  width: 8px;
  height: 8px;
  margin-top: -6px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
  transition: transform .2s ease;
  opacity: .55;
}
.apex-legal .apex-faq-list details[open] summary::after {
  transform: rotate(-135deg);
  margin-top: -2px;
}
.apex-legal .apex-faq-list details[open] summary { color: var(--apex-orange); }

.apex-legal .apex-faq-list details > *:not(summary) {
  font-size: 14.5px;
  line-height: 1.7;
  color: var(--apex-text-gray);
}
.apex-legal .apex-faq-list details > p { margin: 0 0 12px; }
.apex-legal .apex-faq-list details > p:last-child { margin-bottom: 18px; }


/* ── Page CTA ───────────────────────────────────────────────────────────── */

.apex-legal .apex-page-cta {
  margin-top: 34px;
  padding-top: 28px;
  border-top: 1px solid var(--apex-border);
}
.apex-legal .apex-page-btn {
  display: inline-block;
  padding: 14px 30px;
  border-radius: 100px;
  background: var(--apex-orange-grad);
  box-shadow: 0 4px 14px rgba(232, 96, 28, .30);
  color: #fff;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -.01em;
  text-decoration: none;
  transition: transform .18s ease, box-shadow .18s ease, filter .18s ease;
}

/* Hover deepens the SAME gradient with a filter rather than swapping in a
   different background. A background-image cannot be transitioned, so
   changing it on hover would snap; brightness animates smoothly and keeps the
   two states obviously related. */
.apex-legal .apex-page-btn:hover {
  color: #fff;
  filter: brightness(.94);
  transform: translateY(-2px);
  box-shadow: 0 8px 22px rgba(232, 96, 28, .38);
}
.apex-legal .apex-page-btn:active {
  transform: translateY(0);
  box-shadow: 0 3px 10px rgba(232, 96, 28, .28);
}
.apex-legal .apex-page-btn:focus-visible {
  outline: 2px solid var(--apex-orange);
  outline-offset: 3px;
}


@media (max-width: 640px) {
  .apex-legal .apex-values,
  .apex-legal .apex-steps { grid-template-columns: 1fr; gap: 10px; }
  .apex-legal .apex-faq-list summary { font-size: 14.5px; padding-right: 34px; }
  .apex-legal .apex-page-btn { display: block; text-align: center; }
}


/* ── 27. RESEARCH-USE-ONLY NOTICE (Popup Maker) ──────────────────────────── */

/* The modal shown once per visitor before browsing. Markup lives in
 * legal-pages/12-ruo-notice.html and is pasted into a Popup Maker popup.
 *
 * Popup Maker builds its own container and paints it from a "theme" edited in
 * the plugin's admin. Rather than reproduce this design in that editor — where
 * it would live outside this file and outside any diff — the container is
 * matched by CONTENT with :has(.apex-ruo). Whatever theme the popup is
 * assigned, these rules win, and the design stays in one place.
 *
 * Scoped by :has() so no other popup added later inherits any of it. */

.pum-overlay:has(.apex-ruo) {
  background: rgba(15, 23, 42, .72);
  -webkit-backdrop-filter: blur(4px);
          backdrop-filter: blur(4px);
}

.pum-container:has(.apex-ruo) {
  /* !important is required here, and is not laziness.
   *
   * Popup Maker writes the popup's Size setting as an INLINE style on this
   * element — measured on the live site as width: 1140px. An inline style
   * beats any selector in a stylesheet no matter how specific, so the plain
   * max-width this had was silently ignored and the modal rendered nearly
   * full-width on desktop. !important is the only thing in CSS that outranks
   * an inline declaration.
   *
   * The alternative is setting Size > Custom > 460px in the plugin admin, but
   * then half this design lives in a database row that no diff can see. Keep
   * it here.
   *
   * 640px, arrived at by measuring the line length rather than by eye. The
   * comfortable range for reading a paragraph is roughly 45–75 characters per
   * line:
   *
   *   1140px (the plugin default here)  far past it, and much too wide
   *    460px   ~40 chars/line  cramped, and 640px tall
   *    640px   ~53 chars/line  527px tall
   *    720px   ~80 chars/line  over the top of the range
   *
   * Width and height trade against each other for a fixed body of text, and
   * 640 is the point where the measure is comfortable and the modal is still
   * short enough to clear a laptop screen without scrolling. */
  max-width: 640px !important;
  width: calc(100vw - 32px) !important;
  padding: 0;
  border: 0;
  border-radius: 16px;
  background: #fff;
  box-shadow: 0 24px 60px rgba(15, 23, 42, .28);

  /* Cap the height and scroll inside, or the modal outgrows short screens.
   * Measured at 390x667 (iPhone SE, and every older iPhone) before this: the
   * box was 713px tall, overflowing 23px past the top with the heading clipped
   * and the fine print cut off the bottom. Since every other way of closing
   * this modal is disabled by design, content escaping the viewport is not a
   * cosmetic problem — it is how a visitor gets stuck.
   *
   * dvh second so it wins where supported: on mobile Safari 100vh is the
   * height with the address bar RETRACTED, which is taller than what is
   * actually on screen when the page first loads. */
  max-height: calc(100vh - 32px);
  max-height: calc(100dvh - 32px);
  overflow-y: auto;
  overscroll-behavior: contain;
}

.apex-ruo {
  padding: 34px 34px 28px;
  font-family: var(--apex-font);
  text-align: left;
}

/* Orange rule across the top, tying the modal to the buttons and headings
   without needing an image or an icon font. */
.apex-ruo::before {
  content: '';
  display: block;
  height: 3px;
  margin: -34px -34px 24px;
  border-radius: 16px 16px 0 0;
  background: var(--apex-orange-grad);
}

.apex-ruo h2 {
  margin: 0 0 12px;
  font-size: 24px;
  font-weight: 800;
  line-height: 1.2;
  letter-spacing: -.02em;
  color: var(--apex-text-dark);
}

.apex-ruo p {
  margin: 0 0 14px;
  font-size: 14.5px;
  line-height: 1.65;
  color: var(--apex-text-gray);
}

.apex-ruo ul {
  margin: 0 0 18px;
  padding: 16px 18px;
  list-style: none;
  border: 1px solid var(--apex-border);
  border-radius: var(--apex-radius-md);
  background: var(--apex-bg-light);
}
.apex-ruo ul li {
  position: relative;
  margin: 0 0 9px;
  padding-left: 18px;
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--apex-text-gray);
}
.apex-ruo ul li:last-child { margin-bottom: 0; }
.apex-ruo ul li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 8px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--apex-orange);
}

.apex-ruo strong { color: var(--apex-text-dark); font-weight: 600; }

.apex-ruo a { color: var(--apex-orange); text-decoration: underline; }
.apex-ruo a:hover { color: var(--apex-orange-hover); }


/* ── Actions ────────────────────────────────────────────────────────────── */

.apex-ruo-actions {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 20px;
}

/* The confirm control is a <button>, not a styled <a href="#">. It has to be
   reachable and pressable from the keyboard — this modal is the only way into
   the site, so an element a keyboard user cannot activate locks them out
   entirely. A button gets Enter and Space for free; an anchor does not. */

.apex-ruo-btn {
  flex: 1 1 auto;
  padding: 14px 24px;
  border: 0;
  border-radius: 100px;
  background: var(--apex-orange-grad);
  box-shadow: 0 4px 14px rgba(232, 96, 28, .30);
  color: #fff;
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -.01em;
  cursor: pointer;
  transition: transform .18s ease, box-shadow .18s ease, filter .18s ease;
}
.apex-ruo-btn:hover {
  filter: brightness(.94);
  transform: translateY(-2px);
  box-shadow: 0 8px 22px rgba(232, 96, 28, .38);
}
.apex-ruo-btn:active { transform: translateY(0); }
.apex-ruo-btn:focus-visible {
  outline: 2px solid var(--apex-orange);
  outline-offset: 3px;
}

.apex-ruo-decline {
  flex: 0 0 auto;
  font-size: 14px;
  color: var(--apex-text-muted);
  text-decoration: none;
}
.apex-ruo-decline:hover { color: var(--apex-text-dark); text-decoration: underline; }

/* Written as `p.apex-ruo-note`, and the element part is load-bearing.
 *
 * `.apex-ruo p` above is (0,1,1) and sets margin: 0 0 14px — a shorthand, so
 * it also sets margin-top: 0. A plain `.apex-ruo-note` is only (0,1,0) and
 * loses to it, which meant the gap below silently stayed at zero no matter
 * what value was put here. Adding the element selector takes this to (0,2,1)
 * so it wins without needing !important.
 *
 * The gap itself: at 16px the note crowded the button, and the button's soft
 * orange shadow bleeds a few pixels past its edge which makes the space read
 * tighter still. The fine print is also the one thing in this modal nobody
 * needs to read before deciding, so it should sit clearly apart from the
 * control rather than beside it. */

.apex-ruo p.apex-ruo-note {
  margin: 26px 0 0;
  font-size: 12px;
  line-height: 1.55;
  color: var(--apex-text-muted);
}

/* Popup Maker renders its own × in the corner. Hidden here because dismissing
   this particular modal has to be a deliberate acknowledgement — an × invites
   people past a legal notice without reading the one sentence that matters.
   The confirm button is the close control, and it carries .pum-close. */
.pum-container:has(.apex-ruo) .pum-content + .pum-close,
.pum-container:has(.apex-ruo) > .pum-close { display: none; }


@media (max-width: 560px) {
  .apex-ruo { padding: 28px 22px 24px; }
  .apex-ruo::before { margin: -28px -22px 20px; }
  .apex-ruo h2 { font-size: 20px; }
  .apex-ruo-actions { flex-direction: column-reverse; align-items: stretch; gap: 12px; }
  .apex-ruo-btn { width: 100%; }
  .apex-ruo-decline { text-align: center; }
}

/* Short viewports, keyed to HEIGHT not width — a small laptop in a half-height
   window hits this as readily as a phone. Tightens the spacing so the notice
   fits without scrolling wherever it reasonably can; the max-height above is
   the safety net for when it still cannot. */

@media (max-height: 720px) {
  .apex-ruo { padding: 24px 22px 22px; }
  .apex-ruo::before { margin: -24px -22px 16px; }
  .apex-ruo h2 { margin-bottom: 8px; font-size: 19px; }
  .apex-ruo p { margin-bottom: 10px; font-size: 14px; }
  .apex-ruo ul { margin-bottom: 14px; padding: 12px 14px; }
  .apex-ruo ul li { margin-bottom: 7px; }
  .apex-ruo-actions { margin-top: 16px; }
  /* Same specificity trap as the base rule — needs the element selector to
     outrank `.apex-ruo p`. */
  .apex-ruo p.apex-ruo-note { margin-top: 20px; }
}


/* ── 28. CHECKOUT ────────────────────────────────────────────────────────── */

/* WooCommerce/Blocksy markup, not .apex — same exception as sections 14, 15,
 * 18, 21 and 24. This is the CLASSIC (shortcode) checkout; the block checkout
 * uses entirely different markup and none of this would apply to it. */


/* ── Selection colour on radios and checkboxes ──────────────────────────── */

/* The selected payment radio rendered Blocksy blue (#2872fa). It is not
 * styled directly — the control has appearance: none and Blocksy paints it in
 * main.min.css from:
 *
 *   .ct-checkbox:checked, … , .woocommerce … input:checked {
 *     border-color: var(--theme-form-selection-field-active-color);
 *     background:   var(--theme-form-selection-field-active-color); }
 *
 * which resolves to palette colour 1. So rebind the variable rather than
 * chase the rule — the same approach as the menu hover and the cart icon, and
 * it catches checkboxes as well as radios in one declaration.
 *
 * Scoped to the Woo pages: this deliberately does NOT change form controls
 * elsewhere on the site. */

body.woocommerce-checkout,
body.woocommerce-cart,
body.woocommerce-account {
  --theme-form-selection-field-active-color: var(--apex-orange);
}


/* ── Order summary: product thumbnails ──────────────────────────────────── */

/* The thumbnail is injected into td.product-name by the Apex Checkout snippet,
 * because review-order.php builds that cell from three separate echoes — the
 * name (filterable), a bare &nbsp;, then the quantity <strong>. Only the name
 * passes through a filter, so the image goes inside it and the layout is done
 * here instead of by wrapping markup that cannot be wrapped.
 *
 * 56px, not the cart's 88px. This is a summary of a decision already made, so
 * the image is there for recognition, not inspection — and the checkout column
 * is roughly half the width of the cart table. */

.woocommerce-checkout-review-order-table td.product-name {
  display: flex;
  align-items: center;
  gap: 12px;
}

.woocommerce-checkout-review-order-table td.product-name .apex-co-thumb {
  flex: 0 0 auto;
  line-height: 0;
}
.woocommerce-checkout-review-order-table td.product-name .apex-co-thumb img {
  display: block;
  width: 56px;
  height: 56px;
  border-radius: var(--apex-radius-sm);
  border: 1px solid var(--apex-border);
  background: var(--apex-bg-light);
  object-fit: cover;
}

.woocommerce-checkout-review-order-table td.product-name .apex-co-title {
  flex: 1 1 auto;
  min-width: 0;
}

/* margin-left: auto pins the quantity to the right edge of the cell. The
   title above is flex: 1, so without this the two would sit together against
   the thumbnail with a ragged gap after them. */
.woocommerce-checkout-review-order-table td.product-name .product-quantity {
  flex: 0 0 auto;
  margin-left: auto;
  padding-left: 10px;
  font-weight: 600;
  color: var(--apex-text-gray);
  white-space: nowrap;
}


/* ── Research-use-only consent ──────────────────────────────────────────── */

/* Rendered by the Apex Checkout snippet immediately above Place Order.
 * Deliberately boxed and tinted: this is the one control on the page a
 * customer must consciously act on, and an unstyled checkbox in a run of grey
 * legal text is exactly the thing people click past without reading. */

/* Same construction as .apex-legal-callout and .apex-cart-trust-item: the
   orange-fade tint, a border mixed from the orange at 30%, the standard 12px
   radius and the small shadow. An earlier version used a hand-picked tint with
   a thick accent bar down the left edge — a house style that is not this
   house's. Using the tokens means it also tracks any future change to the
   orange rather than drifting away from it. */

.woocommerce-checkout .apex-ruo-consent {
  margin: 0 0 18px;
  padding: 16px 18px;
  background: var(--apex-orange-fade);
  border: 1px solid color-mix(in srgb, var(--apex-orange) 30%, transparent);
  border-radius: var(--apex-radius-md);
  box-shadow: var(--apex-shadow-sm);
}

.woocommerce-checkout .apex-ruo-consent label {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  margin: 0;
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--apex-text-dark);
  cursor: pointer;
}

/* margin-top nudges the box onto the first line's optical centre — with
   align-items: flex-start it otherwise sits slightly high against the cap
   height of the text beside it. */
.woocommerce-checkout .apex-ruo-consent input[type="checkbox"] {
  flex: 0 0 auto;
  margin-top: 2px;
}

/* WooCommerce marks required fields with <abbr class="required">*</abbr>.
   Hidden here: the whole box is required, the sentence says so, and a lone
   asterisk after a paragraph reads like a footnote marker pointing nowhere. */
.woocommerce-checkout .apex-ruo-consent .required {
  display: none;
}

.woocommerce-checkout .apex-ruo-consent.woocommerce-invalid {
  border-color: #dc2626;
  background: #fef2f2;
}


/* ── Place Order button ─────────────────────────────────────────────────── */

/* Gradient to match .apex-page-btn, the hero buttons and the step badges. It
   was flat orange because Blocksy paints it from
   --theme-button-background-initial-color, which is a single colour and cannot
   express a gradient — so the background is set directly here instead of by
   rebinding a variable, unlike the radio fix above. */

body.woocommerce-checkout #place_order {
  padding: 14px 30px;
  border: 0;
  border-radius: 100px;
  background: var(--apex-orange-grad);
  box-shadow: 0 4px 14px rgba(232, 96, 28, .30);
  color: #fff;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -.01em;
  transition: transform .18s ease, box-shadow .18s ease, filter .18s ease,
              background .18s ease, color .18s ease;
}

/* :not([disabled]) on the hover rules — without it the greyed-out button still
   lifts and glows orange on hover, which reads as clickable and is precisely
   the wrong signal for a control that is refusing input. */
body.woocommerce-checkout #place_order:not([disabled]):hover {
  filter: brightness(.94);
  transform: translateY(-2px);
  box-shadow: 0 8px 22px rgba(232, 96, 28, .38);
}
body.woocommerce-checkout #place_order:not([disabled]):active {
  transform: translateY(0);
  box-shadow: 0 3px 10px rgba(232, 96, 28, .28);
}
body.woocommerce-checkout #place_order:focus-visible {
  outline: 2px solid var(--apex-orange);
  outline-offset: 3px;
}

/* Disabled until the research-use box is ticked. Set by the Apex Checkout
   snippet; the server-side check is what actually enforces it. Flat grey with
   no shadow, because a disabled control should not look like it is sitting
   above the page waiting to be pressed. */
body.woocommerce-checkout #place_order[disabled] {
  background: #e5e7eb;
  color: #9ca3af;
  box-shadow: none;
  filter: none;
  transform: none;
  cursor: not-allowed;
}


@media (max-width: 560px) {
  .woocommerce-checkout-review-order-table td.product-name .apex-co-thumb img {
    width: 44px;
    height: 44px;
  }
  .woocommerce-checkout .apex-ruo-consent { padding: 14px 15px; }
}


/* ── 29. HERO CANVAS (molecule / helix) ──────────────────────────────────── */

/* Decorative canvas created by the Apex Hero Helix snippet and appended to
 * .apex .hero. Not a pseudo-element, because the hero already spends both:
 * ::after carries the photo and ::before the scrim.
 *
 * Layer order in the hero, lowest first:
 *   ::after   z 0   photograph
 *   ::before  z 1   dark gradient scrim
 *   canvas    z 1   this — same z as the scrim, but a real child, and a
 *                   pseudo-element ::before paints before its element's
 *                   children, so the helix lands ON TOP of the scrim
 *   .hero-inner z 2 headline and buttons
 *
 * Above the scrim on purpose: below it, the darkening would mute the orange
 * to nothing on the left of the hero, which is the only part dark enough for
 * it to read in the first place. */

.apex .hero > canvas.apex-helix,
.apex .hero > canvas.apex-molecule {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  pointer-events: none;

  /* screen, so the helix behaves like light rather than paint: it glows over
     the dark left side and falls away over the bright right of the photo,
     instead of sitting on top of it as a flat orange overlay. */
  mix-blend-mode: screen;

  /* Overall strength — THE knob for how present the molecule is.
   *
   * Done here rather than by lowering the per-particle alphas in the snippet,
   * because those alphas are composited additively: dropping them changes how
   * overlapping particles accumulate, so bright junctions fade faster than
   * sparse bond lines and the structure loses its shading. Fading the finished
   * layer scales everything by the same factor and keeps the colour
   * relationships exactly as they are. */
  opacity: .58;
}

/* pointer-events stays none on the canvas — the pointer listeners are bound to
   .hero itself, so moves over the headline and buttons still drive the helix
   while the canvas never intercepts a click on the CTA. */

/* Held back on mobile, where the headline occupies most of the hero and the
   helix has far less empty space to live in. The helix is atmosphere; the
   moment it competes with the one sentence the page is built around, it has
   failed. */
@media (max-width: 900px) {
  .apex .hero > canvas.apex-helix,
  .apex .hero > canvas.apex-molecule { opacity: .42; }
}
