/* Shop listing. Chips scroll
   horizontally (more exist than fit, per design). Cards:
   3:4 image, SemiBold 20 name, Light 20 price, bordered pill.
   Card link pattern per accessible-cards practice (see product-card). */

/* ---- filter chips: one scrollable row ---- */

.chips-anchor {
  display: block;
  height: 0;
}

/* Every listing page (shop/journal/gatherings) filters its grid in place:
   the fold animation and the card hiding both change heights above the
   browser's scroll anchor, so anchoring "corrects" scrollY mid-animation
   and mid-smooth-scroll — seen as the page tearing by one row height.
   All landmark images carry width/height attributes, so anchoring has
   nothing to protect here; off page-wide wherever a chips row exists. */
body:has(.chips-anchor) {
  overflow-anchor: none;
}

.chips {
  position: sticky; /* stays available once the hero scrolls away */
  top: 3.625rem;    /* below the sticky header */
  z-index: 5;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding: 0.375rem calc(var(--gutter) + 1%) 0.375rem;
}

/* Expanded (and .is-folding, held by sticky-chips.js during the fold
   animation): the row's flow box stays locked to the folded height —
   0.75rem row padding + one chip line (--text-body at line-height 1,
   0.3em vertical padding each side, 1px borders) — and the wrapped
   chip lines simply overflow it, painting over the content below
   (z-index above). Nothing enters or leaves the flow, so opening,
   picking and closing can't shift the page — the previous fixed-
   position overlay + sibling-margin swap did, by one frame, on every
   toggle. */
.chips.is-stuck.is-expanded,
.chips.is-stuck.is-folding {
  height: calc(0.75rem + 1.6 * var(--text-body) + 2px);
  align-content: flex-start; /* stack lines from the top; default stretch would squash them into the locked height */
}

/* Once pinned (.is-stuck via sticky-chips.js), fold to just the active
   chip. Collapses width/padding/border rather than display so plain
   transitions animate both directions; per-chip random transition-delay
   (set in JS) makes them pop one at a time. */
.chips.is-stuck:not(.is-expanded) .chip:not([aria-current]) {
  opacity: 0;
  max-width: 0;
  padding: 0;
  margin: 0;
  border-width: 0;
  pointer-events: none;
}

/* Flex `gap` still counts between collapsed (zero-width) siblings, so
   the visible chip's position depended on how many others came before
   it in the DOM — ordering it first while folded fixes it to one spot
   regardless of which category is active. */
.chips.is-stuck:not(.is-expanded) .chip[aria-current] {
  order: -1;
}

/* white at rest, own color on hover/focus/selected */
.chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.3em 0.85em;
  border: var(--border-thin);
  border-radius: var(--radius-l);
  background: var(--color-paper); /* white pill at rest; the strip behind stays transparent */
  font-size: var(--text-body); /* 20px */
  font-weight: var(--weight-light);
  font-style: italic;
  line-height: 1;
  text-decoration: none;
  opacity: 1;
  max-width: 20rem;
  overflow: hidden;
  white-space: nowrap;
  /* per-chip random transition-delay (sticky-chips.js) staggers the fold */
  transition: opacity 0.19s ease-in-out, max-width 0.19s ease-in-out, padding 0.19s ease-in-out, margin 0.19s ease-in-out, border-width 0.19s ease-in-out;
}

.chip:hover,
.chip:focus-visible,
.chip[aria-current] {
  background: var(--chip-color);
}

.chip--pink { --chip-color: #ffeefd; } /* "All ..." chip, one-off */

/* ---- product grid ---- */

.products {
  list-style: none;
  margin: 0; /* .page (parent) already has its own top padding — an extra margin here just stacked on top of it */
  padding: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 1.4375rem; /* 23px */
  row-gap: 2.25rem;
}

/* flex column filling the grid cell's full stretched height (grid's
   default align-items: stretch already equalizes each row's cards) so
   .product-card__basket's auto top-margin can push it to the bottom —
   same technique as journal's .article-card. */
.product-card {
  position: relative;
  display: flex;
  flex-direction: column;
  text-align: center;
}

/* author styles beat the UA [hidden] display:none regardless of
   specificity, so the hide must be explicit */
.product-card[hidden] {
  display: none;
}

/* 3:4 — the shop's uniform photo format, shown whole; cover only guards
   the odd stray upload from breaking the grid */
.product-card__img {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 4;
  object-fit: cover;
}

.product-card__img--empty {
  background: #ececec; /* neutral placeholder until a photo is set */
}

.product-card__media {
  position: relative;
}

/* Best-seller badge — starburst + peeking-character
   illustrations. Text sits at ~8.25px, under --text-micro's 11px floor
   (base.css) — a deliberate, approved exception for this one
   decorative, non-interactive badge only, not a precedent generally.
   Badge/peek/text position/size are all scaled together in proportion. */
.product-card__bestseller {
  position: absolute;
  right: 0.25rem;
  bottom: 0.25rem;
  z-index: 3; /* above .product-card__slider's own z-index: 2 */
  width: 5.4375rem;    /* 87px, 116px * 0.75 */
  height: 4.359375rem; /* 69.75px, 93px * 0.75 */
  pointer-events: none;
}

.product-card__bestseller-badge {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 2.8125rem; /* 45px, 60px * 0.75 */
  height: 2.8125rem;
}

.product-card__bestseller-peek {
  position: absolute;
  right: 0;
  top: 0;
  width: 2.671875rem;  /* 42.75px, 57px * 0.75 */
  height: 4.359375rem; /* 69.75px, 93px * 0.75 */
}

.product-card__bestseller-text {
  position: absolute;
  left: 0.1875rem;    /* centered in the 45px circle */
  top: 2.390625rem;   /* vertically centered across its two lines */
  width: 2.4375rem;   /* 39px — the circle's widest usable chord at this band */
  font-size: 0.515625rem; /* 8.25px — see comment above: an approved exception below --text-micro's 11px floor, for this badge only */
  font-weight: var(--weight-medium);
  line-height: 1.1;
  text-align: center;
  color: var(--color-ink);
}

/* Continuous drift for cards with more than one gallery photo — same
   engine as the product page's slider and the marquee (js/scrub-strip.js,
   wired via the shared .js-slider/.js-slider-track classes); the
   @keyframes no-JS fallback is product.css's product-slide-scroll,
   reused as-is (translateX(-50%) doesn't care about element size). */
.product-card__slider {
  position: relative;
  z-index: 2; /* above the name link's card-wide ::before hitbox below, same escape hatch as .product-card__basket — otherwise that invisible overlay eats every drag before the slider sees it */
  width: 100%;
  aspect-ratio: 3 / 4; /* matches .product-card__img */
  overflow: hidden;
  cursor: grab;
}

.product-card__slider.is-dragging {
  cursor: grabbing;
}

.product-card__slider-track {
  display: flex;
  height: 100%;
  touch-action: pan-y;
  animation: product-slide-scroll 18.5s linear infinite; /* no-JS fallback, ~28px/s (30% slower than the product page) at the 172px card width */
}

/* js/scrub-strip.js splits the track's two photo sets into their own
   .scrub-strip__group wrappers (position set inline by the JS) so they
   can relay past each other independently. */
.product-card__slider-track .scrub-strip__group {
  display: flex;
  height: 100%;
  touch-action: pan-y;
  width: max-content; /* shrink-to-fit — getBoundingClientRect() must report real content width, not the viewport's */
}

.product-card__slide {
  flex: 0 0 100%;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.product-card__name {
  margin-top: 0.5rem;
  font-size: var(--text-body); /* 20px */
  font-weight: var(--weight-medium);
  line-height: 1.15;
  /* reserves 2 lines (20px × 1.15 line-height) regardless of actual name
     length, so Add to basket lines up between two cards in a row even
     when one name wraps and the other doesn't */
  min-height: 2.875rem;
}

.product-card__name a {
  text-decoration: none;
}

/* expand the name link's hitbox over the whole card */
.product-card__name a::before {
  content: "";
  position: absolute;
  inset: 0;
}

.product-card__price {
  font-size: var(--text-body);
  font-weight: var(--weight-light);
  line-height: 1.4;
}

.product-card__stock {
  font-size: var(--text-caption); /* 13px — supporting text, matches .product__stock's role */
  font-weight: var(--weight-light);
  margin-bottom: 0.25rem; /* floor gap before the basket, on top of its own auto push */
}

.product-card__basket {
  position: relative; /* above the expanded name hitbox */
  z-index: 2;
  align-self: center; /* .product-card's default stretch (needed for img/name/price) would otherwise force this to full card width */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-top: auto; /* pushes to the bottom of the grid-stretched card */
  padding: 0.25em 0.9em;
  border: var(--border-thin); /* matches .product__basket's own border */
  border-radius: var(--radius-m);
  /* buttons get a UA-default grey face color unless reset — without
     this, the real <button> (real Shopify variant) and the mailto <a>
     fallback (no variant yet) render with different backgrounds */
  background: none;
  font-size: var(--text-ui); /* 16px — matches every other basket-style button site-wide */
  font-weight: var(--weight-regular);
  line-height: 1;
  text-decoration: none;
  cursor: pointer; /* a <button> defaults to the arrow, unlike the links beside it */
}

/* fills pink on hover, as the home strip's basket does. :not(:disabled)
   because a disabled button is still hoverable — an out-of-stock one
   would otherwise light up as if it were live. */
.product-card__basket:not(:disabled):focus-visible {
  background: #ffdff9;
}

@media (hover: hover) {
  .product-card__basket:not(:disabled):hover {
    background: #ffdff9;
  }
}

/* explicit, not the browser's own inconsistent disabled style — same
   opacity as .u-dimmed (base.css), applied to .product-card__stock when
   out of stock, so the label and button dim together as one state */
.product-card__basket:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* Same 1-big/2-small rhythm as journal listing's article cards and
   story-card — a lone related product spans the full row instead of
   sitting in a half-width column with nothing beside it. */
.product-card--featured {
  grid-column: 1 / -1;
}

.product-card--featured .product-card__name {
  font-size: var(--text-body); /* 20px */
  min-height: 0; /* nothing to align against when it's alone, full-width */
}

.product-card--featured .product-card__basket {
  font-size: var(--text-body); /* 20px */
}

/* phone band: one scale keeps starburst/peek/text in proportion,
   pinned to the corner the badge sits in */
@media (max-width: 45.9375rem) {
  .page.shop .product-card__bestseller {
    scale: 0.9;
    transform-origin: bottom right;
  }
}

/* tablet band: the bestseller badge doubles — its phone size reads as
   dust on the stretched cards (same move as home's strip) */
@media (min-width: 46rem) and (max-width: 71.9375rem) {
  .page.shop .product-card__bestseller {
    scale: 1.6;
    transform-origin: bottom right;
  }

  /* +10% for the same reason — the box follows via its em padding */
  .page.shop .product-card__basket {
    font-size: 1.1rem; /* 17.6px, the 16px tier +10% */
  }
}

/* ---- desktop ---- */

@media (min-width: 72rem) {
  .page.shop {
    padding-top: var(--space-3); /* air under the chips row — matches gatherings and journal */
  }

  /* Scoped to the listing: .products also lays out the Related products
     rows on product and article pages, which stay two-up. */
  .page.shop .products {
    grid-template-columns: repeat(5, 1fr);
    column-gap: 4.75rem; /* 76px */
    /* Row-gap 0, the card's own margin carries it instead: the cards are
       subgrids (below), so a page row-gap would open between their inner
       photo/name/price/stock/basket tracks too — same pattern as
       gatherings.css and journal.css. */
    row-gap: 0;
  }

  /* Each card spans five shared rows, so photos, names, prices and baskets
     line up across a row however long a name runs — replacing the mobile
     card's two-line min-height reservation, which bought that alignment
     with a block of empty space under every one-line name. */
  /* re-beats the display:grid below, which outranks the plain
     .product-card[hidden] rule earlier in this file */
  .page.shop .product-card[hidden] {
    display: none;
  }

  .page.shop .product-card {
    display: grid;
    grid-row: span 5;
    grid-template-rows: subgrid;
    align-content: start;
    margin-bottom: var(--space-5);
  }

  .page.shop .product-card__media { grid-row: 1; }
  .page.shop .product-card__price { grid-row: 3; }
  .page.shop .product-card__stock { grid-row: 4; }
  /* align-self keeps the button from stretching in the flex card; in the
     grid it's the inline axis that has to be pinned instead */
  .page.shop .product-card__basket {
    grid-row: 5;
    justify-self: center;
  }

  .page.shop .product-card__name {
    grid-row: 2;
    min-height: 0;
    margin-bottom: 0.625rem;
  }

  /* 15% up on the listing's cards. One scale rather than five retuned
     values keeps the starburst, the peeking character and the text in
     proportion, and pins the growth to the corner the badge sits in. */
  .page.shop .product-card__bestseller {
    scale: 1.15;
    transform-origin: bottom right;
  }
}
