/* ============================================================================
   Peptide Portal — homepage bands
   Geometry MEASURED at 1440 on 2026-08-25. Reference band map: recon/ANALYSIS.md §2.
     hero    y74   h240      proof  y314  h337  (cards y328 h210 w391 r32)
     ruo     y552  h99  r16  rail   y651  h456  (cards y700 h403 w325)
     process y1108 h380 r24  textus y1488 h162 r24   faq y1650 h292 r24
   Total document height 2550.
   ============================================================================ */

.dp-sec__h   { font-size: var(--dp-fs-h2); font-weight: var(--dp-fw-semi); letter-spacing: -.01em; line-height: 1.08; }  /* 24 * 1.08 = 26, measured */
/* DP styles this differently in the two places it appears: 16/400 under the FAQ head,
   but 20/600 in brand ink at 60% beside the Text-us panel. One rule cannot serve both. */
.dp-sec__sub { margin-top: var(--dp-s-1); font-size: var(--dp-fs-lg); line-height: 17.6px; color: var(--dp-muted); }
.dp-textus .dp-sec__sub { font-size: var(--dp-fs-h3); line-height: 22px; font-weight: var(--dp-fw-semi); color: var(--dp-ink-60); }
.dp-sec__bar { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--dp-s-9); }

/* --- 1. Hero — 240px. The whole fold depends on this staying 240px. -------- */
.dp-hero { position: relative; height: 240px; overflow: hidden; }
/* MEASURED on DP: a solid black layer at opacity .4 over the photo. Without it the white
   headline sits on raw photo and is unreadable at this crop. It carries the same mask as
   the image so the hero still fades to page-white at the bottom rather than to grey. */
.dp-hero::after {
  content: "";
  position: absolute; inset: 0;
  z-index: 1;
  pointer-events: none;
  background: var(--dpx-bg-000, #000);
  opacity: .4;
  -webkit-mask-image: linear-gradient(#000 45%, transparent 100%);
          mask-image: linear-gradient(#000 45%, transparent 100%);
}
.dp-hero__bg {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  /* MEASURED on DP: 50.1% 33.3% — the upper third of the frame. Centring it lands on the
     gloved-hands mid-section instead of the microscope, a visibly different photo. */
  object-fit: cover; object-position: 50.1% 33.3%;
  /* MEASURED on DP: mask-image: linear-gradient(rgb(0,0,0) 45%, rgba(0,0,0,0) 100%).
     The image itself fades out into the page — it is a mask on the image, NOT a white
     overlay on top, which is why an overlay never looked right. */
  -webkit-mask-image: linear-gradient(#000 45%, transparent 100%);
          mask-image: linear-gradient(#000 45%, transparent 100%);
}
.dp-hero__in {
  position: relative;
  z-index: 2;                          /* above the scrim */
  max-width: none; margin-inline: auto;
  padding: var(--dp-s-9) var(--dp-gutter) 0;
  height: 100%;
}
/* MEASURED on DP (and confirmed against its published type preset, 44/600/1.1em/-.1px):
   the leading is 1.1em = 48.4px, and the tracking is a flat -0.1px — NOT -.02em, which at
   44px is -0.88px and visibly over-tightens the display line. */
.dp-hero__h {
  font-size: var(--dp-fs-display);
  font-weight: var(--dp-fw-semi);
  line-height: 1.1;
  letter-spacing: -.1px;
  color: var(--dp-on-dark);
}

/* The rotating phrase. Every variant is stacked in one grid cell so the block
   never resizes between phrases, and nowrap stops a long variant wrapping and
   shoving the fixed line down — which would move the CTA and break the fold. */
/* Cycling headline — matched to the Ideal GLPs treatment: a rise + fade on
   cubic-bezier(.16,1,.3,1) over .5s, not a bare opacity swap.
   Items share ONE grid cell, so the cell is as tall as the LONGEST phrase — every
   phrase must stay on a single line or the headline gains dead space above it. */
.dp-hero__rots { display: block; position: relative; }
.dp-hero__rot { display: block; white-space: nowrap;
  transition: opacity var(--dp-t-rot) var(--dp-ease-rot),
              transform var(--dp-t-rot) var(--dp-ease-rot); }
.has-js .dp-hero__rots { display: grid; }
.has-js .dp-hero__rot {
  grid-area: 1 / 1;
  opacity: 0;
  transform: translateY(.22em);
  pointer-events: none;
}
.has-js .dp-hero__rot.is-live { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .has-js .dp-hero__rot { transition: none; }
}
.dp-hero__fixed { display: block; color: var(--dp-on-dark-80); font-weight: var(--dp-fw-semi); }

.dp-hero .dp-btn { margin-top: var(--dp-s-9); }

/* Video hero: the white lockup centered over the blurred lab loop. The <video>
   reuses .dp-hero__bg so it inherits the cover fit and bottom fade mask. */
.dp-hero__in--c {
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  height: 100%; padding-top: 0; text-align: center;
}
.dp-hero__h--logo { margin: 0; line-height: 0; }
.dp-hero__logo {
  display: block;
  width: min(460px, 68vw); height: auto;
  filter: drop-shadow(0 12px 30px var(--dpx-shadow-a000p45, rgba(0, 0, 0, .45)));
}
.dp-hero__logo--mark { width: min(56px, 13vw); }
.dp-hero__in--c .dp-btn { margin-top: var(--dp-s-7); }

/* The "footage" is a live CSS animation over the blurred lab frame — a video here
   shows a dead play button on iPhones in Low Power Mode, where autoplay is refused.
   A slow palindromic pan plus two drifting bokeh glows reads as moving lab footage
   and can never be blocked. */
.dp-hero__bg--anim {
  animation: dpHeroKb 18s ease-in-out infinite;
  transform-origin: 50% 40%;
  will-change: transform;
}
@keyframes dpHeroKb {
  0%, 100% { transform: scale(1.06); }
  50%      { transform: scale(1.15) translate(-1.6%, 1.4%); }
}
.dp-hero__bokeh {
  position: absolute;
  border-radius: 50%;
  filter: blur(9px);
  opacity: .38;
  pointer-events: none;
  will-change: transform;
  animation: dpHeroDrift 18s ease-in-out infinite;
}
.dp-hero__bokeh--a {
  left: 14%; top: 16%; width: 150px; height: 150px;
  background: radial-gradient(circle at 38% 38%, var(--dpx-bg-a255218180p55, rgba(255, 218, 180, .55)), transparent 70%);
}
.dp-hero__bokeh--b {
  right: 16%; top: 30%; width: 105px; height: 105px;
  background: radial-gradient(circle at 40% 40%, var(--dpx-bg-a236120060p45, rgba(236, 120, 60, .45)), transparent 70%);
  animation-delay: -9s;
}
@keyframes dpHeroDrift {
  0%, 100% { transform: translate(0, 0); }
  50%      { transform: translate(34px, -18px); }
}
@media (prefers-reduced-motion: reduce) {
  .dp-hero__bg--anim, .dp-hero__bokeh { animation: none; }
}

/* --- 2. Proof cards ------------------------------------------------------- */
.dp-proof { background: var(--dp-surface); padding: 14px 0 0; }   /* cards land at y328 */
.dp-proof__in {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;                                   /* measured: 525 - (120+391) */
  max-width: none; margin-inline: auto;
  padding-inline: var(--dp-gutter);
}
.dp-pcard {
  position: relative;
  min-height: 210px;
  padding: 15px 20px 0;                        /* measured: head lands at dy15, dx20 */
  border-radius: var(--dp-r-lg);               /* 32px */
  background: var(--dp-surface-alt);
  overflow: hidden;
}
.dp-pcard__txt { position: relative; z-index: 2; }
.dp-pcard__h { font-size: var(--dp-fs-h3); font-weight: var(--dp-fw-semi); line-height: 22px; }
.dp-pcard__accent { color: var(--dpx-fg-blue, var(--dp-blue)); }
/* MEASURED on DP: 16px/17.6 — NOT the 14px used elsewhere. The size drives the line
   breaks, so at 14px every card wrapped a word later than DP and the copy block was the
   wrong shape. The column is narrower on the mol/phone cards, which is what makes DP
   break "US-made, third-party / batch tested." where it does. */
.dp-pcard__b {
  margin-top: 7px;
  font-size: 16px; line-height: 17.6px; letter-spacing: -.1px;
  color: var(--dp-muted);
}
/* Copy column caps, measured at a 391px card. Below 1440 the card narrows while the type
   stays 16px, so each cap falls back to the share of the card the column occupied at
   391px — it shrinks with the card and stays clear of the right-anchored art instead of
   running under it. At ≥391px both arms meet, so the measured breaks are untouched.
   Desktop-only: the mobile band was measured separately and resets these. */
@media (min-width: 810px) {
  .dp-pcard__b                  { max-width: min(174px, calc(49.9% - 1px)); }
  .dp-pcard--vial .dp-pcard__b  { max-width: min(203px, 52%); }
  .dp-pcard--phone .dp-pcard__b { max-width: min(174px, calc(54.2% - 16px)); }
}
/* The COA card alone runs its body at 500/20 on DP; the other two are 400/17.6. */
/* The COA card alone runs 500/20 at -0.4 tracking; the other two are 400/17.6 at -0.1. */
.dp-pcard--phone .dp-pcard__b { font-weight: var(--dp-fw-medium); line-height: 20px; letter-spacing: -.4px; }
/* Pinned to the card floor. The target is DP's LABEL baseline (top 158 in a 210 card),
   not the button box — our pill is ~4px taller than DP's, so matching the box offset
   would sit the visible text 4px high. */
.dp-pcard .dp-btn { position: absolute; z-index: 2; left: 20px; bottom: 20px; margin-top: 0; }

/* Art bleeds off the right edge of the card, behind the copy. Offsets are measured from
   DP at a 391px card and anchored RIGHT. The px widths only hold at the full 391px column
   (viewport ≥1440): below that the card narrows and fixed-size art marched left into the
   copy, so each width is capped at the share of the card it filled at 391px and shrinks
   with the column. Heights are auto — every asset's intrinsic ratio matches its measured
   box within 1.5px, so nothing moves at 391. */
.dp-pcard__img { position: absolute; z-index: 1; pointer-events: none; object-fit: contain; height: auto; }
/* The art drifts up and down a hair, like the catalog vials: translate so it
   never fights the absolute positioning, offset phases per card. */
@keyframes dp-pcard-float { from { translate: 0 3px; } to { translate: 0 -3px; } }
.dp-pcard__img { animation: dp-pcard-float 5s ease-in-out infinite alternate; }
.dp-pcard--mol   .dp-pcard__img { animation-duration: 5.8s; animation-delay: -2.9s; }
.dp-pcard--phone .dp-pcard__img { animation-duration: 5.4s; animation-delay: -1.6s; }
@media (prefers-reduced-motion: reduce) { .dp-pcard__img { animation: none; } }
.dp-pcard--vial  .dp-pcard__img { right: 12px; top: auto; bottom: 12px; width: min(210px, 36%); }
.dp-pcard--mol   .dp-pcard__img { right: -2px; top: 16px; width: min(196px, 50.1%); }
/* The box-of-vials render is near-square (not the tall phone it replaced). */
.dp-pcard--phone .dp-pcard__img { right: 14px; top: 48px; width: min(158px, 40%); }

/* --- 3. RUO strip --------------------------------------------------------- */
.dp-ruo { background: var(--dp-surface); padding: 18px 0 4px; }
/* The disclaimer is a single quiet outlined row: warning triangle + one sentence,
   hairline border, no fill, no heading. */
.dp-ruo__panel--note {
  display: flex; align-items: center; gap: 12px;
  max-width: calc(100% - 2 * var(--dp-gutter)); margin-inline: auto;
  padding: 11px 16px;
  border: 1px solid var(--dp-hair-panel);
  border-radius: var(--dp-r-xs);
  background: transparent;
}
.dp-ruo__ico { flex: 0 0 16px; width: 16px; height: 16px; color: var(--dp-muted); }
.dp-ruo__panel--note .dp-ruo__b { margin: 0; font-size: 12.5px; line-height: 1.45; color: var(--dp-muted); }
.dp-ruo__panel {
  max-width: calc(100% - 2 * var(--dp-gutter));
  margin-inline: auto;
  padding: var(--dp-s-7);
  border-radius: var(--dp-r-2xs);               /* 16px */
  background: var(--dp-surface-alt);
}
.dp-ruo__h { font-size: var(--dp-fs-h3); line-height: 22px; font-weight: var(--dp-fw-semi); }
.dp-ruo__b { margin-top: 10px; font-size: var(--dp-fs-base); color: var(--dp-muted); line-height: 17.6px; }
.dp-ruo__b em { font-style: italic; }

/* --- 4. Product rail ------------------------------------------------------ */
.dp-rail { background: var(--dp-surface); padding: 24px 0 0; }
.dp-rail__head { max-width: none; margin-inline: auto; padding-inline: var(--dp-gutter); }

/* Infinite marquee.
   TRAVEL IS ONE MEASURED UNIT, never a percentage: a -50% keyframe makes the SPEED
   depend on how many copies exist, and leaves a growing dead gap whenever one copy is
   narrower than the viewport (worse on wide monitors, invisible on mobile).
   ui.js measures the pitch and writes --dp-unit / --dp-marq-dur; the -50% below is only
   the no-JS fallback. */
/* MEASURED on DP: the marquee is clipped to the CONTENT COLUMN (x120 w1200 at 1440), not
   to the viewport — cards disappear at the same gutter every other band lines up on,
   rather than running edge to edge. The width rule keeps that gutter at every breakpoint
   instead of hard-coding 1200.
   The edge fade is DP's own declaration verbatim. It is 270deg, so 0% is the RIGHT edge:
   the fade is 9% wide on the right and 4% on the left, NOT symmetric. */
.dp-rail__track {
  margin-top: 7px;
  width: calc(100% - 2 * var(--dp-gutter));
  max-width: var(--dp-content);
  margin-inline: auto;
  overflow: hidden;
  overflow: clip;
  -webkit-mask-image: linear-gradient(270deg, rgba(0, 0, 0, 0) 0%, rgb(0, 0, 0) 9%, rgb(0, 0, 0) 96%, rgba(0, 0, 0, 0) 100%);
          mask-image: linear-gradient(270deg, rgba(0, 0, 0, 0) 0%, rgb(0, 0, 0) 9%, rgb(0, 0, 0) 96%, rgba(0, 0, 0, 0) 100%);
}
.dp-rail__marq {
  display: flex;
  width: max-content;
  animation: dp-marq var(--dp-marq-dur, 90s) linear infinite;
  will-change: transform;
}
.dp-rail__unit { display: flex; gap: var(--dp-s-9); padding-right: var(--dp-s-9); }
@keyframes dp-marq {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(calc(-1 * var(--dp-unit, 50%)), 0, 0); }
}
/* Hovering a moving list makes it unreadable and unclickable. */
/* No hover-pause here. DP slows to ~20% and keeps moving; ui.js does that through the
   Web Animations API so the speed change does not jump the strip. A CSS
   `animation-play-state: paused` would stop it dead, which is the wrong behaviour AND
   would fight the playback rate. */
@media (prefers-reduced-motion: reduce) {
  .dp-rail__marq { animation: none; }
  .dp-rail__track { overflow-x: auto; }
}

.dp-rcard {
  position: relative;
  flex: 0 0 325px;
  height: 403px;
  padding: 17px 24px 0;
  border-radius: var(--dp-r-md);                 /* 30px — measured */
  /* MEASURED on DP: linear-gradient(#FAF8F5 0%, #E6E6EA 100%). A flat #FAF8F5 (what this
     was) is only a hair off page-white, so the cards stopped reading as cards at all. */
  background: url("../img/rail-card-bg.svg") center bottom / 307% 227px no-repeat,
              linear-gradient(var(--dpx-bg-250250250, rgb(250, 250, 250)) 0%, var(--dpx-bg-230230234, rgb(230, 230, 234)) 100%);
  text-align: center;                            /* DP centres every element in the card */
  overflow: hidden;
}
.dp-rcard__cas  { font-size: var(--dp-fs-md); font-weight: var(--dp-fw-semi); color: var(--dpx-fg-a000p3, rgba(0, 0, 0, .3)); line-height: 24px; letter-spacing: -.75px; }
.dp-rcard__name { margin-top: 6px; font-size: var(--dp-fs-h2); font-weight: var(--dp-fw-semi); color: var(--dp-ink); line-height: 28px; letter-spacing: -.9px; }

/* The name is the card's link now that the "View Product" pill is gone. Inherit the
   heading's colour so the change is invisible until you hover it. */
.dp-rcard__name a { color: inherit; text-decoration: none; }
.dp-rcard__name a:hover { color: var(--dpx-fg-blue, var(--dp-blue)); }

/* DELIBERATE DEPARTURE FROM DP, requested: DP's card carries a "View Product" pill and
   shows no price. Price and Add to Cart share ONE centred row, which occupies the same
   38px the pill did — the photo below is absolutely positioned with its bottom flush to
   the card edge (measured, and signed off), so a stacked price line would push the button
   straight into the vial. */
/* One control now, so the row simply centres it. The block still occupies exactly the
   vertical space the price-beside-button row did — the photo below is absolutely positioned
   with its bottom flush to the card edge, and any extra height here drives the button into it. */
.dp-rcard__buy {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 24px;
}
/* The price now lives INSIDE the button, after a hairline rule — one control that says both
   what it does and what it costs, instead of a price parked next to a button. */
.dp-rcard__addLbl { white-space: nowrap; }
.dp-rcard__rule {
  width: 1px; align-self: stretch; flex: 0 0 1px;
  margin-inline: var(--dp-s-5);
  /* On the blue fill a solid white rule is too loud; this reads as a division, not a border. */
  background: var(--dpx-bg-a255255255p32, rgba(255, 255, 255, .32));
}
.dp-rcard__amt { font: inherit; color: inherit; white-space: nowrap; }
/* WooCommerce wraps the figure in its own span with its own colour. */
.dp-rcard__amt .woocommerce-Price-amount,
.dp-rcard__amt bdi { font: inherit; color: inherit; }

.dp-rcard__add {
  display: flex; align-items: center; justify-content: center;
  height: 38px;
  padding: 0 var(--dp-s-7);
  border: 0;
  border-radius: var(--dp-r-pill);
  background: var(--dp-blue);
  color: var(--dp-on-dark);
  font-family: inherit;
  font-size: var(--dp-fs-base);
  font-weight: var(--dp-fw-semi);
  line-height: 1;
  letter-spacing: -.28px;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--dp-t-cta) var(--dp-ease-cta), transform .12s ease;
}
.dp-rcard__add:hover { background: var(--dpx-bg-ink, var(--dp-ink)); }
.dp-rcard__add:active { transform: scale(.97); }
.dp-rcard__add:focus-visible { outline: 2px solid var(--dp-ink); outline-offset: 2px; }
/* Pointer-events off, not just dimmed: a second click while the first is in flight adds twice. */
.dp-rcard__add.is-busy { opacity: .55; pointer-events: none; }

/* Photo sits flush to the card edges from y165 down. The vial label in the shot already
   carries the product name and CAS — that is DP's "second" label, not markup. */
.dp-rcard__shot {
  position: absolute; left: 0; right: 0; top: 165px; height: 238px;
  display: block;
}
/* DP's image BOX is WIDER THAN THE CARD (328 against a 299 card) and only 214 tall, with
   object-fit:contain. The extra width is what lets the vial render large without being
   cropped — cover cuts the vial label off, contain in a card-width box leaves it small. */
.dp-rcard__shot img { width: 100%; height: 100%; object-fit: contain; }
.dp-rail__empty  { padding-inline: var(--dp-gutter); color: var(--dp-muted); }

/* --- 5. Our Process ------------------------------------------------------- */
.dp-proc { background: var(--dp-surface); padding: 19px 0 0; }    /* panel y1122, steps y1206 */
.dp-proc__panel {
  /* min() keeps the page gutter once the viewport is narrower than the content width —
     a bare max-width let the panel run edge to edge there. Same on the panels below. */
  max-width: calc(100% - 2 * var(--dp-gutter)); margin-inline: auto;
  padding: var(--dp-s-9);
  border-radius: var(--dp-r-sm);                /* 24px */
  background: var(--dp-surface-alt);
}
.dp-proc__grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--dp-s-9); margin-top: var(--dp-s-9); }

/* MEASURED on DP: the card is filled #E3DFD9 and the photo is inset 20px from the top,
   overflowing the bottom. The fill is what gives the card a visible top edge — these
   photos are near-white up top, so a card with no fill simply vanishes into the panel. */
.dp-step {
  position: relative;
  height: 262px;
  border-radius: var(--dp-r-xs);                /* 20px */
  background: var(--dp-step-bg);
  box-shadow: 0 0 2px var(--dpx-shadow-a000p04, rgba(0, 0, 0, .04));
  overflow: hidden;
}
/* The process photos are full-bleed photographs: cover the whole card so no card
   fill shows around them at any size. */
.dp-step__img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center; }
/* MEASURED on DP: the scrim covers the WHOLE card and is brand ink, not black — it starts
   at 30% and is fully opaque by 105%, so the photo is already deep navy behind the label.
   A black band over only the bottom third (what this was) leaves the label sitting on a
   much lighter photo and reads as washed out next to DP. */
.dp-step::after {
  content: "";
  position: absolute; inset: 0;
  pointer-events: none;
  background: linear-gradient(rgba(255, 255, 255, 0) 30%, var(--dpx-bg-a414676p6, rgba(46, 36, 30, .6)) 75%, var(--dpx-bg-414676, rgb(72, 53, 44)) 105%);
}
.dp-step__txt {
  position: absolute; inset: auto 0 0 0;
  z-index: 1;
  padding: var(--dp-s-7);
  color: var(--dp-on-dark);
}
.dp-step__n { font-size: var(--dp-fs-sm); font-weight: var(--dp-fw-semi); line-height: 13.2px; color: var(--dp-on-dark-90); }
.dp-step__t { margin-top: var(--dp-s-1); font-size: var(--dp-fs-lg); font-weight: var(--dp-fw-semi); line-height: 19.2px; color: var(--dp-on-dark-90); }
.dp-step__b { margin-top: var(--dp-s-1); font-size: var(--dp-fs-sm); color: var(--dp-on-dark-85); line-height: 13.2px; }

/* --- 6. Text us ----------------------------------------------------------- */
.dp-textus { background: var(--dp-surface); padding: 14px 0 0; }  /* panel y1502 */
.dp-textus__panel {
  position: relative;
  display: flex; align-items: center; justify-content: space-between; gap: var(--dp-s-9);
  max-width: calc(100% - 2 * var(--dp-gutter)); margin-inline: auto;
  min-height: 148px;
  padding: var(--dp-s-7);
  border-radius: var(--dp-r-sm);
  background: var(--dp-surface-alt);
  overflow: hidden;
}
.dp-textus__l .dp-btn { margin-top: var(--dp-s-5); }
/* The chat art is the TALLEST thing in this row, so it — not the copy — sets the panel
   height: DP's is 116, giving 116 + 32 of padding = the measured 148 band. At 120 the
   whole band ran 4px long. */
.dp-textus__chat { width: 470px; max-width: 46%; height: 116px; object-fit: contain; object-position: right center; }

/* --- 7. FAQ --------------------------------------------------------------- */
/* DP leaves 12px between the FAQ panel and the footer band (panel ends 1930, footer
   starts 1942). Without it the footer butts straight onto the panel. */
.dp-faq { background: var(--dp-surface); padding: 14px 0 12px; }
/* Redesigned as a ruled, numbered register — journal front-matter rather than
   bordered cards: a heavy rule under the section head, hairline rules between
   entries, monospace numerals, serif answers set to a reading measure. */
.dp-faq__panel {
  /* Full page width (only the gutter holds it off the viewport edge), carried
     on a card; the register's rules live inside it. */
  max-width: calc(100% - 2 * var(--dp-gutter)); margin-inline: auto;
  padding: var(--dp-s-8) var(--dp-s-8) var(--dp-s-6);
  border-radius: var(--dp-r-lg);
  background: var(--dp-surface-alt);
}
@media (max-width: 809px) { .dp-faq__panel { padding: var(--dp-s-7) var(--dp-s-6) var(--dp-s-5); } }
.dp-faq .dp-sec__bar { padding-bottom: 10px; }
.dp-faq__grid {
  display: grid; grid-template-columns: 1fr 1fr;
  align-items: start;  /* an opened answer must not stretch its row-mate */
  column-gap: clamp(32px, 5vw, 72px); row-gap: 0;
  margin-top: 0;
  counter-reset: dpfaq;
}
.dp-faq__item {
  border: 0; border-radius: 0; background: none;
  border-bottom: 1px solid var(--dp-hair-row);
  counter-increment: dpfaq;
}
.single-product .dp-faq__item { border-bottom-color: var(--dp-hair-row); }
.single-product .dp-faq__panel { border-color: transparent; }
.dp-faq__q {
  display: flex; align-items: baseline; gap: 14px;
  width: 100%;
  padding: 16px 2px;
  border: 0; background: none; text-align: left;
  font-size: var(--dp-fs-lg); line-height: 1.35; font-weight: var(--dp-fw-semi);
  letter-spacing: -.1px;
  cursor: pointer;
}
/* The register numeral. Counted in document order, so the two columns read
   across a row the same way the boxes used to. */
.dp-faq__q::before {
  content: counter(dpfaq, decimal-leading-zero);
  flex: 0 0 30px;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 11.5px; font-weight: var(--dp-fw-medium); letter-spacing: .06em;
  color: var(--dpx-fg-blue, var(--dp-blue));
}
.dp-faq__q > span { flex: 1 1 auto; }
/* The chevron is replaced by a mono plus that turns into a multiplication
   cross when open; the svg stays in the markup for every page, just unused. */
.dp-faq__q svg { display: none; }
.dp-faq__q::after {
  content: "+";
  flex: 0 0 auto; align-self: center;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 16px; line-height: 1;
  color: var(--dp-muted);
  transition: transform .2s ease, color .2s ease;
}
.dp-faq__item.is-open .dp-faq__q::after { transform: rotate(45deg); color: var(--dpx-fg-blue, var(--dp-blue)); }
@media (hover: hover) {
  .dp-faq__q:hover > span { text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }
}
/* Collapsed by default but never display:none — the answer stays in the
   accessibility tree and in the page for search engines. */
.dp-faq__a { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .22s ease; }
.dp-faq__item.is-open .dp-faq__a { grid-template-rows: 1fr; }
.dp-faq__a > p {
  overflow: hidden; margin: 0;
  padding: 0 28px 0 44px;    /* 30px numeral + 14px gap: aligned under the question */
  max-width: 58ch;
  font-family: Charter, "Iowan Old Style", Palatino, Georgia, "Times New Roman", serif;
  font-size: 15px; line-height: 1.62; letter-spacing: .005em;
  color: var(--dp-muted);
}
.dp-faq__item.is-open .dp-faq__a > p { padding-bottom: 18px; }

/* --- Responsive — DP's real breakpoints: 810 / 1200 ----------------------- */
@media (max-width: 1199px) {
  /* Both card bands stay three-up at every desktop width — the 2-col wrap left an
     orphaned third card and read as bloated. Tighter gaps keep them compact. */
  .dp-proof__in   { gap: 10px; }
  /* Bottom padding reserves the absolute button's row: with the narrower copy
     column the text runs deeper and otherwise slides under the pill. */
  .dp-pcard       { padding: 13px 16px 72px; }
  .dp-pcard .dp-btn { left: 16px; bottom: 16px; }
  /* At three-up widths the cards run 250–360px wide and the desktop art caps
     (31–50% of card WIDTH) march into the copy — the chromatogram sat behind
     "guessing games." The headline keeps the left column and the art is sized
     from the card's height instead, so neither crosses the other at any width
     in the band. */
  .dp-pcard__h { max-width: 56%; }
  .dp-pcard--vial  .dp-pcard__img { right: 10px; top: auto; bottom: 64px; width: min(34%, 130px); height: auto; max-width: none; }
  .dp-pcard--mol   .dp-pcard__img { right: 2px;  top: 46px; width: auto; height: min(44%, 92px); max-width: none; }
  .dp-pcard--phone .dp-pcard__img { right: 10px; top: 56px; width: auto; height: min(40%, 86px); max-width: none; }
  .dp-proc__grid  { gap: var(--dp-s-5); }
  .dp-step        { height: 240px; }
}

@media (max-width: 809px) {
  .dp-hero        { height: 141px; }          /* measured on DP at 390: hero 85..226 */
  .dp-hero__h     { font-size: 26px; }

  /* DP's mobile layout is STRUCTURALLY different, not just narrower:
     the RUO strip moves ABOVE the proof cards, and proof cards 2 and 3 swap.
     Measured at 390: RUO y234, proof wrapper y226..885. */
  .dp-main { display: flex; flex-direction: column; }
  .dp-hero   { order: 1; }
  .dp-ruo    { order: 2; }
  .dp-cat    { order: 3; }   /* catalog right after hero/RUO — unset it defaults to 0 and jumps above the hero */
  .dp-rail   { order: 3; }
  .dp-volcta { order: 3; }   /* follows the rail in document order; unset it defaults to 0 and jumps above the hero */
  .dp-proof  { order: 4; }   /* selling-point cards BELOW the catalog, same as desktop document order */
  .dp-proc   { order: 5; }
  .dp-textus { order: 6; }
  .dp-faq    { order: 7; }
  .dp-proof__in .dp-pcard:nth-child(2) { order: 3; }   /* "No more guessing games" last */
  .dp-proof__in .dp-pcard:nth-child(3) { order: 2; }   /* "Batch Produced" second */
  .dp-hero__in    { padding-block: var(--dp-s-7); }
  .dp-proof__in   { grid-template-columns: 1fr; gap: var(--dp-s-4); }
  .dp-pcard       { min-height: 166px; padding: var(--dp-s-7); }
  .dp-pcard__h    { font-size: var(--dp-fs-lg); line-height: 1.2; }
  /* The 16px copy and the 174px column are DESKTOP measurements; the mobile band was
     measured separately and stays on the flow layout. */
  .dp-pcard__b    { font-size: var(--dp-fs-sm); line-height: 1.35; max-width: none; }
  .dp-pcard .dp-btn { position: static; margin-top: var(--dp-s-5); }
  /* Desktop art offsets are fixed px against a 391px card and would swamp a 358px one,
     so mobile keeps the proportional placement the mobile bands were verified against.
     Each reset has to live on the two-class selector: a bare .dp-pcard__img is (0,1,0)
     and loses to the desktop .dp-pcard--vial .dp-pcard__img at (0,2,0) no matter that
     this block comes later. */
  /* Sized by HEIGHT, not width. At width:30% the vial rendered 221px tall inside a 166px
     card — it broke 32px out of the TOP, which is what reads as "pulled too high". The vial
     is meant to hang below the card (it does on desktop too), so the bottom overhang stays
     and the height is capped instead: 105% + a -14% bottom leaves the top ~15px inside. */
  /* The vial render now ends just below the 5mg pill — show it whole, bottom-anchored,
     instead of the old oversize-and-bleed (which would clip the pill off). */
  /* Stacked cards are full-width but stay ~166px TALL, so percentage WIDTHS blow the art
     through the card's top edge (33% of a 640px card is a 292px-tall vial in a 166px box).
     In this band the card's height is the stable dimension — size the art from it and the
     proportions hold at every squeeze width. */
  .dp-pcard--vial  .dp-pcard__img { right: 6px; top: auto; bottom: 8px; width: min(42%, 180px); height: auto; max-width: none; }
  .dp-pcard--vial  .dp-pcard__b { max-width: 52%; }
  .dp-pcard--mol   .dp-pcard__b { max-width: 56%; }
  .dp-pcard--phone .dp-pcard__b { max-width: 54%; }
  .dp-pcard--mol   .dp-pcard__img { right: 2%;  top: 12%;  bottom: auto; width: auto; height: 64%; max-width: none; }
  .dp-pcard--phone .dp-pcard__img { right: 4%;  top: auto; bottom: 10%; width: auto; height: 66%; max-width: none; }
  .dp-ruo__panel  { padding: var(--dp-s-6); }
  .dp-ruo__b      { font-size: var(--dp-fs-sm); line-height: 1.45; }
  .dp-rcard       { height: 360px; flex-basis: 268px; }
  .dp-rcard__shot { bottom: 48px; height: 200px; }
  .dp-pcard__txt  { max-width: 66%; }
  /* Our Process is a CAROUSEL on mobile — one card at a time. Stacking all three
     is what made this band 999px against DP's measured 404px. */
  .dp-proc__grid {
    display: flex;
    grid-template-columns: none;
    gap: var(--dp-s-5);
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
  }
  .dp-proc__grid::-webkit-scrollbar { display: none; }
  .dp-proc__grid .dp-step { flex: 0 0 100%; scroll-snap-align: center; height: 236px; }
  .dp-proc__panel { padding: var(--dp-s-7); }
  .dp-proc__grid  { margin-top: var(--dp-s-7); }
  .dp-textus__panel { flex-direction: column; align-items: flex-start; }
  .dp-textus__chat  { width: 100%; max-width: 100%; }
  .dp-faq__grid   { grid-template-columns: 1fr; }
  /* DP runs the FAQ question at 14/15.4 on mobile; ours inherited the 1.45 body leading
     and every one of the six rows grew ~5px. */
  .dp-faq__q      { font-size: 14.5px; padding-block: 14px; }
  .dp-sec__bar    { flex-direction: column; }
}

/* The swipe wrapper ui.js inserts between the track and the marquee. It exists so the drag
   offset and the marquee animation sit on different elements — one transform each. */
.dp-rail__drag { display: flex; width: max-content; will-change: transform; }
/* pan-y, not none: the strip owns horizontal gestures, the PAGE keeps vertical ones. Without
   this a swipe across the rail on a phone scrolls the page instead of moving the products. */
.dp-rail__track { touch-action: pan-y; cursor: grab; }
.dp-rail__track.is-dragging { cursor: grabbing; }
/* No text selection mid-drag — otherwise a swipe highlights every card name it crosses. */
.dp-rail__track.is-dragging, .dp-rail__track.is-dragging * { user-select: none; }
/* Card photos are the thing a finger lands on; a native image-drag there cancels the pointer
   stream the swipe depends on. */
.dp-rail__track img { -webkit-user-drag: none; user-select: none; }

/* ============================================================================
   Product reel (.dp-reel) — the 2026-10 restyle of the rail.

   Rendered only when dp_restyle_on() (see functions.php); until then the page still carries
   .dp-rail__track above and none of this matches anything.

   ui.js owns the motion: one transform on .dp-reel__strip, nothing else moves it. The names
   are deliberately NOT dp-rail__*: two plugins reach into those and would fight it.
   ============================================================================ */
.dp-reel {
  --dp-reel-card: 286px;
  --dp-reel-gap: var(--dp-s-7);
  margin-top: var(--dp-s-5);
  width: calc(100% - 2 * var(--dp-gutter));
  max-width: none;
  margin-inline: auto;
  /* Room for the cards' shadow, which overflow would otherwise slice off flat. */
  padding-block: 6px 22px;
  overflow: hidden;
  /* pan-y: the page keeps vertical scrolling; sideways movement is ours. */
  touch-action: pan-y;
  cursor: grab;
  -webkit-user-select: none; user-select: none;
  -webkit-mask-image: linear-gradient(270deg, rgba(0, 0, 0, 0) 0%, rgb(0, 0, 0) 7%, rgb(0, 0, 0) 97%, rgba(0, 0, 0, 0) 100%);
          mask-image: linear-gradient(270deg, rgba(0, 0, 0, 0) 0%, rgb(0, 0, 0) 7%, rgb(0, 0, 0) 97%, rgba(0, 0, 0, 0) 100%);
}
.dp-reel.is-dragging { cursor: grabbing; }
.dp-reel__strip { display: flex; width: max-content; will-change: transform; }
.dp-reel__unit { display: flex; gap: var(--dp-reel-gap); padding-right: var(--dp-reel-gap); }
.dp-reel img { -webkit-user-drag: none; }
/* Without JavaScript there is no engine: let the browser scroll it instead. */
html:not(.has-js) .dp-reel { overflow-x: auto; -webkit-mask-image: none; mask-image: none; cursor: auto; }

/* --- The card, sectioned ---------------------------------------------------
   Three bands instead of one loose frame: the photo in its own panel, the name and CAS
   under a hairline, and the action pinned to the foot. Same structure as the cards on the
   Products page, so the two read as one family. The markup is untouched (cas, name, buy,
   shot); `order` puts the photo first.

   Two-class selectors throughout: the base .dp-rcard rules above and the phone overrides
   further up are single-class and must lose to these wherever they sit in the file. */
.dp-reel .dp-rcard {
  flex: 0 0 var(--dp-reel-card);
  width: var(--dp-reel-card);
  height: auto;
  padding: 0;
  display: flex; flex-direction: column; align-items: stretch;
  border: 1px solid var(--dpx-border-e3e8ee, #E3E8EE);
  border-radius: 22px;
  /* !important only because the rail-touch plugin paints every .dp-rcard with one. */
  background: var(--dp-surface) !important;
  box-shadow: 0 8px 22px var(--dpx-shadow-a314477p055, rgba(72, 46, 36, .055));
  text-align: left;
  overflow: hidden;
}

.dp-reel .dp-rcard__shot {
  order: 1;
  position: relative; inset: auto; top: auto; left: auto; right: auto; bottom: auto;
  display: block;
  height: 206px;
  padding: var(--dp-s-6) var(--dp-s-6) 0;
  border-bottom: 1px solid var(--dpx-border-e5e9ee, #E5E9EE);
  background:
    radial-gradient(circle, var(--dpx-bg-a414676p10, rgba(46, 36, 30, .10)) .7px, transparent .85px) center bottom / 7px 7px repeat,
    linear-gradient(180deg, var(--dpx-bg-fbfbfc, #FBFBFC) 0%, var(--dpx-bg-f1f3f6, #F1F3F6) 100%);
}
/* The vial rises from the bottom edge of its panel — the photos are shot cut off there. */
.dp-reel .dp-rcard__shot img {
  display: block; width: 100%; height: 100%;
  object-fit: contain; object-position: center bottom;
}

.dp-reel .dp-rcard__name {
  order: 2;
  margin: 0;
  padding: var(--dp-s-6) var(--dp-s-7) 0;
  font-size: var(--dp-fs-h4); line-height: 24px; letter-spacing: -.4px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.dp-reel .dp-rcard__cas {
  order: 3;
  margin: 2px 0 0;
  padding: 0 var(--dp-s-7);
  font-size: var(--dp-fs-xs); font-weight: var(--dp-fw-semi);
  line-height: 16px; letter-spacing: .02em; text-transform: uppercase;
  color: var(--dpx-fg-8a91a0, #8A91A0);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.dp-reel .dp-rcard__buy {
  order: 4;
  display: block;
  margin: auto 0 0;
  padding: var(--dp-s-6) var(--dp-s-7) var(--dp-s-7);
}
/* One full-width control: what it does on the left, what it costs on the right. */
.dp-reel .dp-rcard__add {
  display: flex; align-items: center; justify-content: space-between;
  width: 100%; height: 44px;
  padding: 0 var(--dp-s-8);
  font-size: var(--dp-fs-base);
}
.dp-reel .dp-rcard__rule { margin-inline: auto var(--dp-s-6); }
.dp-reel .dp-rcard__amt { font-variant-numeric: tabular-nums; }
/* While the strip is being dragged nothing under the pointer should react. */
.dp-reel.is-dragging .dp-rcard__add,
.dp-reel.is-dragging a { pointer-events: none; }

@media (hover: hover) and (pointer: fine) {
  .dp-reel .dp-rcard { transition: box-shadow .2s var(--dp-ease-cta), border-color .2s var(--dp-ease-cta); }
  .dp-reel .dp-rcard:hover { border-color: var(--dpx-border-d3dbe5, #E3D8D5); box-shadow: 0 12px 28px var(--dpx-shadow-a314477p09, rgba(72, 46, 36, .09)); }
}

@media (max-width: 809px) {
  /* Edge to edge on a phone, so a card slides off the screen rather than into a gutter, and
     the next one is always peeking in: that peek is what says "this moves". */
  .dp-reel {
    --dp-reel-card: min(264px, calc(100vw - 110px));
    --dp-reel-gap: var(--dp-s-5);
    width: 100%; max-width: none;
    -webkit-mask-image: none; mask-image: none;
  }
  .dp-reel__strip { padding-left: var(--dp-gutter); }
  .dp-reel .dp-rcard { height: auto; flex-basis: var(--dp-reel-card); }
  .dp-reel .dp-rcard__shot { height: 184px; bottom: auto; }
}
