/**
 * Shared components: buttons, section head, cards, paper pieces, reveal,
 * product cards. Graphique design, 12 October 2026. Tokens only.
 */

/* === BUTTONS ==============================================================
   The design's pill buttons. Primary is pink, outline is purple text on white
   with a lilac border, purple is the "Message us" button. Every button lifts
   on hover. An arrow inside a button is an inline SVG icon (.btn-arrow). */
.btn-primary,
.btn-outline,
.btn-soft,
.btn-ghost,
.btn-purple {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  min-height: var(--btn-min-height);
  padding: var(--btn-padding-block) var(--btn-padding-inline);
  border-radius: var(--btn-radius);
  border: var(--btn-border-width) solid transparent;
  font-family: var(--font-body);
  font-size: var(--btn-font-size);
  font-weight: var(--btn-font-weight);
  letter-spacing: var(--btn-letter-spacing);
  text-transform: var(--btn-text-transform);
  line-height: 1.2;
  text-align: center;
  cursor: pointer;
  transition: transform var(--duration-normal) var(--ease-out),
              box-shadow var(--duration-normal),
              background-color var(--duration-fast),
              color var(--duration-fast),
              border-color var(--duration-fast);
}

.btn-primary {
  background: var(--brand-accent);
  color: var(--white);
  box-shadow: var(--shadow-button);
}

.btn-primary:hover {
  background: var(--brand-accent-hover);
  box-shadow: var(--shadow-button-hover);
  transform: translateY(var(--lift));
}

.btn-outline {
  background: var(--white);
  color: var(--brand-secondary);
  border-color: var(--hairline-strong);
  box-shadow: var(--shadow-outline-button);
}

.btn-outline:hover {
  border-color: var(--brand-secondary);
  transform: translateY(var(--lift));
}

.btn-soft {
  background: var(--btn-soft-bg);
  color: var(--btn-soft-text);
  border-color: var(--btn-soft-border);
}

.btn-soft:hover {
  background: var(--btn-soft-bg-hover);
  border-color: var(--brand-secondary);
  transform: translateY(var(--lift));
}

.btn-purple {
  background: var(--brand-secondary);
  color: var(--white);
  box-shadow: var(--shadow-button-purple);
}

.btn-purple:hover {
  background: var(--brand-secondary-hover);
  transform: translateY(var(--lift));
}

.btn-ghost {
  background: transparent;
  color: var(--ink);
  border-color: var(--ink);
}

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

.btn-primary:active,
.btn-outline:active,
.btn-soft:active,
.btn-purple:active,
.btn-ghost:active {
  transform: translateY(0);
}

.btn-primary:focus-visible,
.btn-outline:focus-visible,
.btn-soft:focus-visible,
.btn-purple:focus-visible,
.btn-ghost:focus-visible {
  outline: 3px solid var(--brand-secondary);
  outline-offset: 3px;
  border-radius: var(--btn-radius);
}

.btn-primary[disabled],
.btn-primary[aria-disabled="true"],
.btn-outline[disabled],
.btn-soft[disabled],
.btn-purple[disabled] {
  opacity: 0.55;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

.btn-arrow,
.btn-icon {
  width: 1.15em;
  height: 1.15em;
  flex-shrink: 0;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform var(--duration-normal) var(--ease-out);
}

.btn-primary:hover .btn-arrow,
.btn-outline:hover .btn-arrow {
  transform: translateX(3px);
}

.btn-primary .material-symbols-outlined,
.btn-outline .material-symbols-outlined,
.btn-ghost .material-symbols-outlined {
  font-size: 1.15em;
}

/* === SECTION HEAD ======================================================== */
.section-head {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  max-width: var(--container-text);
}

.section-head.is-centered {
  align-items: center;
  text-align: center;
  margin-inline: auto;
}

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

.section-lead {
  font-size: var(--text-lead);
  color: var(--ink-muted);
  max-width: 60ch;
}

/* === WORDMARK ============================================================ */
.wordmark {
  display: inline-flex;
  width: var(--logo-width);
  flex-shrink: 0;
}

.wordmark img {
  width: 100%;
  height: auto;
}

/* === CARDS =============================================================== */
.card {
  background: var(--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(var(--lift));
  box-shadow: var(--shadow-md);
}

/* === PAPER PIECES =======================================================
   Small decorative parts the design repeats: gingham washi tape, sparkles,
   flowers. Always aria-hidden in the markup. */
.tape {
  position: absolute;
  width: 5.5rem;
  height: 1.6rem;
  background-color: var(--tape-bg);
  background-image:
    linear-gradient(90deg, var(--gingham-tape) 50%, transparent 50%),
    linear-gradient(var(--gingham-tape) 50%, transparent 50%);
  background-size: var(--tape-size) var(--tape-size);
  clip-path: var(--tape-edge);
  opacity: 0.9;
  pointer-events: none;
}

.sparkle {
  position: absolute;
  width: 1.4rem;
  aspect-ratio: 3 / 4;
  background: var(--brand-gold);
  clip-path: var(--sparkle);
  pointer-events: none;
}

.flower {
  position: absolute;
  width: 4.5rem;
  height: auto;
  pointer-events: none;
}

/* === 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(--brand-primary);
  color: var(--surface);
  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(--brand-accent);
}

.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(--brand-accent);
}

/* === 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 {
  min-height: var(--btn-min-height-sm);
  padding: var(--space-2xs) var(--space-s);
  font-size: var(--text-xs);
}

/* === 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(--ink-muted);
  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);
}

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

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


/* ===========================================================================
   PRODUCT GRID + CARD (parts/product-card.php)
   ===========================================================================
   Shop, category and search archives, and the product page's "pairs nicely"
   row. Same language as the homepage category tiles: white card, rounded
   photo, centred title. Two across on phones, three from 36rem, four from
   62rem. The title link stretches over the whole card. */

.product-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-s) var(--space-xs);
}

.product-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  padding: var(--space-3xs) var(--space-3xs) var(--space-s);
  background: var(--white);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  transition:
    transform var(--duration-normal) var(--ease-out),
    box-shadow var(--duration-normal) var(--ease-out);
}

.product-card-media {
  display: grid;
  place-items: center;
  aspect-ratio: 1;
  overflow: hidden;
  border-radius: var(--radius-md);
  background: var(--brand-tint-soft);
}

.product-card-photo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--duration-normal) var(--ease-out);
}

.product-card-placeholder {
  width: var(--tap-target);
  height: var(--tap-target);
  color: var(--hairline-strong);
}

.product-card-body {
  display: flex;
  flex: 1;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3xs);
  padding-inline: var(--space-2xs);
  text-align: center;
}

.product-card-title {
  font-size: var(--text-body);
  line-height: 1.25;
}

.product-card-link {
  color: inherit;
  text-decoration: none;
}

/* The whole card is the link's hit area. */
.product-card-link::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: var(--radius-lg);
}

.product-card-link:focus-visible {
  outline: none;
}

.product-card-link:focus-visible::after {
  outline: 3px solid var(--brand-secondary);
  outline-offset: 3px;
}

.product-card-price {
  font-family: var(--font-heading);
  font-weight: var(--heading-weight);
  font-size: var(--text-h4);
  color: var(--brand-accent);
  font-variant-numeric: tabular-nums;
}

.product-card-price del {
  font-size: var(--text-small);
  color: var(--ink-muted);
}

.product-card-packs {
  font-size: var(--text-xs);
  color: var(--ink-muted);
}

.product-card:hover {
  transform: translateY(var(--lift));
  box-shadow: var(--shadow-md);
}

.product-card:hover .product-card-title {
  color: var(--brand-accent);
}

.product-card:hover .product-card-photo {
  transform: scale(1.04);
}

@media (min-width: 36rem) {
  .product-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-m) var(--space-s);
  }
}

@media (min-width: 62rem) {
  .product-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

@media (prefers-reduced-motion: reduce) {
  .product-card,
  .product-card:hover,
  .product-card:hover .product-card-photo {
    transform: none;
  }
}


/* ===========================================================================
   PRODUCT CARD — shared chrome
   ===========================================================================
   Moved here from sections/featured-products.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/featured-products.css now holds only its own section wrapper.

   Ported from the original design reference (FEATURED KITS, 387-518),
   the original design reference (.kit-grid.two, .kit-desc) and
   the original design reference (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(--surface);
  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(--brand-tint-soft);
}

.kit-img-note {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  color: var(--brand-primary);
  font-size: 12px;
  font-weight: var(--weight-strong);
  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: var(--weight-strong);
  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(--brand-support);
}

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

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

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

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

.kit-body h3,
.kit-body h2,
.kit-body h4 {
  font-size: 20px;
  font-weight: var(--weight-strong);
  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(--brand-accent);
  outline-offset: 3px;
}

.kit-desc {
  font-size: var(--text-small);
  color: var(--ink-muted);
  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: var(--weight-strong);
  font-size: 22px;
  color: var(--brand-primary);
}

.kit-price del {
  font-weight: var(--weight-strong);
  font-size: 0.7em;
  color: var(--ink-muted);
  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: graphique-spin 0.6s linear infinite;
}

@keyframes graphique-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: graphique-pulse 1.2s ease-in-out infinite;
  }

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

/* === FORMS ================================================================
   Contact and Get a Quote (inc/forms.php). Field look lives in base.css
   (.field input, select, textarea); this is layout, choices and notices. */
.site-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-m);
}

.form-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-s) var(--space-s);
}

.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
  min-width: 0;
}

.field fieldset {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  border: 0;
  min-width: 0;
}

.field-label {
  font-size: var(--text-small);
  font-weight: var(--weight-strong);
  color: var(--ink);
}

.field-required {
  color: var(--brand-accent);
}

.field-hint {
  font-size: var(--text-xs);
  color: var(--ink-muted);
}

.field-error {
  font-size: var(--text-xs);
  font-weight: var(--weight-strong);
  color: var(--brand-danger);
}

.field-choices {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
}

.choice {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  min-height: var(--tap-target);
  padding: var(--space-3xs) var(--space-s) var(--space-3xs) var(--space-xs);
  border: var(--border-thin) solid var(--brand-tint);
  border-radius: var(--radius-pill);
  background: var(--white);
  font-size: var(--text-small);
  font-weight: var(--weight-strong);
  color: var(--ink);
  cursor: pointer;
  transition: border-color var(--duration-fast), background-color var(--duration-fast);
}

.choice:hover {
  border-color: var(--hairline-strong);
}

.choice:has(input:checked) {
  border-color: var(--brand-secondary);
  background: var(--brand-tint-soft);
}

.choice:has(input:focus-visible) {
  outline: 3px solid var(--brand-secondary);
  outline-offset: 2px;
}

.form-hp {
  position: absolute;
  left: -9999em;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.form-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-s);
}

.form-privacy {
  font-size: var(--text-xs);
}

.form-notice {
  display: flex;
  align-items: flex-start;
  gap: var(--space-xs);
  padding: var(--space-s) var(--space-m);
  border-radius: var(--radius-md);
  font-weight: var(--weight-strong);
  font-size: var(--text-small);
}

.form-notice p {
  color: inherit;
}

.form-notice .icon {
  flex-shrink: 0;
  width: var(--icon-size);
  height: var(--icon-size);
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.form-notice.is-success {
  background: var(--brand-tint-lilac);
  color: var(--brand-primary);
}

.form-notice.is-error {
  background: var(--brand-tint-pink);
  color: var(--brand-danger);
}

/* Text link with an arrow, for secondary routes ("Planning a big order?"). */
.text-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3xs);
  font-weight: var(--weight-strong);
  color: var(--brand-accent);
}

.text-link:hover {
  text-decoration: underline;
}

.text-link .btn-arrow {
  width: 1em;
  height: 1em;
}

@media (min-width: 36rem) {
  .form-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .form-grid > .field:not(.is-half) {
    grid-column: 1 / -1;
  }
}
