/* transplant.css — Hopscotch's theme-owned support layer.

   Three jobs, none of which belong in the lifted reference.css:

     1. no-JS / reduced-motion / ?ts-anim=off fallbacks, so the page is complete and
        deterministic even when the motion drivers are off or never boot;
     2. a style layer for the PLATFORM-RENDERED surfaces (promotion sections, the sf
        auth/login views, the /recommend related-products render, the base-copied
        search / 404 / cart / account templates) — those arrive in the base/Timber
        class vocabulary, which the reference's CSS knows nothing about;
     3. a theme-owned listing grid, because this reference is utility-CSS based and
        defines no reusable N-column grid family to borrow.

   Everything here is additive. Nothing overrides reference.css for the reference's
   own markup.
   ------------------------------------------------------------------------------- */


/* ============================================================================
   1. Motion fallbacks
   ============================================================================ */

/* The reference's <noscript> rules — the page must be complete without JS. */
html.no-js .no-js-hidden { display: none !important; }
html.no-js motion-component {
  visibility: visible !important;
  opacity: 1 !important;
  transform: none !important;
}
html.no-js .tabs__panels { display: flex; flex-direction: column; gap: 4rem; }
html.no-js .tabs__panel { display: flex; flex-direction: column; gap: 2.4rem; }
html.no-js .accordion__details .accordion__summary-arrow.icon--caret svg { transform: rotateX(0deg) !important; }
html.no-js .accordion__details[open] .accordion__summary-arrow.icon--caret svg { transform: rotateX(180deg) !important; }
html.no-js .card-layered__description { max-height: unset; }
html.no-js .scrolling-cards__headings { height: auto; }
html.no-js .block-scrolling__headings { position: static; }

/* ?ts-anim=off (audit/screenshot kill-switch) and reduced-motion: every effect is
   flattened to its FINAL state, so a capture is deterministic and nothing is left
   mid-transition. animations.js sets html[ts-anim-off]. */
html[ts-anim-off] motion-component {
  visibility: visible !important;
  opacity: 1 !important;
  transform: none !important;
  transition: none !important;
}
html[ts-anim-off] .page-transition { display: none !important; }
html[ts-anim-off] *,
html[ts-anim-off] *::before,
html[ts-anim-off] *::after {
  animation-play-state: paused !important;
  animation-delay: 0s !important;
  transition-duration: 0s !important;
}

@media (prefers-reduced-motion: reduce) {
  motion-component {
    visibility: visible !important;
    opacity: 1 !important;
    transform: none !important;
  }
  .marquee__content,
  .scrolling-cards__track,
  .block-scrolling__track { animation: none !important; }
  .page-transition { display: none; }
}


/* ============================================================================
   2. Platform-rendered surfaces (base / Timber vocabulary)
   ============================================================================ */

/* --- 2a. The .btn alias -----------------------------------------------------
   The reference happens to use `.btn` too, so the base recipe is already styled.
   What it does NOT define is the platform's single-hyphen modifiers, emitted by
   the promotion sections (`btn btn-primary`) and the sf auth views
   (`btn button--full-width`, `btn button--secondary`). Map them onto the
   reference's own modifiers so they inherit the real button design. */
.btn.btn-primary,
.btn.button--primary {
  background-color: rgb(var(--color-primary-button-background));
  color: rgb(var(--color-primary-button-text));
  border-color: rgb(var(--color-primary-button-background));
}
.btn.btn-secondary,
.btn.button--secondary {
  background-color: rgb(var(--color-secondary-button-background));
  color: rgb(var(--color-secondary-button-text));
  border-color: rgb(var(--color-secondary-button-border));
}
.btn.button--full-width,
.btn.btn--full-width { width: 100%; }

/* sf's auth views (the login / register entry, setup's "Almost done", the password
   step, the phone-OTP and email-link steps) render their submit as a bare
   `btn button--full-width`: no colourway class, so nothing above colours it and it
   fell through to the browser's ButtonFace grey. Put it on the primary colourway
   (the one 2e gives /account/challenge's submit) with the reference's hover sweep —
   a <button> can draw the ::before an <input> can't, but the reference hangs it on
   `[class*=btn--]`, which `button--full-width` doesn't contain, so it is spelled out
   here. Scoped to .customer because the PDP / quick-view submits wear
   button--full-width too (beside btn--primary); the password step's
   `button--secondary` alternative keeps its own look. */
.customer .btn.button--full-width:not(.button--secondary) {
  background-color: rgb(var(--color-primary-button-background));
  color: rgb(var(--color-primary-button-text));
  border-color: rgb(var(--color-primary-button-background));
}
.customer .btn.button--full-width:not(.button--secondary)::before {
  content: "";
  position: absolute;
  width: 0;
  inset-block: calc(var(--button-border-thickness, 0.1rem) * -1);
  inset-inline-start: calc(var(--button-border-thickness, 0.1rem) * -1);
  z-index: -1;
  transition: width 0.3s;
  border-radius: var(--button-corner-radius, 0);
  background-color: rgb(var(--color-primary-button-text));
}
.customer .btn.button--full-width:not(.button--secondary):hover {
  color: rgb(var(--color-primary-button-background));
}
.customer .btn.button--full-width:not(.button--secondary):hover::before {
  width: calc(100% + var(--button-border-thickness, 0.1rem) * 2);
}
/* In flight: sf adds .btn--loading, vouchers.css blanks the label and customer.css
   draws its ring in ButtonText — black, lost on this fill. Ring in the label's colour,
   and drop the sweep for the whole request, as 26 does for the footer sign-up: a click
   made mid-hover otherwise leaves the white fill retracting over the white ring.
   Same (0,5,1) as the hover rule above, so it wins by coming later. */
.customer .btn.button--full-width.btn--loading:not(.button--secondary)::before {
  width: 0;
  transition: none;
}
.customer .btn.button--full-width.btn--loading:not(.button--secondary)::after {
  border-color: rgb(var(--color-primary-button-text));
  border-top-color: transparent;
}

/* Plain <button>/<a> that the platform styles as a button but tags only `.button`. */
.button:not(.btn) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.8rem;
  height: var(--button-height, 5rem);
  padding-inline: 2.4rem;
  border: 1px solid rgb(var(--color-primary-button-background));
  border-radius: var(--button-corner-radius, 100px);
  background-color: rgb(var(--color-primary-button-background));
  color: rgb(var(--color-primary-button-text));
  font-family: var(--font-button--family);
  font-weight: var(--font-button--weight);
  text-decoration: none;
  cursor: pointer;
}

/* --- 2b. The sf auth / login flow wrappers ---------------------------------
   `sf/resources/views/storefront/account/*` renders inside this layout using
   Timber-era wrapper classes that no modern theme defines. Without these the
   login/OTP pages render as unbounded full-bleed text. */
.section-width-control { max-width: 80rem; margin-inline: auto; padding-inline: var(--page-padding, 2rem); }
.section-width-control.small-width { max-width: 50rem; }
.wrapper { max-width: var(--page-width, 144rem); margin-inline: auto; padding-inline: var(--page-padding, 2rem); }
.large--five-twelfths { width: 100%; }

.divider {
  display: flex;
  align-items: center;
  gap: 1.2rem;
  margin-block: 2rem;
  color: rgb(var(--color-foreground) / 0.6);
}
.divider::before,
.divider::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background-color: rgb(var(--color-foreground) / 0.15);
}

.profile-list { display: flex; flex-direction: column; gap: 1.2rem; list-style: none; padding: 0; }
.profile-img,
.profile-avatar { width: 4.8rem; height: 4.8rem; border-radius: 50%; object-fit: cover; flex-shrink: 0; }

.otp-input {
  letter-spacing: 1.2rem;
  text-align: center;
  font-size: var(--font-size--h4, 2.4rem);
}

/* --- 2c. Layout-only custom elements ---------------------------------------
   <product-recommendations> is rendered by the platform and defined by no shipped
   script. An unknown element defaults to display:inline, collapsing its grid to
   zero width. */
product-recommendations,
recommended-products,
store-locator { display: block; }

/* --- 2d. Dawn float-label on base-copied forms ------------------------------
   `.field__label` is positioned by base.css, which this theme does not ship, so it
   renders as static text on top of the submit button. The inputs already carry a
   placeholder. */
.search-modal .field__label,
.customer .field__label { display: none; }

/* --- 2e. sf's /account/challenge --------------------------------------------
   The manual reCAPTCHA step after a low invisible score. sf renders it with
   template 'app' and a bare <form class="text-center"> straight inside <main>:
   there is no .customer wrapper, so customer.css's login column (the
   `.customer:not(.account, .order)` width, gutters and margins) never reaches it,
   and its submit is an `input.btn` with no colourway modifier, left with the grey
   fill of the base `input` rule. The form takes the login column; the submit takes
   the primary colourway 2a maps `btn-primary` onto (an <input> draws no ::before,
   so the hover swaps the colours instead of sweeping the fill in). */
#MainContent form[action$="challenge"] {
  container-type: inline-size;
  width: min(30.4rem, 100% - 3rem);
  margin: 6rem auto 9rem;
  text-align: center;
}
@media screen and (min-width: 750px) {
  #MainContent form[action$="challenge"] { width: min(44.8rem, 100% - 3rem); }
}
#MainContent form[action$="challenge"] h2 { font-size: var(--font-size--h1); line-height: var(--font-h1--line-height); }
#MainContent form[action$="challenge"] p { margin-block: 1.5rem; color: rgb(var(--color-foreground) / 0.75); }
/* the widget's own 304×78 box, held before its async script paints, so the button doesn't jump */
#MainContent form[action$="challenge"] .g-recaptcha { min-height: 78px; }
#MainContent form[action$="challenge"] > br { display: none; }
#MainContent form[action$="challenge"] .btn {
  width: 100%;
  margin-top: 1.5rem;
  background-color: rgb(var(--color-primary-button-background));
  color: rgb(var(--color-primary-button-text));
  border-color: rgb(var(--color-primary-button-background));
  appearance: none;
  -webkit-appearance: none;
}
#MainContent form[action$="challenge"] .btn:hover {
  background-color: rgb(var(--color-primary-button-text));
  color: rgb(var(--color-primary-button-background));
}
/* the widget is a fixed 304px iframe: step it down when the column is narrower (phones) */
@container (width < 304px) { #MainContent form[action$="challenge"] .g-recaptcha { scale: 0.9; } }
@container (width < 274px) { #MainContent form[action$="challenge"] .g-recaptcha { scale: 0.8; } }

/* --- 2f. Account cards ------------------------------------------------------
   The account pages' card outlines, nav dividers, order-table borders and panel
   tints are the base's own (customer.css / account-compat.css, now written
   `rgb(var(--color-foreground) / a)` — the reference's colour variables are space
   triplets, so the base's `rgba(var(--x), a)` was invalid and every one of them was
   dropped). They are square in the base; here they take the 1rem corner of the
   membership card they sit beside (the reference's own card radius). */
.customer .card-box,
.customer .flex-table { border-radius: 1rem; }
/* Profile page: the base pads the first column 3.2rem on the right for a second
   column that this page does not have — its cards stopped 32px short of the
   Authentication card below them. */
@media screen and (min-width: 990px) {
  .customer:is(.account, .order) > div:nth-of-type(2) > div:first-of-type:only-child { padding-right: 0; }
}


/* ============================================================================
   3. Theme-owned listing grid
   ============================================================================
   This reference is utility-CSS based (`.grid` + custom properties) and defines no
   reusable N-column grid family, so there is nothing to borrow for the surfaces
   that need one: search results, 404 featured products, /recommend related
   products. CSS-grid based, so an underfilled row never stretches. */
.ts-listing-grid {
  display: grid;
  gap: var(--grid-horizontal-spacing, 1.6rem);
  grid-template-columns: repeat(2, minmax(0, 1fr));
  list-style: none;
  margin: 0;
  padding: 0;
}
@media (min-width: 750px) {
  .ts-listing-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 990px) {
  .ts-listing-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
.ts-listing-grid--2-desktop { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 990px) {
  .ts-listing-grid--2-desktop { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.ts-listing-grid__item { min-width: 0; }


/* ============================================================================
   4. Utility-collision guard (REQUIRED — not cosmetic)
   ============================================================================
   The reference's layout is utility-CSS driven: elements carry `hidden lg:flex`
   and rely on SOURCE ORDER for the breakpoint variant to beat the base class.
   Storefront APPS inject their own global stylesheets AFTER the theme's, and two
   of them redefine the same generic names — the star-rating app ships a bare
   `.hidden { display: none }` and the marketplace app a bare `.flex`. Being later
   in the document, they beat every `.lg\:flex` / `.md\:block` in the theme, and
   the whole responsive layout silently collapses: on a store with the star-rating
   app installed the desktop header nav, the logo and every `lg:`-revealed element
   render display:none.

   Order can't be won (app CSS is always injected later), so the display utilities
   are re-asserted here at one level higher specificity (`html .x` = 0,1,1). That
   outranks any app's bare class without !important, so JS-driven inline display
   still wins as the reference intends. Emitted base-first then ascending
   breakpoints, mirroring the reference's own cascade.
   ------------------------------------------------------------------------- */

html .flex { display: flex; }
html .block { display: block; }
html .grid { display: grid; }
html .inline-block { display: inline-block; }
html .inline-flex { display: inline-flex; }
html .hidden { display: none; }
html .table-cell { display: table-cell; }
html .contents { display: contents; }

@media (min-width: 640px) {
  html .sm\:flex { display: flex; }
  html .sm\:block { display: block; }
  html .sm\:grid { display: grid; }
  html .sm\:inline-block { display: inline-block; }
  html .sm\:inline-flex { display: inline-flex; }
  html .sm\:hidden { display: none; }
  html .sm\:table-cell { display: table-cell; }
  html .sm\:contents { display: contents; }
}

@media (min-width: 768px) {
  html .md\:flex { display: flex; }
  html .md\:block { display: block; }
  html .md\:grid { display: grid; }
  html .md\:inline-block { display: inline-block; }
  html .md\:inline-flex { display: inline-flex; }
  html .md\:hidden { display: none; }
  html .md\:table-cell { display: table-cell; }
  html .md\:contents { display: contents; }
}

@media (min-width: 1024px) {
  html .lg\:flex { display: flex; }
  html .lg\:block { display: block; }
  html .lg\:grid { display: grid; }
  html .lg\:inline-block { display: inline-block; }
  html .lg\:inline-flex { display: inline-flex; }
  html .lg\:hidden { display: none; }
  html .lg\:table-cell { display: table-cell; }
  html .lg\:contents { display: contents; }
}

@media (min-width: 1200px) {
  html .xl\:flex { display: flex; }
  html .xl\:block { display: block; }
  html .xl\:grid { display: grid; }
  html .xl\:inline-block { display: inline-block; }
  html .xl\:inline-flex { display: inline-flex; }
  html .xl\:hidden { display: none; }
  html .xl\:table-cell { display: table-cell; }
  html .xl\:contents { display: contents; }
}

@media (min-width: 1440px) {
  html .xxl\:flex { display: flex; }
  html .xxl\:block { display: block; }
  html .xxl\:grid { display: grid; }
  html .xxl\:inline-block { display: inline-block; }
  html .xxl\:inline-flex { display: inline-flex; }
  html .xxl\:hidden { display: none; }
  html .xxl\:table-cell { display: table-cell; }
  html .xxl\:contents { display: contents; }
}


/* --- 2e. Base-theme button variant used by the store-locator template ---------
   templates/store-locator.liquid is copied functional from the base theme and uses
   `button--tertiary`, which base.css defines and this theme does not ship. */
.btn.button--tertiary,
.button--tertiary {
  background-color: transparent;
  color: rgb(var(--color-foreground));
  border: 1px solid rgb(var(--color-foreground) / 0.2);
}

/* ============================================================================
   5. Collection facets — snippets/collection-filters.liquid
   ============================================================================
   The reference's own filter bar was inert (its behaviour was the hosted
   platform's JS, its facet names that platform's vocabulary), so it was replaced
   by a snippet driven by collection.filters + /filtered_section_html. That snippet
   is theme-owned markup in a `ts-facets` namespace the reference has never heard
   of, so its styling has to live here — a copied snippet is only half a copy
   until its stylesheet comes with it.

   Written against THIS reference's design tokens (`--color-foreground` as space-
   separated RGB channels, `--button-corner-radius`, `--font-body--family` with a
   double dash). Do not paste another theme's facet CSS in: its custom-property
   names silently resolve to nothing here.
   ------------------------------------------------------------------------- */
.ts-facets {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1.6rem;
  padding: 1.6rem 0;
  margin-block-end: 2.4rem;
  border-block: 1px solid rgb(var(--color-foreground) / 0.12);
  font-family: var(--font-body--family);
}
.ts-facets__group { display: flex; flex-wrap: wrap; align-items: center; gap: 2.4rem; }
.ts-facets__open { display: none; }

.ts-facet { position: relative; }
.ts-facet__trigger {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  cursor: pointer;
  list-style: none;
  font-size: 1.4rem;
  font-weight: 500;
  color: rgb(var(--color-foreground) / 0.75);
}
.ts-facet__trigger::-webkit-details-marker { display: none; }
.ts-facet[open] .ts-facet__trigger { color: rgb(var(--color-foreground)); }
.ts-facet__caret { width: 1.4rem; height: 1.4rem; transition: transform var(--animation-duration--medium, 0.25s) ease; }
.ts-facet[open] .ts-facet__caret { transform: rotate(180deg); }
.ts-facet__count {
  min-width: 1.8rem;
  padding-inline: 0.5rem;
  border-radius: 999px;
  background: rgb(var(--color-foreground));
  color: rgb(var(--color-background));
  font-size: 1.1rem;
  line-height: 1.8rem;
  text-align: center;
}
/* desktop dropdown panel */
.ts-facet__panel {
  position: absolute;
  z-index: 30;
  top: calc(100% + 1.2rem);
  inset-inline-start: 0;
  min-width: 26rem;
  max-height: 34rem;
  overflow-y: auto;
  padding: 1.2rem 1.6rem;
  border: 1px solid rgb(var(--color-foreground) / 0.1);
  border-radius: 1.6rem;
  background: rgb(var(--color-background));
  box-shadow: 0 1.6rem 4rem rgb(0 0 0 / 0.1);
}
.ts-facet__option {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.7rem 0;
  cursor: pointer;
  font-size: 1.4rem;
  color: rgb(var(--color-foreground) / 0.8);
}
.ts-facet__option + .ts-facet__option { border-top: 1px solid rgb(var(--color-foreground) / 0.06); }
.ts-facet__option:hover { color: rgb(var(--color-foreground)); }
.ts-facet__checkbox {
  appearance: none;
  -webkit-appearance: none;
  flex: 0 0 auto;
  width: 1.8rem;
  height: 1.8rem;
  margin: 0;
  border: 1.5px solid rgb(var(--color-foreground) / 0.35);
  border-radius: 0.5rem;
  background: transparent;
  display: grid;
  place-items: center;
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}
.ts-facet__checkbox::after {
  content: "";
  width: 0.5rem;
  height: 0.9rem;
  margin-top: -0.2rem;
  border: solid rgb(var(--color-background));
  border-width: 0 2px 2px 0;
  transform: rotate(45deg) scale(0);
  transition: transform 0.15s ease;
}
.ts-facet__checkbox:checked { background: rgb(var(--color-foreground)); border-color: rgb(var(--color-foreground)); }
.ts-facet__checkbox:checked::after { transform: rotate(45deg) scale(1); }
.ts-facet__checkbox:focus-visible { outline: 2px solid rgb(var(--color-foreground)); outline-offset: 2px; }
.ts-facet__label { flex: 1 1 auto; }
.ts-facet__n { color: rgb(var(--color-foreground) / 0.45); font-size: 1.2rem; font-variant-numeric: tabular-nums; }
/* price: two labelled fields on one row */
.ts-facet__panel--price { min-width: 30rem; }
.ts-facet__range { display: flex; align-items: flex-end; gap: 1rem; padding: 0.4rem 0; }
.ts-facet__price { display: flex; flex-direction: column; gap: 0.5rem; flex: 1 1 0; min-width: 0; font-size: 1.2rem; color: rgb(var(--color-foreground) / 0.6); }
.ts-facet__price-label { letter-spacing: 0.04em; text-transform: uppercase; }
.ts-facet__price input {
  width: 100%;
  min-width: 0;
  height: 4.2rem;
  padding: 0 1.2rem;
  border: 1px solid rgb(var(--color-foreground) / 0.2);
  border-radius: 999px;
  background: transparent;
  color: rgb(var(--color-foreground));
  font: inherit;
  font-size: 1.4rem;
  appearance: textfield;
  -moz-appearance: textfield;
}
.ts-facet__price input::-webkit-outer-spin-button,
.ts-facet__price input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.ts-facet__price input:focus { outline: 0; border-color: rgb(var(--color-foreground)); }
.ts-facet__dash { padding-bottom: 1.3rem; color: rgb(var(--color-foreground) / 0.4); }
.ts-facets__sort { display: flex; align-items: center; gap: 0.8rem; font-size: 1.4rem; }
.ts-facets__sort select {
  padding: 0.6rem 2.4rem 0.6rem 1rem;
  border: 1px solid rgb(var(--color-foreground) / 0.2);
  border-radius: var(--button-corner-radius, 6rem);
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
.ts-facets__clear {
  border: 0;
  background: none;
  padding: 0;
  color: rgb(var(--color-foreground) / 0.6);
  font: inherit;
  text-decoration: underline;
  cursor: pointer;
}
.ts-facets__clear:hover { color: rgb(var(--color-foreground)); }
.ts-facets__active { display: flex; flex-wrap: wrap; gap: 0.8rem; width: 100%; }
.ts-facets__pill {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.4rem 1.2rem;
  border: 1px solid rgb(var(--color-foreground) / 0.2);
  border-radius: 999px;
  background: transparent;
  color: inherit;
  font-size: 1.3rem;
  cursor: pointer;
}
/* ---- phones: a Filter button + Sort on one row; the facet group lives in a
   left drawer (the reference's own filters-drawer dialog vocabulary) ---- */
.ts-facets__drawer { display: contents; }
.ts-facets__dialog { width: min(40rem, 92vw); }
.ts-facets__dialog[open] { display: flex; flex-direction: column; overflow: hidden; }
.ts-facets__drawer-header {
  flex: 0 0 auto;
  padding: 2rem 2.4rem;
  border-bottom: 1px solid rgb(var(--color-border) / var(--color-border-alpha, 0.1));
}
.ts-facets__drawer-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 0.4rem 2.4rem 2.4rem; scroll-snap-type: none; }
.ts-facets__drawer-foot {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  gap: 1.2rem;
  padding: 1.6rem 2.4rem 2.4rem;
  border-top: 1px solid rgb(var(--color-border) / var(--color-border-alpha, 0.1));
}
.ts-facets__drawer-foot .ts-facets__apply { width: 100%; }
.ts-facets__drawer-foot .ts-facets__drawer-clear { align-self: center; }
@media (max-width: 749px) {
  .ts-facets { gap: 1.2rem; padding: 1.2rem 0; flex-wrap: nowrap; }
  .ts-facets--has-filters .ts-facets__open {
    display: inline-flex;
    align-items: center;
    gap: 0.8rem;
    --button-height: 4rem;
  }
  .ts-facets__open .icon { width: 1.6rem; height: 1.6rem; }
  .ts-facets__open-count { background: rgb(var(--color-background)); color: rgb(var(--color-foreground)); }
  .ts-facets__sort-label { display: none; }
  .ts-facets__sort select { max-width: 16rem; }
  /* inside the drawer: every facet is an open block, options are pill chips */
  .ts-facets--drawer .ts-facets__group { display: block; }
  .ts-facets--drawer .ts-facet { border-bottom: 1px solid rgb(var(--color-foreground) / 0.08); padding: 1.6rem 0; }
  .ts-facets--drawer .ts-facet__trigger { font-size: 1.3rem; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: rgb(var(--color-foreground)); pointer-events: none; }
  .ts-facets--drawer .ts-facet__trigger .ts-facet__caret { display: none; }
  .ts-facets--drawer .ts-facet__panel { position: static; min-width: 0; max-height: none; box-shadow: none; border: 0; border-radius: 0; padding: 1.2rem 0 0; display: flex; flex-wrap: wrap; gap: 0.8rem; }
  .ts-facets--drawer .ts-facet__panel--price { display: block; }
  .ts-facets--drawer .ts-facet__option {
    padding: 0.7rem 1.4rem;
    border: 1px solid rgb(var(--color-foreground) / 0.2);
    border-radius: 999px;
    font-size: 1.3rem;
    color: rgb(var(--color-foreground));
    gap: 0.6rem;
  }
  .ts-facets--drawer .ts-facet__option + .ts-facet__option { border-top: 1px solid rgb(var(--color-foreground) / 0.2); }
  .ts-facets--drawer .ts-facet__option:has(:checked) { background: rgb(var(--color-foreground)); color: rgb(var(--color-background)); border-color: rgb(var(--color-foreground)); }
  .ts-facets--drawer .ts-facet__option .ts-facet__checkbox { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
  .ts-facets--drawer .ts-facet__option .ts-facet__n { color: inherit; opacity: 0.6; }
  .ts-facets--drawer .ts-facet__range { padding: 0; }
  .ts-facets--drawer .ts-facets__group .ts-facets__clear { display: none; } /* the drawer footer carries its own */
  .ts-facets--drawer .ts-facet--toggle { padding: 1.2rem 0; }
  /* the drawer's reset stays in view; it only dims while nothing is applied */
  .ts-facets__drawer-clear[disabled] { opacity: 0.4; text-decoration: none; cursor: default; }
}
/* product cards: the add button is white at EVERY width (the phone circle and the
   desktop hover pill alike), so it vanishes on a white product shot. The reference
   already draws a 1px border on it — in white; recolour it to the button's own
   text colour. A real border sits outside the padding box, so the hover fill
   (.btn::before) never covers it. */
.product-card .quick-add--combine .quick-add__button {
  border-color: currentColor;
}

/* ============================================================================
   6. Contracts that theme-authored markup inherits from the lifted CSS
   ============================================================================
   Belt-and-braces for the two reference contracts init.js satisfies at runtime, so
   the page is still correct with JS off or before the driver boots. */

/* The reveal contract: `.media__image` is an opacity:0 initial state waiting for
   `.loaded.in-view` / `.is-revealed`, added only for <img is="responsive-image">.
   Without JS, images the theme renders would be permanently invisible. */
html.no-js .image-zoom-reveal .media__image,
html.no-js .media-zoom-reveal .media__image,
html[ts-anim-off] .image-zoom-reveal .media__image:not([is]),
html[ts-anim-off] .media-zoom-reveal .media__image:not([is]) {
  opacity: 1 !important;
  transform: none !important;
}

/* The quantity stepper's icon must not swallow the click — global.js reads the
   pressed control's name to decide up vs down. */
.quantity-selector .quantity-button > * { pointer-events: none; }

/* OptionSelectors' fieldset is a bare <fieldset>; give it the reference's option-row
   rhythm once init.js has tagged it .variant-option. */
.variant-option.single-option-selector { border: 0; padding: 0; margin-block: 1.6rem; }
.variant-option__list { display: flex; flex-wrap: wrap; gap: 0.8rem; margin: 0; padding: 0; list-style: none; }
.variant-option__item { display: block; }

/* Sale price + compare-at price. The reference renders the pair as two children of
   `.price-container`, spaced by its gap (--price-gap-x: 0.4rem, 1.2rem on the PDP).
   This theme's markup wraps the pair in `.price__sale` so global.js can toggle it,
   and the base theme's component-price.css (loaded after reference.css) makes that
   wrapper a flex row of its own — no gap, and a flex row drops the white space
   between the two spans, so the prices touched. Give the wrapper the container's
   gap; 0.8rem off the PDP is the base theme's own spacing (`.price dd`), which
   snippets/price (quick view) keeps — the child combinator leaves its dl alone. */
.price--on-sale > .price__sale {
  align-items: center;
  column-gap: var(--price-gap-x, 0.8rem);
}

/* The reference's .collection__wrapper is a two-column grid whose first column is a
   29.5rem sidebar reserved for ITS facet panel, with children placed by named areas
   (toolbar / remove-buttons / filtering / main-collection-grid). That panel is deleted
   here — its behaviour was the hosted platform's — and the theme's replacement is a
   horizontal row, so collapse the sidebar column and claim the toolbar area. Without
   this the whole product grid sits in column 2 with an empty 29.5rem gutter beside it. */
.ts-facets { grid-area: toolbar; }
/* Unconditional (not :has()-gated): this theme never ships the reference's facet
   sidebar, so the reserved column is always dead space. Marked !important because the
   reference sets the two-column template from several selectors, including one keyed on
   results-list[show-filtering=true]. */
.collection__wrapper,
results-list .collection__wrapper,
results-list[show-filtering="true"]:not(.collection-empty) .collection__wrapper {
  grid-template-columns: 1fr !important;
  grid-template-areas:
    "toolbar"
    "remove-buttons"
    "main-collection-grid" !important;
}
/* The snippet's sort wrapper is .ts-facets__select; style it here and suppress any
   caret pseudo the reference attaches to generic select wrappers — the native select
   already draws one, and two carets stack. */
.ts-facets__select { position: relative; display: inline-flex; }
.ts-facets__select::after,
.ts-facets__sort::after { content: none !important; }
.ts-facets__select select {
  padding: 0.6rem 1.2rem;
  border: 1px solid rgb(var(--color-foreground) / 0.2);
  border-radius: var(--button-corner-radius, 6rem);
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
.ts-facets__sort-label { white-space: nowrap; }

/* ============================================================================
   7. Utilities the base-copied templates need
   ============================================================================
   404 / search / list-collections / blog / article are copied functional from the
   base theme and lean on a handful of base.css utilities. base.css is never shipped
   by a transplant, so each of these has to exist here — written against THIS
   reference's tokens, never pasted from another theme's stylesheet. */
.ts-listing-grid--1,
.ts-listing-grid--1 > * { grid-column: 1 / -1; }

.spaced-section { padding-block: clamp(4.8rem, calc(7.558vw - 0.997rem), 10rem); }
.element-margin { margin-block-start: 2.4rem; }
.rte-width { max-width: var(--page-width-narrow, 88rem); margin-inline: auto; }
.title--primary { font-family: var(--font-heading--family); }
.caption-with-letter-spacing {
  font-size: 1.2rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgb(var(--color-foreground) / 0.7);
}
.circle-divider::after {
  content: "\00b7";
  margin-inline: 0.8rem;
  color: rgb(var(--color-foreground) / 0.5);
}
.circle-divider:last-child::after { content: none; }
.media--landscape { aspect-ratio: 4 / 3; overflow: hidden; }
.media--landscape img { width: 100%; height: 100%; object-fit: cover; }

/* list-collections card */
.collection-list { display: block; }
.card__media { display: block; overflow: hidden; border-radius: var(--media-radius, 2rem); }
.card__media img { width: 100%; height: 100%; object-fit: cover; }
.card__heading { margin-block-start: 1.2rem; font-family: var(--font-heading--family); }

/* odd utilities the copies reference (the search/404 pages no longer use the
   base .field form — they include snippets/search-form, see §31) */
.large-up-hide { display: initial; }
@media (min-width: 990px) { .large-up-hide { display: none; } }
.top-shadow { box-shadow: inset 0 1px 0 rgb(var(--color-foreground) / 0.08); }
.footer__payment { display: flex; flex-wrap: wrap; gap: 0.8rem; align-items: center; }
.localization-form { display: inline-flex; align-items: center; gap: 0.8rem; }
.content-for-layout { display: block; }
.header__menu { display: flex; }
.mega-menu__item { display: block; }
.price-item { display: inline-block; }
.product-details { display: block; }
.product-description { margin-block-start: 2.4rem; }
.quantity-selector .quantity-minus { inset-inline-start: 0; }

/* Cart's featured-collection block is base-copied and leans on a few more base.css
   utilities; the grid itself now uses this theme's own listing grid. */
.page-width-desktop { max-width: var(--page-width, 144rem); margin-inline: auto; }
@media (max-width: 989px) { .page-width-desktop { padding-inline: var(--page-padding, 2rem); } }
.page-margin { margin-inline: auto; }
.slider-mobile-gutter { margin-inline: 0; }
.title-wrapper--self-padded-tablet-down { padding-inline: 0; }
@media (max-width: 989px) { .title-wrapper--self-padded-tablet-down { padding-inline: var(--page-padding, 2rem); } }
.underlined-link { text-decoration: underline; text-underline-offset: 0.3rem; }
.medium-hide { display: initial; }
@media (min-width: 750px) and (max-width: 989px) { .medium-hide { display: none; } }

/* base.css's motion-reduce hook: base markup tags links/images with it so a
   reduced-motion visitor gets no transition. base.css is never shipped, so define it. */
@media (prefers-reduced-motion: reduce) {
  .motion-reduce { transition: none !important; animation: none !important; }
}


/* ============================================================================
   8. Utility collision, escalated: a platform sheet with `!important`
   ============================================================================
   §4 raises the breakpoint display utilities above app stylesheets that redefine a
   bare `.hidden`. That is enough while the app rule is normal-weight — but the
   platform's own css/vouchers.css, which sections/main-cart.liquid loads, declares

       .hidden { display: none !important }

   and an !important declaration cannot be outranked by specificity alone from a
   normal one. It is loaded by the SECTION, i.e. later than transplant.css, so source
   order does not help either. The result was a cart page whose header nav and logo
   were invisible while every other page rendered them — `hidden lg:flex` is the
   reference's standard responsive idiom, so this hits the whole chrome.

   Fix: match on BOTH classes (specificity 0,2,1) and mark it important, so it
   outranks `.hidden`'s important declaration on exactly the elements that opted into
   a breakpoint override — and nothing else. Deliberately narrow: a blanket
   !important on every utility would also override the JS-driven inline display the
   reference's own components rely on.
   ------------------------------------------------------------------------- */
@media (min-width: 640px) {
  html .hidden.sm\:flex { display: flex !important; }
  html .hidden.sm\:block { display: block !important; }
  html .hidden.sm\:grid { display: grid !important; }
  html .hidden.sm\:inline-block { display: inline-block !important; }
  html .hidden.sm\:inline-flex { display: inline-flex !important; }
  html .hidden.sm\:table-cell { display: table-cell !important; }
  html .hidden.sm\:contents { display: contents !important; }
}

@media (min-width: 768px) {
  html .hidden.md\:flex { display: flex !important; }
  html .hidden.md\:block { display: block !important; }
  html .hidden.md\:grid { display: grid !important; }
  html .hidden.md\:inline-block { display: inline-block !important; }
  html .hidden.md\:inline-flex { display: inline-flex !important; }
  html .hidden.md\:table-cell { display: table-cell !important; }
  html .hidden.md\:contents { display: contents !important; }
}

@media (min-width: 1024px) {
  html .hidden.lg\:flex { display: flex !important; }
  html .hidden.lg\:block { display: block !important; }
  html .hidden.lg\:grid { display: grid !important; }
  html .hidden.lg\:inline-block { display: inline-block !important; }
  html .hidden.lg\:inline-flex { display: inline-flex !important; }
  html .hidden.lg\:table-cell { display: table-cell !important; }
  html .hidden.lg\:contents { display: contents !important; }
}

@media (min-width: 1200px) {
  html .hidden.xl\:flex { display: flex !important; }
  html .hidden.xl\:block { display: block !important; }
  html .hidden.xl\:grid { display: grid !important; }
  html .hidden.xl\:inline-block { display: inline-block !important; }
  html .hidden.xl\:inline-flex { display: inline-flex !important; }
  html .hidden.xl\:table-cell { display: table-cell !important; }
  html .hidden.xl\:contents { display: contents !important; }
}

@media (min-width: 1440px) {
  html .hidden.xxl\:flex { display: flex !important; }
  html .hidden.xxl\:block { display: block !important; }
  html .hidden.xxl\:grid { display: grid !important; }
  html .hidden.xxl\:inline-block { display: inline-block !important; }
  html .hidden.xxl\:inline-flex { display: inline-flex !important; }
  html .hidden.xxl\:table-cell { display: table-cell !important; }
  html .hidden.xxl\:contents { display: contents !important; }
}


/* ============================================================================
   9. Loading states the platform JS toggles
   ============================================================================
   Three separate scripts flag "work in progress" by adding a class and expect the
   theme to have something to show for it. All three were silent no-ops: the request
   fired, the UI never acknowledged it, and the control read as dead.

   * product-form.js  -> `.loading` on the PDP / quickview submit button. This
     reference's own recipe is `.btn--loading` (hide the label, reveal
     `.loading-spinner`), so alias `.loading` onto it rather than inventing a look.
   * global.js AddToCartButton -> `.transparent` on the card's add control while its
     sibling `.loading-overlay` is un-hidden.
   * collection-filters.liquid -> `.ts-facets-loading` on #product-grid for the
     duration of the /filtered_section_html round trip.
   ------------------------------------------------------------------------- */
.btn.loading > *:not(.loading-spinner),
.btn--loading > *:not(.loading-spinner) { opacity: 0; }
.btn.loading .loading-spinner,
.btn--loading .loading-spinner { display: inline-flex !important; opacity: 1; }
.btn.loading,
.btn--loading { pointer-events: none; }
.btn .loading-spinner .spinner { width: 2rem; height: 2rem; animation: spinLoading 1s linear infinite; }
.btn .loading-spinner .spinner .path {
  stroke: currentColor;
  stroke-dasharray: 90 150;
  stroke-dashoffset: 0;
  stroke-linecap: round;
}

/* sf also flags PLAIN-TEXT buttons .btn--loading outside the account pages: promotion.js
   on an add-on promotion's "Add to Cart" (`btn btn-primary`, name=addPromoBtn), the
   product page's getPromotionList on #get-more-promotion (`btn btn--secondary`) and
   wishlist.js on "Add to wishlist" (a bare `btn`). There is no .loading-spinner to
   reveal, and the platform's vouchers.css blanks the label (`.btn.btn--loading { color:
   transparent !important }`), so the button went blank for the whole request. Draw the
   ring customer.css draws on the account pages. currentColor went with the label, so
   each colourway names its own label colour (a bare .btn inherits the text colour);
   :where() keeps these at (0,2,1), under customer.css's `.customer .btn.btn--loading::after`. */
.btn.btn--loading:where(:not(:has(.loading-spinner)))::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 2rem;
  height: 2rem;
  margin: -1rem 0 0 -1rem;
  border: 2px solid rgb(var(--color-foreground));
  border-top-color: transparent;
  border-radius: 50%;
  animation: spinLoading 0.8s linear infinite;
}
.btn.btn--loading:where(.btn--primary, .btn-primary, .button--primary):where(:not(:has(.loading-spinner)))::after {
  border-color: rgb(var(--color-primary-button-text));
  border-top-color: transparent;
}
.btn.btn--loading:where(.btn--secondary, .btn-secondary, .button--secondary):where(:not(:has(.loading-spinner)))::after {
  border-color: rgb(var(--color-secondary-button-text));
  border-top-color: transparent;
}
/* The click lands under the pointer and :hover outlives it until the pointer moves, so
   the hover fill (the primary button colour, the same as a secondary button's ring)
   stayed over the ring — and `btn--loading` itself matches the reference's
   `[class*=btn--]` fill, so a bare .btn grew one mid-request. Drop it for the request,
   as 26 does for the footer sign-up. (0,3,2) beats the reference's hover rule, (0,3,1). */
html .btn.btn--loading:not(:has(.loading-spinner))::before {
  width: 0;
  transition: none;
}

/* the card add control: global.js hides the label with .transparent and reveals the
   sibling overlay by removing .hidden from it */
.transparent { opacity: 0 !important; }
.loading-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
.loading-overlay.hidden { display: none; }
/* The base spinners (66-unit viewBox, stroke-width 4-6) rendered as a spinning black
   diamond: reference.css's `svg *{vector-effect:non-scaling-stroke}` (meant for its
   icon strokes) made the ring 6 SCREEN px thick, which overflows a 20px svg and is
   clipped to its square. The reference exempts its own `.loading-spinner .path`;
   exempt every spinner ring (quick view, add buttons, cart drawer, cart page). */
.spinner .path { vector-effect: none; }
/* The svg fills its wrapper and each context sizes the wrapper — html-scoped
   because component-loading-overlay.css loads later (in the body) with
   `.loading-overlay__spinner{width:3rem}` at equal specificity. */
html .loading-overlay__spinner { width: 2rem; }
.loading-overlay__spinner .spinner {
  display: block;
  width: 100%;
  height: auto;
  animation: spinLoading 1s linear infinite;
}
.loading-overlay__spinner .spinner .path {
  stroke: currentColor;
  stroke-dasharray: 90 150;
  stroke-linecap: round;
}

/* <add-to-cart-button> (product card, Hot Items, Product Bundle): the button and its
   overlay share one grid cell, so the spinner sits exactly on the button whatever
   the host's positioning (absolute on desktop cards, static on phones, in flow
   elsewhere) — the absolute overlay used to fall back to the base's 3rem corner box
   at the button's left edge. The button keeps its fill and only its content hides
   (fully transparent, the card's white pill vanished and a white spinner landed on
   the photo); the spinner takes the button's text colour. No clicks mid-request. */
html add-to-cart-button { display: grid; }
add-to-cart-button > .addToClassList,
add-to-cart-button > .loading-overlay { grid-area: 1 / 1; }
add-to-cart-button > .loading-overlay {
  position: relative;
  inset: auto;
  width: auto;
  z-index: 1;
  color: rgb(var(--color-primary-button-text));
}
add-to-cart-button .addToClassList.transparent {
  opacity: 1 !important;
  pointer-events: none;
}
add-to-cart-button .addToClassList.transparent > * { opacity: 0; }
/* A button that is loading is not a hover target, but it is still hovered: a tap
   leaves :hover stuck on a touch screen, and on desktop the pointer is on the button
   it has just clicked. The reference's hover recipes sweep a fill in the button's TEXT
   colour across it and flip the text colour — under a spinner drawn in that same
   text colour. On phones the card's round button turned into a black disc with a
   black ring on it; in the card's options sheet the ring was white on white. The
   recipes differ per button variant and out-rank any one selector, hence !important. */
add-to-cart-button .addToClassList.transparent::before {
  width: 0 !important;
  transition: none !important;
}
/* ...and the round card button draws its outline in currentColor */
add-to-cart-button .btn--primary.addToClassList.transparent {
  color: rgb(var(--color-primary-button-text)) !important;
}
/* the desktop card pill only shows while the image is hovered — hold it (and its
   spinner) in view until the request finishes, even if the pointer moves away */
.product-card--standard .product-card__media .quick-add:has(.transparent) {
  opacity: 1;
  transform: translateY(0);
}

/* The Wishlist app prints its heart through the card's `collection/product_top` slot,
   which sits in the text column — and its own stylesheet only floats the heart inside
   the wrappers of the themes it was written for (.card-information__wrapper,
   .product_grid-item), so here it stood in the flow as a row of its own above the
   product name. Float it over the image's top right corner, opposite the badge rail and
   at the rail's inset. <product-card> is the positioned ancestor and the image is its
   first row, so the card's top right IS the image's. z-index: over the link that covers
   the whole card, and over the badges. */
product-card .wishlist-icon-btn {
  position: absolute;
  top: 1rem;
  right: 1rem;
  z-index: 2;
  margin: 0;
}

/* the collection filter round trip */
.ts-facets-loading {
  opacity: 0.45;
  pointer-events: none;
  transition: opacity 0.2s ease;
}
.ts-facets-loading::after {
  content: "";
  position: absolute;
  inset-inline-start: 50%;
  margin-block-start: 4rem;
  width: 2.4rem;
  height: 2.4rem;
  border: 2px solid rgb(var(--color-foreground) / 0.2);
  border-top-color: rgb(var(--color-foreground));
  border-radius: 50%;
  animation: spinLoading 0.8s linear infinite;
}

/* Quickview: the base's sticky action bar targets `.button` and hardcodes a light
   background; this theme's submit is `.btn` and the palette is token-driven. */
.quickview-sticky-actions .btn { position: relative; margin: 0; }
.quickview-sticky-actions {
  background: rgb(var(--color-background));
  box-shadow: 0 0 30px 25px rgb(var(--color-background));
}
.product-quickview__content-wrapper { font-family: var(--font-body--family); }

/* cart-count-bubble is the PLATFORM's hook, not a look: cart-notification.js finds it
   by class to show/hide the count after every add. The visual comes from the
   reference's own .cart-count, so this only has to exist and not fight it. */
.cart-count-bubble { pointer-events: none; }
/* (was `position: relative`, which overrode the reference's .cart-count--absolute by
   load order and turned the badge into a full-size inline red blob beside the icon) */

/* ============================================================================
   10. The base quantity stepper, mapped onto the reference's
   ============================================================================
   snippets/cart-item.liquid is a mandated verbatim base copy, so it keeps base's
   `quantity` / `quantity__button` / `quantity__input` vocabulary — styled by
   base.css, which a transplant never ships. The PDP was re-skinned to the
   reference's own `quantity-selector` classes; the cart cannot be (it must stay
   diffable against the base), so alias the base names onto the same look here.
   Without this the cart renders a raw vertical number spinner with two unstyled
   buttons. ------------------------------------------------------------------- */
.quantity {
  position: relative;
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  width: var(--quantity-selector-max-width, 11.5rem);
  max-width: 100%;
}
.quantity .quantity__input {
  appearance: textfield;
  -moz-appearance: textfield;
  width: 100%;
  height: var(--quantity-selector-height, 5rem);
  min-width: 0;
  text-align: center;
  color: rgb(var(--color-foreground));
  background-color: rgb(var(--color-background));
  border: 0.1rem solid rgb(var(--color-foreground) / 10%);
  border-radius: var(--button-corner-radius, 6rem);
  font: inherit;
  padding-inline: 2.8rem;
}
.quantity .quantity__input::-webkit-outer-spin-button,
.quantity .quantity__input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.quantity .quantity__button {
  position: absolute;
  top: 0;
  bottom: 0;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding-inline: 1.4rem;
  border: 0;
  background: none;
  color: inherit;
  cursor: pointer;
}
.quantity .quantity__button > * { pointer-events: none; }
.quantity .quantity__button[name="minus"] { inset-inline-start: 0; }
.quantity .quantity__button[name="plus"] { inset-inline-end: 0; }

/* The `.button:not(.btn)` fallback above is a FILLED primary pill, which is wrong for
   the icon-only variants base markup uses for cart row actions (remove / edit). They
   were rendering as solid black blobs with an invisible icon. Same specificity, later
   in the file, so these win. */
.button.button--tertiary,
.button.button--secondary {
  background: transparent;
  color: rgb(var(--color-foreground));
  border-color: rgb(var(--color-foreground) / 0.2);
  padding-inline: 1.2rem;
  height: auto;
  min-height: 4rem;
}
.button.button--tertiary { border: 0; }
.button.button--tertiary svg,
.button.button--secondary svg { width: 1.8rem; height: 1.8rem; }

/* §8's !important guard wins over the REFERENCE's own component rules too — the
   escalation is class-shape-based, so an element like the PDP thumb rail
   (`hidden md:block` + a component rule `display: grid`) gets forced to block.
   Re-assert the component display at the same strength for the known cases.
   If a future element hits this, prefer adding it here over weakening §8 — the
   app-sheet collision §8 solves is store-wide, this is per-component. */
@media (min-width: 768px) {
  html .media-gallery__grid-thumbnails.hidden.md\:block { display: grid !important; }
}

/* ============================================================================
   11. Header logo cap
   ============================================================================
   The reference sizes its own logo with --logo-width + width/height attrs, but a
   MERCHANT-uploaded logo (or the de-brand placeholder swap) renders at intrinsic
   size and can blow the chrome up to its full height. Cap it; width follows.
   48px keeps the header at the reference's ~80px total (16px block padding each
   side); the earlier 90px cap made the desktop header read bulky. */
.header__logo img,
.header__logo svg,
.header__logo--image img {
  /* --logo-max-height comes from the header section's logo_height setting */
  max-height: var(--logo-max-height, 48px);
  width: auto;
  object-fit: contain;
}

/* ============================================================================
   12. App swiper-bundle vs the reference's grid-mode gallery
   ============================================================================
   The sales-pop app loads swiper-bundle.min.css from a CDN AFTER every theme
   stylesheet, re-asserting `.swiper { overflow: hidden }`. The reference's desktop
   gallery (@media min-768) sets the SAME container to overflow:visible via
   .media-gallery--has-grid-thumbs — losing that turns thumbnail clicks into a
   scroll INSIDE the clipped container: scrollIntoView spends its scroll there, the
   left column never moves, and the page scroll makes the right-side content appear
   to scroll instead. 0-2-1 beats the app's bare class regardless of load order. */
@media (min-width: 768px) {
  html .swiper.media-gallery--has-grid-thumbs { overflow: visible; }
}

/* ============================================================================
   13. Sticky-column inversion on the PDP
   ============================================================================
   The reference marks BOTH columns sticky and relies on its demo data to pick the
   winner: its gallery is the TALL column (sticky never engages) and the short
   details box pins. On a real store the platform inflates the details column
   (promotions, reviews, in-store stock, long descriptions), the gallery becomes
   the SHORT column and PINS — and a pinned element cannot be scrolled into view,
   so the thumbnail rail's scrollIntoView moves the page while the images stay
   frozen ("clicking a thumb scrolls the right side"). Un-stick the gallery in
   desktop grid mode: whichever column is taller, thumb clicks then always scroll
   the images, and only the details box pins — the reference's visible behavior.
   Mobile (carousel mode) is untouched. */
@media (min-width: 768px) {
  html .media-gallery--grid.sticky-element { position: static; }
}


/* ============================================================================
   14. Utility collision, round 3: the marketplace app poisons `.flex`
   ============================================================================
   apps.easystore.blue/assets/css/marketplace/main.css ships

       .flex { justify-content: space-between }

   — a bare rule on the most common utility class in the theme, loaded after every
   theme stylesheet. Every flex group without a LATER justify rule spreads.

   The first attempt here was `html .flex { justify-content: normal }` plus the
   justify-* utilities re-asserted. That fixed the header and BROKE the homepage:
   at 0-1-1 it also outranks the reference's own COMPONENT rules
   (`.slideshow__content { justify-content: center }` and friends, all 0-1-0), so
   every component that also carries `.flex` lost its alignment. Those component
   rules were already losing to the app sheet before the guard — so the correct
   scope is: neutralise the poisoned property, then re-assert BOTH the utilities
   AND every component justify-content rule the reference ships, all one level up.
   Generated from reference.css, so it stays in sync by construction.
   ------------------------------------------------------------------------- */
html .flex { justify-content: normal; }
html .layout-panel-flex--row { justify-content: var(--horizontal-alignment); }
html .layout-panel-flex--column { justify-content: var(--vertical-alignment); }
html .content-overlay-style { justify-content: var(--h-alignment); }
html .deferred-media__poster-icon, html .video-placeholder-wrapper__poster-icon { justify-content: center; }
html .deferred-media__loading { justify-content: center; }
html .video-interaction__control { justify-content: center; }
html .cart-count { justify-content: center; }
html .divider { justify-content: var(--divider-justify-content); }
html .btn { justify-content: center; }
html .btn__icon { justify-content: space-between; }
html .btn__icon-icon { justify-content: center; }
html .btn--icon-only { justify-content: center; }
html .badge { justify-content: center; }
html .accordion__summary { justify-content: space-between; }
html .accordion--icon-solid .accordion__summary-arrow { justify-content: center; }
html .carousel__controls--relative { justify-content: space-between; }
html .pagination-item { justify-content: space-between; }
html .product-card .quick-add--combine .quick-add__button { justify-content: center; }
html .variant-picker__form { justify-content: flex-start; }
html .variant-option__button-label--has-swatch { justify-content: center; }
html .hidden-swatches__count { justify-content: flex-start; }
html .pagination__list .pagination__item { justify-content: center; }
html .justify-start { justify-content: flex-start; }
html .justify-end { justify-content: flex-end; }
html .justify-start, html .justify-left { justify-content: flex-start; }
html .justify-center { justify-content: center; }
html .justify-end, html .justify-right { justify-content: flex-end; }
html .justify-between { justify-content: space-between; }
html .justify-around { justify-content: space-around; }
html .promotion-alert__wrapper { justify-content: space-between; }
html .image-comparison__button-range { justify-content: center; }
html .products-bundle__number { justify-content: center; }
html .testimonial-slider__slide-avatar--placeholder { justify-content: center; }
html .testimonial-slider__controls-wrapper { justify-content: flex-end; }
html .testimonials-split-slider--center .testimonial-split__content, html .testimonials-split-slider--center .author-info { justify-content: center; }
html .dialog__close { justify-content: center; }
html .cart-items__price .cart-items__price-item { justify-content: flex-end; }
html .cart-items__price .cart-items__discounts { justify-content: flex-end; }
html .cart-items__table-row--gift-wrap .cart-items__price-total { justify-content: flex-end; }
html .pswp__top-bar { justify-content: center; }
html .filters-drawer__header .dialog__close { justify-content: center; }
html .sorting-filter--drawer .facets__checkbox-wrapper { justify-content: center; }
html .sorting-filter--drawer .facets__checkbox { justify-content: center; }
html .video-testimonial .video-interaction__controls-inner { justify-content: center; }
@media (max-width:767.98px) {
  html .mobile-column { justify-content: var(--vertical-alignment-mobile); }
}
@media (max-width:767.98px) {
  html .content-overlay-style { justify-content: var(--h-alignment-mobile); }
}
@media (max-width:767.98px) {
  html .btn--full-mobile.btn--icon-solid { justify-content: space-between; }
}
@media (min-width:640px) {
  html .sm\:justify-start, html .sm\:justify-left { justify-content: flex-start; }
  html .sm\:justify-center { justify-content: center; }
  html .sm\:justify-end, html .sm\:justify-right { justify-content: flex-end; }
  html .sm\:justify-between { justify-content: space-between; }
  html .sm\:justify-around { justify-content: space-around; }
}
@media (min-width:768px) {
  html .md\:justify-start, html .md\:justify-left { justify-content: flex-start; }
  html .md\:justify-center { justify-content: center; }
  html .md\:justify-end, html .md\:justify-right { justify-content: flex-end; }
  html .md\:justify-between { justify-content: space-between; }
  html .md\:justify-around { justify-content: space-around; }
}
@media (min-width:1024px) {
  html .lg\:justify-start, html .lg\:justify-left { justify-content: flex-start; }
  html .lg\:justify-center { justify-content: center; }
  html .lg\:justify-end, html .lg\:justify-right { justify-content: flex-end; }
  html .lg\:justify-between { justify-content: space-between; }
  html .lg\:justify-around { justify-content: space-around; }
}
@media (min-width:1200px) {
  html .xl\:justify-start, html .xl\:justify-left { justify-content: flex-start; }
  html .xl\:justify-center { justify-content: center; }
  html .xl\:justify-end, html .xl\:justify-right { justify-content: flex-end; }
  html .xl\:justify-between { justify-content: space-between; }
  html .xl\:justify-around { justify-content: space-around; }
}
@media (min-width:1440px) {
  html .xxl\:justify-start, html .xxl\:justify-left { justify-content: flex-start; }
  html .xxl\:justify-center { justify-content: center; }
  html .xxl\:justify-end, html .xxl\:justify-right { justify-content: flex-end; }
  html .xxl\:justify-between { justify-content: space-between; }
  html .xxl\:justify-around { justify-content: space-around; }
}
@media (min-width:1600px) {
  html .products-highlight__products { justify-content: space-around; }
}
@media (max-width:1023.98px) {
  html .testimonial-slider__controls-wrapper { justify-content: flex-start; }
}
@media (min-width:768px) {
  html .testimonials-split-slider--media-wide .testimonial-split__product { justify-content: flex-start; }
}
@media (min-width:768px) {
  html .press-slider--center .carousel__controls, html .press-slider--center .author-info__bio { justify-content: center; }
}
@media (max-width:767.98px) {
  html .press-slider--mobile-center .carousel__controls, html .press-slider--mobile-center .author-info__bio { justify-content: center; }
}
@media (max-width:1023.98px) {
  html .cart-drawer__close:not(.cart-drawer__close--addon) { justify-content: center; }
}


/* ============================================================================
   15. Utility collision, round 4: the app's swiper-bundle.min.css
   ============================================================================
   The sales-pop app loads Swiper's stock stylesheet from a CDN, AFTER every theme
   stylesheet. It re-asserts Swiper's own class names — which this reference restyles
   heavily — so the vendor defaults win everywhere a carousel renders:

     .swiper-button-next/prev  -> position:absolute; 27x44 (Swiper's default sizing)
       => the homepage slider's arrows overlapped each other with a 4px-wide icon:
          they looked like a stray glyph and were near-impossible to hit.
     .swiper-pagination-progressbar -> position:absolute; left:0; width:100%
       => "Explore Categories"' progress bar stretched the full viewport instead of
          sitting 140px wide inside the controls row.

   §12 patched ONE of these by hand (.swiper overflow). That was treating symptoms:
   every Swiper class the reference restyles is exposed the same way. This block is
   GENERATED from reference.css — every rule whose selector mentions a swiper-* class,
   re-asserted at html-scope (0-1-1) so it outranks the app's bare classes without
   !important and without touching Swiper's genuine base behaviour.
   ------------------------------------------------------------------------- */
html .carousel .swiper { container-type:inline-size; }
html .carousel--visible .swiper { overflow:visible; }
html .carousel__button.swiper-button-disabled { opacity:.5!important;cursor:not-allowed; }
html .carousel__pagination--progressbar .swiper-pagination-progressbar-fill { position:absolute;left:0;top:0;width:100%;height:100%;transform:scale(0);transform-origin:var(--transform-origin-start) top;background-color:rgb(var(--color-foreground)); }
html .swiper-pagination.swiper-pagination-hidden { opacity:0; }
html .swiper-pagination.swiper-pagination-disabled { display:none!important; }
html .swiper-pagination-bullets { display:inline-flex;flex-wrap:wrap;align-items:center; }
html .swiper-pagination-bullet { position:relative;width:var(--swiper-pagination-bullet-width, var(--swiper-pagination-bullet-size, .8rem));height:var(--swiper-pagination-bullet-height, var(--swiper-pagination-bullet-size, .8rem));border-radius:var(--swiper-pagination-bullet-border-radius, 50%);background:var(--swiper-pagination-bullet-inactive-color, rgb(var(--color-foreground)));opacity:var(--swiper-pagination-bullet-inactive-opacity, .35);transition:all var(--animation-duration--medium) var(--animation-easing);transition-property:background-color,opacity; }
html .swiper-pagination-bullet:only-child { display:none!important; }
html .swiper-pagination-bullet:before { content:"";position:absolute;top:-.6rem;right:-.6rem;bottom:-.6rem;left:-.6rem; }
html .swiper-pagination-bullet-active { opacity:var(--swiper-pagination-bullet-opacity, 1);background:var(--swiper-pagination-color, rgb(var(--color-foreground))); }
html .swiper-pagination-clickable .swiper-pagination-bullet { cursor:pointer; }
html .swiper-pagination-horizontal .swiper-pagination-bullet { margin-inline:var(--swiper-pagination-bullet-horizontal-gap, .6rem); }
html .swiper-pagination-lock { display:none; }
html .slideshow .swiper-slide { height:auto; }
html .flex-carousel .swiper-slide-custom { flex-shrink:0;height:100%;position:relative;transition-property:transform; }
html .flex-carousel .swiper-backface-hidden .swiper-slide-custom { transform:translateZ(0);backface-visibility:hidden; }
html .flex-carousel--columns .swiper-slide-custom { width:100%; }
html .lookbook-carousel .swiper-slide { height:auto; }
html .testimonials-split-slider .swiper { max-width:100%; }
html .press-slider__thumbnails .swiper { container-type:unset; }
html .press-slider__thumbnails .swiper-slide { width:fit-content; }
@media (max-width:767.98px) {
  html .carousel--mobile-visible .swiper { overflow:visible; }
}
@media (hover:hover) {
  html .carousel:hover .carousel__controls--absolute .carousel__button.swiper-button-disabled { opacity:.5!important; }
}
@media (max-width:767.98px) {
  html .swipe-mobile.swiper { padding-inline:var(--page-padding);margin-inline:calc(var(--page-padding) * -1); }
}
@media (max-width:767.98px) {
  html .products-bundle .swiper-wrapper { flex-direction:column; }
  html .products-bundle .swiper-slide+.swiper-slide { margin-top:2rem;padding-top:2rem;border-top:.1rem solid rgb(var(--color-border)/var(--color-border-alpha)); }
}
@media (max-width:767.98px) {
  html .flex-carousel .swipe-mobile .swiper-slide-custom { height:auto; }
}
@media (min-width:768px) {
  html .testimonials .carousel--view-auto .swiper-slide { width:var(--slides-width, 64.36%); }
}
@media (min-width:768px) {
  html .section--icon-boxes .carousel .swiper-slide { height:auto; }
}
@media (min-width:1200px) {
  html .section--icon-boxes .swiper-initialized .swiper-slide:not(.swiper-slide-fully-visible) { opacity:.4; }
}

/* --- 15b. Neutralise the vendor GEOMETRY the app injects --------------------
   Re-asserting the reference's swiper-* rules (above) is not enough, because the
   collision is not rule-vs-rule: swiper-bundle.min.css sets properties the reference
   never declares at all, so there is nothing of the reference's to re-assert.

     .swiper-button-next/prev   -> position:absolute; top:50%; margin-top:-22px;
                                   width:calc(size/44*27); height:size
       The reference's `.carousel__controls--group .carousel__button` only sets
       display/cursor/position and lets the ICON size the button. With the vendor
       width applied the two arrows became 27x44, overlapped each other, and squashed
       their 16px icon to 4px — the "arrow looks weird / click does nothing" report.

     .swiper-pagination-progressbar -> position:absolute; left:0; top:0; width:100%
       The reference sizes it with --carousel-progressbar-width inside the controls
       row; the vendor default stretched it across the whole viewport.

   Reset exactly those properties, on elements carrying BOTH the vendor class and the
   reference's own, so nothing else is touched. --absolute carousels keep their
   positioning: that comes from the reference's own more-specific rules. */
html .carousel__button.swiper-button-next,
html .carousel__button.swiper-button-prev {
  width: auto;
  height: auto;
  margin-top: 0;
  /* the vendor sheet also paints the buttons Swiper-blue via
     color: var(--swiper-navigation-color, var(--swiper-theme-color, #007aff));
     the reference's arrows take the surrounding color scheme — inherit it */
  color: inherit;
}
/* .btn-styled arrows (btn--outline btn--icon-only) are sized by the reference's
   own --button-width/--button-height vars — width:auto above would shrink them
   to the bare 16px icon; re-assert the reference's .btn sizing instead. */
html .carousel__button.btn.swiper-button-next,
html .carousel__button.btn.swiper-button-prev {
  width: var(--button-width, auto);
  height: var(--button-height, 5rem);
}
html .carousel__controls--group .carousel__button.swiper-button-next,
html .carousel__controls--group .carousel__button.swiper-button-prev {
  position: relative;
  top: auto;
  /* the vendor also sets left:10px / right:10px — on a RELATIVE box those still
     shift the two buttons into each other (prev +10px, next -10px = overlap) */
  left: auto;
  right: auto;
  /* the controls row is flex; without this the button stretches to the row height
     (44px from --button-height) instead of hugging its 16px icon like the reference */
  align-self: center;
}
html .carousel__button .icon,
html .carousel__button svg {
  width: var(--icon-size, 1.6rem);
  height: var(--icon-size, 1.6rem);
  flex: none;
}
html .carousel__pagination.swiper-pagination-progressbar {
  position: relative;
  inset-inline-start: auto;
  top: auto;
  width: var(--carousel-progressbar-width, 25rem);
  height: var(--carousel-progressbar-height, 0.2rem);
}

/* --- 15c. Neutralise the vendor ICON GLYPH the app injects ------------------
   swiper-bundle.min.css also paints Swiper's default font arrows into every
   navigation button via `.swiper-button-prev/next::after { content:"prev"/"next";
   font-family:swiper-icons; font-size:44px }`. The reference never loads that
   stylesheet (its Swiper CSS is compiled into the theme CSS, ::after stays at its
   `content:none` default), so each button shows ONE svg icon there — but here the
   glyph renders on top of the theme's own 16px svg: the "arrows are doubled"
   report. It is also what defeated 15b's `width:auto`: the button grew back to
   ~39x44 to hug the 44px glyph. 0-2-2 beats the vendor's 0-1-1. */
html .carousel__button.swiper-button-next::after,
html .carousel__button.swiper-button-prev::after {
  content: none;
}

/* --- 15d. Promo carousel: a disabled arrow is hidden, not dimmed -----------
   This carousel does not loop, so Swiper disables "previous" on the first slide
   and "next" on the last. The reference only dims a disabled arrow to 50%, and
   its `.btn--outline:hover` fill still plays on it — the arrow kept reading as
   clickable. Hide it instead. `visibility`, not `display`: the arrow keeps its
   slot in the controls row, so the one that is left never slides under the
   cursor when the other disappears. <flex-carousel> is this section's alone. */
html .flex-carousel .carousel__button.swiper-button-disabled {
  visibility: hidden;
}

/* ============================================================================
   16. Pinned-hero stacking must live on the PLATFORM wrapper, not .hopscotch-section
   ============================================================================
   The reference pins its hero behind the sections that follow with
   `.hopscotch-section:has(.section--allow-overlap){position:sticky;top:0;z-index:-1}`.
   On the reference platform that wrapper is the section's OUTERMOST element (a direct child of
   <main>, which is itself a stacking context via `relative z-1`), so the
   negative-z subtree has no in-flow ancestor box above it and stays clickable.
   This platform inserts an EXTRA `<div id="easystore-section-<key>">` wrapper
   around the theme's own wrapper (Storefront Controller renders content_for_index
   that way). An in-flow transparent ancestor box hit-tests ABOVE a negative
   z-index subtree, so every click on the hero — slideshow arrows included — landed
   on the platform wrapper: "arrows can't be clicked". Promote the reference's rule
   one wrapper up (the platform div takes the sticky/negative-z role, exactly the
   position the reference's own section wrapper holds) and flatten the inner copy. */
[id^="easystore-section-"]:has(.section--allow-overlap) {
  position: sticky;
  top: 0;
  z-index: var(--layer-lowest, -1);
}
html .hopscotch-section:has(.section--allow-overlap) {
  position: static;
  z-index: auto;
}
/* The platform ALSO wraps all home sections in a #content_for_index div between
   <main> and the section wrappers. Every in-flow ancestor that is NOT itself a
   stacking context paints its (transparent, still hit-testable) box above a
   negative-z descendant — so each one must become a context root, exactly like
   <main> is on the reference. */
#content_for_index {
  position: relative;
  z-index: 1;
}

/* ============================================================================
   17. Nav overflow -> burger drawer (paired with init.js navOverflowGuard)
   ============================================================================
   When the top-level menu cannot fit the header row on one line, the guard adds
   .nav-overflow to the SECTION root (not the <header> — the drawer dialog is a
   SIBLING of the header inside the section root, so the hook must sit above
   both). The inline nav collapses and the burger + drawer (mobile chrome, bound
   to the same contents.main-menu data) takes over at desktop widths.
   0-2-0 beats the lg: utilities' 0-1-0 regardless of order. */
@media (min-width: 1024px) {
  /* the nav wrap is `hidden lg:flex`, which §14's utility guard re-asserts with
     !important — match it per-component at the same strength (never weaken the
     store-wide guard) */
  html .header-section.nav-overflow .header__navigation.hidden.lg\:flex {
    display: none !important;
  }
  .header-section.nav-overflow .header__icons--left,
  .header-section.nav-overflow .menu-drawer-button {
    display: flex;
  }
  /* the desktop grid template is "nav logo icons" — the burger's own
     icons-left area only exists in the mobile template, so it would be
     auto-placed into an implicit second row. Put it in the freed nav area. */
  .header-section.nav-overflow .header__icons--left {
    grid-area: nav;
    justify-self: start;
    align-self: center;
  }
  /* at desktop the header already shows the logo — the drawer opened from the
     overflow burger doesn't need a second one; keep the close button right-aligned */
  .menu-drawer .menu-drawer__logo {
    display: none;
  }
  .menu-drawer .dialog__header {
    justify-content: flex-end;
  }
}

/* ============================================================================
   18. Collection-cards carousel: arrows live in the controls row, bottom-right
   ============================================================================
   The reference places this section's prev/next as part of the controls row
   under the cards (counter | progressbar | buttons), right-aligned — not as
   absolute mid-side overlays. The lifted CSS absolutely positions
   .carousel__button inside a --relative controls context; flatten it here so
   the flex row does the layout.
   The arrows stay POSITIONED (relative, never static): they are .btn--outline,
   whose hover fill is an absolutely positioned ::before. On a static button that
   fill anchored to the whole carousel and swept a black ellipse over the cards. */
html .collection-tabs .carousel__controls--relative .carousel__button.swiper-button-prev,
html .collection-tabs .carousel__controls--relative .carousel__button.swiper-button-next {
  position: relative;
  top: auto;
  bottom: auto;
  left: auto;
  right: auto;
  transform: none;
}

/* ============================================================================
   19. Header right icon cluster breathing room
   ============================================================================
   The nav's first link sits ~20px from the header edge but the last icon
   BUTTON box ends ~10px from it — the cart icon reads as glued to the rounded
   corner. Give the cluster the same visual inset as the left side. */
@media (min-width: 1024px) {
  html .header__icons--right {
    padding-inline-end: 0.8rem;
  }
}

/* ============================================================================
   20. Cart drawer (snippets/cart-drawer.liquid + assets/cart-drawer.js)
   ============================================================================
   The drawer reuses snippets/cart-template.liquid — the SAME markup the /cart
   page renders (and what /new_cart returns server-rendered), so the page-scale
   layout needs a compact re-flow inside the 480px drawer, mirroring the
   reference's cart drawer: stacked item rows with 80px thumbs, totals +
   full-width checkout pinned at the end. */
.cart-drawer {
  width: min(48rem, 92vw);
}
/* The <dialog> itself used to be the scroll container, so the absolute loading
   overlay scrolled away with the list, and at rest it sat over the header — the
   close button was dead for the whole request. Make the dialog a fixed-height
   flex column that never scrolls: the header stays put, .cart-drawer__body owns
   the overlay, and only .cart-drawer__content scrolls. The overlay then covers
   exactly the visible content viewport, stays there while the list scrolls
   (the list cannot scroll under it either — the overlay's scroll chain has no
   scrollable ancestor), and never touches the close button. `[open]`-scoped so
   the UA's closed-dialog display:none still applies. */
.cart-drawer[open] {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.cart-drawer .cart-drawer__body {
  position: relative;
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}
.cart-drawer .cart-drawer__header {
  padding: 2rem 2.4rem;
  border-bottom: 1px solid rgb(var(--color-border) / var(--color-border-alpha, 0.1));
}
.cart-drawer .cart-drawer__content {
  /* no bottom inset: the sticky footer below carries it, so at rest and stuck it
     ends on the same line */
  padding: 0 2.4rem 0;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  scroll-snap-type: none; /* .v-scrollable's y-mandatory snap is for the reference's own lists */
  /* a column the cart form fills, so the footer can sit at the foot (below) */
  display: flex;
  flex-direction: column;
}
/* With a few items the totals + checkout sat right under the last line, leaving the
   bottom of the drawer empty. The form fills the content viewport and the footer
   takes the slack above it (flex-shrink 0 — nothing is squeezed). A long list used
   to flow the footer after the items, so the checkout button was off-screen until
   the whole list had been scrolled: it sticks to the foot of the scroller instead,
   over the list, on its own opaque background. The form is its containing block
   and ends with it, so it rides along until the list's last line is in view. */
.cart-drawer #cart-template,
.cart-drawer #cart-form {
  display: flex;
  flex-direction: column;
  flex: 1 0 auto;
}
.cart-drawer #main-cart-footer {
  margin-top: auto;
  position: sticky;
  bottom: 0;
  z-index: 2;
  background: rgb(var(--color-background));
  border-top: 1px solid rgb(var(--color-border) / var(--color-border-alpha, 0.1));
}
/* both wear .page-width, whose auto inline margins stop a flex item stretching —
   the line items collapsed to 0px wide. Pin them to the column. */
.cart-drawer #cart-form > cart-items,
.cart-drawer #main-cart-footer {
  width: 100%;
}
/* overlay fills .cart-drawer__body — the visible content viewport, which
   never scrolls — so the spinner stays centred on screen for any list length */
.cart-drawer .cart-drawer__loading {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgb(var(--color-background) / 0.6);
  z-index: 5;
}
.cart-drawer .cart-drawer__spinner {
  width: 4.8rem;
  height: 4.8rem;
}
.cart-drawer .cart-drawer__loading.hidden {
  display: none;
}
/* page chrome that has no place in the drawer */
.cart-drawer .title-wrapper-with-link,
.cart-drawer .cart-items thead {
  display: none;
}
.cart-drawer .page-width {
  padding-inline: 0;
}
/* stack each line item: thumb | details, price row below */
.cart-drawer .cart-items,
.cart-drawer .cart-items tbody {
  display: block;
  width: 100%;
}
.cart-drawer .cart-item {
  display: grid;
  /* the /cart page's scale (80px thumb, paragraph-size name, a 50px stepper) made
     each line 172px tall on a 375px phone, the edit icon pushed onto a third row;
     drawer scale here, smaller again on phones below */
  grid-template-columns: 7.2rem 1fr;
  gap: 1.2rem;
  padding-block: 1.4rem;
  border-bottom: 1px solid rgb(var(--color-border) / var(--color-border-alpha, 0.1));
}
.cart-drawer .cart-item > td {
  display: block;
  padding: 0;
}
.cart-drawer .cart-item__media {
  grid-row: 1 / span 3;
}
.cart-drawer .cart-item__image {
  width: 7.2rem;
  height: auto;
  border-radius: 1rem;
}
.cart-drawer .cart-item__name {
  font-size: 1.4rem;
  line-height: 1.35;
  margin: 0;
}
.cart-drawer .cart-item .product-option,
.cart-drawer .cart-item__price-wrapper,
.cart-drawer .cart-item .price {
  font-size: 1.4rem;
}
/* the stepper and the two action icons on one row */
.cart-drawer .cart-item .quantity {
  --quantity-selector-height: 3.6rem;
  --quantity-selector-max-width: 9.6rem;
}
.cart-drawer .cart-item .quantity .quantity__input { padding-inline: 2.4rem; }
.cart-drawer .cart-item .quantity .quantity__button { padding-inline: 1rem; }
.cart-drawer .cart-item__quantity {
  align-items: center;
  gap: 0.2rem;
  margin-top: 0.6rem;
}
.cart-drawer .cart-action-icon .button {
  min-width: 3.6rem;
  min-height: 3.6rem;
  margin: 0;
}
/* The drawer is phone-wide at every viewport, so it keeps the phone row throughout:
   price beside the name (the phone totals block), the stepper row under it — not the
   /cart table's desktop placement, which put the price on a third row of its own and
   kept the line 161px tall. `html` + !important: the §21 visibility utilities are
   `html .small-hide { display: none !important }` at (0,1,1). */
.cart-drawer .cart-item__details { grid-column: 2 / 4; }
.cart-drawer .cart-item__quantity { grid-column: 2 / 5; flex-wrap: wrap; padding-left: 0; }
html .cart-drawer .cart-item__totals.small-hide { display: none !important; }
html .cart-drawer .cart-item__totals.large-up-hide {
  display: flex !important;
  align-items: flex-start;
  justify-content: flex-end;
}
@media screen and (max-width: 749px) {
  .cart-drawer .cart-item {
    grid-template-columns: 6.4rem 1fr;
    gap: 1rem;
    padding-block: 1.2rem;
  }
  .cart-drawer .cart-item__image { width: 6.4rem; }
}
/* totals + checkout — the bottom inset is the content's, moved here (sticky footer) */
.cart-drawer .cart__footer {
  display: block;
  padding-block: 1.6rem 2.4rem;
}
/* ...not on an empty cart: the base's `.is-empty .cart__footer { display: none }` ties
   with the rule above and loses on order, so Checkout showed under "your cart is empty" */
.cart-drawer .is-empty .cart__footer {
  display: none;
}
.cart-drawer .cart__footer .button,
.cart-drawer .cart__footer .btn,
.cart-drawer button[name="checkout"] {
  width: 100%;
}
/* the page-scale cart css pins several boxes to fixed widths (500px) —
   cap everything inside the drawer to its own width */
.cart-drawer .cart-item__details,
.cart-drawer .cart-item__totals,
.cart-drawer .cart-item__price-wrapper,
.cart-drawer .cart-item__quantity,
.cart-drawer .cart__blocks,
.cart-drawer .price {
  width: auto;
  max-width: 100%;
  min-width: 0;
}
.cart-drawer .cart__footer .totals {
  justify-content: space-between;
  width: 100%;
}
/* The line spinner cart.js shows in place of the line price (quantity change,
   remove) kept the /cart table's offsets: component-cart-items.css pushes it
   `padding-top: 4.5rem` down a totals cell that is as tall as the whole row
   there. Here the row is re-stacked and that cell is one price line, so the
   spinner landed under the row's bottom border. Cover the cell instead and sit
   where the price it replaces was — right edge, centred on its line. */
.cart-drawer .cart-item .loading-overlay {
  inset: 0;
  width: auto;
  padding: 0;
  justify-content: flex-end;
}
/* voucher modal: the base float-label renders as stray text beside the input */
.vouchers-modal .field__label {
  display: none;
}
/* voucher modal: platform markup with square corners (5px on its phone bottom sheet),
   opened beside a cart drawer on the theme's dialog radius. Same token, shaped like the
   reference's own dialogs: every corner while it floats, the top two once the platform
   docks it to the bottom edge (750px) — the reference's --bottom-dialog-radius, spelled
   out because that variable exists only on <dialog> and this is a <div>. The platform's
   `.vouchers-modal` rules load after this file (cart-drawer.liquid), hence the element
   prefix: (0,1,1). */
vouchers-modal .vouchers-modal {
  border-radius: var(--dialog-radius, 0);
}
@media screen and (max-width: 750px) {
  vouchers-modal .vouchers-modal {
    border-radius: min(2rem, var(--dialog-radius, 0)) min(2rem, var(--dialog-radius, 0)) 0 0;
  }
}

/* ============================================================================
   21. Cart drawer polish round 2
   ============================================================================ */
/* The base theme's responsive visibility utilities were never shipped — the
   cart line items render BOTH their mobile price block (.medium-hide
   .large-up-hide) and the desktop one (.small-hide), so every price shows
   twice (cart page and drawer alike). Dawn's breakpoints: small < 750,
   medium 750–989, large ≥ 990. */
@media screen and (max-width: 749px) {
  html .small-hide {
    display: none !important;
  }
}
@media screen and (min-width: 750px) and (max-width: 989px) {
  html .medium-hide {
    display: none !important;
  }
}
@media screen and (min-width: 990px) {
  html .large-up-hide {
    display: none !important;
  }
}

/* Voucher modal: the discount-code field arrives in base-theme vocabulary with
   no shipped styles — dress it in this theme's own input/button design. */
.vouchers-modal discount-input .field {
  display: flex;
  align-items: stretch;
  gap: 0.8rem;
}
.vouchers-modal discount-input .field__input {
  flex: 1 1 auto;
  min-width: 0;
  height: var(--button-height, 4.8rem);
  padding: 0 1.8rem;
  border: 1px solid rgb(var(--color-border) / 0.25);
  border-radius: var(--button-corner-radius, 10rem);
  background: rgb(var(--color-background));
  color: rgb(var(--color-foreground));
  font-size: var(--font-size--paragraph);
}
.vouchers-modal discount-input .field__input:focus {
  outline: none;
  border-color: rgb(var(--color-foreground) / 0.6);
}
.vouchers-modal discount-input .button {
  flex: 0 0 auto;
  height: var(--button-height, 4.8rem);
  padding-inline: 2.4rem;
  border: none;
  border-radius: var(--button-corner-radius, 10rem);
  background: rgb(var(--color-primary-button-background));
  color: rgb(var(--color-primary-button-text));
  font-family: var(--font-button--family);
  font-weight: var(--font-button--weight);
  cursor: pointer;
}
.vouchers-modal discount-input .form__message {
  margin-top: 0.8rem;
  color: rgb(var(--color-error, 220 53 69));
  font-size: var(--font-size--paragraph-sm, 1.3rem);
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

/* ============================================================================
   22. Header overlay only when the OVERLAY HERO is genuinely first
   ============================================================================
   The reference floats the header over the first section with
   `body:has(.hopscotch-section:first-child [data-header-overlay]) .header
   { position: absolute; … }`. On this platform every .hopscotch-section is
   :first-child OF ITS OWN `easystore-section-<key>` wrapper, so that selector
   matches whenever the hero exists ANYWHERE on the page — sort any other
   section first and the absolute header covers its content. Re-scope: restore
   the normal in-flow header unless the FIRST content wrapper holds the hero.
   (Same broken-:first-child family as the pinned-hero stacking recipe, §16.) */
body:has(.hopscotch-section [data-header-overlay]):not(:has(#content_for_index > :first-child [data-header-overlay])) .header {
  position: relative;
  transform: none;
  will-change: auto;
  padding-inline: 0;
}
body:has(.hopscotch-section [data-header-overlay]):not(:has(#content_for_index > :first-child [data-header-overlay])) .header__top {
  padding-inline: var(--page-padding, 2rem);
}
body:has(.hopscotch-section [data-header-overlay]):not(:has(#content_for_index > :first-child [data-header-overlay])) .header__background {
  margin: 0;
  padding-inline: 0;
}
body:has(.hopscotch-section [data-header-overlay]):not(:has(#content_for_index > :first-child [data-header-overlay])) .header-sticky {
  position: sticky;
  top: 0;
}
/* The floating header is a rounded card: its background and the mega menu hanging
   off it carry a 1rem radius, and the menu's content sits on the card's 3rem inset.
   Back in flow the header spans the page, so all three return to the plain header's
   (what a page with no hero at all gets). Same breakpoint as the reference's block. */
@media (min-width: 768px) {
  body:has(.hopscotch-section [data-header-overlay]):not(:has(#content_for_index > :first-child [data-header-overlay])) .header__background-inner {
    border-radius: 0;
  }
  body:has(.hopscotch-section [data-header-overlay]):not(:has(#content_for_index > :first-child [data-header-overlay])) .mega-menu {
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
  }
  body:has(.hopscotch-section [data-header-overlay]):not(:has(#content_for_index > :first-child [data-header-overlay])) .mega-menu__wrapper {
    padding-inline: var(--page-padding);
  }
}

/* ============================================================================
   23. Cart drawer checkout + overlay-card contrast
   ============================================================================ */
.cart-drawer .cart__checkout-button {
  /* the page css caps it at max-width 36rem — lift that inside the drawer */
  width: 100%;
  max-width: none;
}
/* cart.js flags the pressed submit `.loading` while the form posts to /cart (Checkout,
   Express checkout — drawer and cart page alike). They are base-theme `.button`s: no
   spinner child and no loading recipe anywhere in the theme, so the click looked dead
   until the checkout page arrived. The §9 ring, in the button's own text colour; the
   label goes transparent so the button keeps its width. */
.cart__ctas .button.loading {
  position: relative;
  color: transparent !important;
  pointer-events: none;
}
.cart__ctas .button.loading::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 2rem;
  height: 2rem;
  margin: -1rem 0 0 -1rem;
  border: 2px solid rgb(var(--color-primary-button-text));
  border-top-color: transparent;
  border-radius: 50%;
  animation: spinLoading 0.8s linear infinite;
}
/* the express checkout button is an outline button: its ring is its text colour */
.cart__ctas .button--secondary.loading::after {
  border-color: rgb(var(--color-foreground));
  border-top-color: transparent;
}

/* scrolling-cards / scrolling-cards-layered / promo-carousel: one shared
   contrast-plate material for everything laid over imagery — the overlay strip
   renders light text directly on (often white) product shots, and the
   image-card label badge shipped its own faint gradient material. Standardise. */
.product-overlay-card__content,
.image-card__label.badge {
  background: rgb(0 0 0 / 0.28);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}
.image-card__label.badge::before {
  /* the reference's gradient ring around the badge — off the standard plate */
  content: none;
}
.product-overlay-card__content {
  border-radius: 1.4rem;
  padding: 1rem 1.2rem;
}

/* §24 Product Bundle: hotspot accuracy. The editor's position picker measures
   top/left % against the FULL banner image, so the preview must render the
   full image too. The reference stretched the banner to the content column's
   height (products-bundle--image-height-full, removed from the section) and
   its .media__image is absolute + object-fit:cover — a crop that silently
   shifts every hotspot. Let the image flow at its natural ratio and define
   the banner box itself, so % positions map 1:1 to the picker. */
.products-bundle__banner .media {
  aspect-ratio: auto;
  height: auto;
}
.products-bundle__banner .media__image {
  position: static !important;
  width: 100%;
  height: auto;
  object-fit: unset;
}

/* ============================================================================
   25. PDP details: price scale, quantity row, review button, lower sections
   ============================================================================ */
/* Price: the reference wraps the PDP price in `price-container h3`, so it renders
   at heading-3 scale. The base theme's component-price.css pins `.price` to
   1.4rem, which shrank it to card-price size. Re-assert the reference scale for
   the PDP price block only (cards keep the small size); the compare-at price
   keeps the reference's own h5 rule. */
.product-information--main .product-price-block .price {
  font-size: var(--font-size--h3);
  line-height: var(--font-h3--line-height);
}
/* Quantity: label + stepper share one flex row. The reference's
   .variant-option__label carries a 1.2rem block-end margin meant for the stacked
   variant chips, which parked the label at the top of the 50px row. */
.product-form__quantity {
  align-items: center;
  gap: 1.6rem;
  flex-wrap: nowrap;
}
.product-form__quantity .variant-option__label {
  margin-block-end: 0;
}
/* Review app's "Write review" button: the reference resets every <svg> to
   display:block, so the app's inline pencil icon broke onto its own line above
   the label. Lay the button out as an inline-flex row (html-scoped to outrank
   the app's own stylesheet, which loads after ours). */
html .write-review-button {
  display: inline-flex !important; /* the app's own sheet pins inline-block */
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
}
html .write-review-button svg {
  display: inline-block;
  flex: 0 0 auto;
  vertical-align: middle;
}
/* Below the product grid + related products: their wrappers wore `section-padding`,
   base theme vocabulary that nothing here defines, so "You may also like" sat flush
   against what came before it and the footer. The first wrapper now holds only the
   platform's full-width app hook (the description moved into the details column,
   §38), so its padding is spent only when an app printed something into it. */
.product-description-section > .page-width:not(:empty) {
  padding-block: 0 clamp(2.4rem, calc(2.326vw + 0.616rem), 4rem);
}
.product-related-section {
  padding-block: clamp(4rem, calc(5.814vw - 0.459rem), 8rem);
}
.product-related-section .product-recommendations__heading {
  margin-block: 0 2.4rem;
}
/* One page gutter, not three: the section's own .page-width supplies it, and both
   the injection target (`.wrapper`, §2) and the platform snippet's
   <product-recommendations class="page-width"> added it again — 48px a side on a
   phone, against 16px for everything above it. */
.product-related-section .wrapper,
.product-related-section .product-recommendations.page-width { padding-inline: 0; }

/* ============================================================================
   26. Footer: platform payment marks
   ============================================================================
   The lifted marks are 38x24 svgs; the platform's payment_type_image_url marks
   share a 300x100 artboard, so pin them to the same row height. */
.footer__payment .footer__payment-mark {
  height: 2.4rem;
  width: auto;
  display: block;
}
/* Sign-up button mid-submit (account-auth-form.liquid adds .btn--loading). The click
   lands while the pointer is over the button, and its hover fill (::before) is WHITE —
   the same colour as the in-flight ring, which carries the RESTING text colour inline
   (vouchers.css blanks `.btn.btn--loading` text). The fill then slid away over 0.3s, and
   the POST usually navigates before that, so the ring never showed. Drop the fill for the
   whole request: ring on the button's resting colour from the first frame.
   (0,3,2) beats the reference's hover rule, (0,3,1). */
html .newsletter-form__button.btn.btn--loading::before {
  width: 0;
  transition: none;
}

/* ============================================================================
   27. Product Bundle: per-card add-to-cart + a swipeable product list
   ============================================================================
   The lifted cards carried the hosted platform's <form>+<select> and one "Add
   all to cart" button, both dead here. Each card now uses the platform's
   <add-to-cart-button> (sole variant = direct add, options = quick view, sold
   out = badge). The carousel used to be destroyed below 768px into a stacked
   column with dividers; it now runs on every viewport, so undo the reference's
   column rules and give it a prev/next row like the Collection Cards (§18). */
html .products-bundle .swiper-wrapper {
  flex-direction: row;
}
html .products-bundle .swiper-slide + .swiper-slide {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}
.products-bundle .swiper-slide {
  height: auto;
}
.products-bundle__actions {
  width: 100%;
  margin-top: 0.8rem;
}
.products-bundle__add .btn,
.products-bundle__actions > .btn {
  width: 100%;
  --button-height: 4rem;
}
/* Sold out stands in the add-to-cart button's slot, so it takes that button's box:
   the card's width, btn--small's height, the button's corner and label size. As
   a bare badge it was a 71x29 pill at the card's left edge beside 225x38 buttons.
   The colours stay the sold-out badge's own. */
.products-bundle__actions > .badge {
  display: flex;
  width: 100%;
  height: min(var(--button-height, 4rem), 3.8rem);
  padding-block: 0;
  border-radius: var(--button-corner-radius, 0);
  font-size: var(--font-size--paragraph);
}
.products-bundle__controls {
  display: flex;
  justify-content: flex-end;
  margin-top: 1.6rem;
}
.products-bundle__controls .carousel__controls-buttons {
  display: flex;
  gap: 0.8rem;
}
/* The arrows MUST stay positioned: the reference widens each one's hit area with
   `.carousel__controls--group .carousel__button::before{position:absolute;
   inset:-.4rem}`. When this rule said `position:static`, that pseudo-element
   anchored to the .swiper instead — an invisible box over every visible card that
   sent clicks on the products and their add buttons to "next". */
html .products-bundle__controls .carousel__button.swiper-button-prev,
html .products-bundle__controls .carousel__button.swiper-button-next {
  position: relative;
  top: auto;
  bottom: auto;
  left: auto;
  right: auto;
  transform: none;
}
/* The arrows wear the reference's outlined icon button (btn--outline btn--icon-only,
   as in Product Tabs) — the 16px bare icons were too small to hit. That ::before is
   the button's hover fill, and the hit-area rule above also lands on it: its
   inset:-.4rem would stretch the fill into an off-centre 44x50 ellipse. Keep the
   fill's own geometry (the 44px button needs no wider hit area). */
html .products-bundle__controls .carousel__button::before {
  inset-block: calc(var(--button-border-thickness, .1rem) * -1);
  inset-inline-start: calc(var(--button-border-thickness, .1rem) * -1);
  inset-inline-end: auto;
}

/* ============================================================================
   28. Collection sort select: one arrow. PDP option groups: one per row.
   ============================================================================
   The sort <select> kept the browser's native arrow (appearance:auto) while the
   facets snippet also draws its own .ts-facet__caret — two arrows. Hide the
   native one and overlay the theme caret inside the field. */
html .ts-facets__select {
  position: relative;
  display: inline-flex;
  align-items: center;
}
html .ts-facets__select select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  padding-right: 3.2rem;
  background-image: none;
}
html .ts-facets__select select::-ms-expand {
  display: none;
}
html .ts-facets__select .ts-facet__caret {
  position: absolute;
  right: 1.2rem;
  top: 50%;
  transform: translateY(-50%);
  width: 1.4rem;
  height: 1.4rem;
  pointer-events: none;
}
/* EasyStore.OptionSelectors emits one `selector-wrapper-<id>` div per option
   inside .variants-selector-wrapper, which the base css lays out as a WRAPPING
   FLEX ROW — so two short option groups (Size, Material) sat side by side with
   their chips running into each other. Stack the groups, one per row. */
html .variants-selector-wrapper.product-form__input {
  flex-direction: column;
  align-items: stretch;
}
html [class*="selector-wrapper-"] {
  width: 100%;
  flex: 0 0 100%;
}

/* ============================================================================
   29. PDP gallery: one-image scroll-snap slider + thumbnails (init.js §3c)
   ============================================================================
   Replaces the reference's <media-gallery> Swiper grid (stacked full-size images
   with a thumb rail) with popneutral's slider on request. §8b's thumb-rail
   guard, §12's overflow guard and §13's sticky un-pin (all .media-gallery
   selectors) are inert now — the class no longer renders on the PDP. Sticky
   comes from the reference's own .sticky-element / --sticky-element-top.

   The frame. Every slide used to be a 3:4 box its image covered: a square photo
   lost a quarter of its width, and at desktop widths the box (973px in a 730px
   column) stood taller than the window, so while the gallery was pinned its foot
   and the thumbnails under it were never on screen. Now:
   - the frame has ONE shape for the whole gallery, --gallery-ratio, printed by the
     section as `width / height` (the first image's own shape, or the merchant's
     pick), so swiping never moves the thumbnails or the page under it;
   - it is never taller than the window has room for: header, the section's top
     padding, the thumbnail row (--gallery-thumbs, when there is one) and a little
     air. Only its height gives way: the frame, and the thumbnail row under it,
     always span the whole media column, so the arrows, the magnifier and the
     first thumbnail sit on the column's own edges whatever the image's shape;
   - each image is laid out at its own shape (--r on the slide) as large as the
     frame allows, centred, with the rounded corners on the image itself. The
     frame paints nothing, so a narrower image floats on the page colour instead
     of sitting on grey bars. `.gallery--cover` is the old behaviour for merchants
     whose photos share one shape: fill the frame, crop the rest. */
.gallery {
  --gallery-radius: 2rem;
  --gallery-ratio: 3 / 4;
  --gallery-thumbs: 0rem;
  --gallery-max-h: max(32rem, calc(100vh - var(--header-height, 8rem) - 4rem - var(--gallery-thumbs) - 2rem));
}
/* The header is on screen from the first pixel of scroll, but its script marks the
   body header-pinned only once the page has moved a header's height (80px), and
   only that class lifts --sticky-element-top from 3rem to "under the header". In
   between, the gallery slid up beneath the header — the magnifier went with it —
   and was then pushed back down as .sticky-element animated `top`. Clear the
   header whenever it is showing: before the page has scrolled (.header-scrolled is
   not on its section yet) as well as while it is pinned (the reference's rule). */
body:has(.header-section.header-sticky:not(.header-scrolled)) .product-information__grid {
  --sticky-element-top: calc(var(--header-height) + var(--spacing-6));
}
/* phones: the browser bars come and go, size against the small viewport */
@supports (height: 100svh) {
  .gallery { --gallery-max-h: max(32rem, calc(100svh - var(--header-height, 8rem) - 4rem - var(--gallery-thumbs) - 2rem)); }
}
.gallery--thumbs { --gallery-thumbs: 7.6rem; } /* thumb 6.4 + its 1rem gap + 0.2 */
.gallery__main {
  position: relative;
  /* an explicit width, or the height cap is carried through the ratio and narrows
     the frame: the column's full width always, the ratio's height up to the cap */
  width: 100%;
  aspect-ratio: var(--gallery-ratio);
  max-height: var(--gallery-max-h);
  overflow: hidden;
  border-radius: var(--gallery-radius);
}
.gallery--cover .gallery__main { background: rgb(var(--color-background-secondary, var(--color-background))); }
.gallery__track {
  position: absolute; /* fills the frame: the slides size against it, not the other way round */
  inset: 0;
  display: flex;
  margin: 0;
  padding: 0;
  list-style: none;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  cursor: grab;
  outline: none;
}
.gallery__track::-webkit-scrollbar {
  display: none;
}
.gallery__track.is-dragging {
  scroll-snap-type: none;
  cursor: grabbing;
  user-select: none;
}
.gallery__track:focus-visible {
  outline: 2px solid rgb(var(--color-foreground));
  outline-offset: -2px;
}
.gallery__track:has(> .gallery__slide:only-child) {
  cursor: auto;
}
.gallery__slide {
  display: flex;
  flex: 0 0 100%;
  align-items: center;
  justify-content: center;
  min-width: 0;
  height: 100%;
  scroll-snap-align: start;
  scroll-snap-stop: always;
  container-type: size; /* the image below is sized in this slide's own cqw / cqh */
}
/* an image the platform has no pixel size for, or a browser without container
   units: still whole, only its corners stay square */
.gallery__slide img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  -webkit-user-drag: none;
}
@supports (width: 1cqw) {
  /* the largest box of the image's own shape that fits the slide */
  .gallery__slide--sized img {
    width: min(100cqw, calc(100cqh * var(--r)));
    height: auto;
    aspect-ratio: var(--r);
    border-radius: var(--gallery-radius);
  }
}
.gallery--cover .gallery__slide img {
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  object-fit: cover;
  border-radius: 0;
}
/* phones: a small "2 / 5" pill says there is more to swipe */
.gallery__count {
  position: absolute;
  right: 1.2rem;
  bottom: 1.2rem;
  z-index: 1;
  display: none;
  padding: 0.4rem 1rem;
  border-radius: 999px;
  background: rgb(255 255 255 / 0.85);
  color: #000;
  font-size: 1.2rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}
@media (max-width: 767px) {
  .gallery__count {
    display: block;
  }
}
/* desktop: prev / next fade in while the pointer is over the image; touch swipes */
.gallery__arrow.btn {
  position: absolute;
  top: 50%;
  left: auto;
  right: auto;
  translate: 0 -50%;
  transform: none;
  z-index: 2;
  display: none;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
  /* beside a narrower image the white disc sits on the page colour */
  box-shadow: 0 0.5rem 2rem rgb(var(--color-foreground) / 12%);
}
.gallery__arrow--prev.btn {
  left: 1.2rem;
}
.gallery__arrow--next.btn {
  right: 1.2rem;
}
@media (hover: hover) and (min-width: 768px) {
  .gallery__arrow.btn {
    display: inline-flex;
  }
  .gallery__main:hover .gallery__arrow,
  .gallery__main:focus-within .gallery__arrow {
    opacity: 1;
    pointer-events: auto;
  }
  .gallery__main:hover .gallery__arrow[disabled],
  .gallery__main:focus-within .gallery__arrow[disabled] {
    opacity: 0.35;
    pointer-events: none;
  }
}
/* thumbnails: a fixed small row under the image, scrolled by the driver */
.gallery__thumbs {
  position: relative;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 6.4rem;
  gap: 0.8rem;
  overflow-x: auto;
  margin-top: 1rem;
  padding-bottom: 0.2rem;
  scrollbar-width: none;
}
.gallery__thumbs::-webkit-scrollbar {
  display: none;
}
@media (max-width: 767px) {
  .gallery__thumbs {
    grid-auto-columns: 5.2rem;
    gap: 0.6rem;
    margin-top: 0.8rem;
  }
}
.thumb {
  display: block;
  padding: 0;
  margin: 0;
  border: 2px solid transparent;
  border-radius: 1rem;
  overflow: hidden;
  background: rgb(var(--color-background-secondary, var(--color-background)));
  cursor: pointer;
  transition: border-color 0.2s ease;
}
.thumb img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: cover;
}
.thumb:hover {
  border-color: rgb(var(--color-foreground) / 0.3);
}
.thumb.is-active,
.thumb.is-active:hover {
  border-color: rgb(var(--color-foreground));
}

/* Footer newsletter block placed anywhere but first: the divider it carries is
   the line between it and the columns below, so it is rendered only in first
   position; elsewhere the block gets its own breathing room instead. */
.footer__top-block--newsletter-inner {
  padding-block: clamp(2.4rem, calc(2.326vw + 0.616rem), 4rem);
}

/* ============================================================================
   30. Mega menu: rawjoy's arrangement — compact content-sized link columns on
   the left, optional promo cards on the right, roomier type and row pitch
   ============================================================================ */
@media (min-width: 1024px) {
  html .mega-menu__list {
    grid-template-columns: repeat(var(--menu-columns, 3), max-content);
    column-gap: 6.4rem;
    row-gap: 3.2rem;
    flex: 0 1 auto;
    align-content: start;
  }
  html .mega-menu__wrapper { justify-content: space-between; align-items: flex-start; }
  html .mega-menu__column { padding-inline: 0; }
  html .mega-menu__link--top { font-size: 1.9rem; font-weight: 600; line-height: 1.3; margin-bottom: 1.4rem; }
  html .mega-menu__link--spacer { visibility: hidden; }
  html .mega-menu__item-child li > a { font-size: 1.6rem; line-height: 1.5; padding-block: 0.9rem; }
  html .mega-menu__item-child li:last-child > a { padding-bottom: 0.9rem; }
}
.mega-menu__promotions--cards {
  --promotions-width: 46%;
  display: flex;
  gap: 2.4rem;
  min-width: 0;
}
.mega-promo {
  position: relative;
  display: block;
  flex: 1 1 0;
  min-width: 0;
  aspect-ratio: 1 / 1;
  border-radius: 2rem;
  overflow: hidden;
  background: rgb(var(--color-foreground) / 0.08);
  color: #fff;
  text-decoration: none;
}
.mega-promo__img { display: block; width: 100%; height: 100%; object-fit: cover; transition: transform 0.6s cubic-bezier(0.2, 0.8, 0.2, 1); }
.mega-promo:hover .mega-promo__img { transform: scale(1.04); }
.mega-promo__label { position: absolute; top: 1.6rem; left: 1.6rem; z-index: 1; }
.mega-promo__title {
  position: absolute;
  left: 2rem;
  right: 8rem;
  bottom: 2rem;
  z-index: 1;
  font-family: var(--font-heading--family);
  font-size: 2.4rem;
  font-weight: 600;
  line-height: 1.2;
  color: #fff;
  text-shadow: 0 1px 12px rgb(0 0 0 / 0.35);
}
.mega-promo__arrow {
  position: absolute;
  right: 2rem;
  bottom: 2rem;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 4.8rem;
  height: 4.8rem;
  border-radius: 50%;
  background: #fff;
  color: #000;
}
/* fourth navigation level: nested under its third-level link, indented and lighter */
html .mega-menu__item-grandchild.list-unstyled {
  /* .list-unstyled:not(.spacing-style) zeroes margin+padding at (0,2,0); outrank it.
     The rail sits flush with the parent link's text edge. */
  margin-inline-start: 0;
  padding-inline-start: 1.4rem;
  margin-block: 0.2rem 0.4rem;
  border-inline-start: 1px solid rgb(var(--color-foreground) / 0.12);
}
html .mega-menu__item-grandchild li > a {
  font-size: 1.4rem;
  padding-block: 0.5rem;
  opacity: 0.8;
}
/* the dropdown (every menu that is not the catalog's) lists its fourth level the
   same way: under its third-level link inside the flyout, on the same rail. The
   reference pads every dropdown link 3rem inline; the nested ones start at the rail. */
html .dropdown__nav-nested.list-unstyled {
  margin-inline-start: 3rem;
  padding-inline-start: 1.4rem;
  margin-block: 0.2rem 0.6rem;
  border-inline-start: 1px solid rgb(var(--color-foreground) / 0.12);
}
html .dropdown__nav .dropdown__nav-nested li > a {
  padding-inline: 0 3rem;
  font-size: 0.93em;
  opacity: 0.8;
}
/* phone drawer: the rail is inset to the parent link's text edge (the link
   carries 2rem inline padding), and the nested links indent from the rail */
html .menu-drawer__nested.list-unstyled {
  margin-inline-start: 2rem;
  padding-inline-start: 0;
  margin-block: 0.2rem 0.8rem;
  border-inline-start: 1px solid rgb(var(--color-foreground) / 0.15);
}
html .menu-drawer__nested .menu-drawer__item-link {
  padding-inline-start: 1.6rem;
  padding-block: 0.7rem;
  font-size: 0.92em;
  opacity: 0.85;
}
/* drawer rows breathe: the reference's 0.4rem rows read as one dense block */
html .menu-drawer .menu-drawer__item-link:not(.menu-drawer__item-link-heading):not(.menu-drawer__item-link-back) {
  padding-block: 1.3rem;
}
/* the reference rules a divider onto the drawer's close button (rawjoy has none) */
html .menu-drawer .dialog__close {
  border-inline-start: 0;
}

/* ============================================================================
   31. Search + 404 page head, recent searches, search drawer chips
   ============================================================================
   templates/search + templates/404 share one head: a centred heading over the
   reference's OWN search field (snippets/search-form: .search__field +
   .search__input.form-control + .search__reset + .search__submit, styled by
   reference.css). Only the page layout and the platform's recent-searches
   dropdown are theme-owned. */
.search-page__head { gap: var(--spacing-6, 2.4rem); }
.search-page__title { margin: 0; }
.search-page__lede {
  margin: 0;
  max-width: 56rem;
  color: rgb(var(--color-foreground) / 0.7);
}
.search-page__form {
  --search-input-height: var(--input-height, 5rem);
  width: 100%;
  max-width: 60rem;
}
/* spacing only — on search it sits on the .ts-listing-grid <ul>, whose display
   must stay grid (<product-recommendations> gets display:block from §2c) */
.search-page__results { margin-block-start: clamp(3.2rem, calc(3.488vw + 0.525rem), 5.6rem); }
.search-page__subtitle { margin: 0 0 2.4rem; }
/* recent searches: the base theme's dropdown, restyled onto this reference's
   input tokens. [hidden] must win over the display below. */
.search-history {
  position: absolute;
  top: calc(100% + 0.8rem);
  inset-inline: 0;
  z-index: 5;
  display: flex;
  flex-direction: column;
  max-height: 32rem;
  overflow-y: auto;
  padding-block: 0.8rem;
  border-radius: 2rem;
  background: rgb(var(--color-background));
  color: rgb(var(--color-foreground));
  box-shadow: 0 0 0 var(--input-border-width, 1px) rgb(var(--color-foreground) / 0.12),
    0 1.2rem 3.2rem rgb(0 0 0 / 0.08);
  text-align: start;
}
.search-history[hidden] { display: none; }
.search-history__clear,
.search-history__item {
  width: 100%;
  cursor: pointer;
  text-align: start;
}
.search-history__clear {
  width: auto;
  align-self: flex-end;
  padding: 0.2rem 1.6rem 0.4rem;
  font-size: 1.2rem;
  text-decoration: underline;
  opacity: 0.7;
}
.search-history__item {
  padding: 0.8rem 1.6rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.search-history__item:hover,
.search-history__item:focus-visible { background: rgb(var(--color-foreground) / 0.06); }
/* keep a sticky header in place while the dropdown is open */
.search-input-focus .hopscotch-section-header-hidden { transform: none; }
/* search drawer (header.liquid): the reference's drawer carries only the field
   and the popular-search chips; the chips row wraps under the field. */
.search-drawer .search-form { flex: 1 1 auto; min-width: 0; }

/* ============================================================================
   32. Lazy collection panels (init.js §6, snippets/collection-list-skeleton)
   ============================================================================
   Product Tabs / Style In Steps fetch every tab/step after the first when it is
   opened; until the cards land the panel shows four card-shaped shimmer blocks
   sized like the real cards (3:4 media + two text lines) so the section keeps
   its height and the swap does not jump. Columns follow the carousel's own
   breakpoints (4 / 3 / 2). */
.lazy-collection__skeleton {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--f-column-gap, 1rem);
}
.lazy-collection__skeleton[hidden] { display: none; }
@media screen and (max-width: 1199px) {
  .lazy-collection__skeleton { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .lazy-collection__skeleton-card:nth-child(n + 4) { display: none; }
}
@media screen and (max-width: 767px) {
  .lazy-collection__skeleton { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .lazy-collection__skeleton-card:nth-child(n + 3) { display: none; }
}
.lazy-collection__skeleton-card {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.lazy-collection__skeleton-media,
.lazy-collection__skeleton-line {
  border-radius: 0.8rem;
  background-color: rgb(var(--color-foreground) / 0.06);
  background-image: linear-gradient(90deg, rgb(var(--color-foreground) / 0) 0%, rgb(var(--color-foreground) / 0.07) 50%, rgb(var(--color-foreground) / 0) 100%);
  background-size: 200% 100%;
  animation: lazy-collection-shine 1.4s ease-in-out infinite;
}
.lazy-collection__skeleton-media { aspect-ratio: 3 / 4; }
.lazy-collection__skeleton-line {
  height: 1.4rem;
  width: 70%;
}
.lazy-collection__skeleton-line--short { width: 40%; }
@keyframes lazy-collection-shine {
  from { background-position: 200% 0; }
  to { background-position: -200% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .lazy-collection__skeleton-media,
  .lazy-collection__skeleton-line { animation: none; }
}
/* fetch failed: the skeleton hides and init.js §6 offers a retry */
.lazy-collection__error {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.2rem;
  padding: 2.4rem 0;
  text-align: center;
}
.lazy-collection__error-text { margin: 0; }

/* ============================================================================
   33. Slideshow: a slide's text waits for its slide (init.js §8)
   ============================================================================
   init.js marks every slide but the active one [data-ts-staged]; this is what
   "staged" looks like — the state the component's replay() starts a slide's motion
   from (text transparent, image at the zoom-out's 1.15), so a slide cross-fades in
   bare and its text and zoom play once it has arrived. !important because the
   entered state these override is an inline style, or an animation still running,
   on the <motion-component> itself. */
html .slideshow .swiper-slide[data-ts-staged] motion-component:not([data-motion^='zoom']) {
  opacity: 0 !important;
}
html .slideshow .swiper-slide[data-ts-staged] motion-component[data-motion='zoom-out'] {
  transform: scale(1.15) !important;
}

/* ============================================================================
   34. Store locator — templates/store-locator.liquid
   ============================================================================
   The template keeps the hooks the platform's script reads (#province-select,
   .store-locator-grid_item[data-province], #business-hour-N) and the elements
   that script builds for each location's opening hours (.dropdown[data-toggle] >
   .dropdown-toggle + .caret + .dropdown-menu). The platform's stylesheet for the
   page is not loaded, so this is all of their styling. */
.store-locator__head {
  gap: var(--spacing-3, 1.2rem);
  margin-block-end: clamp(3.2rem, calc(2.326vw + 1.416rem), 4.8rem);
}
.store-locator__title { margin: 0; }
.store-locator__lede { color: var(--color-foreground-secondary); }
.store-locator .store-locator-search {
  width: 100%;
  max-width: 44rem;
  margin-block-start: 0.4rem;
}
/* the reference's `.select` shrinks to its content; this one is the page's one field */
.store-locator .store-locator-search .select { display: flex; width: 100%; }
.store-locator .store-locator-search select { flex: 1 1 auto; width: 100%; min-width: 0; }

.store-locator .store-locator-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(28rem, 100%), 1fr));
  gap: 2rem;
}
/* one to three stores (most merchants): a centred row under the centred head, not
   the first cells of a part-filled grid */
.store-locator .store-locator-grid:not(:has(> :nth-child(4))) {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
}
.store-locator .store-locator-grid:not(:has(> :nth-child(4))) > .store-locator-grid_item {
  flex: 1 1 28rem;
  max-width: 36rem;
}
.store-locator .store-locator-grid_item { min-width: 0; }
.store-locator .store-selector-item {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 2.4rem;
  height: 100%;
  padding: 2.4rem;
  border: 0.1rem solid rgb(var(--color-border) / var(--color-border-alpha));
  border-radius: var(--product-card-media-radius, 2rem);
}
.store-locator .location-detail {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.4rem;
  min-width: 0;
  /* a long e-mail address must wrap inside the card */
  overflow-wrap: anywhere;
}
.store-locator__name { display: block; margin-block-end: 0.8rem; }
.store-locator .location-detail address {
  margin-block-start: 0.8rem;
  font-style: normal;
  color: var(--color-foreground-secondary);
}
.store-locator__navigate { --button-width: 100%; }

/* Opening hours. reference.css owns `.dropdown` — it is the header's menu panel
   (position: absolute; top: 100%; pointer-events: none; min-width: 26rem; a boxed,
   outlined surface) — and every one of those reaches the element the script
   builds: the "Open ⋅ Closes 8:00pm" line was drawn as a 26rem outlined box and
   could not be clicked.
   The week opens IN the card, not as a popup over the page: <main> clips its
   overflow, so a popup under the last row of cards lost its last days behind the
   footer. */
.store-locator .location-detail .dropdown {
  position: relative;
  top: auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  width: auto;
  min-width: 0;
  max-width: 100%;
  margin-block-start: 1.2rem;
  border-radius: 0;
  background: none;
  box-shadow: none;
  color: rgb(var(--color-foreground));
  pointer-events: auto;
  cursor: pointer;
}
.store-locator .location-detail .caret {
  flex: none;
  width: 0;
  height: 0;
  margin-inline-start: 0.8rem;
  border-inline: 0.4rem solid transparent;
  border-block-start: 0.5rem solid currentColor;
  transition: transform var(--animation-duration--fast, 0.25s);
}
.store-locator .location-detail .dropdown[data-toggle='1'] .caret { transform: rotate(180deg); }
.store-locator .location-detail .dropdown-menu {
  display: none;
}
.store-locator .location-detail .dropdown[data-toggle='1'] .dropdown-menu {
  display: block;
  flex: 0 0 100%;
  margin-block-start: 1.2rem;
  padding-block-start: 1.2rem;
  border-block-start: 0.1rem solid rgb(var(--color-border) / var(--color-border-alpha));
  color: var(--color-foreground-secondary);
  cursor: default;
}
.store-locator .location-detail .dropdown-menu a {
  display: block;
  padding-block: 0.2rem;
  color: inherit;
  cursor: default;
}

.store-locator__empty { gap: 2.4rem; }
.store-locator__empty img { width: min(40rem, 100%); height: auto; }
.store-locator__empty p { margin: 0; }
/* the pager sits inside the page's own bottom padding */
.store-locator .pagination-wrapper { margin-block-end: 0; }

/* ============================================================================
   35. Blog + article — sections/main-blog, sections/main-article,
       snippets/article-card
   ============================================================================
   Both pages are the reference's own markup, and reference.css already styles
   most of it (article-card*, layout-panel-grid, section-grid, breadcrumbs, rte).
   What it does not carry is the reference's two per-template sheets — the capture
   took no blog or article page — so their few rules are here, followed by what is
   this theme's own. */
/* reference: template-blog.css */
.blog-post .section__header { margin-block-end: 3.2rem; }
@media (min-width: 768px) {
  .blog-post .section__header { margin-block-end: 6rem; }
}
/* reference: template-article.css. No bare `.article` rule, ever: the article page's
   <body> carries that class. */
.article__main { row-gap: var(--article-main-row-gap); }
.article__wrapper > * + * { margin-top: 2.4rem; }
.article__footer > * + * { margin-top: 2.4rem; }
.article--width {
  position: relative;
  width: 100%;
  max-width: 88rem;
  margin-inline: auto;
}
.article-navigation {
  padding-block-start: 2.4rem;
  border-block-start: 0.1rem solid rgb(var(--color-border) / var(--color-border-alpha));
}
@media (min-width: 768px) {
  .article__wrapper > * + * { margin-top: 4rem; }
}

/* theme-owned from here */
.article-card__excerpt { margin: 0; }
/* A post with no cover: no empty frame. Its text sits at the foot of a tinted panel
   the size of the frame, so a row of cards keeps one height either way. */
.article-card.article-card--no-image .article-card__content {
  justify-content: flex-end;
  aspect-ratio: 1.708;
  padding: 2.4rem;
  border-radius: 2rem;
  background-color: rgb(var(--color-foreground) / 5%);
}
/* the cover keeps its own proportions (no image dimensions to frame it by) up to a
   cap, so a portrait photo does not push the post a screen and a half down */
.article__image img {
  width: 100%;
  max-height: 60rem;
  object-fit: cover;
}
/* older on the left, newer on the right, each in its own half */
.article-navigation > .f-column { flex: 1 1 0; min-width: 0; }
.article-navigation__next { text-align: end; }
/* The share popover (snippets/social-sharing, also on the product page).
   component-share.css frames it with `rgba(var(--color-foreground), 0.55)` — this
   reference's colour tokens are space-separated channels, so that value is invalid
   and the popover had no edge at all: loose icons over whatever lay beneath. */
html .share-button__fallback {
  padding: 0.8rem;
  border-radius: 1.2rem;
  box-shadow: 0 0 0 0.1rem rgb(var(--color-border) / var(--color-border-alpha)),
    0 1.2rem 3.2rem rgb(0 0 0 / 0.08);
}
/* it opens from its button's left edge; on desktop the article's button is at the
   right end of the row */
.article__share .share-button details { width: fit-content; }
@media (min-width: 768px) {
  .article__share .share-button__fallback { left: auto; right: 0; }
}
/* the article's products: the platform snippet brings its own page gutter */
.article__products .product-recommendations.page-width { padding-inline: 0; }
.article__products .product-recommendations__heading {
  margin-block: 0 4rem;
  font-size: var(--font-size--h2);
  line-height: var(--font-h2--line-height);
  text-align: center;
}
@media (max-width: 767.98px) {
  .article__products .product-recommendations__heading { margin-block-end: 2.4rem; }
}
/* the pager sits inside the page's own bottom padding */
.blog-post .pagination-wrapper { margin-block-end: 0; }

/* ============================================================================
   36. Pager — snippets/pagination (collection, search, blog, store locator)
   ============================================================================
   The snippet is the base theme's, styled twice over: component-pagination.css
   (4.4rem cells, an underline under the current page) and the reference's own
   `.pagination__list .pagination__item` (a 4rem circle, grey for the current
   page). Together the current page was a 44x40 oval with a line through its foot.
   The reference's circle wins; component-pagination.css is linked again by the
   snippet itself, after this file, hence the `html` on every selector. */
html .pagination__list > li {
  flex: 0 0 auto;
  max-width: none;
}
html .pagination__list .pagination__item {
  width: 4rem;
  padding: 0;
  transition: background-color var(--animation-duration--fast, 0.25s);
}
html .pagination__list .pagination__item--current::after,
html .pagination__list .pagination__item:hover::after { content: none; }
html .pagination__list a.pagination__item:hover { background-color: rgb(var(--color-foreground) / 6%); }

/* ============================================================================
   37. Sales Countdown: the ended line (sections/sales-countdown)
   ============================================================================
   The vendored <countdown-timer> only marks itself when a fixed end date has
   passed: it swaps `flex` for `hidden` and stops. The merchant's ended line is
   printed as its next sibling and shows only then, in the timer's place: one
   card in the number cards' own material, exactly as tall as they are at every
   width (their side is 2.1875em of the h2 size, never under 5rem), so the
   section keeps its height when the clock runs out. */
.countdown-timer__ended { display: none; }
countdown-timer.hidden + .countdown-timer__ended {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  max-width: 100%;
  min-height: clamp(5rem, calc(var(--font-size--h2) * 2.1875), 10rem);
  padding: 1rem var(--font-size--h2);
  border-radius: 1rem;
  background: rgb(var(--number-background));
  text-align: center;
  text-wrap: balance;
}
/* from the width where the images float beside the copy, the card keeps to the
   column between them, so a long line wraps instead of running across the images */
@media (min-width: 768px) {
  countdown-timer.hidden + .countdown-timer__ended { max-width: clamp(28rem, 34vw, 46rem); }
}
@media (prefers-reduced-motion: no-preference) {
  countdown-timer.hidden + .countdown-timer__ended {
    animation: countdown-ended-in var(--animation-duration--medium, 0.4s) var(--animation-easing, ease-out) both;
  }
}
@keyframes countdown-ended-in {
  from { opacity: 0; transform: translateY(0.6rem); }
}

/* ============================================================================
   38. PDP details column: highlight icons, description, image zoom
   ============================================================================
   sections/main-product. Under the buy buttons the reference stacks an icon row
   and the description as an accordion row, 2.4rem apart, and puts a magnifier on
   the image that opens its lightbox.

   Highlights ("Icon with title" blocks): one centred row, never wrapping — three
   items shrink and break their own titles instead of dropping one to a second
   line. An icon is 3.2rem, drawn in the text colour at the theme's icon stroke
   width; a merchant's own image takes the same box. */
.product-highlights {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  gap: clamp(1.2rem, calc(5.458vw - 2.986rem), 4rem);
  margin: 2.4rem 0 0;
  padding: 0;
  list-style: none;
}
.product-highlights__item {
  display: flex;
  flex: 0 1 auto;
  flex-direction: column;
  align-items: center;
  gap: 1.2rem;
  min-width: 0;
  text-align: center;
}
.product-highlights__icon {
  display: block;
  flex: none;
  width: 3.2rem;
  height: 3.2rem;
}
img.product-highlights__icon { object-fit: contain; }
.product-highlights__icon svg {
  display: block;
  width: 100%;
  height: 100%;
  stroke-width: var(--icon-stroke-width, 2);
}
.product-highlights__title {
  margin: 0;
  overflow-wrap: anywhere;
  text-wrap: balance;
}

/* Description: the reference's accordion row with an h5 summary, or the bare text
   when the merchant cleared the heading. The base product sheet, linked after
   this file, gives `.product__description.rte` a 3rem top margin: the bare text
   keeps the column's 2.4rem step, and inside the row the summary already spaces
   it, hence the extra class on both selectors. */
.product-description-accordion {
  --summary-font-family: var(--font-h5--family);
  --summary-font-style: var(--font-h5--style);
  --summary-font-weight: var(--font-h5--weight);
  --summary-font-size: var(--font-h5--size);
  --summary-line-height: var(--font-h5--line-height);
  display: block;
  margin-block-start: 2.4rem;
}
.product-description-accordion .product-description.rte { margin-block-start: 0; }
.product-details > .product-description.rte { margin-block-start: 2.4rem; }
.product-details > .product__share { margin-block-start: 2.4rem; }
/* The platform's promotion list (the description_top hook) closes the column,
   after the share row. Its shell is always printed and stays 0 tall until
   promotions load into it, so it takes the column's step only once it has cards. */
.product-details > #promotion-section:has(#sf_promo-container > *) { margin-block-start: 2.4rem; }

/* Zoom: the magnifier sits in the image's top corner like the reference's — always
   there on touch screens, fading in with the arrows where a pointer can hover.
   Positioned, never static (a .btn's hover fill is an absolute ::before). The
   white disc gets the lightbox buttons' soft shadow, or it vanishes on a white
   product shot. */
.gallery__zoom.btn {
  position: absolute;
  inset-block-start: 2rem;
  inset-inline-end: 2rem;
  z-index: 2;
  box-shadow: 0 0.5rem 2rem rgb(var(--color-foreground) / 12%);
}
/* Under 700px the platform's account/vouchers/vouchers.css (on every page, loaded
   after this file) sets `.btn { padding: 10px 20px }`. A text button barely
   notices; a round icon-only button has no room to give — the 4.4rem magnifier
   kept 0.4rem for its icon and the lightbox's prev / close / next 1rem, so the
   icons were squeezed to slivers on phones. The reference's own padding for
   every icon-only button is zero (both --button-padding vars), restated here. */
html .btn.btn--icon-only { padding: 0; }
/* Same sheet, the arrow button: `.btn--icon-solid` reserves room for its round
   arrow with a larger padding-inline-end, and the platform's 20px replaces it —
   a button sized to its label (not full width) then runs the label under the
   arrow. The reference's inline paddings restated; the start side too, so the
   label lines up with its hover copy in .btn__icon. */
html .btn.btn--icon-solid {
  padding-inline: var(--button-padding-inline, 2.4rem) calc(1rem + var(--icon-solid-size) * 2 + var(--button-icon-gap));
}
.gallery__zoom svg { stroke-width: var(--icon-stroke-width, 2); }
@media (max-width: 767px) {
  .gallery__zoom.btn {
    inset-block-start: 1.2rem;
    inset-inline-end: 1.2rem;
  }
}
@media (hover: hover) and (min-width: 768px) {
  .gallery__zoom.btn {
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.2s ease;
  }
  .gallery__main:hover .gallery__zoom,
  .gallery__main:focus-within .gallery__zoom {
    opacity: 1;
    pointer-events: auto;
  }
}
.gallery--zoom .gallery__track:not(.is-dragging) .gallery__slide img { cursor: zoom-in; }


/* ============================================================================
   39. Cart Featured collection: per-row columns + the phone swipe row
   ============================================================================
   sections/main-cart-featured-collection is base-copied; its list is §3's grid.

   Products per row: the section writes its range onto the list as custom
   properties — the desktop count as picked, the tablet count capped at 3 — and
   this modifier reads them. Phones keep §3's two columns.

   Swipe on Mobile: the section wraps the list in the reference's own phone row
   (`swipe-mobile swipe-mobile--2-cols` around a `swipe__inner`, reference.css,
   under 768px) in place of the base theme's `slider slider--tablet` and its
   prev / "1 / N" / next pager, which need a flex row and scrolled nothing on a
   grid. That row takes its gutter from --f-column-gap-mobile, which the
   reference's sections set inline; with none the cards touch. 0.8rem is the
   phone gutter of the reference's own product rows. */
@media (min-width: 750px) {
  .ts-listing-grid--per-row { grid-template-columns: repeat(var(--ts-per-row-tablet, 3), minmax(0, 1fr)); }
}
@media (min-width: 990px) {
  .ts-listing-grid--per-row { grid-template-columns: repeat(var(--ts-per-row, 4), minmax(0, 1fr)); }
}
.ts-swipe-row { --f-column-gap-mobile: 0.8rem; }
