/**
 * Shared components — buttons, eyebrow, section head, cards, reveal.
 * Ported from .docs/mockup/css/components.css (6 Aug 2026).
 *
 * One deliberate change: the button rules referenced hardcoded padding,
 * weight, size, letter-spacing and text-transform values, which silently
 * opted every button out of Theme Settings. Those literals are replaced by
 * the --btn-* tokens in tokens.css, seeded with the identical values, so
 * rendering is unchanged.
 */

/* === BUTTONS === */
.btn-primary {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  background: var(--terracotta);
  color: var(--off-white);
  padding: var(--btn-padding-block) var(--btn-padding-inline);
  border-radius: var(--btn-radius);
  font-family: var(--font-heading);
  font-weight: var(--btn-font-weight);
  font-size: var(--btn-font-size);
  letter-spacing: var(--btn-letter-spacing);
  text-transform: var(--btn-text-transform);
  transition: transform var(--duration-normal) var(--ease-out),
              background var(--duration-fast);
}

.btn-primary:hover {
  transform: translateY(-2px);
  background: var(--terracotta-hover);
}

.btn-primary:focus-visible {
  /* Two-tone ring, because one colour cannot serve every background this button
     sits on. It was --terracotta at 3px offset: a terracotta ring around a
     terracotta button, 1.00:1 against the button itself, 2.21:1 against the
     Deep Ocean summary bar in the kit builder. A keyboard user lost their place
     on the most important control on the page.
     The off-white fills the gap and reads on dark (6.86:1 on Deep Ocean); the
     charcoal sits outside it and reads on Pebble and Off-White. Whatever is
     behind the button, one of the two is visible. */
  outline: 2px solid var(--charcoal);
  outline-offset: 3px;
  box-shadow: 0 0 0 3px var(--off-white);
}

.btn-primary .material-symbols-outlined,
.btn-ghost .material-symbols-outlined {
  font-size: 18px;
}

.btn-sm .material-symbols-outlined {
  font-size: 16px;
}

.btn-ghost {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  background: transparent;
  color: var(--charcoal);
  padding: var(--btn-padding-block) var(--btn-padding-inline-secondary);
  border-radius: var(--btn-radius);
  border: var(--btn-border-width) solid var(--charcoal);
  font-family: var(--font-heading);
  font-weight: var(--btn-font-weight-secondary);
  font-size: var(--btn-font-size);
  letter-spacing: var(--btn-letter-spacing);
  text-transform: var(--btn-text-transform);
  transition: background var(--duration-fast), color var(--duration-fast);
}

.btn-ghost:hover {
  background: var(--charcoal);
  color: var(--off-white);
}

.btn-ghost:focus-visible {
  outline: 2px solid var(--deep-ocean);
  outline-offset: 3px;
}

.btn-outline {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  background: transparent;
  color: var(--deep-ocean);
  padding: var(--btn-padding-block) var(--btn-padding-inline-secondary);
  border-radius: var(--btn-radius);
  border: var(--btn-border-width) solid var(--deep-ocean);
  font-family: var(--font-heading);
  font-weight: var(--btn-font-weight-secondary);
  font-size: var(--btn-font-size);
  letter-spacing: var(--btn-letter-spacing);
  text-transform: var(--btn-text-transform);
  transition: background var(--duration-fast), color var(--duration-fast),
              transform var(--duration-normal) var(--ease-out);
}

.btn-outline:hover {
  background: var(--deep-ocean);
  color: var(--off-white);
  transform: translateY(-2px);
}

.btn-outline:focus-visible {
  outline: 2px solid var(--deep-ocean);
  outline-offset: 3px;
}

.btn-outline .material-symbols-outlined {
  font-size: 18px;
}

/* === EYEBROW === */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-body);
  font-size: var(--text-eyebrow);
  font-weight: 700;
  letter-spacing: 2.4px;
  text-transform: uppercase;
  color: var(--terracotta);
}

.eyebrow::before {
  content: "";
  width: 28px;
  height: 1px;
  background: var(--terracotta);
}

/* === WORDMARK === */
.wordmark {
  display: inline-flex;
  align-items: center;
}

.wordmark img {
  height: 40px;
  width: auto;
  display: block;
}

/* === SECTION HEAD === */
.section-head {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  margin-bottom: var(--space-xl);
}

.section-title-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.section-title-group .eyebrow {
  line-height: 1;
}

.section-head h2 {
  font-size: var(--text-h2);
  font-weight: 800;
  letter-spacing: -0.025em;
  line-height: 1.05;
  max-width: 720px;
}

.section-head h2 em {
  font-style: italic;
  font-weight: 500;
  color: var(--terracotta);
}

.section-lead {
  font-size: var(--text-lead);
  color: var(--graphite);
  max-width: 620px;
  line-height: 1.55;
}

/* === CARDS === */
.card {
  background: var(--off-white);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: transform var(--duration-normal) var(--ease-out),
              box-shadow var(--duration-normal);
}

.card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lg);
}

/* === MATERIAL SYMBOLS === */
.material-symbols-outlined {
  font-variation-settings:
    'FILL' 0,
    'wght' 300,
    'GRAD' 0,
    'opsz' 24;
  vertical-align: middle;
}

.stars .material-symbols-outlined {
  font-variation-settings:
    'FILL' 1,
    'wght' 300,
    'GRAD' 0,
    'opsz' 20;
  font-size: 16px;
}

/* === BACK TO TOP === */
.back-to-top {
  position: fixed;
  bottom: 32px;
  right: 32px;
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--deep-ocean);
  color: var(--off-white);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-md);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--duration-normal), transform var(--duration-normal) var(--ease-out);
  transform: translateY(12px);
  z-index: 40;
}

.back-to-top.is-visible {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
}

.back-to-top:hover {
  background: var(--terracotta);
}

.back-to-top .material-symbols-outlined {
  font-size: 22px;
}

/* === PAGE-LOAD ANIMATIONS === */
@keyframes fadeUp {
  from { opacity: 0; transform: translateY(24px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes fadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes scaleIn {
  from { opacity: 0; transform: scale(0.96); }
  to   { opacity: 1; transform: scale(1); }
}

.load-fade-up {
  opacity: 0;
  animation: fadeUp 0.7s var(--ease-out) forwards;
}

.load-fade-in {
  opacity: 0;
  animation: fadeIn 0.6s ease forwards;
}

.load-scale-in {
  opacity: 0;
  animation: scaleIn 0.8s var(--ease-out) forwards;
}

.load-d1 { animation-delay: 0.1s; }
.load-d2 { animation-delay: 0.2s; }
.load-d3 { animation-delay: 0.35s; }
.load-d4 { animation-delay: 0.5s; }
.load-d5 { animation-delay: 0.65s; }

/* === SCROLL REVEAL === */
.reveal {
  opacity: 0;
  transform: translateY(32px);
  transition: opacity 0.6s var(--ease-out), transform 0.6s var(--ease-out);
}

.reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
}

.reveal-d1 { transition-delay: 0.08s; }
.reveal-d2 { transition-delay: 0.16s; }
.reveal-d3 { transition-delay: 0.24s; }
.reveal-d4 { transition-delay: 0.32s; }

/* Progressive enhancement. `.reveal` starts at opacity 0 and is lifted by
   main.js on intersection, so with scripting off nothing ever lifts it and
   the content stays permanently invisible — a whole category page of
   products rendered but unseeable. main.js already covers a missing
   IntersectionObserver and a reduced-motion preference; it cannot cover its
   own absence, so that case has to be answered in CSS. */
@media (scripting: none) {
  .reveal {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .load-fade-up,
  .load-fade-in,
  .load-scale-in,
  .reveal {
    opacity: 1;
    transform: none;
    animation: none;
    transition: none;
  }
}

/* === STARS === */
.stars {
  display: inline-flex;
  align-items: center;
  gap: 1px;
  color: var(--terracotta);
}

/* === PRODUCT SEARCH OVERLAY (static mockup — client-side index) === */
.search-overlay {
  position: fixed;
  inset: 0;
  z-index: 200;
}

.search-overlay[hidden] {
  display: none;
}

.search-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(27, 94, 122, 0.35);
  backdrop-filter: blur(3px);
}

.search-panel {
  position: relative;
  background: var(--off-white);
  width: min(760px, calc(100% - 2 * var(--space-gutter)));
  margin: clamp(40px, 9vh, 110px) auto 0;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  max-height: min(72vh, 660px);
  display: flex;
  flex-direction: column;
  animation: searchIn var(--duration-normal) var(--ease-out);
}

@keyframes searchIn {
  from { opacity: 0; transform: translateY(-12px); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .search-panel { animation: none; }
}

.search-bar {
  display: flex;
  align-items: center;
  gap: var(--space-s);
  padding: var(--space-m);
  border-bottom: 1px solid var(--hairline);
}

.search-bar > .material-symbols-outlined {
  color: var(--deep-ocean);
  font-size: 26px;
}

.search-input {
  flex: 1;
  min-width: 0;
  border: none;
  background: none;
  outline: none;
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: clamp(18px, 2.5vw, 24px);
  color: var(--charcoal);
}

.search-input::placeholder {
  color: var(--graphite);
  font-weight: 500;
}

.search-close {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  border-radius: var(--radius-pill);
  color: var(--graphite);
  transition: background var(--duration-fast), color var(--duration-fast);
}

.search-close:hover {
  background: var(--pebble);
  color: var(--deep-ocean);
}

.search-meta {
  padding: var(--space-s) var(--space-m) 0;
  font-size: var(--text-small);
  color: var(--graphite);
}

.search-results {
  overflow-y: auto;
  padding: var(--space-2xs) var(--space-s) var(--space-s);
}

.search-item {
  display: flex;
  align-items: center;
  gap: var(--space-s);
  padding: var(--space-s);
  border-radius: var(--radius-md);
  transition: background var(--duration-fast);
}

.search-item:hover,
.search-item:focus-visible {
  background: var(--pebble);
  outline: none;
}

.search-thumb {
  width: 52px;
  height: 52px;
  flex-shrink: 0;
  border-radius: var(--radius-sm);
  background: var(--coastal-soft) center / cover no-repeat;
}

.search-item-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
  min-width: 0;
}

.search-item-name {
  font-family: var(--font-heading);
  font-weight: 700;
  color: var(--deep-ocean);
}

.search-item-cat {
  font-size: var(--text-small);
  color: var(--graphite);
}

.search-item-price {
  font-family: var(--font-heading);
  font-weight: 700;
  color: var(--charcoal);
  white-space: nowrap;
}

.search-empty {
  padding: var(--space-l) var(--space-m);
  color: var(--graphite);
  text-align: center;
}

.search-hint {
  padding: var(--space-s) var(--space-m);
  border-top: 1px solid var(--hairline);
  font-size: var(--text-small);
  color: var(--graphite);
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  flex-wrap: wrap;
}

.search-hint button {
  font-size: var(--text-small);
  font-weight: 600;
  color: var(--deep-ocean);
  background: var(--coastal-soft);
  padding: 4px 12px;
  border-radius: var(--radius-pill);
  transition: background var(--duration-fast);
}

.search-hint button:hover {
  background: var(--coastal-blue);
}

/* === SMALL BUTTON ===
   Lived in the mockup's sections.css but is a shared component, not a
   section-local style — moved here so a section stylesheet does not have to
   own it. Values unchanged. */
.btn-sm {
  padding: 12px 18px;
  font-size: 12px;
}

/* === LEAD PARAGRAPH ===
   Shared interior chrome from the mockup css/shop.css (114-120). Used by the
   page head and several interior sections, so it lives here rather than being
   duplicated into each of them. */
.lead {
  font-size: var(--text-lead);
  color: var(--graphite);
  line-height: 1.55;
  max-width: 60ch;
}

/* === BUTTON ROW ===
   Shared CTA cluster from the mockup's css/shop.css (122-128). Used by the cta
   sections and several interior blocks, so it lives here rather than being
   copied into each of them. */
.btn-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-s);
  margin-top: var(--space-xs);
}

@media (max-width: 560px) {
  .btn-row {
    flex-direction: column;
    align-items: stretch;
  }

  .btn-row .btn-primary,
  .btn-row .btn-outline,
  .btn-row .btn-ghost {
    justify-content: center;
  }
}


/* ===========================================================================
   PRODUCT CARD — shared chrome
   ===========================================================================
   Moved here from sections/kits.css. The mockup uses the identical .kit-card
   on the homepage, the Shop page, all four collection pages and the PDP
   upsell rows, so it is a component, not one section's private styling.
   sections/kits.css now holds only its own section wrapper.

   Ported from .docs/mockup/css/sections.css (FEATURED KITS, 387-518),
   .docs/mockup/css/shop.css (.kit-grid.two, .kit-desc) and
   .docs/mockup/css/collection.css (the collection grid override).
   =========================================================================== */

.kit-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-s);
}

.kit-grid.two {
  grid-template-columns: repeat(2, 1fr);
}

.kit-card {
  background: var(--off-white);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-lg);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.kit-img {
  aspect-ratio: 1 / 1;
  background-size: cover;
  background-position: center;
  position: relative;
  overflow: hidden;
}

/* The product-backed card (parts/product-card.php) paints its photo with a
   real <img> so it can lazy-load and be seen by the preload scanner. Absolute
   + object-fit renders pixel-identically to the mockup's background-image. */
/* Scoped to .kit-img to win against WooCommerce. woocommerce-layout.css ships
   `.woocommerce img, .woocommerce-page img { height: auto }` at (0,1,1),
   which outranks a bare `.kit-img-photo` at (0,1,0). Unscoped, every product
   card on every Woo surface letterboxed — a 405px tall photo floating in a
   435px box — while the same card was correct on the homepage. */
.kit-img .kit-img-photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.kit-img--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--coastal-soft);
}

.kit-img-note {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  color: var(--deep-ocean);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  opacity: 0.6;
}

.kit-img-note .material-symbols-outlined {
  font-size: 38px;
}

.kit-badge {
  position: absolute;
  top: var(--space-xs);
  left: var(--space-xs);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  padding: 5px 12px;
  border-radius: var(--radius-pill);
  color: var(--white);
  z-index: 1;
}

.kit-badge--green {
  background: var(--eucalypt);
}

.kit-badge--terracotta {
  background: var(--terracotta);
}

.kit-badge--ocean {
  background: var(--deep-ocean);
}

.kit-body {
  padding: var(--space-m);
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  flex: 1;
}

.kit-range {
  font-size: 11px;
  color: var(--graphite);
  letter-spacing: 1.2px;
  text-transform: uppercase;
  font-weight: 600;
  line-height: 1;
}

.kit-body h3,
.kit-body h2,
.kit-body h4 {
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.015em;
  line-height: 1.2;
}

/* The title is the card's one tab stop. The footer button repeats the same
   href and is hidden from assistive tech, so the card is one link, not two. */
.kit-link {
  color: inherit;
  text-decoration: none;
}

.kit-link::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
}

.kit-card {
  position: relative;
}

/* The stretched ::after makes the whole card clickable, which would put the
   focus ring on the title text alone. Move it to the card so the visible
   focus matches the actual hit area. Matches base.css's global treatment. */
.kit-link:focus-visible {
  outline: none;
}

.kit-card:has(.kit-link:focus-visible) {
  outline: 2px solid var(--terracotta);
  outline-offset: 3px;
}

.kit-desc {
  font-size: var(--text-small);
  color: var(--graphite);
  line-height: 1.55;
}

.kit-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: auto;
  padding-top: var(--space-s);
  border-top: 1px solid var(--hairline);
  gap: var(--space-xs);
}

.kit-price {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 22px;
  color: var(--deep-ocean);
}

.kit-price del {
  font-weight: 600;
  font-size: 0.7em;
  color: var(--graphite);
  margin-right: 4px;
}

.kit-price ins {
  text-decoration: none;
}

@media (max-width: 980px) {
  .kit-grid,
  .kit-grid.two {
    grid-template-columns: 1fr;
  }
}


/* ===========================================================================
   CART BUTTON — busy state
   ===========================================================================
   Adding to the cart takes a beat, and a button that looks idle while it works
   invites a second click. `.btn-cart` carries both labels in the markup and
   swaps them on `aria-busy`, which assets/js/woo/cart-drawer.js already sets
   for the duration of the request and clears on success or failure.

   Both labels stay in the DOM rather than being rewritten by script: the
   resting label cannot drift from the busy one, the swap survives a fragment
   refresh, and with JavaScript off the button renders and submits exactly as
   it always did.

   The two are stacked in one grid cell so the button never changes width
   mid-request. "Adding to cart…" is the wider of the two, so it sets the
   resting width — a little generous, but a button that resizes as you click it
   reads as a glitch. */

.btn-cart {
  display: inline-grid;
  position: relative;
}

.btn-cart > .btn-cart-rest,
.btn-cart > .btn-cart-busy {
  grid-area: 1 / 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  transition: opacity var(--duration-fast) var(--ease-out);
}

.btn-cart > .btn-cart-busy {
  opacity: 0;
  visibility: hidden;
}

.btn-cart[aria-busy="true"] > .btn-cart-rest {
  opacity: 0;
  visibility: hidden;
}

.btn-cart[aria-busy="true"] > .btn-cart-busy {
  opacity: 1;
  visibility: visible;
}

/* Not `disabled`: a disabled submit drops its own name and value from the
   POST, which would break the no-JS fallback path the drawer falls back to.
   This blocks the second click without touching what gets submitted. */
.btn-cart[aria-busy="true"] {
  pointer-events: none;
  cursor: progress;
}

.btn-spinner {
  width: 1em;
  height: 1em;
  flex: 0 0 auto;
  border-radius: 50%;
  border: 2px solid currentColor;
  border-top-color: transparent;
  animation: catnip-spin 0.6s linear infinite;
}

@keyframes catnip-spin {
  to { transform: rotate(1turn); }
}

/* Honour a reduced-motion preference: the state still changes, it just does
   not spin. A pulse still reads as "working" without the rotation. */
@media (prefers-reduced-motion: reduce) {
  .btn-spinner {
    animation: catnip-pulse 1.2s ease-in-out infinite;
  }

  @keyframes catnip-pulse {
    50% { opacity: 0.35; }
  }
}
