/* Catalogue pages — products.html (listing) and product.html (detail).
 *
 * Loaded after css/styles.css, and only by those two pages, so the homepage
 * pays nothing for it. Uses the existing tokens only; no new colours.
 *
 * The homepage's clip-path selector lists (styles.css:135-155) are pinned as
 * literal strings by tests/style-contract.test.js — the page rhythm below is
 * declared in its own blocks rather than appended to them.
 */

/* .visually-hidden lives in css/styles.css, which always loads first. */

/* ===============================================================
   Page rhythm — angled section seams
   =============================================================== */

.catalogue-page main > section:not(.catalogue-section),
.product-page main > section:not(.product-hero) {
  position: relative;
  margin-top: calc(-1 * var(--section-angle));
}

/* Rising: cut on the top-left, apex top-right. */
.catalogue-cta,
.product-claims {
  clip-path: polygon(0 var(--section-angle), 100% 0, 100% 100%, 0 100%);
}

/* Falling: apex top-left, cut on the top-right. */
.product-related {
  clip-path: polygon(0 0, 100% var(--section-angle), 100% 100%, 0 100%);
}

/* .product-facts sits flush against .product-claims: warm -> paper is a
   near-invisible luminance step, and an angled seam there would cut into
   a 22-row table. */
.product-facts {
  margin-top: 0 !important;
}

/* Products with no authorised claims have no .product-claims section, so the
   facts panel becomes the first light band after the dark hero and takes the
   rising seam itself. js/product-detail.js sets the class. */
.product-facts--lead {
  margin-top: calc(-1 * var(--section-angle)) !important;
  clip-path: polygon(0 var(--section-angle), 100% 0, 100% 100%, 0 100%);
}

/* ===============================================================
   Listing — grid band
   =============================================================== */

/* --sticky-top clears the floating header. Measured .nav-shell bottom edges:
   desktop 96px resting / 78px scrolled; <=860px 76px resting / 78px scrolled
   (.is-scrolled .nav-shell wins on specificity there). Take the worst case
   per breakpoint and add 10px of breathing room. */
.catalogue-page {
  --sticky-top: 106px;
  background: var(--ink-deep);
}

/* overflow: clip, never hidden — an overflow: hidden ancestor becomes the
   sticky scrollport and would kill .catalogue-bar.
   padding-top == --sticky-top so the bar opens at exactly the position it
   will stick to; there is no hero band above it. */
.catalogue-section {
  overflow: clip;
  padding-top: var(--sticky-top);
  background:
    radial-gradient(circle at 85% 10%, rgba(37, 98, 76, 0.32), transparent 28%),
    linear-gradient(135deg, #08100e, #111815 62%, #07100d);
  color: white;
}

.catalogue-section::before {
  position: absolute;
  top: 0;
  right: -10%;
  width: 44%;
  height: 100%;
  transform: translate3d(var(--section-shift, 0px), 0, 0) skewX(-22deg);
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.025));
  content: "";
  pointer-events: none;
}

.catalogue-bar {
  position: sticky;
  z-index: 30;
  top: var(--sticky-top);
  border-block: 1px solid var(--line-light);
  background: rgba(9, 16, 14, 0.92);
  backdrop-filter: blur(18px);
}

/* Three tracks with equal outer columns, so the tab row stays centred on the
   container whatever the tools measure. The first track is deliberately
   empty — it is the counterweight to the tools on the right. The columns are
   assigned explicitly because there are only two children.
   minmax(0, 1fr), not 1fr: a grid item's automatic minimum size is its
   min-content width, and the nowrap tab row would otherwise force the track
   wider than the viewport.
   This only survives because the tabs are single words. Spelled out
   ("Strength & recovery") at --text-label they measure 1117px against a
   1217px container, and the auto middle track crushes both outer ones to
   zero — the icon buttons vanish rather than wrap. Keep tab labels to one
   word; the full category name lives in the .visually-hidden span so the
   accessible name stays complete. */
.catalogue-bar__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, auto) minmax(0, 1fr);
  gap: 10px 16px;
  align-items: center;
  padding-block: 9px;
}

.category-tabs {
  min-width: 0;
  grid-column: 2;
}

.catalogue-tools {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  grid-column: 3;
}

/* ---- Category tabs -------------------------------------------------- */

.category-tabs__scroll {
  display: flex;
  overflow-x: auto;
  padding-block: 2px;
  scroll-padding-inline: 26px;
  scrollbar-width: none;
}

.category-tabs__scroll::-webkit-scrollbar {
  display: none;
}

/* margin-inline: auto rather than justify-content: center — centring a flex
   scroll container makes the overflowing start unreachable. */
.category-tabs__list {
  display: flex;
  gap: clamp(18px, 2.4vw, 34px);
  margin: 0 auto;
  padding: 0;
  list-style: none;
}

.category-tab {
  position: relative;
  display: inline-flex;
  min-height: 44px;
  align-items: center;
  gap: 7px;
  padding-block: 12px;
  color: rgba(255, 255, 255, 0.6);
  /* Navigational labels, so they sit on the floor rather than --text-base:
     six nowrap names at 1rem overflow the container by 100px+. */
  font-size: var(--text-label);
  font-weight: 800;
  letter-spacing: var(--track-label);
  line-height: 1;
  text-transform: uppercase;
  white-space: nowrap;
  transition: color 200ms ease;
}

.category-tab::after {
  position: absolute;
  right: 50%;
  bottom: 2px;
  left: 50%;
  height: 2px;
  background: var(--orange);
  content: "";
  transition: right 220ms var(--ease), left 220ms var(--ease);
}

.category-tab:hover,
.category-tab[aria-current] {
  color: white;
}

.category-tab:hover::after,
.category-tab[aria-current]::after {
  right: 0;
  left: 0;
}

.category-tab__count {
  color: rgba(255, 255, 255, 0.34);
  font-size: var(--text-label);
  font-weight: 700;
  letter-spacing: 0.02em;
}

.category-tab[aria-current] .category-tab__count {
  color: var(--orange-bright);
}

/* ---- Status + icon controls ----------------------------------------- */

.icon-button {
  position: relative;
  display: inline-flex;
  width: 44px;
  height: 44px;
  flex: none;
  align-items: center;
  justify-content: center;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.06);
  color: rgba(255, 255, 255, 0.82);
  transition: border-color 200ms ease, background-color 200ms ease, color 200ms ease;
}

.icon-button:hover {
  border-color: rgba(255, 255, 255, 0.34);
  color: white;
}

.icon-button[aria-expanded="true"],
.icon-button.is-active {
  border-color: var(--orange);
  background: rgba(237, 103, 3, 0.16);
  color: white;
}

.icon-button__glyph {
  width: 18px;
  height: 18px;
  flex: none;
}

.icon-button__badge {
  position: absolute;
  top: -8px;
  right: -8px;
  display: grid;
  min-width: 24px;
  height: 24px;
  padding: 0 6px;
  place-items: center;
  /* Pill, not a circle: the count is set at --text-label now, and a fixed
     square clipped a two-digit filter count. */
  border-radius: 999px;
  background: var(--orange);
  color: white;
  font-size: var(--text-label);
  font-weight: 800;
  line-height: 1;
}

/* The Shop owns currency selection. Its trigger matches the sort/filter tool
   buttons; the three fixed currencies stay explicit inside a compact popover. */
.currency-tool {
  position: relative;
  display: flex;
  flex: none;
}

.currency-toggle__code {
  font-size: var(--text-label);
  font-weight: 800;
  letter-spacing: 0.03em;
  line-height: 1;
}

.currency-popover {
  position: absolute;
  z-index: 50;
  top: calc(100% + 10px);
  right: 0;
  width: 230px;
  padding: 8px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 14px;
  background: rgba(9, 16, 14, 0.98);
  box-shadow: var(--shadow-dark);
  backdrop-filter: blur(18px);
}

.currency-options {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 6px;
}

.currency-options button {
  display: inline-flex;
  min-width: 0;
  min-height: 44px;
  align-items: center;
  justify-content: center;
  gap: 5px;
  padding: 8px 6px;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.05);
  color: rgba(255, 255, 255, 0.72);
  font-size: var(--text-label);
  font-weight: 800;
  line-height: 1;
  transition: border-color 180ms ease, background-color 180ms ease, color 180ms ease;
}

.currency-options button b {
  color: var(--orange-bright);
  font: inherit;
}

.currency-options button:hover,
.currency-options button[aria-pressed="true"] {
  border-color: var(--orange);
  background: rgba(237, 103, 3, 0.16);
  color: white;
}

/* ---- Facet panel ----------------------------------------------------
   Absolutely positioned inside the sticky bar, so it hangs directly under
   the header on every viewport and travels with the bar while the page
   keeps scrolling behind it. */

.catalogue-filters {
  position: absolute;
  z-index: 40;
  top: 100%;
  right: 0;
  left: 0;
  max-height: calc(100svh - var(--sticky-top) - 96px);
  overflow-y: auto;
  visibility: hidden;
  border-bottom: 1px solid var(--line-light);
  background: rgba(9, 16, 14, 0.98);
  box-shadow: var(--shadow-dark);
  opacity: 0;
  overscroll-behavior: contain;
  transform: translateY(-8px);
  backdrop-filter: blur(18px);
  transition: opacity 220ms ease, transform 220ms var(--ease), visibility 220ms;
}

.catalogue-filters[data-open="true"] {
  visibility: visible;
  opacity: 1;
  transform: none;
}

.catalogue-filters__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 18px 34px;
  padding-block: 22px 24px;
}

/* The panel is out of flow, so while it is open the grid is pushed down by
   exactly its height. Nothing ever sits underneath it, and the extra height
   is real scroll distance rather than a dead zone.
   js/catalogue.js keeps --filter-offset in step with the panel. */
.catalogue-section > .container {
  padding-top: var(--filter-offset, 0px);
  transition: padding-top 220ms var(--ease);
}

.facet {
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

.facet__legend {
  margin: 0 0 10px;
  padding: 0;
  color: rgba(255, 255, 255, 0.46);
  font-family: var(--font-body);
  font-size: var(--text-label);
  font-style: normal;
  font-weight: 800;
  letter-spacing: var(--track-label);
  line-height: 1.2;
  text-transform: uppercase;
}

.facet__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* Chip — the missing primitive, on the previously unused --radius-sm.
   Driven by a visually-hidden checkbox, so grouping, state and keyboard
   behaviour are all native. */
.chip {
  display: inline-flex;
  min-height: 44px;
  align-items: center;
  gap: 9px;
  padding: 10px 15px;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.05);
  color: rgba(255, 255, 255, 0.78);
  cursor: pointer;
  font-size: var(--text-base);
  font-weight: 800;
  letter-spacing: var(--track-base);
  line-height: 1;
  text-transform: uppercase;
  transition: border-color 200ms ease, background-color 200ms ease, color 200ms ease;
}

/* Brand tell: the same skewX(-28deg) orange bar as .eyebrow > span. */
.chip::before {
  width: 0;
  height: 3px;
  transform: skewX(-28deg);
  background: var(--orange);
  content: "";
  transition: width 220ms var(--ease);
}

.chip:hover {
  border-color: rgba(255, 255, 255, 0.32);
  color: white;
}

.chip__input:checked + .chip {
  border-color: var(--orange);
  background: rgba(237, 103, 3, 0.16);
  color: white;
}

.chip__input:checked + .chip::before {
  width: 14px;
}

.chip__input:focus-visible + .chip {
  outline: 3px solid var(--orange-bright);
  outline-offset: 4px;
}

.chip__count {
  color: rgba(255, 255, 255, 0.4);
  font-size: var(--text-label);
  font-weight: 700;
  letter-spacing: 0.02em;
}

.chip__input:checked + .chip .chip__count {
  color: rgba(255, 255, 255, 0.66);
}

/* Zero-result chips dim but stay clickable — disabling would trap a user
   who needs to un-narrow. */
.chip--empty {
  opacity: 0.38;
}

/* ---- Active filter tokens ------------------------------------------- */

.active-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding-top: 18px;
  border-top: 1px solid rgba(255, 255, 255, 0.09);
}

.active-filters .facet__legend {
  margin: 0 4px 0 0;
}

.active-filters__list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.token {
  display: inline-flex;
  min-height: 40px;
  align-items: center;
  gap: 9px;
  padding: 9px 13px;
  border: 1px solid rgba(237, 103, 3, 0.4);
  border-radius: var(--radius-sm);
  background: rgba(237, 103, 3, 0.14);
  color: white;
  font-size: var(--text-base);
  font-weight: 800;
  letter-spacing: var(--track-base);
  line-height: 1;
  text-transform: uppercase;
  transition: border-color 200ms ease, background-color 200ms ease;
}

.token > b {
  font-size: 1.05rem;
  font-weight: 700;
  line-height: 0;
  opacity: 0.72;
}

.token:hover {
  border-color: var(--orange-bright);
  background: rgba(237, 103, 3, 0.24);
}

/* ---- Grid ----------------------------------------------------------- */

.catalogue-grid {
  --card-visual-height: 300px;
  --card-title-size: 1.65rem;
  --card-pad: 24px;
  --card-price-size: 1.8rem;
  position: relative;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 18px;
  margin-top: clamp(30px, 4vw, 48px);
}

/* Listing card — an <a> wrapper on the shared .product-card shell, so it
   inherits every existing rule and both motion systems in main.js. */
.product-card--link {
  display: block;
  color: inherit;
}

.product-card--link::after {
  position: absolute;
  z-index: 6;
  inset: auto 0 0;
  height: 3px;
  transform: scaleX(0);
  transform-origin: left;
  background: var(--card-accent);
  content: "";
  transition: transform 320ms var(--ease);
}

.product-card--link:hover::after,
.product-card--link:focus-visible::after {
  transform: scaleX(1);
}

.product-card--link:focus-visible {
  border-color: color-mix(in srgb, var(--card-accent) 60%, white 10%);
  outline-offset: 3px;
}

/* The test-locked values from styles.css are re-expressed through custom
   properties, so the listing can retune them per breakpoint without
   touching the pinned rules. Two-class specificity always wins. */
.product-card--link .product-card__visual {
  height: var(--card-visual-height, 300px);
}

.product-card--link .product-card__body {
  padding: var(--card-pad, 24px);
}

.product-card--link .product-card__body h3 {
  font-size: var(--card-title-size, 1.65rem);
}

.product-card--link .product-card__price strong {
  font-size: var(--card-price-size, 1.8rem);
}

/* Entrance for a card that filtering brings back into the grid.
   Opacity only: .motion-card composes `transform` from the pointer-tilt
   custom properties (styles.css:1494), and animating it here would fight
   setupCardMotion.
   fill-mode is `backwards`, never `both` — once the animation ends the
   cascade governs again, so a stale .is-entering can never pin a card at
   opacity 0. */
.product-card--link.is-entering {
  animation: catalogue-card-in 380ms var(--ease) backwards;
  animation-delay: var(--delay, 0ms);
}

@keyframes catalogue-card-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

.product-card--link .product-card__price--request strong {
  color: rgba(255, 255, 255, 0.72);
  /* Tracks the price size at 62%, but the floor wins: --card-price-size drops
     to 1.28rem on narrow cards, where 62% would land at 0.79rem. */
  font-size: max(var(--text-label), calc(var(--card-price-size, 1.8rem) * 0.62));
  letter-spacing: 0.01em;
}

/* ---- Empty state ---------------------------------------------------- */

.empty-state--catalogue {
  display: grid;
  justify-items: start;
  gap: 12px;
  padding: clamp(30px, 5vw, 54px);
  border: 1px solid var(--line-light);
  border-radius: var(--radius-lg);
  background: rgba(255, 255, 255, 0.04);
}

.empty-state--catalogue h2 {
  margin-bottom: 4px;
  color: white;
  font-size: clamp(1.8rem, 3.4vw, 2.6rem);
}

.empty-state--catalogue p {
  margin-bottom: 0;
  color: rgba(255, 255, 255, 0.62);
}

.empty-state--catalogue .button-row {
  margin-top: 6px;
}

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

.catalogue-cta {
  overflow: clip;
  background: var(--warm);
  text-align: center;
}

.catalogue-cta h2 {
  margin-bottom: 18px;
}

.catalogue-cta p {
  max-width: 560px;
  margin-inline: auto;
  color: var(--muted);
}

.catalogue-cta .eyebrow,
.catalogue-cta .button-row {
  justify-content: center;
}

/* Hide controls that cannot work rather than showing dead UI.
   index.html already ships the <html class="no-js"> + inline swap. */
.no-js .catalogue-bar,
.no-js .catalogue-filters,
.noscript-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 6px 24px;
  margin: 0 0 8px;
  padding: 0;
  list-style: none;
}

.noscript-list a {
  color: var(--orange-bright);
  text-decoration: underline;
}

/* ===============================================================
   Breakpoints — the four the site already uses
   =============================================================== */

@media (max-width: 1120px) {
  .catalogue-grid {
    --card-visual-height: 320px;
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

@media (max-width: 860px) {
  .catalogue-page {
    --sticky-top: 88px;
  }

  .catalogue-grid {
    --card-visual-height: 280px;
    --card-title-size: 1.35rem;
    --card-pad: 20px;
    --card-price-size: 1.5rem;
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  /* One row: the scrollable tab rail with the tools pinned to its end. The
     centring counterweight is dropped here — every pixel of the narrow
     viewport goes to the tabs. */
  .catalogue-bar__inner {
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 6px 12px;
  }

  .category-tabs {
    grid-column: 1;
  }

  .catalogue-tools {
    grid-column: 2;
  }

  /* Mask rather than a coloured gradient: the bar sits on a multi-stop
     background no solid colour can match. Only here, where the row
     actually overflows, so desktop focus rings are never clipped. */
  .category-tabs__scroll {
    margin-inline: -15px;
    padding-inline: 15px;
    mask-image: linear-gradient(
      90deg,
      transparent 0,
      #000 26px,
      #000 calc(100% - 26px),
      transparent 100%
    );
  }
}

@media (max-width: 680px) {
  .currency-popover {
    position: fixed;
    /* .catalogue-bar's backdrop-filter makes it the fixed containing block. */
    top: 68px;
    right: 15px;
    left: 15px;
    width: auto;
  }

  .catalogue-grid {
    --card-visual-height: 240px;
    --card-title-size: 1.2rem;
    --card-pad: 16px;
    --card-price-size: 1.28rem;
    gap: 12px;
  }

  .catalogue-intro {
    padding-top: 126px;
  }

  .catalogue-intro__stats {
    gap: 28px;
  }

  .catalogue-intro__stats dd {
    font-size: 2.6rem;
  }

  .catalogue-bar__inner {
    gap: 8px;
    padding-block: 8px 10px;
  }

  .category-tabs__list {
    gap: 20px;
  }

}

@media (max-width: 420px) {
  .catalogue-grid {
    --card-visual-height: 300px;
    --card-title-size: clamp(1.95rem, 8.4vw, 2.2rem);
    --card-pad: 22px;
    --card-price-size: 1.8rem;
    grid-template-columns: 1fr;
  }

  .catalogue-search,
  .catalogue-sort {
    flex: 1 0 100%;
  }
}

@media (pointer: coarse) {
  .token {
    min-height: 44px;
  }
}

/* ===============================================================
   Reduced motion — styles.css:2772-2828 already neutralises .reveal,
   .motion-card, every transition and every animation. Only these are new.
   =============================================================== */

@media (prefers-reduced-motion: reduce) {
  .catalogue-grid .product-card--link {
    --delay: 0ms !important;
  }

  .product-card--link::after {
    transform: scaleX(1);
    transition: none;
  }

  .catalogue-filters {
    transform: none;
  }
}

/* ===============================================================
   Product detail page
   =============================================================== */

.product-page {
  background: var(--ink-deep);
}

/* ---- Hero ----------------------------------------------------------- */

.product-hero {
  overflow: clip;
  padding-top: clamp(126px, 13vw, 172px);
  padding-bottom: clamp(72px, 8vw, 112px);
  background:
    radial-gradient(circle at 12% 8%, rgba(37, 98, 76, 0.3), transparent 32%),
    var(--ink-deep);
  color: white;
}

.breadcrumb {
  margin-bottom: clamp(30px, 4vw, 48px);
}

.breadcrumb ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 10px;
  margin: 0;
  padding: 0;
  list-style: none;
  color: rgba(255, 255, 255, 0.45);
  font-size: var(--text-label);
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

.breadcrumb li + li::before {
  margin-right: 10px;
  content: "/";
  opacity: 0.5;
}

.breadcrumb a {
  transition: color 180ms ease;
}

.breadcrumb a:hover {
  color: var(--orange-bright);
}

.breadcrumb [aria-current] {
  color: white;
}

.product-hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 0.92fr) minmax(0, 1fr);
  gap: clamp(30px, 5vw, 68px);
  align-items: center;
}

/* Same accent-glow recipe as .product-card__visual, at hero scale. */
.product-hero__media {
  position: relative;
  display: grid;
  aspect-ratio: 1 / 1;
  place-items: center;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: var(--radius-lg);
  background:
    linear-gradient(145deg, rgba(255, 255, 255, 0.07), transparent 46%),
    radial-gradient(circle at 50% 75%, color-mix(in srgb, var(--card-accent) 34%, transparent), transparent 52%);
}

.product-hero__glow {
  position: absolute;
  bottom: 12%;
  left: 50%;
  width: 52%;
  height: 14%;
  transform: translateX(-50%);
  border-radius: 50%;
  background: var(--card-accent);
  filter: blur(58px);
  opacity: 0.34;
}

.product-hero__media img {
  position: relative;
  z-index: 2;
  width: 92%;
  height: 92%;
  object-fit: contain;
  filter: drop-shadow(0 30px 30px rgba(0, 0, 0, 0.34));
}

.product-hero__media .badge {
  position: absolute;
  z-index: 5;
  top: 22px;
  left: 22px;
  border-color: transparent;
  /* --card-accent is never set on the hero, so it was a dead fallback that
     made the whole declaration invalid; --orange is a real one. */
  background: var(--badge-bg, var(--orange));
  color: white;
}

.product-hero__body h1 {
  margin-bottom: 10px;
  font-family: var(--font-display);
  font-size: clamp(3.2rem, 6vw, 5.4rem);
  font-style: italic;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 0.9;
  text-transform: uppercase;
}

.product-hero__variant {
  margin-bottom: 20px;
  color: color-mix(in srgb, var(--card-accent) 70%, white);
  font-size: var(--text-label);
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

.product-hero__descriptor {
  max-width: 46ch;
  margin-bottom: 24px;
  color: rgba(255, 255, 255, 0.72);
  font-size: clamp(0.95rem, 1.3vw, 1.06rem);
  line-height: 1.65;
}

.prop-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 26px;
}

.prop-badge {
  display: inline-flex;
  min-height: 32px;
  align-items: center;
  gap: 8px;
  padding: 7px 13px;
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.06);
  color: rgba(255, 255, 255, 0.88);
  font-size: var(--text-label);
  font-weight: 800;
  letter-spacing: var(--track-label);
  line-height: 1;
  text-transform: uppercase;
}

.prop-badge--alert {
  border-color: rgba(237, 103, 3, 0.45);
  background: rgba(237, 103, 3, 0.16);
}

.product-keyfacts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 14px 24px;
  margin: 0 0 28px;
  padding: 20px 0;
  border-block: 1px solid rgba(255, 255, 255, 0.12);
}

.product-keyfacts div {
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 0;
}

.product-keyfacts dt {
  color: rgba(255, 255, 255, 0.42);
  font-size: var(--text-label);
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

.product-keyfacts dd {
  margin: 0;
  color: white;
  font-size: var(--text-base);
  font-weight: 600;
  line-height: 1.35;
}

.product-price {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin-bottom: 22px;
}

.product-price span {
  color: rgba(255, 255, 255, 0.45);
  font-size: var(--text-label);
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

.product-price strong {
  color: white;
  font-family: var(--font-display);
  font-size: 2.6rem;
  font-style: italic;
  font-weight: 700;
  line-height: 0.9;
}

.product-price--request strong {
  font-size: 1.5rem;
  opacity: 0.78;
}

.product-hero__legal {
  margin: 22px 0 0;
  color: rgba(255, 255, 255, 0.5);
  font-size: var(--text-base);
  line-height: 1.6;
}

/* ---- Claims strip --------------------------------------------------- */

.product-claims {
  overflow: clip;
  background: var(--warm);
}

.claims-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 16px;
  margin-bottom: 30px;
}

.claim-card {
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-height: 0;
  padding: 26px 24px 24px;
  border: 1px solid rgba(11, 22, 19, 0.09);
  border-radius: var(--radius-md);
  background: white;
}

.claim-card__subject {
  color: var(--orange);
  font-size: var(--text-label);
  font-weight: 800;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

/* Authorised wording is quoted verbatim and never promoted to a heading. */
.claim-card p {
  margin: 0;
  color: var(--graphite);
  font-size: 0.95rem;
  line-height: 1.55;
}

.claim-card--condition {
  border-style: dashed;
  background: transparent;
}

.claim-card--condition p {
  color: var(--muted);
  font-size: var(--text-base);
}

.fda-note {
  max-width: 74ch;
  margin: 0;
  color: var(--muted);
  font-size: var(--text-base);
  line-height: 1.6;
}

/* ---- Facts ---------------------------------------------------------- */

.product-facts {
  background: var(--paper);
}

.facts-nav {
  position: sticky;
  z-index: 20;
  top: calc(var(--header-height) + 26px);
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: clamp(36px, 4vw, 56px);
  padding: 7px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter: blur(14px);
}

.facts-nav a {
  padding: 10px 16px;
  border-radius: 999px;
  color: var(--graphite);
  font-size: var(--text-base);
  font-weight: 800;
  letter-spacing: var(--track-base);
  text-transform: uppercase;
  transition: background-color 180ms ease, color 180ms ease;
}

.facts-nav a:hover {
  background: var(--mist);
}

.facts-nav a[aria-current] {
  background: var(--ink);
  color: white;
}

.facts-panel {
  margin-bottom: clamp(40px, 5vw, 64px);
  scroll-margin-top: calc(var(--header-height) + 92px);
}

.facts-panel h2 {
  margin-bottom: 10px;
  font-size: clamp(2.2rem, 4vw, 3.4rem);
}

.facts-panel__lines {
  margin: 0 0 20px;
  padding: 0;
  color: var(--muted);
  font-size: var(--text-base);
  list-style: none;
}

.facts-panel__body {
  max-width: 78ch;
  color: var(--graphite);
  line-height: 1.7;
  white-space: pre-line;
}

/* Focusable scroll region, so a narrow viewport can reach a wide table
   with the keyboard and not only by dragging. */
.facts-table__scroll {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: white;
}

.facts-table__scroll:focus-visible {
  outline: 3px solid var(--orange-bright);
  outline-offset: 3px;
}

.facts-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-base);
}

.facts-table caption {
  padding: 18px 20px 0;
  color: var(--muted);
  font-size: var(--text-label);
  font-weight: 800;
  letter-spacing: var(--track-label);
  text-align: left;
  text-transform: uppercase;
}

.facts-table th,
.facts-table td {
  padding: 11px 16px;
  border-bottom: 1px solid rgba(11, 22, 19, 0.08);
  text-align: right;
  vertical-align: baseline;
}

.facts-table thead th {
  color: var(--muted);
  font-size: var(--text-label);
  font-weight: 800;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  white-space: nowrap;
}

.facts-table tbody th[scope="row"],
.facts-table thead th:first-child {
  position: sticky;
  left: 0;
  background: white;
  color: var(--graphite);
  font-weight: 600;
  text-align: left;
}

.facts-table td {
  color: var(--graphite);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.facts-table tr:last-child th,
.facts-table tr:last-child td {
  border-bottom: 0;
}

.facts-row--sub th[scope="row"] {
  padding-left: 32px;
  color: var(--muted);
  font-weight: 500;
}

.facts-row--sub th[scope="row"]::before {
  margin-right: 8px;
  color: rgba(11, 22, 19, 0.3);
  content: "–";
}

.facts-row--group th {
  padding-bottom: 2px;
  border-bottom: 0;
  color: var(--muted);
  font-size: var(--text-label);
  font-weight: 800;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

.facts-value__ref {
  margin-left: 5px;
  color: var(--muted);
  font-size: var(--text-label);
}

.facts-value__marker {
  color: var(--orange);
  font-weight: 700;
}

.facts-value--derived {
  color: var(--muted);
  font-style: italic;
}

.facts-footnotes {
  margin: 14px 0 0;
  padding: 0;
  color: var(--muted);
  font-size: var(--text-base);
  line-height: 1.6;
  list-style: none;
}

.facts-footnotes b {
  color: var(--orange);
}

.inheritance-note {
  margin: 0 0 18px;
  padding: 14px 18px;
  border-left: 3px solid var(--orange);
  background: rgba(237, 103, 3, 0.07);
  color: var(--graphite);
  font-size: var(--text-base);
  line-height: 1.6;
}

.absent-note {
  max-width: 74ch;
  margin: 0;
  padding: 18px 20px;
  border: 1px dashed rgba(11, 22, 19, 0.24);
  border-radius: var(--radius-md);
  color: var(--muted);
  font-size: var(--text-base);
  line-height: 1.65;
}

.legal-note {
  margin-top: clamp(30px, 4vw, 48px);
  padding-top: 26px;
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: var(--text-base);
  line-height: 1.7;
}

.legal-note h2 {
  margin-bottom: 14px;
  font-size: 1.6rem;
}

.legal-note ul {
  margin: 0 0 12px;
  padding-left: 18px;
}

.legal-note strong {
  color: var(--graphite);
}

/* ---- Related -------------------------------------------------------- */

.product-related {
  overflow: clip;
  background:
    radial-gradient(circle at 85% 10%, rgba(37, 98, 76, 0.32), transparent 28%),
    linear-gradient(135deg, #08100e, #111815 62%, #07100d);
  color: white;
}

.product-related .catalogue-grid {
  margin-top: 0;
}

/* ---- Unknown product ------------------------------------------------ */

.product-missing {
  max-width: 60ch;
  padding-block: clamp(40px, 6vw, 80px);
}

.product-missing h1 {
  margin-bottom: 18px;
  font-family: var(--font-display);
  font-size: clamp(3rem, 7vw, 5rem);
  font-style: italic;
  font-weight: 600;
  line-height: 0.9;
  text-transform: uppercase;
}

.product-missing p {
  margin-bottom: 26px;
  color: rgba(255, 255, 255, 0.66);
}

/* ---- Detail breakpoints --------------------------------------------- */

@media (max-width: 860px) {
  .product-hero__grid {
    grid-template-columns: 1fr;
    gap: 30px;
  }

  .product-hero__media {
    max-width: 420px;
    margin-inline: auto;
  }

  .facts-nav {
    top: calc(var(--header-height) + 22px);
  }
}

@media (max-width: 680px) {
  .facts-nav {
    flex-wrap: nowrap;
    overflow-x: auto;
    border-radius: 14px;
    scrollbar-width: none;
  }

  .facts-nav::-webkit-scrollbar {
    display: none;
  }

  .facts-nav a {
    white-space: nowrap;
  }

  .facts-table {
    font-size: var(--text-base);
  }

  .facts-table th,
  .facts-table td {
    padding: 10px 12px;
  }

  .product-price strong {
    font-size: 2.1rem;
  }
}

/* ---- Print ---------------------------------------------------------- */

@media print {
  .site-header,
  .skip-link,
  .facts-nav,
  .product-related,
  .catalogue-bar,
  .catalogue-filters,
  .product-hero__glow {
    display: none !important;
  }

  /* A 42px angled clip cuts the first line off a printed heading. */
  .catalogue-page main > section,
  .product-page main > section {
    margin-top: 0 !important;
    clip-path: none !important;
  }

  .product-hero,
  .product-claims,
  .product-facts,
  .catalogue-intro,
  .catalogue-section {
    padding-block: 0 18px;
    background: white !important;
    color: var(--ink) !important;
  }

  .product-hero__body h1,
  .product-hero__descriptor,
  .product-keyfacts dd,
  .product-price strong {
    color: var(--ink) !important;
  }

  .facts-table__scroll {
    overflow: visible;
  }

  .facts-table {
    min-width: 0;
    /* The floor holds on paper too. Raised from 9pt; page fit is untested. */
    font-size: var(--text-label);
  }

  .facts-table tbody th[scope="row"],
  .facts-table thead th:first-child {
    position: static;
  }

  .facts-panel {
    break-inside: avoid;
  }
}
