/* ==========================================================================
   OASIS INFLATABLES — shop.css
   Product cards, collection grid, PDP gallery, buy box, add-ons, cart page.
   ========================================================================== */

/* --- Product card --------------------------------------------------------- */
.pcard {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--shell);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
  transition: transform .34s var(--ease), box-shadow .34s var(--ease), border-color .34s var(--ease);
}
.pcard:hover { transform: translateY(-4px); box-shadow: var(--sh-3); border-color: transparent; }
.pcard__media {
  position: relative;
  aspect-ratio: 3 / 2;
  background: linear-gradient(168deg, var(--sand-2), var(--sand-3));
  overflow: hidden;
}
.pcard__media img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  transition: opacity .5s var(--ease), transform .8s var(--ease);
}
.pcard__media img.pcard__alt { opacity: 0; }
.pcard:hover .pcard__media img.pcard__primary { opacity: 0; }
.pcard:hover .pcard__media img.pcard__alt { opacity: 1; transform: scale(1.03); }
.pcard__badges { position: absolute; top: .9rem; left: .9rem; display: flex; gap: .4rem; z-index: 2; flex-wrap: wrap; }
/* Always visible: a hover-only quick-add leaves touch devices with no way to
   add to cart from the grid, and stays clickable while invisible on desktop. */
.pcard__quick {
  position: absolute; left: .9rem; right: .9rem; bottom: .9rem;
  z-index: 3;
  transition: transform .3s var(--ease);
}
.pcard__quick .btn {
  width: 100%;
  backdrop-filter: blur(8px) saturate(150%);
  background: rgba(255,255,255,.93);
  color: var(--ink);
  box-shadow: var(--sh-2);
  border: 1px solid rgba(255,255,255,.7);
}
.pcard:hover .pcard__quick .btn,
.pcard__quick .btn:hover,
.pcard__quick .btn:focus-visible { background: var(--orange); color: #fff; border-color: transparent; }
.pcard:hover .pcard__quick { transform: translateY(-2px); }

.pcard__body { padding: 1.1rem 1.15rem 1.25rem; display: grid; gap: .5rem; flex: 1; align-content: start; }
.pcard__name { font-family: var(--font-display); font-weight: 700; font-size: 1.0625rem; letter-spacing: -0.02em; line-height: 1.2; }
.pcard__name a::after { content: ''; position: absolute; inset: 0; z-index: 1; }
.pcard__sub { font-size: var(--fs-xs); color: var(--muted); line-height: 1.45; text-wrap: pretty; }
.pcard__rating { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.pcard__rating > span { font-size: var(--fs-xs); color: var(--muted); }
.pcard__meta { display: flex; align-items: center; justify-content: space-between; gap: .75rem; margin-top: .15rem; }
.pcard__price { font-family: var(--font-display); font-weight: 700; font-size: 1.0625rem; letter-spacing: -0.02em; }
.pcard__price s { color: var(--muted-2); font-weight: 400; font-size: .8125rem; margin-left: .4rem; }
.pcard__swatches { display: flex; gap: .35rem; align-items: center; }
.pcard__swatch {
  width: 18px; height: 18px; border-radius: 50%;
  border: 1px solid rgba(0,0,0,.14);
  box-shadow: inset 0 -2px 3px rgba(0,0,0,.12);
}
.pcard__swatch--teak { background: linear-gradient(140deg, #C79A5B, #9C6A30); }
.pcard__swatch--white { background: linear-gradient(140deg, #FFFFFF, #E4E0D8); }
.pcard__swatch--orange { background: linear-gradient(140deg, #E0703A, #B44E14); }
.pcard__swatch--deep { background: linear-gradient(140deg, #24544F, #0F2E2B); }
.pcard__swatch--grey { background: linear-gradient(140deg, #B9B4AB, #8D8578); }

/* --- Collection grid + filters -------------------------------------------- */
.plp { display: grid; grid-template-columns: 248px minmax(0, 1fr); gap: clamp(1.5rem, 3vw, 3rem); align-items: start; }
.filters { position: sticky; top: 110px; display: grid; gap: 1.5rem; }
.filters__group { display: grid; gap: .7rem; padding-bottom: 1.35rem; border-bottom: 1px solid var(--line); }
.filters__group:last-child { border-bottom: 0; }
.filters__title {
  font-family: var(--font-display); font-size: var(--fs-eyebrow);
  letter-spacing: .14em; text-transform: uppercase; color: var(--muted); font-weight: 700;
}
.filters__list { display: grid; gap: .3rem; }
.filters__opt {
  display: flex; align-items: center; gap: .7rem;
  min-height: 40px;
  font-size: var(--fs-sm); color: var(--ink-2);
  cursor: pointer; padding: .3rem 0;
  transition: color .16s var(--ease);
}
.filters__opt:hover { color: var(--orange); }
.filters__opt input { width: 20px; height: 20px; accent-color: var(--orange); flex: none; }
.filters__opt .count { margin-left: auto; color: var(--muted-2); font-size: var(--fs-xs); }
.filters__opt input[type="radio"] { border-radius: 50%; }

.plp__bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap; margin-bottom: 1.25rem;
}
.filters__toggle { display: none; }
.filters__toggle svg { width: 16px; height: 16px; }
.plp__count { font-size: var(--fs-sm); color: var(--muted); }
.plp__count b { color: var(--ink); font-weight: 600; }
.plp__tools { display: flex; align-items: center; gap: .75rem; }
.plp__tools .select { min-height: 44px; width: auto; min-width: 190px; }
.chips-active { display: flex; gap: .5rem; flex-wrap: wrap; margin-bottom: 1.25rem; }
.chip-remove {
  display: inline-flex; align-items: center; gap: .45rem;
  padding: .35rem .7rem; border-radius: var(--r-pill);
  background: var(--sand-2); font-size: var(--fs-xs); font-weight: 550;
  transition: background-color .16s var(--ease);
}
.chip-remove:hover { background: var(--sand-3); }
.chip-remove svg { width: 11px; height: 11px; opacity: .6; }

.grid-products { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(1rem, 1.8vw, 1.75rem); }
.grid-products--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-products--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.grid-addons { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: clamp(.85rem, 1.4vw, 1.35rem); }
/* nine unreleased accessories fill three clean rows of three */
.grid-addons--nine { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 860px) { .grid-addons--nine { grid-template-columns: repeat(2, minmax(0,1fr)); } }
@media (max-width: 560px) { .grid-addons--nine { grid-template-columns: 1fr; } }
/* a coming-soon badge has to read on both tile tints */
.acard--soon .acard__badge { background: rgba(23,20,15,.86); color: #fff; backdrop-filter: blur(6px); }
.acard--soon .acard__meta { color: var(--muted); }
.grid-addons--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
/* a one- or two-item group should never stretch across a 1344px row */
.grid-addons--pair { grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: 860px; }
.grid-addons--single { grid-template-columns: minmax(0, 380px); }
/* the shop page mixes product cards and add-on tiles in one grid — keep the
   media boxes the same height so rows do not misalign */
.grid-products .acard__media { aspect-ratio: 3 / 2; }

/* --- Add-on card (Funboy-style compact retail tile) ----------------------- */
.acard {
  position: relative;
  display: flex; flex-direction: column;
  background: var(--shell);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  overflow: hidden;
  transition: transform .3s var(--ease), box-shadow .3s var(--ease), border-color .3s var(--ease);
}
.acard:hover { transform: translateY(-3px); box-shadow: var(--sh-2); border-color: transparent; }
.acard__media {
  position: relative;
  aspect-ratio: 1 / 1;
  display: grid; place-items: center;
  padding: 12%;
  overflow: hidden;
}
.acard__media--sand { background: linear-gradient(160deg, #F6F0E6, #EBE1D2); }
.acard__media--deep { background: linear-gradient(160deg, #143B37, #0B2523); }
.acard__media img { width: 100%; height: 100%; object-fit: contain; transition: transform .5s var(--ease); }
.acard:hover .acard__media img { transform: scale(1.05) rotate(-1.5deg); }
.acard__badge { position: absolute; top: .7rem; left: .7rem; z-index: 2; }
.acard__body { padding: .85rem .95rem 1rem; display: grid; gap: .35rem; flex: 1; align-content: start; }
.acard__name { font-family: var(--font-display); font-weight: 650; font-size: .9375rem; letter-spacing: -0.015em; line-height: 1.25; }
.acard__meta { font-size: var(--fs-xs); color: var(--muted); line-height: 1.4; min-height: 2.2em; }
.acard__foot { display: flex; align-items: center; justify-content: space-between; gap: .6rem; margin-top: .35rem; }
.acard__price { font-family: var(--font-display); font-weight: 700; font-size: .9375rem; }
.acard__price s { color: var(--muted-2); font-weight: 400; font-size: .75rem; margin-left: .3rem; }
.acard__add {
  display: inline-flex; align-items: center; gap: .35rem;
  padding: .5rem .9rem; min-height: 36px;
  border: 1.5px solid var(--line-2); border-radius: var(--r-pill);
  font-family: var(--font-display); font-size: .8125rem; font-weight: 650;
  transition: all .2s var(--ease);
  white-space: nowrap;
}
.acard__add:hover { background: var(--orange); border-color: var(--orange); color: #fff; }
.acard__add.is-added { background: var(--ok); border-color: var(--ok); color: #fff; }

/* --- Product detail ------------------------------------------------------- */
/*
 * The band above the gallery.
 *
 * This was 20px on the breadcrumb plus .section--tight's clamp(2.5rem, 4.5vw,
 * 4.5rem) — 85px of padding between the header and the product at desktop, and
 * neither the breadcrumb nor the gap belonged to the product. It pushed the
 * gallery and the price down the page for no reason, so the top of the PDP read
 * as an accidental hole rather than as space.
 *
 * Both are now 35% of what they were: 7px and 14–24px. The breadcrumb keeps its
 * own line, because it is content and the eye needs it separated from the
 * header; everything else that was padding is gone.
 *
 * Scoped so the other nine pages using .section--tight are untouched, and
 * `.wrap.pdp-head` rather than `.pdp-head` so it wins over `.wrap` on
 * specificity instead of on source order.
 */
.wrap.pdp-head { padding-top: .4375rem; }
.section.pdp-buy { padding-top: clamp(.875rem, 1.6vw, 1.5rem); }

.pdp { display: grid; grid-template-columns: minmax(0, 1.26fr) minmax(0, 0.86fr); gap: clamp(1.5rem, 3.4vw, 3.75rem); align-items: start; }

.gallery { position: sticky; top: 100px; display: grid; gap: .75rem; }
.gallery__stage {
  position: relative;
  aspect-ratio: 4 / 3;
  border-radius: var(--r-lg);
  overflow: hidden;
  background: linear-gradient(168deg, var(--sand-2), var(--sand-3));
  border: 1px solid var(--line);
}
@media (min-width: 721px) { .gallery__stage { aspect-ratio: 3 / 2; } }
.gallery__slide {
  position: absolute; inset: 0;
  opacity: 0;
  transition: opacity .45s var(--ease);
  pointer-events: none;
  /* `.gallery__stage` is the frame with the radius and the clip; this fills it
     edge to edge and carries its own background and its own opacity transition,
     so it is a composited layer in its own right. Rounding it — and the images
     inside it, which are not clipped by a parent whose overflow is visible —
     keeps every full-bleed layer's corners round on their own rather than
     depending on the ancestor's clip surviving a compositor frame. See the
     longer note on `.viewer canvas` in blocks.css. */
  border-radius: inherit;
}
.gallery__slide.is-active { opacity: 1; pointer-events: auto; }
.gallery__slide--3d {
  background:
    radial-gradient(120% 90% at 62% 16%, #FFFDFA 0%, rgba(255,253,250,0) 55%),
    linear-gradient(165deg, var(--sand-2) 0%, var(--sand-3) 100%);
}
.gallery__slide img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }
.gallery__slide:not(.is-active) .gallery__zoom,
.gallery__slide:not(.is-active) .viewer__ui { visibility: hidden; pointer-events: none; }
.gallery__zoom {
  position: absolute; right: .9rem; bottom: .9rem; z-index: 4;
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .45rem .8rem; border-radius: var(--r-pill);
  background: rgba(255,255,255,.9); backdrop-filter: blur(8px);
  font-size: var(--fs-xs); font-weight: 600;
  box-shadow: var(--sh-1);
}
.gallery__zoom:hover { background: #fff; }
.gallery__nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 44px; height: 44px; border-radius: 50%;
  background: rgba(255,255,255,.92); backdrop-filter: blur(8px);
  display: grid; place-items: center; z-index: 4;
  box-shadow: var(--sh-1);
  opacity: .78; transition: opacity .25s var(--ease), background-color .2s var(--ease);
}
.gallery__stage:hover .gallery__nav, .gallery__nav:focus-visible { opacity: 1; }
.gallery__nav:hover { background: #fff; }
.gallery__nav--prev { left: .9rem; }
.gallery__nav--next { right: .9rem; }
.gallery__thumbs { display: flex; flex-wrap: wrap; gap: .5rem; }
.gallery__thumbs .gthumb { flex: 1 1 calc(25% - .375rem); min-width: 0; }
.gthumb {
  position: relative;
  aspect-ratio: 1/1; border-radius: var(--r-sm); overflow: hidden;
  border: 1.5px solid transparent;
  background: var(--sand-2);
  transition: border-color .2s var(--ease), transform .2s var(--ease);
}
.gthumb img { width: 100%; height: 100%; object-fit: cover; }
.gthumb:hover { transform: translateY(-2px); }
.gthumb.is-active { border-color: var(--ink); }
.gthumb--3d {
  display: grid; place-items: center;
  background: linear-gradient(160deg, var(--deep), var(--deep-2));
  color: #fff;
}
.gthumb--3d span { font-family: var(--font-display); font-size: .625rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; }
.gthumb--3d svg { width: 20px; height: 20px; margin-bottom: .15rem; }
.gthumb--3d > div { display: grid; justify-items: center; gap: .15rem; }
.gthumb--3d.is-active { border-color: var(--orange); }

/* Buy box */
.buybox { display: grid; gap: 1.35rem; padding-bottom: 4rem; }
@media (max-height: 860px) and (min-width: 1025px) { .buybox { gap: 1rem; } }
.buybox__head { display: grid; gap: .6rem; }
.buybox__title { font-size: clamp(2rem, 3.1vw, 3rem); }
.buybox__tag { color: var(--muted); font-size: var(--fs-lead); max-width: 46ch; }
.buybox__price { display: flex; align-items: baseline; gap: .75rem; flex-wrap: wrap; }
.buybox__price .now { font-family: var(--font-display); font-size: 1.875rem; font-weight: 700; letter-spacing: -0.03em; }
.buybox__price .was { color: var(--muted-2); text-decoration: line-through; font-size: 1.0625rem; }
.buybox__price .save { font-size: var(--fs-xs); font-weight: 650; color: var(--ok); background: var(--foam); padding: .3rem .6rem; border-radius: var(--r-pill); }
.buybox__price .aud {
  font-family: var(--font-display); font-size: .8125rem; font-weight: 700;
  letter-spacing: .1em; color: var(--muted); align-self: center;
}
.buybox__maths { font-size: var(--fs-sm); color: var(--muted); margin-top: -.5rem; }
/* The delivery range, under the capacity/weight/packed strip. Bordered so it
   reads as a fact about the order rather than another marketing line. */
/* The sticky bar is what a phone buyer reads while the buy box's delivery line
   is off-screen, so the same figure travels with it. */
.buybar__delivery { color: var(--muted); }
.buybox__delivery {
  font-size: var(--fs-sm); color: var(--muted); line-height: 1.6;
  margin-top: .9rem; padding: .7rem .85rem;
  background: var(--sand-2); border: 1px solid var(--line); border-radius: 10px;
}
.pcard__aud { font-size: .6875rem; font-weight: 700; letter-spacing: .08em; color: var(--muted); }
.buybox__rating { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; font-size: var(--fs-sm); color: var(--muted); }

.opt-group { display: grid; gap: .7rem; }
.opt-group__head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.opt-group__label { font-family: var(--font-display); font-size: var(--fs-xs); font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }
.opt-group__value { font-size: var(--fs-xs); color: var(--ink-2); font-weight: 550; }
.opt-tiles { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: .55rem; }
@media (max-width: 1180px) { .opt-tiles { grid-template-columns: repeat(2, minmax(0,1fr)); } }
.opt-tiles--2 { grid-template-columns: repeat(2, minmax(0,1fr)); }

.qty-row { display: flex; gap: .75rem; align-items: stretch; }
.qty-row .btn { flex: 1; }
.buybox__perks { display: grid; gap: .55rem; padding-top: .35rem; }
.perk { display: flex; gap: .65rem; align-items: flex-start; font-size: var(--fs-sm); color: var(--ink-2); }
.perk svg { width: 17px; height: 17px; color: var(--sea); flex: none; margin-top: .18rem; }
.perk b { font-weight: 600; }

.trust-strip {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: .75rem;
  padding: .95rem 1rem; border-radius: var(--r-md);
  background: var(--sand-2);
}
.trust-strip div { display: grid; gap: .12rem; }
.trust-strip b { font-family: var(--font-display); font-size: var(--fs-xs); font-weight: 700; }
.trust-strip span { font-size: .6875rem; color: var(--muted); line-height: 1.35; }

/* Bundle / add-ons block in PDP */
.bundlebox { border: 1px solid var(--line); border-radius: var(--r-md); background: var(--shell); overflow: hidden; }
.bundlebox__head {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: .9rem 1rem; background: var(--orange-050); border-bottom: 1px solid var(--orange-100);
}
.bundlebox__head h3 { font-size: .9375rem; }
.bundlebox__head p { font-size: var(--fs-xs); color: var(--orange-700); }
.bundlebox__list { padding: .6rem; display: grid; gap: .5rem; }
.bundlebox__foot { padding: .75rem 1rem 1rem; display: grid; gap: .6rem; border-top: 1px dashed var(--line-2); }

/* --- Accordions ----------------------------------------------------------- */
.acc { border-top: 1px solid var(--line); }
.acc__item { border-bottom: 1px solid var(--line); }
.acc__trigger {
  width: 100%;
  display: flex; align-items: center; justify-content: space-between; gap: 1.25rem;
  padding: 1.15rem 0;
  text-align: left;
  font-family: var(--font-display);
  font-size: 1.0625rem;
  font-weight: 650;
  letter-spacing: -0.015em;
  color: var(--ink);
  transition: color .18s var(--ease);
}
.acc__trigger:hover { color: var(--orange); }
.acc__icon {
  position: relative; width: 22px; height: 22px; flex: none;
  border-radius: 50%; border: 1.5px solid var(--line-2);
  transition: background-color .25s var(--ease), border-color .25s var(--ease), transform .3s var(--ease);
}
.acc__icon::before, .acc__icon::after {
  content: ''; position: absolute; inset: 50% 5px auto 5px;
  height: 1.5px; background: var(--ink-2); border-radius: 2px;
  transform-origin: center;
  transition: transform .3s var(--ease), background-color .25s var(--ease);
}
.acc__icon::after { transform: rotate(90deg); }
.acc__item.is-open .acc__icon { background: var(--orange); border-color: var(--orange); }
.acc__item.is-open .acc__icon::before, .acc__item.is-open .acc__icon::after { background: #fff; }
.acc__item.is-open .acc__icon::after { transform: rotate(0deg); }
.acc__panel { height: 0; overflow: hidden; transition: height .42s var(--ease); }
.acc__inner { padding: 0 0 1.35rem; color: var(--muted); font-size: var(--fs-body); max-width: 68ch; }
.acc__inner p + p { margin-top: .8rem; }
.acc__inner ul { display: grid; gap: .5rem; margin-top: .5rem; }
.acc__inner li { display: flex; gap: .6rem; align-items: flex-start; }
.acc__inner li::before { content: ''; width: 5px; height: 5px; border-radius: 50%; background: var(--orange); margin-top: .6rem; flex: none; }

/* --- Spec table ----------------------------------------------------------- */
.spectable { border-top: 1px solid var(--line); }
.spectable__row { display: grid; grid-template-columns: minmax(140px, 1fr) 1.6fr; gap: 1rem; padding: .85rem 0; border-bottom: 1px solid var(--line); font-size: var(--fs-sm); }
.spectable__row dt { color: var(--muted); }
.spectable__row dd { color: var(--ink); font-weight: 500; }

/* --- Reviews -------------------------------------------------------------- */
.reviews__summary { display: grid; grid-template-columns: auto 1fr; gap: clamp(1.5rem, 4vw, 4rem); align-items: center; }
.reviews__score { display: grid; gap: .35rem; justify-items: center; text-align: center; }
.reviews__num { font-family: var(--font-display); font-size: 3.75rem; font-weight: 700; letter-spacing: -0.04em; line-height: 1; }
.reviews__bars { display: grid; gap: .45rem; min-width: 0; }
.rbar { display: grid; grid-template-columns: 46px 1fr 44px; align-items: center; gap: .75rem; font-size: var(--fs-xs); color: var(--muted); }
.rbar__track { display: block; height: 7px; border-radius: var(--r-pill); background: var(--sand-3); overflow: hidden; }
.rbar__fill { display: block; height: 7px; background: var(--star); border-radius: var(--r-pill); }
.grid-reviews { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 1rem; }
.rcard {
  display: grid; gap: .75rem; align-content: start;
  padding: 1.25rem; border-radius: var(--r-md);
  background: var(--shell); border: 1px solid var(--line);
}
.rcard__head { display: flex; align-items: center; justify-content: space-between; gap: .75rem; }
.rcard__who { display: flex; align-items: center; gap: .65rem; }
.rcard__avatar {
  width: 36px; height: 36px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--sand-2); color: var(--ink-2);
  font-family: var(--font-display); font-weight: 700; font-size: .8125rem;
}
.rcard__name { font-weight: 600; font-size: var(--fs-sm); line-height: 1.2; }
.rcard__place { font-size: var(--fs-xs); color: var(--muted-2); }
.rcard__title { font-family: var(--font-display); font-weight: 650; font-size: .9375rem; letter-spacing: -0.015em; }
.rcard__body { font-size: var(--fs-sm); color: var(--muted); line-height: 1.6; }
.rcard__foot { display: flex; align-items: center; justify-content: space-between; gap: .75rem; font-size: var(--fs-xs); color: var(--muted-2); }
.verified { display: inline-flex; align-items: center; gap: .3rem; color: var(--ok); font-weight: 600; }
.verified svg { width: 13px; height: 13px; }

/* --- Cart page ------------------------------------------------------------ */
.cartpage { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(320px, 0.85fr); gap: clamp(1.5rem, 3.5vw, 3.5rem); align-items: start; }
/* The summary's column is named rather than left to auto-placement, which put
   it in column 1 and started a second row — that is, below the cross-sell,
   even though the source has it before. Declared here rather than inline so
   the single-column media query below can reset it. */
.cartpage > .summary { grid-column: 2; grid-row: 2; }
.cartpage > .shipmeter { grid-column: 2; grid-row: 1; }
/* The delivery form belongs under the thing it delivers.
 *
 * Auto-placement put it in column 2, under the summary, which left the left
 * column blank for its full 690px height — the summary is dense from the top
 * and the widest column on the page was empty beside it. The form is also the
 * wrong shape for a 458px sidebar: it is a two-column field grid.
 *
 * The cart reads left to right as what you are buying, then where it goes, with
 * the total and the pay button alongside both. */
.cartpage > .checkout-details { grid-column: 1; grid-row: 2; }
/* The cross-sell goes where the decision is.
 *
 * It sat under the summary, which put it BELOW the pay button: at 1440 the
 * button is at y=1234 and the pair offer at y=1849, so the highest-value thing
 * on the page was never on screen at the moment the customer decided. The left
 * column was empty from y=1323 to y=1735 in the same layout.
 *
 * It sits under the delivery form now — beside the summary and the pay button,
 * in the space that was blank. On a phone it is hidden entirely (below 1024):
 * up-selling a $1,614 pair between a delivery form and a pay button on a
 * handset is the wrong moment, and the card is not built for that width. */
.cartpage > .xsell { grid-column: 1; grid-row: 3; }
.cartpage > .row { grid-column: 2; grid-row: 3; align-self: start; margin-top: .5rem; }
.cartlist { border-top: 1px solid var(--line); }
.cartrow {
  display: grid; grid-template-columns: 108px minmax(0,1fr) auto;
  gap: 1.15rem; padding: 1.35rem 0; border-bottom: 1px solid var(--line);
  align-items: start;
}
.cartrow__img { width: 108px; height: 108px; border-radius: var(--r-sm); overflow: hidden; background: var(--sand-2); }
.cartrow__img img { width: 100%; height: 100%; object-fit: cover; }
.cartrow__name { font-family: var(--font-display); font-weight: 650; font-size: 1.0625rem; letter-spacing: -0.02em; }
.cartrow__opt { font-size: var(--fs-xs); color: var(--muted); margin-top: .18rem; }
.cartrow__desc {
  font-size: var(--fs-sm); color: var(--muted); margin-top: .5rem; max-width: 62ch;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.cartrow__ctrl { display: flex; align-items: center; gap: 1rem; margin-top: .85rem; }
.cartrow__price { text-align: right; font-family: var(--font-display); font-weight: 700; font-size: 1.0625rem; }
.cartrow__unit { font-size: var(--fs-xs); color: var(--muted-2); font-weight: 400; font-family: var(--font-body); }

.summary {
  position: sticky; top: 110px;
  display: grid; gap: 1rem;
  padding: 1.5rem; border-radius: var(--r-lg);
  background: var(--shell); border: 1px solid var(--line);
  box-shadow: var(--sh-1);
}
.summary h3 { font-size: 1.1875rem; }
.summary__row { display: flex; justify-content: space-between; gap: 1rem; font-size: var(--fs-sm); color: var(--muted); }
.summary__row--total { font-family: var(--font-display); font-size: 1.375rem; font-weight: 700; color: var(--ink); letter-spacing: -0.03em; padding-top: .9rem; border-top: 1px solid var(--line); }
.summary__note { font-size: var(--fs-xs); color: var(--muted-2); }
.payrow { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; justify-content: center; }
.payrow span { padding: .3rem .5rem; border-radius: 4px; background: var(--sand-2); font-size: .625rem; font-weight: 700; letter-spacing: .05em; color: var(--muted); }

/* --- Sticky PDP bar ------------------------------------------------------- */
.has-buybar .footer { padding-bottom: 84px; }
.buybar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 92;
  background: rgba(250,247,242,.985);
  backdrop-filter: blur(14px) saturate(170%);
  border-top: 1px solid var(--line);
  padding: .7rem 0;
  transform: translateY(110%);
  transition: transform .38s var(--ease);
}
.buybar.is-on { transform: none; }
.buybar__inner { display: flex; align-items: center; gap: 1.25rem; justify-content: space-between; }
.buybar__prod { display: flex; align-items: center; gap: .85rem; min-width: 0; }
.buybar__thumb { width: 50px; height: 50px; border-radius: var(--r-xs); object-fit: cover; background: var(--sand-2); flex: none; }
.buybar__name { font-family: var(--font-display); font-weight: 650; font-size: .9375rem; line-height: 1.2; letter-spacing: -0.015em; }
.buybar__meta {
  font-size: var(--fs-xs);
  color: var(--muted);
  /* One line, always. The bar is 74px tall by design and it sits over the buy
     button on a phone, so a meta line that is allowed to wrap turns a strip
     into a block that covers the thing it exists to offer. The freight figure
     is the first thing to go when there is not room for it. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
.buybar__right { display: flex; align-items: center; gap: 1.15rem; flex: none; }
.buybar__price { font-family: var(--font-display); font-weight: 700; font-size: 1.0625rem; }

/* --- Comparison ----------------------------------------------------------- */
.cmp { width: 100%; max-width: 1080px; margin-inline: auto; table-layout: fixed; border-radius: var(--r-md); overflow: hidden; border: 1px solid var(--line); background: var(--shell); color: var(--ink); }
/* the table is a light component and is sometimes placed inside a dark band */
.section--deep .cmp, .section--deep .cmp th, .section--deep .cmp td { color: var(--ink); }
.section--deep .cmp tbody th { color: var(--muted); }
.section--deep .cmp thead th { color: var(--ink); }
.section--deep .cmp tbody tr:nth-child(even) { background: rgba(23,20,15,.02); }
.cmp th, .cmp td { padding: 1rem 1.15rem; text-align: left; font-size: var(--fs-sm); border-bottom: 1px solid var(--line); vertical-align: top; }
.cmp thead th { background: var(--sand-2); font-family: var(--font-display); font-weight: 700; font-size: .9375rem; letter-spacing: -0.015em; }
.cmp tbody th { font-weight: 500; color: var(--muted); width: 26%; }
.cmp tbody tr:last-child th, .cmp tbody tr:last-child td { border-bottom: 0; }
.cmp__hl { background: rgba(204,92,31,.10); box-shadow: inset 1px 0 0 var(--orange-100), inset -1px 0 0 var(--orange-100); }
.cmp thead th.cmp__hl { background: var(--orange-050); }

/* --- Responsive ----------------------------------------------------------- */
@media (max-width: 1180px) {
  /* `.grid-products` is deliberately absent.
   *
   * It was here restating the default — `.grid-products` is already three
   * columns — but a redundant rule is not a harmless one. This block sits above
   * the phone rules and `.grid-products--2` has the same specificity, so the
   * restatement beat every collapse below it: on a phone the range stayed three
   * across at 106px a card, and from 1025 to 1180 the two-column grid was forced
   * back to three while the odd-card rule still spanned the bundle across all
   * three, leaving a ~310px hole. Deleting it lets the base rule and the
   * variants cascade the way they read. */
  .grid-addons { grid-template-columns: repeat(3, minmax(0,1fr)); }
  .grid-reviews { grid-template-columns: repeat(2, minmax(0,1fr)); }
}
@media (max-width: 1024px) {
  .pdp { grid-template-columns: 1fr; }
  .gallery { position: static; }
  .buybox { padding-bottom: 2rem; }
  .plp { grid-template-columns: 1fr; }
  .cartpage { grid-template-columns: 1fr; }
  /* Single column: the cart, then the total and the pay button, then the
     upsell. Emitting the summary last put a $765 decision between the buyer and
     the button that takes the money, on the device most of them use.
   *
   * The three `order` rules that expressed that never matched. `.cartpage` is a
   * grid whose first-level children are plain `<div>` wrappers, and
   * `.cartpage > div { display: contents }` dissolves them for LAYOUT only —
   * the element tree is unchanged, so `.cartpage > .xsell` selects nothing
   * because `.xsell` is a grandchild. Measured: `matchesSelector` false, order
   * 0, and at 390px the upsell sat between the address form and the total,
   * exactly the arrangement the comment above it says was fixed.
   *
   * `display: contents` children cannot carry an order, so the wrappers are
   * ordered instead. That is stable: the wrapper is what the DOM has. */
  /* One column: reset the explicit column so the stack is pure source order. */
  .cartpage { display: flex; flex-direction: column; }
  .cartpage > .summary { grid-column: auto; grid-row: auto; position: static; top: auto; }
  .summary { position: static; }

  /* the filter rail collapses behind a single button so the first product is
     never more than one screen down on a phone */
  .filters__toggle { display: inline-flex; }
  .filters {
    position: static;
    gap: 0;
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--shell);
    padding: 0 1rem;
    margin-bottom: 1.25rem;
  }
  .filters__group { display: none; }
  .filters.is-open .filters__group { display: grid; }
  .filters.is-open { padding-bottom: 1rem; }
  .filters__group { padding-block: 1rem; }
}
@media (max-width: 860px) {
  .grid-addons { grid-template-columns: repeat(2, minmax(0,1fr)); }
  /* the bar still has to say WHAT is being bought and in which size */
  .buybar__thumb { display: none; }
  .buybar__name { font-size: .875rem; }
  .buybar__meta { font-size: .6875rem; }
  /* At 390px the meta line cannot hold the deck, the date and two freight
     figures. The date stays — it is the pre-order fact — and the freight,
     which the cart quotes properly two taps later, goes. */
  .buybar__delivery { display: none; }
  .buybar__inner { gap: .75rem; }
  .cartrow { grid-template-columns: 76px minmax(0,1fr); }
  .cartrow__img { width: 76px; height: 76px; }
  .cartrow__price { grid-column: 2; text-align: left; margin-top: .35rem; }
  .reviews__summary { grid-template-columns: 1fr; }
  .grid-reviews { grid-template-columns: 1fr; }
  .cartrow { grid-template-columns: 84px minmax(0,1fr); }
  .cartrow__img { width: 84px; height: 84px; }
  .cartrow__price { grid-column: 2; text-align: left; }
  .gallery__thumbs .gthumb { flex: 1 1 calc(33.333% - .334rem); }
  .buybar__prod { min-width: 0; }
}
@media (max-width: 720px) {
  .grid-products, .grid-products--2, .grid-products--4 { grid-template-columns: 1fr; }
  .opt-tiles { grid-template-columns: 1fr; }
  .trust-strip { grid-template-columns: 1fr; }
}


/* --- Lightbox ------------------------------------------------------------- */
.lightbox {
  position: fixed; inset: 0; z-index: 300;
  display: grid; place-items: center;
  gap: 1rem;
  padding: clamp(1rem, 4vw, 3rem);
  background: rgba(12, 10, 8, .93);
  backdrop-filter: blur(6px);
  opacity: 0; visibility: hidden;
  transition: opacity .28s var(--ease), visibility .28s;
}
.lightbox.is-open { opacity: 1; visibility: visible; }
.lightbox img {
  max-width: min(1400px, 94vw);
  max-height: 82vh;
  width: auto; height: auto;
  border-radius: var(--r-md);
  box-shadow: var(--sh-4);
}
.lightbox__cap { color: rgba(255,255,255,.7); font-size: var(--fs-sm); text-align: center; max-width: 70ch; }
.lightbox__close {
  position: absolute; top: clamp(1rem, 3vw, 2rem); right: clamp(1rem, 3vw, 2rem);
  width: 46px; height: 46px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(255,255,255,.12); color: #fff;
  border: 1px solid rgba(255,255,255,.24);
  transition: background-color .18s var(--ease);
}
.lightbox__close:hover { background: rgba(255,255,255,.24); }
.lightbox__close svg { width: 20px; height: 20px; }

/* --- Cart page: the reservation line and the crossed-out regular price --- */
.cartrow__preorder {
  margin-top: .3rem;
  font-size: var(--fs-xs);
  color: var(--orange-700);
  font-weight: 600;
}
.cartrow__was {
  margin-top: .15rem;
  font-size: var(--fs-xs);
  color: var(--muted-2);
  text-decoration: line-through;
}

/* --- Receipt: the pre-order banner above the table ---
   On a receipt the customer keeps for a year, the delivery window and the
   refund right are the two facts that matter, so they sit above the money
   rather than in a footnote below it. */
.receipt-preorder {
  margin: 0 0 1rem;
  padding: .85rem 1rem;
  background: var(--orange-050);
  border: 1px solid var(--orange-100);
  border-left-width: 3px;
  border-radius: var(--r-sm);
  font-size: var(--fs-sm);
  color: var(--ink-2);
  line-height: 1.55;
}
.receipt-preorder b { color: var(--ink); }

/* --- PDP: the buy block and the delivery strip ---------------------------
   The order of the buy box is now price → choose → buy → the fine print. The
   fine print is a compact three-line strip rather than two paragraphs, because
   the paragraphs were what pushed the primary action to ~1013px on a laptop —
   below the fold at both 1440x900 and 1280x800. */
.buybox__buy { display: grid; gap: 1.1rem; }
@media (max-height: 860px) and (min-width: 1025px) { .buybox__buy { gap: .85rem; } }

.delivstrip {
  display: grid;
  gap: .5rem;
  margin: 0;
  padding: .85rem 1rem;
  list-style: none;
  background: var(--sand);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
}
.delivstrip li {
  display: grid;
  grid-template-columns: 16px minmax(0, 1fr);
  gap: .55rem;
  align-items: start;
  font-size: var(--fs-xs);
  line-height: 1.5;
  color: var(--muted);
}
.delivstrip svg { width: 16px; height: 16px; margin-top: .12rem; color: var(--orange-600); }
.delivstrip b { color: var(--ink); font-weight: 650; }

/* --- PDP on a phone: the buy block has to reach the first screen ---------
   Stacked into one column, the gallery and its thumbnail grid filled the whole
   of a 390x844 screen: the product name was cut at the bottom edge and the
   price, the delivery month and the buy button were all a full screen lower.
   Almost all paid social traffic lands on a product page on a phone, so the
   first screen is spent on a picture of something the shopper cannot yet
   learn the price of.

   The stage is capped to a share of the viewport and the thumbnails become a
   single scrollable row, which together return roughly 400px — enough for the
   title, the price and the button. */
/* Every level of the buy box, not just the top one.
   `.pdp > *` covers the two columns; the boxes inside `.buybox` are grid items
   in their own right and each needs the same permission to shrink below its
   content, or `min-width: auto` lets the 476px head overflow a 350px column
   with `overflow: visible` and the badge and the h1 run off the screen edge. */
.buybox > *,
.buybox__buy > *,
.opt-group > *,
.opt-tiles > * { min-width: 0; }

@media (max-width: 640px) {
  .gallery__stage { aspect-ratio: auto; height: min(34vh, 268px); }
  .gallery { gap: .5rem; }
  .gallery__thumbs {
    flex-wrap: nowrap;
    overflow-x: auto;
    gap: .4rem;
    /* A visible row of five at 390 rather than three rows of two: the thumbs
       are a filmstrip, and a filmstrip that wraps stops reading as one. */
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .gallery__thumbs::-webkit-scrollbar { display: none; }
  .gallery__thumbs .gthumb {
    flex: 0 0 68px;
    scroll-snap-align: start;
  }
  /* The buy box starts immediately under the strip so the price is the next
     thing the eye meets, not another band of whitespace. Measured at 390x844:
     268px of gallery plus 68px of filmstrip plus this tightened rhythm puts the
     title, the deck's dimensions, the price and the savings badge on the first
     screen, with the choose-and-buy block immediately below it. */
  .pdp { gap: 1rem; }
  .buybox { gap: .9rem; }
  .buybox__head { gap: .45rem; }
  .buybox__title { font-size: clamp(1.75rem, 8vw, 2.25rem); }
  .buybox__tag { font-size: var(--fs-body); }
  .price-note { margin-top: .5rem; }
  .delivstrip { padding: .7rem .85rem; gap: .4rem; }
}

/* --- PDP: nothing inside the buy box may widen the page ------------------
   A grid column will not shrink below the min-content width of what is in it,
   and `.buybox__head`'s eyebrow-plus-badge row has a min-content width of
   387px — "FLOATING DECK · 2.43 × 2.13 M" sets to 228px on its own at the
   eyebrow's letter-spacing and will not break, and the badge adds 159px. With
   the gap and the page gutter that is a 476px track inside a 390px screen, so
   the whole column — heading, price, delivery strip, buy button — was laid out
   86px wider than the phone and clipped by the overflow guard. The caption and
   the button ran off the right edge, which is what the phone screenshots show.

   Two fixes, both local: let the column shrink, and let the eyebrow wrap. */
.pdp > * { min-width: 0; }
.buybox__head .eyebrow {
  /* 0.16em of tracking makes an already long string unbreakable; this lets it
     fall onto a second line on a narrow screen instead of setting the width of
     the column. Measured on the PDP at 390px: 476px before, 350px after. */
  flex-wrap: wrap;
  min-width: 0;
  overflow-wrap: anywhere;
}
@media (max-width: 480px) {
  /* At phone width the eyebrow wraps to three lines, which reads as a
     mistake. The badge already says "Pre-order · 15% off", so the eyebrow's
     dimensions move into the title block where they belong. */
  .buybox__head .row--wrap { gap: .4rem; }
  .buybox__head .eyebrow { letter-spacing: .1em; }
}

/* --- PDP on a phone: the dimensions line and the kicker ------------------
   `.buybox__dims` carries the deck's measurements beside the title at phone
   width, where the eyebrow is hidden — otherwise the only place they appeared
   was the eyebrow, and hiding it would have lost the size. */
.buybox__dims {
  font-size: var(--fs-xs);
  color: var(--muted);
  letter-spacing: .01em;
  font-variant-numeric: tabular-nums;
  margin-top: -.2rem;
  display: none;
}
@media (max-width: 900px) {
  .buybox__dims { display: block; }
  /* At phone width the eyebrow wraps to two or three tracked-out lines and
     pushes the price a line further down. The badge carries the offer and the
     dimensions now sit under the title, so nothing is lost by dropping it at
     the width where vertical space is the scarce resource. */
  .buybox__kicker .eyebrow { display: none; }
}

/* --- Product card: the price pair and the saving ---
   The card used to render "price, struck price, AUD" — so the currency code
   attached itself to the number being struck through, and the compare-at was
   13px/400 against a 17px/700 price. The anchor is the entire argument of a
   15% pre-order, and it was set as fine print under the wrong label. */
/* No `gap` on this row: the dollars and their cents are two spans, and a gap
   between flex children puts a space *inside* the figure — it rendered as
   "$849 .15". The spacing belongs between the groups, so it is a margin on the
   second group and the third. */
/* The price row was 130px wide inside a 295px row, so "$849.15 AUD $999"
   wrapped into six line boxes and the struck-through price fractured across a
   break — the comparison price is the whole savings argument. `white-space:
   nowrap` alone keeps it on one line wherever there is room.
 *
 * `min-width: max-content` was the wrong way to guarantee that. It set an
 * explicit floor wider than the card at tablet widths — "$1,614.15 AUD $1,899"
 * needs 272px inside a 225px card — and `overflow: hidden` on the card then
 * clipped it silently instead of letting the layout respond. The card's own
 * meta row wraps between its parts now, so the price and the struck price can
 * take a line each when they must. */
/* The row is nowrap only BETWEEN the parts that must not split ("$849.15" and
   its ".15"), and free to wrap between the parts themselves. `white-space:
   nowrap` on the whole row made "$1,614.15 AUD $1,899" a single 272px
   unbreakable run, which `overflow: hidden` on the card then clipped at every
   width below ~1200 — invisible, because a clipped price still looks like a
   price right up to the point where the digits stop. */
.pcard__price {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.0625rem;
  letter-spacing: -0.02em;
  display: inline-flex;
  align-items: baseline;
  flex-wrap: wrap;
  /* No `gap`. `moneyHTML()` emits the dollars as a BARE TEXT NODE followed by
     `<span class="money__c">.15</span>`, and in a flex container a bare text
     node becomes an anonymous flex item — so a column gap lands between the
     dollars and the cents and every card on the site reads "$849 .15" with a
     5.6px hole where the decimal point should be joined. The separation belongs
     between the PARTS of the row, not inside a figure, so it is margined onto
     the parts instead. */
  gap: 0;
}
.pcard__price > .money__c { margin-left: 0; }
.pcard__price .money__c,
.pcard__price s,
.pcard__aud { white-space: nowrap; }
.pcard__aud,
.pcard__price > s { margin-left: .4rem; }
.pcard__meta { flex-wrap: wrap; }
/* The card's price block is now the shelf price plus a code line beneath it, so
   it stacks rather than sitting in one row with the swatches. */
.pcard__meta { align-items: flex-end; }
.pcard__price + .pcard__code { margin-top: -.15rem; }
.pcard__price .pcard__aud { font-size: .625rem; font-weight: 700; letter-spacing: .1em; color: var(--muted); margin-left: .35rem; }
.pcard__price s { margin-left: .35rem;
  font-size: .875rem;
  font-weight: 500;
  color: var(--muted-2);
  /* Struck but legible: at 400 weight the anchor disappears into the card. */
  text-decoration-thickness: 1px;
}
/* The saving is the offer, so it is not grey small print in the same tone as
   the spec line above it. One treatment, borrowed from .price-save. */
.pcard__ship { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem .5rem; margin-top: .55rem; }
.pcard__ship-date { font-size: var(--fs-xs); color: var(--muted); }
.pcard__ship-save {
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--orange-700);
  background: var(--orange-050);
  border: 1px solid var(--orange-100);
  border-radius: var(--r-pill);
  padding: .15rem .5rem;
  /* Beside the delivery line rather than pushed to the far edge of the card:
     at the card's width the auto margin sent it onto its own row, where it read
     as a detached chip instead of as part of the price story. */
  margin-left: 0;
}

/* --- The price table: decimal-aligned money ---
   The table exists so two figures per row can be compared, and every cell was
   left-aligned at a fixed x, so no $, digit or point lined up with the row
   below it and a 15% saving read as approximate. */
.pricetable__head > :nth-child(2),
.pricetable__head > :nth-child(3),
.pricetable__was,
.pricetable__now { text-align: right; }
.pricetable__was,
.pricetable__now { font-variant-numeric: tabular-nums lining-nums; }
.pricetable__now small { text-align: right; }
@media (max-width: 640px) {
  /* Stacked, the row becomes name / was / now, so "right-aligned" is the only
     thing keeping the two figures in one column. */
  .pricetable__was { text-align: right; }
}

/* --- The gallery's zoom control is inset from the corner ---
   It sat flush at the right edge, where on a phone it was clipped by the
   viewport and lost its final letter. */
.gallery__zoom { right: 12px; max-width: calc(100% - 24px); }

/* ==========================================================================
   The buy box, measured to the fold.

   The rule this file follows: on a laptop the price, the deck chooser and the
   "Pre-order now" button are all inside the first screen. The targets are
   1440x900, 1366x768 and 1280x800 — the three viewports the site is built
   against — and the button's bottom edge is the number that matters.

   Achieved by tightening above the button rather than below it: the head gaps,
   the price note and the strip that follows are all compressible, and none of
   them is the reason anyone is on the page. The strip stays *after* the button
   because moving it above pushed the button 60px past the fold on all three;
   the refund right it carries is repeated in the cart drawer, in the sticky
   bar's own page, and in the receipt, so nothing depends on it being read
   before the click.
   ========================================================================== */
@media (min-width: 1025px) {
  .buybox { gap: 1.1rem; }
  .buybox__head { gap: .45rem; }
  .buybox__title { font-size: clamp(1.9rem, 2.7vw, 2.6rem); }
  .buybox__tag { font-size: var(--fs-body); }
  .price-note { margin-top: .55rem; font-size: var(--fs-sm); }
  .opt-group { gap: .5rem; }
  .qty-row { gap: .6rem; }
}
@media (min-width: 1025px) and (max-height: 820px) {
  /* A short laptop: the tagline is the one line that can go without losing a
     fact, and it is repeated in the page's own opening section below. */
  .buybox__tag { display: none; }
  .buybox { gap: .85rem; }
}

/* The risk-reversal line, immediately under the buy button — the one place a
   shopper reads before committing. Small, quiet, and adjacent to the action
   rather than in a card below it. */
.buybox__assure {
  display: flex;
  align-items: center;
  gap: .45rem;
  margin: .75rem 0 0;
  font-size: var(--fs-xs);
  color: var(--muted);
}
.buybox__assure svg { width: 14px; height: 14px; flex: none; color: var(--ok); }

/* --------------------------------------------------------------------------
   The sticky buy bar must never cover the price.

   On a phone it is fixed to the bottom of the viewport and the price block
   sits at 757-909px in an 844px screen, so 75 of its 152px were behind the bar
   the moment the bar appeared — the shopper could see the button and not what
   it costs. The bar reserves its own height at the foot of the buy box at the
   widths where it is fixed.
   -------------------------------------------------------------------------- */
@media (max-width: 1024px) {
  .page-pdp .buybox { padding-bottom: 6rem; }
}

/* The eyebrow-length caption on every card. 13px is easy to let run, and on
   the full-width bundle card it was setting to about 200 characters a line —
   the card is the widest thing on the page and this is its only body copy. */
.pcard__sub { max-width: min(72ch, 100%); }
/* The receipt-oriented notes in the cart summary: they are prose, not labels. */
.summary__note { max-width: min(56ch, 100%); }

/* --------------------------------------------------------------------------
   Tap targets at phone width.

   The build already states the rule — 44px is the smallest comfortable touch
   target, and the viewer controls follow it — but the controls a shopper
   touches most were under it: the quick-add on every product card was 40px,
   the header icon buttons 42px, the stacked footer links 19px.
   -------------------------------------------------------------------------- */
/* Applied at every width, not only on a phone. A 40px quick-add and a 42px
   header icon are under the target on a trackpad and well under it on a
   touchscreen laptop, and the build already sets 44px for its viewer controls.
   The buttons and the header simply follow the rule the rest of the site does. */
.btn--sm,
.btn--xs { min-height: 44px; }
.icon-btn { min-width: 44px; min-height: 44px; }
.footer__col a,
.footer__legal a,
.footer__ask a { display: inline-block; padding-block: .72rem; }

/* --------------------------------------------------------------------------
   Small things the sixth review measured.

   . The promo form's Apply button wrapped mid-word to "App" / "ly" — an 85x67
     box in the money column, beside the pay button. The form is a flex row
     with a text input and a button; the button needs to keep its own width.
   . The delivery-details row's inputs measured 57.5px on a phone against
     68.1px for the street field in the same row — one row, two heights.
   . The cart's quantity steppers were 40x38 at every width, against the 44px
     rule the rest of the build follows, and the gallery's Zoom chip was 35.5px.
   -------------------------------------------------------------------------- */
#promo-form { display: flex; gap: .5rem; align-items: stretch; }
#promo-form .input { flex: 1 1 auto; min-width: 0; }
#promo-form .btn { flex: 0 0 auto; white-space: nowrap; }

/* One row, two heights: the street field measured 68.1px and everything beside
   it 57.5px, because the select carries its own padding on top of the input's.
   A single min-height for the row fixes it without touching either component. */
.form-grid .input,
.form-grid .select { min-height: 58px; }

.qty button { min-width: 44px; min-height: 44px; }
/* 40x38 measured against the project's own 44px rule. */
.qty--sm button { min-width: 44px; min-height: 44px; }
.gallery__zoom { min-height: 44px; padding-block: .55rem; }

/* One field in the delivery form was 69px where the other seven were 58: the
   street field's label wraps to two lines and the grid stretched the input
   below it to match. The labels align to the baseline of their own row and the
   inputs keep one height. */
.form-grid { align-items: start; }
.form-grid .field { align-content: start; }
.form-grid .label { min-height: 1.05rem; }


/* --------------------------------------------------------------------------
   The camera pills, which must not be wider than their own background.

   `.viewer__views` is an absolutely positioned pill holding four buttons. At
   900px the buttons needed 214px inside a 172px box, so "Low" rendered 38px
   outside the white surface on bare grey — the same fault as the product
   switcher above, on the other control in the same corner. `max-content` sizes
   the box to its content; the buttons keep their 44px touch height, which is
   vertical and unaffected.
   -------------------------------------------------------------------------- */
.viewer__views { width: max-content; max-width: calc(100% - 2rem); }
.viewer__views button { min-width: 0; }
@media (max-width: 1024px) {
  .viewer__views { max-width: calc(100% - 5.5rem); }
  .viewer__views button { padding-inline: .5rem; font-size: .75rem; }
}


/* --------------------------------------------------------------------------
   The full-width card, capped to something you can see at once.

   The bundle spans the row at desktop, and its media box is a 3:2 aspect of the
   full container — 896px tall at 1440 and 1108px at 1920. Image, name and
   $1,614.15 could not be on screen together at any desktop width, so the
   highest-value SKU was the only one that could not be scanned. A max-height on
   the media box fixes that without touching the two normal cards.
   -------------------------------------------------------------------------- */
/* The bundle spans the row, and the obvious way to stop it towering over the
   viewport was `max-height: 46vh` on its media box — which turned a 3:2 source
   into a 3.24:1 letterbox at 1440 and 3.34:1 at 1920, so `object-fit: cover`
   threw away 54% of the image height and the $1,614 SKU rendered as a slice of
   teak and rail with no silhouette at all.
 *
 * Cropping it harder was the wrong lever. The card goes two-up instead: the
 * image keeps its own 3:2 ratio at the left, and the name, subtitle, price and
 * button sit beside it. Nothing is cropped, the card fits the viewport, and the
 * highest-value product is the one you can actually see. */
/* The range is TWO columns at desktop, not three.
 *
 * With three there are three items and the third spans the row, so row one
 * renders two cards and a bare hole in column three — directly under a heading
 * that says "3 products". Two columns make the arithmetic come out: two cards,
 * then the bundle across the pair. It also gives each card more room than the
 * 326px it had, which is the other half of why the range read as cramped. */
@media (min-width: 1025px) {
  .grid-products:not([class*="--"]) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* Scoped to the plain grid, like the column rule beside it.
   *
   * Unscoped, this also matched the cart's cross-sell — a `--3` grid whose
   * three items filled all three columns — and stretched its third card across
   * the row, leaving a bare hole in column 3 and a 1344px card with a large
   * internal void. The same fault the column rule was scoped to fix, missed on
   * the rule next to it. */
  .grid-products:not([class*="--"]) > :last-child:nth-child(odd) {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
    align-items: center;
    gap: clamp(1.5rem, 3.2vw, 3rem);
  }
  .grid-products > :last-child:nth-child(odd) .pcard__media {
    aspect-ratio: 3 / 2;
    max-height: none;
  }
  .grid-products > :last-child:nth-child(odd) .pcard__body { padding-block: 0; }
}

/* --------------------------------------------------------------------------
   The viewer tools must not sit on the product.

   They were moved to the middle of the right edge to stop them colliding with
   the switcher, which put them over the deck's own right-hand D-rings and
   corner between roughly 860 and 1320px — they no longer fought a control, they
   covered the thing they control. They go back to the top-right corner above
   that range (where the stage is wide enough for the switcher to clear them) and
   the switcher keeps its own row.
   -------------------------------------------------------------------------- */
@media (min-width: 1181px) {
  .viewer__tools { top: 1rem; bottom: auto; transform: none; }
}

/* --------------------------------------------------------------------------
   The pay button, always reachable on a phone.

   The cart is a long page: the deck, the delivery form, the cross-sell and then
   the summary. The primary action sat at the very bottom of it, 3,176px down at
   390px, so changing a postcode meant scrolling past everything to find the
   button again. The summary's own CTA is repeated in a bar pinned to the bottom
   of the viewport below 720px — the same button, the same label, wired to the
   same handler, so there is exactly one primary action and it is never more
   than a thumb away.

   It is `position: sticky` on the summary itself rather than a second button,
   which means no duplicated state to keep in sync.
   -------------------------------------------------------------------------- */
@media (max-width: 1024px) {
  /* The cross-sell does not appear below the two-column breakpoint.
   *
   * Up-selling a $765 upgrade between the delivery form and the pay button is
   * the wrong moment on a handset, and the card was not built for that width:
   * its `.addon-row` is a "64px 1fr auto" grid, so the text column was squeezed
   * to 62px and the description wrapped to seven lines with the price split
   * across "$1,614.15" and "AUD" while the button sat over the text. The
   * reordering rules meant to move it below the pay button could not reach it
   * either, because the element is inside a display:contents wrapper and a
   * box-less wrapper cannot be ordered.
   *
   * Hiding it removes the defect, shortens the page by 276px, and puts the
   * summary and the pay button where a thumb expects them. A desktop shopper
   * gets the same card at full width, where it measures cleanly. */
  .xsell { display: none; }
}

/* --------------------------------------------------------------------------
   The phone pay bar.

   The cart is 7,040px long at 390px and the button that charges the customer
   sits 2,761px down it. This pins a copy of that button to the bottom of the
   viewport below 720px, where the page is long enough to need it.

   `position: fixed` only on a phone: at 1024 and up the summary column is
   beside the cart and the real button is visible without it.
   -------------------------------------------------------------------------- */
.paybar { display: none; }
/* 1024, not 720.
 *
 * The cart is single-column all the way up to 1025, so a tablet in portrait —
 * 768, 860, 900 — has the same problem a phone has: the pay button 800 to 1,170px
 * below the fold with nothing persistent. Showing the bar only under 720 fixed
 * the size the reviewer happened to measure and left the next three. The bar's
 * range is the range where the LAYOUT stacks, which is what the rule below
 * already keys on everywhere else. */
@media (max-width: 1024px) {
  .paybar {
    display: block;
    position: fixed;
    left: 0; right: 0; bottom: 0;
    /* The shared `.wrap > *` ceiling caps this at the content width, which left
       a full-bleed bar 20px from the left and 60px from the right at 390px with
       its hairline stopping short of the edge. A fixed bar is not in the
       content column and should not inherit its measure. */
    max-width: none;
    z-index: 40;
    padding: .7rem var(--gutter) calc(.7rem + env(safe-area-inset-bottom, 0px));
    background: rgba(250, 247, 242, .96);
    backdrop-filter: blur(10px) saturate(160%);
    border-top: 1px solid var(--line);
  }
  .paybar[hidden] { display: none; }
  .paybar .btn { width: 100%; }
  /* A disabled button at 45% opacity puts white on a washed orange at 1.38:1,
     and this one carries a SENTENCE — "We cannot price this delivery yet" — not
     a label. The reason for the block has to be readable on the control that is
     blocked; it is already 5.35:1 in the delivery tile above, and there is no
     reason for the same words to be unreadable here.
     A muted fill and full-strength text says disabled without going illegible. */
  .paybar .btn[disabled] {
    opacity: 1;
    background: var(--sand-3);
    color: var(--ink-2);
    border-color: var(--line-2);
  }
  /* Keep the last of the page clear of the bar. On the PAGE, not the cart grid:
     a fixed bar overlaps whatever the document ends with, which is the footer,
     and a tap aimed at "Warranty" was landing on the pay button. */
  body.page-cart { padding-bottom: 6.5rem; }
}

/* The code line under a card's price.
   The shelf price is the price; this is what comes off it, named so the
   customer knows what will be applied at checkout without typing anything. */
.pcard__code {
  display: block;
  font-family: var(--font-display);
  font-size: var(--fs-xs);
  font-weight: 650;
  letter-spacing: .01em;
  color: var(--orange-700);
}

/* The code line on a cart row, where the struck-through shelf price used to be. */
.cartrow__code {
  font-family: var(--font-display);
  font-size: var(--fs-xs);
  font-weight: 650;
  color: var(--orange-700);
  margin-top: .1rem;
}
