/* HPL Peptides — theme stylesheet.

   Two things live here and nothing else (BUILD-BRIEF §6):

   1. The page shell that the prototype carried in each file's <helmet> <style>
      block — box-sizing, body, link colour, focus ring, reduced motion.
   2. The `style-hover` / `style-active` attributes from the prototype, converted
      to real :hover / :active rules, plus the handful of state styles that were
      `{{ holes }}` in the prototype (nav pill on scroll, open/closed menus,
      drawer, search panel). Those were never static inline styles, so a class is
      the faithful translation, not a liberty.

   Static inline styles stay inline in the templates, as the brief requires.
   Every value here is either a var(--token) or the literal the prototype used. */

/* ——— page shell (from the prototype helmet <style>) ——— */

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--surface-tint);
  color: var(--text-primary);
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
  text-wrap: pretty;
}

a { color: var(--text-primary); text-decoration: none; }
a:hover { color: var(--brand); }

button, input, select, textarea { font: inherit; }

/* §6: 2px brand-purple ring at 2px offset, on everything, always. Never remove. */
:focus-visible {
  outline: 2px solid var(--brand-hover);
  outline-offset: 2px;
  border-radius: 4px;
}

/* Available to screen readers and to keyboard focus, invisible on screen. */
.hpl-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Skip link — the first thing a keyboard reaches, past the marquee and nav. */
.hpl-skip {
  position: absolute;
  top: -100px;
  left: 16px;
  z-index: 100;
  padding: 12px 20px;
  border-radius: var(--radius-pill);
  background: var(--brand);
  color: var(--text-on-dark);
  font-size: var(--text-small);
  font-weight: var(--weight-medium);
  transition: top 160ms var(--ease-standard);
}
.hpl-skip:focus { top: 12px; color: var(--text-on-dark); }
.admin-bar .hpl-skip:focus { top: 44px; }

/* A radio hidden inside a selectable card still takes keyboard focus, but its
   ring is invisible — so the card wears it instead. */
.hpl-choice:has(input:focus-visible) {
  outline: 2px solid var(--brand-hover);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* ——— announcement bar ———
   Pinned at the top of the viewport on every page and never scrolls away.

   For logged-in users the WordPress admin bar is fixed at top:0 with
   z-index:99999 — so a bar pinned at top:0 slides underneath it and reads as
   having vanished on scroll. Both it and the nav pill below it are pushed down
   by the admin bar's height. !important because the offsets they override are
   inline styles. Logged-out visitors are unaffected. */
.admin-bar .hpl-announce { top: 32px !important; }
.admin-bar .hpl-header { top: 62px !important; }

@media screen and (max-width: 782px) {
  .admin-bar .hpl-announce { top: 46px !important; }
  .admin-bar .hpl-header { top: 76px !important; }
}

/* Below 601px WordPress stops fixing the admin bar, so it scrolls away with the
   page and the offset must go with it. */
@media screen and (max-width: 600px) {
  .admin-bar .hpl-announce { top: 0 !important; }
  .admin-bar .hpl-header { top: 30px !important; }
}

/* ——— nav pill ——— */

/* transparent over the hero; translucent white with a hairline after 80px.
   These three were `{{ holes }}` in the prototype, so they belong in CSS —
   an inline value here would also outrank the scrolled state. */
.hpl-pill {
  border: 1px solid transparent;
  background: transparent;
  box-shadow: none;
}
.hpl-header[data-solid="true"] .hpl-pill {
  border-color: var(--border-light);
  background: rgba(255, 255, 255, 0.86);
  box-shadow: 0 18px 40px -28px rgba(23, 16, 43, 0.4);
}

/* Nav link underline: a background image grown from 0 width on hover. The whole
   mechanic lives here because an inline `background-size:0% 1px` would outrank
   the :hover rule. */
.hpl-navlink {
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-position: 14px calc(100% - 8px);
  background-size: 0% 1px;
  transition: background-size 200ms cubic-bezier(0.22, 1, 0.36, 1);
}
.hpl-navlink:hover {
  color: var(--text-primary);
  background-size: calc(100% - 28px) 1px;
}

/* Same reason: the resting background belongs with the state that replaces it. */
.hpl-iconbtn { background: transparent; }
.hpl-iconbtn:hover { background: rgba(255, 255, 255, 0.55); }
.hpl-iconbtn:active { transform: scale(0.92); }
.hpl-burger { background: transparent; }
.hpl-burger:active { background: rgba(255, 255, 255, 0.55); }

/* ——— mega menus and the search panel ——— */

/* The frosted card is the panel's child, and the fade has to happen ON that
   child rather than on the panel around it.
   Anything that makes an ancestor a "backdrop root" — opacity below 1, a
   filter, or will-change on opacity/transform — leaves a descendant's
   backdrop-filter with nothing to sample, so the blur silently dies and the
   panel renders as clear glass. Fading the wrapper did exactly that.
   Here the wrapper only toggles visibility; the card fades and rises itself,
   and its own opacity composites *after* its own blur, so the blur survives. */
.hpl-panel {
  visibility: hidden;
  pointer-events: none;
}
.hpl-panel.is-open {
  visibility: visible;
  pointer-events: auto;
}

.hpl-panel > div {
  opacity: 0;
  transform: translateY(8px);
  transition-property: opacity, transform;
  transition-duration: 200ms;
  transition-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
}
.hpl-panel.is-open > div {
  opacity: 1;
  transform: translateY(0);
}

.hpl-menurow:hover { background: rgba(255, 255, 255, 0.55); }

/* ——— cross-document page transitions ———
   The curtain covers the old page and uncovers the new one, but between those
   two the browser still has to swap documents — and Chromium paints one blank
   frame at the moment it commits, before any of the new document's styles
   exist. It is a single frame of near-white between two frames of violet, which
   is exactly long enough to read as a glitch. Nothing inside the new document
   can get in front of it: it happens before that document has any CSS.

   This opts both documents into a cross-document view transition, which makes
   the browser hold the last painted frame of the old page until the new one is
   ready to paint. There is no uncovered moment left for the blank frame to
   occupy. Where it is unsupported the navigation simply behaves as before.

   The default cross-fade is left in place but shortened: both snapshots are the
   same violet curtain, so it is invisible, and it smooths over any difference
   between the two. */
@view-transition {
  navigation: auto;
}

::view-transition-group(root) {
  animation-duration: 140ms;
}

@media (prefers-reduced-motion: reduce) {
  @view-transition {
    navigation: none;
  }
}

/* ——— nav search ———
   The icon expands a plain input leftwards. No results list, no hint row: Enter
   goes to the catalogue, and anything shaped like a lot number goes to the COA
   library. Without JS the form is an ordinary product search.

   Open, the whole control is ONE pill. The form carries the background, the
   border, the blur and the radius; the input and the button sit inside it fully
   transparent.

   It was previously built as two halves — the input rounded on the left, the
   button rounded on the right, each with its own background and its own
   backdrop-filter. That cannot line up. Two adjacent backdrop-filters each blur
   only what is behind their own box, so the halves sample different regions and
   meet at a visible seam, and they were 2px different in height as well: the
   button was an explicit 44px while the input took its height from a 44px
   line-height plus two 1px borders. One element cannot disagree with itself. */
.hpl-search {
  border-radius: var(--radius-pill);
  transition:
    background var(--duration-standard) var(--ease-standard),
    box-shadow var(--duration-standard) var(--ease-standard);
}
.hpl-search.is-open {
  background: rgba(255, 255, 255, 0.72);
  /* Frosted like the mega menus, not a flat white box — the pill sits over the
     hero photograph. Nothing above it is a backdrop root, so the blur holds. */
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  /* Inset rather than a real border, so opening the pill cannot change its size
     and nudge the account and basket icons beside it. */
  box-shadow: inset 0 0 0 1px var(--border-light);
}
.hpl-search.is-open:focus-within {
  box-shadow: inset 0 0 0 1px var(--brand);
}

/* Closed, the icon is a plain square button like the account and basket ones,
   and it takes the same hover fill as them through .hpl-iconbtn.

   Open, it is only the icon — the pill behind it belongs to the form. Any hover
   background here paints a lighter rectangle over one end of that pill and
   brings back the two-tone look the single pill exists to prevent, so the icon
   itself carries the hover instead of its box. */
.hpl-searchbtn {
  background: transparent;
  border: 1px solid transparent;
  border-radius: 8px;
  color: var(--text-primary);
  transition:
    background var(--duration-standard) var(--ease-standard),
    color var(--duration-standard) var(--ease-standard);
}
.hpl-search.is-open .hpl-searchbtn,
.hpl-search.is-open .hpl-searchbtn:hover {
  background: transparent;
}
.hpl-search.is-open .hpl-searchbtn:hover { color: var(--brand); }

.hpl-search-input {
  width: 0;
  /* Explicit, so it can never disagree with the 44px button beside it. */
  height: 44px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: var(--text-small);
  outline: none;
  opacity: 0;
  transition:
    width var(--duration-physical) var(--ease-physical),
    padding var(--duration-physical) var(--ease-physical),
    opacity 160ms var(--ease-standard);
}
.hpl-search.is-open .hpl-search-input {
  width: min(260px, 26vw);
  padding: 0 6px 0 18px;
  opacity: 1;
}
.hpl-search-input::-webkit-search-cancel-button { -webkit-appearance: none; }

/* ——— mobile drawer ——— */

/* Two states, not one, and the difference matters.
   `is-rendered` makes the drawer visible while it is still translated fully off
   screen; `is-open` slides it in. Splitting them gives the browser a frame to
   resolve the closed transform, which is what it interpolates from — tie both to
   one class and the panel jumps straight to its end position with no slide. */
.hpl-drawer { visibility: hidden; pointer-events: none; }
.hpl-drawer.is-rendered { visibility: visible; }
.hpl-drawer.is-open { pointer-events: auto; }

.hpl-drawer-scrim {
  opacity: 0;
  transition: opacity var(--duration-standard) var(--ease-standard);
}
.hpl-drawer.is-open .hpl-drawer-scrim { opacity: 1; }

.hpl-drawer-panel {
  transform: translateX(100%);
  transition: transform var(--duration-physical) var(--ease-physical);
}
.hpl-drawer.is-open .hpl-drawer-panel { transform: translateX(0); }

.hpl-drawer-link:active { background: var(--brand-wash); }

/* ——— buttons ———
   13px / 500 / 12px 24px / pill / 260ms standard. Never scale on hover.
   The arrow grows from zero width, so the label never moves. */

.hpl-arrow { max-width: 0; opacity: 0; }
a:hover > .hpl-arrow,
a:hover .hpl-card-btn > .hpl-arrow { max-width: 1.4em; opacity: 1; }

.hpl-btn-primary {
  background: var(--brand);
  color: var(--text-on-dark);
  transition: background var(--duration-standard) var(--ease-standard);
}
.hpl-btn-primary:hover { background: var(--brand-hover); color: var(--text-on-dark); }

/* white pill on the dark and violet panels */
.hpl-btn-secondary {
  background: var(--surface);
  color: var(--text-primary);
  transition: background var(--duration-standard) var(--ease-standard);
}
.hpl-btn-secondary:hover { background: var(--brand-wash); color: var(--text-primary); }

.hpl-btn-outline {
  border: 1px solid var(--border-accent);
  background: transparent;
  color: var(--text-primary);
  transition: background var(--duration-standard) var(--ease-standard);
}
.hpl-btn-outline:hover { background: var(--surface-sunken); color: var(--text-primary); }

.hpl-textlink {
  border-bottom: 1px solid var(--border-accent);
  transition: border-bottom-color var(--duration-standard) var(--ease-standard), color var(--duration-standard) var(--ease-standard);
}
.hpl-textlink:hover { color: var(--brand); border-bottom-color: var(--brand); }

.hpl-link-on-dark {
  color: var(--text-on-dark);
  border-bottom: 1px solid rgba(255, 255, 255, 0.4);
  transition: border-bottom-color 200ms ease;
}
.hpl-link-on-dark:hover { color: var(--text-on-dark); border-bottom-color: var(--text-on-dark); }

.hpl-rail-btn { background: none; }
.hpl-rail-btn:hover { background: var(--surface-dark-raised); transform: scale(1.06); }
.hpl-rail-btn:active { transform: scale(0.94); }
.hpl-rail-btn[disabled] { opacity: 0.4; cursor: not-allowed; }
.hpl-rail-btn[disabled]:hover { background: none; transform: none; }

/* ——— product card ———
   Hover: card lightens one step, image scales 1.04, button flips to solid with
   an arrow. The pointer-tracked tilt is JS; everything here is CSS. */

.hpl-card[data-tone="dark"] { background: var(--surface-dark-raised); border: 1px solid var(--border-dark); }
.hpl-card[data-tone="dark"]:hover { background: var(--surface-dark-hover); }
.hpl-card[data-tone="light"] { background: var(--surface); border: 1px solid var(--border-light); }
.hpl-card[data-tone="light"]:hover { border-color: var(--border-accent); box-shadow: 0 18px 40px -28px rgba(23, 16, 43, 0.35); }

.hpl-card[data-tone="dark"] .hpl-card-plate { background: var(--surface-dark-hover); }
.hpl-card[data-tone="light"] .hpl-card-plate { background: var(--surface-sunken); }

.hpl-card-img { transform: scale(1); }
.hpl-card:hover .hpl-card-img { transform: scale(1.04); }

.hpl-card[data-tone="dark"] .hpl-card-btn { background: transparent; color: var(--text-on-dark); border: 1px solid var(--border-dark); }
.hpl-card[data-tone="dark"]:hover .hpl-card-btn { background: var(--text-on-dark); color: var(--text-primary); border-color: var(--text-on-dark); }
.hpl-card[data-tone="light"] .hpl-card-btn { background: transparent; color: var(--text-primary); border: 1px solid var(--border-accent); }
.hpl-card[data-tone="light"]:hover .hpl-card-btn { background: var(--brand); color: var(--text-on-dark); border-color: var(--brand); }

/* ——— FAQ accordion ——— */

.hpl-faq-a { grid-template-rows: 1fr; }
.hpl-faq[data-js="true"] .hpl-faq-a { grid-template-rows: 0fr; }
.hpl-faq[data-js="true"] .hpl-faq-item.is-open .hpl-faq-a { grid-template-rows: 1fr; }
.hpl-faq-item.is-open .hpl-faq-icon { transform: rotate(45deg); }
.hpl-faq .hpl-faq-item.is-open .hpl-faq-icon { background: var(--brand); color: var(--text-on-dark); }

/* ——— product page ——— */

/* The option groups carry an inline display:grid, which outranks the browser's
   [hidden] { display: none }. Without this the standing-order intervals and the
   one-off card are both on screen at once. */
[data-options][hidden] { display: none !important; }

/* Same trap, twice more. Both of these carry an inline display so their layout
   is set before JavaScript touches them, and both start hidden — so both were
   full-screen fixed overlays that happened to be invisible, sitting on top of
   the whole site and swallowing every click. The offer modal did exactly that.
   The gate is only saved by its own script removing it immediately. */
.hpl-offer[hidden],
.hpl-gate[hidden] { display: none !important; }

/* selectable cards: purchase mode and plan */
.hpl-choice {
  background: var(--surface);
  border: 1px solid var(--border-light);
  box-shadow: none;
}
.hpl-choice[aria-pressed="true"] {
  background: var(--surface-tint);
  border-color: var(--brand);
  box-shadow: inset 0 0 0 2px var(--brand);
}
.hpl-radio { border: 1px solid var(--border-accent); background: var(--surface); }
.hpl-radio > span { background: transparent; }
.hpl-choice[aria-pressed="true"] .hpl-radio { border-color: var(--brand); background: var(--brand); }
.hpl-choice[aria-pressed="true"] .hpl-radio > span { background: var(--surface); }

.hpl-chip-save { color: var(--success); background: rgba(31, 122, 107, 0.08); border: 1px solid var(--success-border); }
.hpl-chip-none { color: var(--text-muted); background: var(--surface-sunken); border: 1px solid var(--border-light); }

.hpl-tab { color: var(--text-secondary); border-bottom: 2px solid transparent; }
.hpl-tab[aria-selected="true"] { color: var(--text-primary); border-bottom-color: var(--brand); }

.hpl-thumb { border: 1px solid var(--border-light); }
.hpl-thumb[aria-pressed="true"] { border-color: var(--brand); box-shadow: 0 0 0 3px rgba(75, 50, 168, 0.14); }

/* Without JS every shot would stack; the first one wins until product.js runs. */
.hpl-shot { opacity: 0; }
.hpl-shot[data-shot="0"] { opacity: 1; }

/* ——— catalogue filter chips ——— */

.hpl-chip {
  background: var(--surface);
  border: 1px solid var(--border-light);
  color: var(--text-primary);
}
.hpl-chip:hover { border-color: var(--border-accent); color: var(--text-primary); }
.hpl-chip[aria-current="page"] {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--text-on-dark);
}

/* ——— slide-out basket ———
   Reuses the mobile nav drawer's scrim, panel and motion, so the two read as
   one behaviour. Not in the prototype; added after sign-off. */

.hpl-cartdrawer[aria-busy="true"] .hpl-drawer-panel { opacity: 0.6; pointer-events: none; }
.hpl-cartdrawer .hpl-drawer-panel { transition: transform var(--duration-physical) var(--ease-physical), opacity 160ms ease; }

/* The panel slides in; its contents settle a beat later, in the same language as
   the page's hpl-bloom reveals. Re-declaring the animation under .is-open means
   it replays on every open, not just the first. */
@keyframes hpl-drawer-settle {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}
.hpl-cartdrawer.is-open [data-cartdrawer-contents] > * {
  animation: hpl-drawer-settle 300ms var(--ease-physical) 90ms both;
}
.hpl-cartdrawer.is-open [data-cartdrawer-contents] > * + * {
  animation-delay: 150ms;
}

/* Full-viewport overlays sit under the WordPress admin bar, which owns the top
   32px (46px on small screens) for logged-in users. Without this the basket and
   nav drawers have their headers clipped. */
.admin-bar .hpl-drawer { top: 32px; }
@media screen and (max-width: 782px) {
  .admin-bar .hpl-drawer { top: 46px; }
}

/* ——— editor prose ———
   Guides and legal pages are written in the WordPress editor, so their body
   markup is whatever the editor produces. These give it the design's type
   without needing inline styles on every paragraph. */

.hpl-prose p { margin: 0 0 12px; }
.hpl-prose h3 {
  font-size: var(--text-step-title);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-title);
  color: var(--text-primary);
  margin: 28px 0 0;
}
.hpl-prose ul, .hpl-prose ol { margin: 0 0 12px; padding-left: 20px; }
.hpl-prose li { margin: 0 0 6px; }
.hpl-prose strong { color: var(--text-primary); font-weight: var(--weight-medium); }
.hpl-prose a {
  color: var(--text-primary);
  border-bottom: 1px solid var(--border-accent);
  transition: color var(--duration-standard) var(--ease-standard), border-bottom-color var(--duration-standard) var(--ease-standard);
}
.hpl-prose a:hover { color: var(--brand); border-bottom-color: var(--brand); }
.hpl-prose code {
  font-family: var(--font-mono);
  font-size: var(--text-small);
  background: var(--surface-sunken);
  border-radius: 4px;
  padding: 1px 5px;
}
.hpl-prose blockquote {
  margin: 20px 0;
  padding: 14px 18px;
  border-left: 3px solid var(--border-accent);
  background: var(--surface-tint);
  border-radius: var(--radius-sm);
}
.hpl-prose table { width: 100%; border-collapse: collapse; margin: 0 0 16px; font-size: var(--text-small); }
.hpl-prose th, .hpl-prose td { text-align: left; padding: 12px 14px; border-bottom: 1px solid var(--border-light); }
.hpl-prose th { font-family: var(--font-mono); font-size: var(--text-micro); letter-spacing: 0.06em; color: var(--text-secondary); font-weight: var(--weight-regular); }

/* legal sections sit under their numbered heading with a rule between them */
.hpl-legal > div + p { margin-top: 14px; }

.hpl-toc-link { transition: color var(--duration-standard) var(--ease-standard); }
.hpl-toc-link:hover { color: var(--brand); }

/* ——— account ———
   WooCommerce floats the account navigation (22%) and content (78%) into two
   columns. Floats do not contribute to their parent's height, so the wrapper
   collapsed and its bottom padding vanished — the last order card sat directly
   on the footer. The design puts the tabs in a full-width pill row above the
   content, so both floats go. */
.woocommerce-account .woocommerce-MyAccount-navigation,
.woocommerce-account .woocommerce-MyAccount-content {
  float: none;
  width: 100%;
}
.woocommerce-account .woocommerce-MyAccount-content { margin-bottom: 0; }

/* ——— WooCommerce notices ———
   "Removed — undo?", stock warnings, payment failures, and the research-use
   gate's refusal all arrive as these. Styled to the design system rather than
   suppressed: a silent refusal is worse than an ugly one. */

.hpl-notices { margin-top: 24px; }

/* Also catches the notices WooCommerce prints itself — the account area's
   "confirm your email address" prompt among them. */
.hpl-notices .woocommerce-message,
.hpl-notices .woocommerce-info,
.hpl-notices .woocommerce-error,
.woocommerce-MyAccount-content .woocommerce-message,
.woocommerce-MyAccount-content .woocommerce-info,
.woocommerce-MyAccount-content .woocommerce-error,
.woocommerce-notices-wrapper .woocommerce-message,
.woocommerce-notices-wrapper .woocommerce-info,
.woocommerce-notices-wrapper .woocommerce-error {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 16px;
  margin: 0 0 10px;
  padding: 14px 18px;
  list-style: none;
  border: 1px solid var(--border-light);
  border-left: 3px solid var(--border-accent);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text-primary);
  font-size: var(--text-small);
  line-height: var(--leading-body);
}

.hpl-notices .woocommerce-message,
.woocommerce-MyAccount-content .woocommerce-message,
.woocommerce-notices-wrapper .woocommerce-message { border-left-color: var(--success); }
.hpl-notices .woocommerce-error,
.woocommerce-MyAccount-content .woocommerce-error,
.woocommerce-notices-wrapper .woocommerce-error { border-left-color: var(--brand); }

/* WooCommerce puts an icon-font glyph in an absolutely positioned ::before at
   left:1.5em, which lands on top of the text once the padding is the design
   system's rather than Woo's. The coloured left edge is the status signal here,
   and the design has no icon font. */
.hpl-notices .woocommerce-message::before,
.hpl-notices .woocommerce-info::before,
.hpl-notices .woocommerce-error::before,
.woocommerce-MyAccount-content .woocommerce-message::before,
.woocommerce-MyAccount-content .woocommerce-info::before,
.woocommerce-MyAccount-content .woocommerce-error::before,
.woocommerce-notices-wrapper .woocommerce-message::before,
.woocommerce-notices-wrapper .woocommerce-info::before,
.woocommerce-notices-wrapper .woocommerce-error::before { content: none; }

.hpl-notices .woocommerce-error li { list-style: none; margin: 0; }

.hpl-notices a,
.woocommerce-MyAccount-content .woocommerce-message a,
.woocommerce-notices-wrapper a {
  margin-left: auto;
  font-size: var(--text-small);
  font-weight: var(--weight-medium);
  color: var(--text-primary);
  border-bottom: 1px solid var(--border-accent);
  padding-bottom: 1px;
  transition: color var(--duration-standard) var(--ease-standard), border-bottom-color var(--duration-standard) var(--ease-standard);
}
.hpl-notices a:hover,
.woocommerce-MyAccount-content .woocommerce-message a:hover,
.woocommerce-notices-wrapper a:hover { color: var(--brand); border-bottom-color: var(--brand); }

/* ——— cart ——— */

.hpl-qty { background: none; }
.hpl-qty:hover { background: var(--surface-tint); }
.hpl-remove:hover { color: var(--brand); }

/* ——— checkout form fields ———
   WooCommerce writes its own field markup; these give it the design's inputs
   without fighting Woo's structure. */

/* The .woocommerce form prefix matches WooCommerce's own specificity:
   `.woocommerce form .form-row { padding: 3px; margin: 0 0 6px }` and
   `.woocommerce form .form-row label { line-height: 2 }`. Unprefixed, these
   rules lost, and every field carried 6px of margin, 6px of padding and a
   double-height label — which is where most of the space between rows was
   coming from. The grid gap alone should set the rhythm. */
.hpl-fields .form-row,
.woocommerce form .hpl-fields .form-row { margin: 0; padding: 0; }
.hpl-fields .form-row.hpl-span-1 { grid-column: span 1; }
.hpl-fields .form-row.hpl-span-2 { grid-column: span 2; }

.hpl-fields label,
.woocommerce form .hpl-fields label {
  display: block;
  font-size: var(--text-caption);
  line-height: 1.35;
  color: var(--text-secondary);
  margin-bottom: 6px;
  font-weight: var(--weight-regular);
}
.hpl-fields .required { color: var(--brand); border: none; text-decoration: none; }

.hpl-fields input[type="text"],
.hpl-fields input[type="email"],
.hpl-fields input[type="tel"],
.hpl-fields input[type="password"],
.hpl-fields select,
.hpl-fields .select2-container .select2-selection {
  width: 100%;
  height: 48px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-light);
  background: var(--surface);
  color: var(--text-primary);
  padding: 0 16px;
  font-family: var(--font-sans);
  font-size: var(--text-small);
}
.hpl-fields .description {
  display: block;
  font-size: var(--text-micro);
  color: var(--text-muted);
  margin-top: 6px;
}
.hpl-fields .woocommerce-invalid input { border-color: var(--brand); }

/* delivery option cards use the product page's selectable-card treatment */
[data-shipping-option]:has(input:checked) {
  background: var(--surface-tint);
  border-color: var(--brand);
  box-shadow: inset 0 0 0 2px var(--brand);
}
[data-shipping-option]:has(input:checked) .hpl-radio { border-color: var(--brand); background: var(--brand); }
[data-shipping-option]:has(input:checked) .hpl-radio > span { background: var(--surface); }

/* ——— product page: mobile buy bar ———
   Frosted so the page reads through it, and sitting above the drawer's overlay
   but below the drawer itself. Hidden from 681px up by responsive.css, where
   the real buy block is on screen next to the image. */
.hpl-buybar {
  background: rgba(255, 255, 255, 0.86);
  backdrop-filter: blur(18px) saturate(180%);
  -webkit-backdrop-filter: blur(18px) saturate(180%);
}

/* Woo's registration privacy line, sitting in the account panel rather than
   inside the form. Its own markup is a bare <div><p>. */
.hpl-reg-privacy .woocommerce-privacy-policy-text p {
  font-size: var(--text-small);
  line-height: var(--leading-body);
  color: var(--text-secondary);
  margin: 10px 0 0;
}

/* ——— first-visit offer ———
   A corner panel, not a modal: nothing dimmed, nothing blocked, the page still
   usable behind it. It can therefore sit there being ignored, which is the
   point — taking it is a choice rather than the price of getting past a wall.
   On a phone it spans the width above the fold's bottom edge instead, since a
   380px card in a corner is most of a small screen anyway. */
.hpl-offer {
  position: fixed;
  right: 24px;
  bottom: 24px;
  z-index: 95;
  width: 360px;
  max-width: calc(100vw - 32px);
}

.hpl-offer-card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-md);
  padding: 22px 24px;
  box-shadow: 0 30px 70px -40px rgba(23, 16, 43, 0.55);
  opacity: 0;
  transform: translateY(14px);
  transition:
    opacity var(--duration-standard) var(--ease-standard),
    transform var(--duration-standard) var(--ease-physical);
}

.hpl-offer.is-open .hpl-offer-card {
  opacity: 1;
  transform: none;
}

.hpl-offer-close:hover { background: var(--surface-sunken); color: var(--text-primary); }

@media (max-width: 680px) {
  .hpl-offer {
    right: 16px;
    left: 16px;
    bottom: 16px;
    width: auto;
  }
}

/* ——— product card: sale price ———
   WooCommerce renders a reduced price as <del> then <ins>, with a visually
   hidden span after each for screen readers. Both are kept: the struck original
   sits quietly beside the current price, and the hidden text still explains
   which is which to anyone not looking at it. */
.hpl-card-price del {
  opacity: 0.55;
  font-size: var(--text-caption);
  margin-right: 8px;
  text-decoration-thickness: 1px;
}
.hpl-card-price ins {
  text-decoration: none;
  background: none;
  color: inherit;
}

/* ——— product page: image panel ———
   Every shot inside this panel is position:absolute;inset:0, so the panel has
   to be a positioned ancestor for them to be contained by it. That is what the
   `position: relative` below is for, and it is not optional.

   The panel used to get its containment as a side effect of being
   position:sticky inline, and the mobile stylesheet set it to `static` to
   unstick it. That silently removed the containment as well: the absolute shots
   escaped, sized themselves against the page instead of the panel, and painted
   over the title, the buy block and the tabs. The phone layout looked as though
   the content was "behind the image" because it literally was.

   So: relative always, sticky only where there are two columns for it to stick
   beside. Nothing overrides `position` at any width now. */
.hpl-shotpanel { position: relative; }

@media (min-width: 681px) {
  .hpl-shotpanel {
    position: sticky;
    top: 0;
    height: 100vh;
  }
}

/* ——— order received ———
   WooCommerce's own order-details and customer-details tables, brought into the
   design. The template keeps the `woocommerce_thankyou` hook so gateways and
   plugins still render into this page; only the presentation is ours. */
.hpl-order-details h2,
.hpl-order-details h3 {
  font-size: var(--text-card-title);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-title);
  margin: 0 0 12px;
}
.hpl-order-details .woocommerce-order-details,
.hpl-order-details .woocommerce-customer-details { margin: 0 0 32px; }
.hpl-order-details table.woocommerce-table--order-details {
  width: 100%;
  border-collapse: collapse;
  border: 1px solid var(--border-light);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--surface);
  font-size: var(--text-small);
}
.hpl-order-details table.woocommerce-table--order-details th,
.hpl-order-details table.woocommerce-table--order-details td {
  padding: 13px 18px;
  border-bottom: 1px solid var(--border-light);
  text-align: left;
  font-weight: var(--weight-regular);
}
.hpl-order-details table.woocommerce-table--order-details thead th {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: 0.08em;
  color: var(--text-secondary);
  background: var(--surface-sunken);
}
.hpl-order-details table.woocommerce-table--order-details tfoot th { font-weight: var(--weight-medium); }
.hpl-order-details table.woocommerce-table--order-details td:last-child,
.hpl-order-details table.woocommerce-table--order-details th:last-child { text-align: right; font-family: var(--font-mono); }
.hpl-order-details table.woocommerce-table--order-details tfoot tr:last-child th,
.hpl-order-details table.woocommerce-table--order-details tfoot tr:last-child td {
  border-bottom: none;
  font-size: var(--text-body);
  font-weight: var(--weight-medium);
}
.hpl-order-details .woocommerce-columns--addresses { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); gap: 20px; }
.hpl-order-details .woocommerce-column address {
  font-style: normal;
  font-size: var(--text-small);
  line-height: var(--leading-body);
  color: var(--text-secondary);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-md);
  background: var(--surface);
  padding: 16px 18px;
}
/* Gateway instructions — bank details for a transfer. */
.hpl-order-details .woocommerce-bacs-bank-details,
.hpl-order-details .wc-bacs-bank-details-account-name {
  font-size: var(--text-small);
  line-height: var(--leading-body);
  color: var(--text-secondary);
}
.hpl-order-details ul.wc-bacs-bank-details {
  list-style: none;
  margin: 0 0 24px;
  padding: 16px 18px;
  border: 1px solid var(--border-light);
  border-radius: var(--radius-md);
  background: var(--surface);
  font-family: var(--font-mono);
  font-size: var(--text-caption);
}

/* ——— checkout: payment ———
   Every selector here is prefixed .woocommerce-checkout to match the
   specificity of WooCommerce's own (.woocommerce-checkout #payment {…}).
   Plain #payment is a lower specificity than that despite being an id, so the
   earlier version of this block lost every one of these battles: the grey slab
   behind the payment area, the grey speech-bubble with a tail pointing at the
   method name, and the grey band behind the privacy notice were all still
   WooCommerce's. */
.woocommerce-checkout #payment {
  background: none;
  border-radius: 0;
}
.woocommerce-checkout #payment ul.payment_methods {
  border: 1px solid var(--border-light);
  border-radius: var(--radius-md);
  padding: 16px 18px;
  margin: 12px 0 0;
  list-style: none;
  background: var(--surface);
}
.woocommerce-checkout #payment ul.payment_methods li {
  list-style: none;
  margin: 0 0 8px;
  font-size: var(--text-small);
}
.woocommerce-checkout #payment ul.payment_methods li:last-child { margin-bottom: 0; }
.woocommerce-checkout #payment div.payment_box {
  background: var(--surface-tint);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  margin: 10px 0 0;
  font-size: var(--text-small);
  line-height: var(--leading-body);
  color: var(--text-secondary);
}
/* The tail on that box. */
.woocommerce-checkout #payment div.payment_box::before,
.woocommerce-checkout #payment div.payment_box::after {
  display: none;
  content: none;
}
.woocommerce-checkout #payment .woocommerce-privacy-policy-text {
  background: none;
  padding: 0;
  margin: 0;
  font-size: var(--text-micro);
  line-height: 1.6;
  color: var(--text-muted);
}
.woocommerce-checkout #payment .woocommerce-privacy-policy-text p { margin: 0; }
.woocommerce-checkout #payment .place-order { padding: 0; margin-top: 16px; }
.woocommerce-checkout #payment #place_order {
  width: 100%;
  /* The research-use gate renders inside .place-order, directly above this, so
     the button needs its own breathing room rather than inheriting the row's. */
  margin-top: 22px;
  border: none;
  cursor: pointer;
  padding: 16px 24px;
  border-radius: var(--radius-pill);
  background: var(--brand);
  color: var(--text-on-dark);
  font-family: var(--font-sans);
  font-size: var(--text-small);
  font-weight: var(--weight-medium);
  transition: background var(--duration-standard) var(--ease-standard);
}
.woocommerce-checkout #payment #place_order:hover { background: var(--brand-hover); }
.woocommerce-checkout #payment .woocommerce-terms-and-conditions-wrapper {
  font-size: var(--text-small);
  color: var(--text-secondary);
}

/* ——— checkout: research-use gate ———
   The tinted state is the resting one; it turns to the brand wash only after a
   failed submission, so the page does not shout before the buyer has done
   anything wrong. */
.hpl-ruo-box {
  border: 1px solid var(--border-light);
  background: var(--surface-tint);
}
.woocommerce-error ~ * .hpl-ruo-box,
.hpl-ruo-box:has(input:invalid:not(:placeholder-shown)) {
  border-color: var(--brand);
  background: var(--brand-wash);
}

/* ——— COA library ——— */

.hpl-coa-submit { background: transparent; }
.hpl-coa-submit:hover { background: var(--surface-dark-hover); color: var(--text-on-dark); }

.hpl-coa-row:hover { background: var(--surface-tint); }

.hpl-suggestion {
  background: var(--surface);
  border: 1px solid var(--border-accent);
  color: var(--text-primary);
}
.hpl-suggestion:hover { background: var(--brand-wash); border-color: var(--brand); color: var(--text-primary); }

/* ——— researcher gate ——— */

.hpl-gate-check { border: 1px solid var(--border-light); background: var(--surface); }
.hpl-gate-check:has(input:checked) { border-color: var(--brand); background: var(--surface-sunken); }

.hpl-gate-enter { background: var(--brand); cursor: pointer; }
.hpl-gate-enter:active { transform: scale(0.985); }
.hpl-gate-enter[disabled] { background: var(--lavender-400); cursor: not-allowed; }

/* ——— footer ——— */

.hpl-footer-link {
  color: var(--text-on-dark-secondary);
  text-decoration: none;
  transition: color 200ms ease;
}
.hpl-footer-link:hover { color: var(--text-on-dark); }

.hpl-news-btn {
  background: var(--surface);
  transition: background var(--duration-standard) var(--ease-standard), transform var(--duration-standard) var(--ease-standard);
}
.hpl-news-btn:hover { background: var(--brand-wash); transform: translateX(2px); }
.hpl-news-btn:active { transform: scale(0.94); }
