/*
 * components.css — reusable, cross-page components. Consumes tokens.css only;
 * zero literals (aspect ratios are content ratios, not design values). Enqueued
 * globally after base.css on the real site (skipped on the holding page, which
 * has its own self-contained markup).
 *
 * Introduced at PLAN Step 2 for the hero; deliberately shared because each of
 * these appears again in the shop grid and product pages:
 *   - .btn              pill CTA (the sanctioned interactive shape)
 *   - .price            the single price component (placeholder → live at Step 7)
 *   - .media-placeholder tokenized image slot until real assets land
 */

/* — Button — pill is the sanctioned interactive shape (--radius-pill). — */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-8);
  padding: var(--space-16) var(--space-32);
  font-family: var(--font-display);
  font-size: var(--type-body);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-none);
  letter-spacing: var(--tracking-normal);
  text-align: center;
  border: var(--border-hairline) solid transparent;
  border-radius: var(--radius-pill);
  cursor: pointer;
  /* Scope the transition to the exact properties that change (never `all`, which
     desyncs on un-hover). Default easing; reduced-motion drops it below. */
  transition: background-color var(--duration-fast), border-color var(--duration-fast), color var(--duration-fast);
}

/* Primary: solid brand fill. Overrides the base <a> link colors. */
.btn--primary {
  background-color: var(--color-brand);
  border-color: var(--color-brand);
  color: var(--color-text);
}

.btn--primary:hover,
.btn--primary:focus-visible {
  background-color: var(--color-brand-hover);
  border-color: var(--color-brand-hover);
  color: var(--color-text);
}

/* Secondary: ghost outline for dual-CTA rows (ref-06). */
.btn--ghost {
  background-color: transparent;
  border-color: var(--color-border);
  color: var(--color-text);
}

.btn--ghost:hover,
.btn--ghost:focus-visible {
  border-color: var(--color-text);
  color: var(--color-text);
}

/* Disabled / sold-out — shopify.js adds .is-disabled + aria-disabled to a buy
   CTA whose variant is not availableForSale. Muted, non-interactive; the hover
   cues above are suppressed by the lowered opacity + not-allowed cursor. */
.btn.is-disabled,
.btn[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}

/* `hidden` must win over .btn's display:inline-flex (shopify.js hides the
   PDP's second CTA when a variant is sold out, leaving one "Sold out" pill). */
.btn[hidden] {
  display: none;
}

/* — Price — the single reusable price component. Placeholder until Step 7 binds
     it live to Shopify. — */
.price {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  margin-block: 0;
}

.price__label {
  font-size: var(--type-caption);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.price__amount {
  font-family: var(--font-display);
  font-size: var(--type-subheading);
  font-weight: var(--weight-bold);
  line-height: var(--leading-none);
  color: var(--color-text);
}

/* Hero/PDP-prominent treatment — UI-rework Phase 3 sets the price size to
   --type-subheading on BOTH product cards (base .price__amount above) and the
   hero/PDP (this rule), so every price renders at the same size. The heavier
   extrabold weight + tight tracking are all that keep the hero/PDP price a touch
   more prominent than the base card price; the size itself is inherited from
   .price__amount, so no font-size is set here. (Superseded the Phase 2
   --type-heading, which superseded the original --type-display.) */
.price--lg .price__amount {
  font-weight: var(--weight-extrabold);
  letter-spacing: var(--tracking-tight);
}

/* Placeholder marker — quietly signals "not the real price yet". */
.price--placeholder .price__tag {
  font-size: var(--type-caption);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: color-mix(in srgb, var(--color-text-muted) 55%, var(--color-bg));
}

/* — Media placeholder — tokenized image slot with an explicit aspect ratio and a
     visible label, used wherever real photography/video hasn't landed. Sharp
     edges per the clinical default. A ratio modifier is required so the ratio is
     explicit at the call site (OVERVIEW → Build-time placeholders). — */
.media-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-24);
  background-color: var(--color-surface);
  border: var(--border-hairline) dashed var(--color-border);
  border-radius: var(--radius-none);
}

.media-placeholder--4x5  { aspect-ratio: 4 / 5; }
.media-placeholder--16x9 { aspect-ratio: 16 / 9; }
.media-placeholder--1x1  { aspect-ratio: 1 / 1; }

.media-placeholder__label {
  font-size: var(--type-caption);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--color-text-muted);
  text-align: center;
}

/* — Photo — a real image slot (Phase 5, D): fixed ratio, cover crop, sharp edges;
     the real-asset counterpart of .media-placeholder (template-parts/photo.php).
     Ratios are content ratios, not design values. — */
.photo {
  position: relative;
  overflow: hidden;
  background-color: var(--color-surface);
  border-radius: var(--radius-none);
}

.photo--4x5  { aspect-ratio: 4 / 5; }
.photo--16x9 { aspect-ratio: 16 / 9; }
.photo--3x2  { aspect-ratio: 3 / 2; }
.photo--3x4  { aspect-ratio: 3 / 4; }
.photo--1x1  { aspect-ratio: 1 / 1; }
.photo--fill { position: absolute; inset: 0; } /* fills a positioned parent (bento) */

.photo__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Rollover pair — lifestyle photo at rest, the hero product shot fades in when
   the enclosing card (.has-photo-swap) is hovered or holds focus. CSS-only; no JS.
   Reduced motion swaps instantly. */
.photo__img--alt {
  opacity: 0;
  transition: opacity var(--duration-base) var(--ease-out);
}

.has-photo-swap:hover .photo__img--alt,
.has-photo-swap:focus-within .photo__img--alt {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .photo__img--alt {
    transition: none;
  }
}

/* — Stretched card link (UI-rework Phase 1) — turn an entire card into ONE
     clickable link with no extra focus stops: the link's ::after covers the
     nearest positioned ancestor (the card, which must be `position: relative`),
     so a click anywhere on the card follows the link. The card gets a single
     accessible name (the link's own text) and screen readers announce one link.
     Any OTHER interactive control on the card must sit above the overlay via
     `position: relative; z-index: 2`. — */
.card-link::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}

/* Move the focus ring from the link's inline box onto the whole card (its ::after
   spans the card) so keyboard focus outlines the entire clickable surface. */
.card-link:focus-visible {
  outline: none;
}

.card-link:focus-visible::after {
  outline: var(--border-thin) solid var(--color-brand);
  outline-offset: calc(-1 * var(--border-thin));
}

/* — Build note — a quiet marker for provisional copy (clearly placeholder,
     quietly so). Shared by any section still carrying non-final wording. — */
.build-note {
  font-size: var(--type-caption);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: color-mix(in srgb, var(--color-text-muted) 55%, var(--color-bg));
}

/* — Email capture — the reusable capsule form. Shared by the homepage capture
     (Step 4) and the footer capture (Step 6); both are wired to Klaviyo at Step
     11 (inert markup + styling until then). A pill capsule holds the field and a
     nested solid-brand submit in one rounded surface — pills are the sanctioned
     interactive shape. (The holding page keeps its own self-contained .signup.) — */
.capture {
  display: flex;
  flex-direction: column;
  gap: var(--space-16);
}

.capture__row {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  padding: var(--space-4);
  padding-inline-start: var(--space-20);
  background-color: var(--color-surface-2);
  border: var(--border-hairline) solid var(--color-border);
  border-radius: var(--radius-pill);
  /* Only the border recolors on focus — scope to it (never `all`). */
  transition: border-color var(--duration-fast);
}

/* Focus lives on the capsule so the whole unit lights up: the single hairline
   border recolors to brand — one line, no ring. */
.capture__row:focus-within {
  border-color: var(--color-brand);
}

.capture__field {
  flex: 1 1 auto;
  min-width: 0;
  padding-block: var(--space-8);
  background: transparent;
  border: 0;
  border-radius: var(--radius-none);
  color: var(--color-text);
  font-size: var(--type-body);
  letter-spacing: var(--tracking-normal);
}

.capture__field::placeholder {
  color: var(--color-text-muted);
}

.capture__field:focus {
  outline: none;
}

/* Nested submit — solid brand pill; vertical padding matches the field
   (--space-8) so both read equal height inside the capsule. */
.capture__submit {
  flex: 0 0 auto;
  padding: var(--space-8) var(--space-20);
  background-color: var(--color-brand);
  border: var(--border-hairline) solid var(--color-brand);
  border-radius: var(--radius-pill);
  color: var(--color-text);
  font-family: var(--font-display);
  font-size: var(--type-body);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-normal);
  cursor: pointer;
  transition: background-color var(--duration-fast), border-color var(--duration-fast);
}

.capture__submit:hover,
.capture__submit:focus-visible {
  background-color: var(--color-brand-hover);
  border-color: var(--color-brand-hover);
}

/* Submission status — stays empty (and collapses) until Klaviyo wiring at Step 11. */
.capture__status {
  min-height: var(--space-20);
  font-size: var(--type-body);
  color: var(--color-text-muted);
}

.capture__status:empty {
  min-height: 0;
}

.capture__status[data-state="success"] {
  color: var(--color-text);
}

.capture__status[data-state="error"] {
  color: var(--color-brand);
}

@media (prefers-reduced-motion: reduce) {
  .btn,
  .capture__row,
  .capture__submit {
    transition: none;
  }
}

/* — Measured scroll reveal (Step 14) — the site-wide motion primitive (promoted
     from the homepage-only rule of Step 4). Any [data-reveal] element on any real
     page rises into view once. The pre-reveal hidden state applies ONLY when JS
     is on (`.has-reveal`, set on <html> in the <head> before first paint by
     reveal.js) AND motion is welcome; reveal.js then adds `.is-revealed` as each
     element scrolls in. No JS, or reduced motion, → elements render visible with
     no animation (no hidden content). Per-page stagger lives in that page's CSS. */
@media (prefers-reduced-motion: no-preference) {
  .has-reveal [data-reveal] {
    opacity: 0;
    transform: translateY(var(--space-24));
    transition: opacity var(--duration-slow) var(--ease-out),
                transform var(--duration-slow) var(--ease-out);
  }

  .has-reveal [data-reveal].is-revealed {
    opacity: 1;
    transform: none;
  }
}
