/* The GoFillAm store.
 *
 * Built on the same tokens as the rest of the site — the brand green, the same body font sizes,
 * the same card radius — so the shop reads as part of GoFillAm rather than as a page bolted on.
 * Everything is prefixed `st-` so nothing here can collide with `css/styles.css` or
 * `css/checkout.css`, both of which these pages also load.
 *
 * Mobile is the default rather than the exception: the house began on a phone and the store is
 * built the same way, with the two-column layouts arriving at 900px.
 */

.st {
  /* The brand identity sheet, in the store's own names: green #00A048, its dark step #00803A,
     the ink spoken of as Dark Green #1F2F2D. The mail palette's #0a8f3c is deliberately not
     used here — the shop is the website's surface, and the website's green is the brand's.

     These are the light values. The dark block at the foot of the sheet answers every one of
     them for a browser that forces dark; the two `-text` roles are the greens that are
     *written* rather than filled, and are the only ones that have to move there. */
  --st-green: #00a048;
  --st-green-dark: #00803a;
  --st-green-text: #00803a;
  --st-green-text-2: #00a048;
  --st-ink: #1f2f2d;
  /* The ink used as a *fill*: a dark badge, a dark card. It keeps the value the sheet's later
     palette gives the ink (#132B1D), and it is not the token that follows the theme. */
  --st-ink-solid: #132b1d;
  --st-text: #3a453f;
  --st-muted: #6b7a70;
  --st-border: #e8eaed;
  --st-surface: #ffffff;
  --st-soft: #f6f7f9;
  --st-danger: #b91c1c;
  --st-danger-soft: #fef2f2;
  --st-danger-border: #fecaca;
  --st-radius: 14px;
  --st-shadow: 0 1px 2px rgba(31, 47, 45, .06), 0 8px 24px rgba(31, 47, 45, .06);
  color: var(--st-text);
  background: var(--st-soft);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.st *,
.st *::before,
.st *::after { box-sizing: border-box; }

/* ── Header ───────────────────────────────────────────────────────────────
   The header is sticky from a negative offset, which is the whole of how it gives the top of the
   screen back: the adverts row and the phone-number row sit above the line the browser pins at, so
   they slide out of view at exactly the speed of the finger and the bar under them comes to rest at
   the top of the screen. The scroll *is* the animation — nothing is animated, nothing is measured
   in milliseconds, and the page under the header never moves.

   The alternative, and what stood here before, was to collapse those two rows by animating their
   `max-height` as the customer scrolled. Animating a height reflows the document on every frame of
   the scroll — every card, every picture, the whole page below the header moving at a speed the
   finger did not ask for — which is the reading of "not smooth" no transition timing can fix. With
   the offset there is one layout while the page is at rest and one while it is pinned, both fixed,
   and the movement between them is the scroll the customer is already making.

   `--st-rows-h` is the height of the two rows, measured by js/store.js (44px for the adverts and
   51px for the phone number on a phone at its own text scale); the number in the fallback is under
   them on purpose, because an offset shorter than the rows leaves a sliver of a row at the top of
   the screen and an offset past them takes the same amount off the top of the bar. */
.st-head {
  position: sticky;
  top: calc(-1 * var(--st-rows-h, 90px));
  z-index: 40;
  background: var(--st-surface);
  border-bottom: 1px solid var(--st-border);
}
.st-head__inner {
  max-width: 1180px;
  margin: 0 auto;
  padding: 12px 16px;
  display: flex;
  align-items: center;
  gap: 12px;
}
.st-head__brand { display: inline-flex; align-items: center; text-decoration: none; }
.st-head__brand img { display: block; width: 116px; height: auto; }
.st-head__nav { margin-left: auto; display: flex; align-items: center; gap: 4px; }
.st-head__link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 10px;
  border-radius: 10px;
  font-size: .92rem;
  font-weight: 600;
  color: var(--st-text);
  text-decoration: none;
  white-space: nowrap;
}
.st-head__link:hover { background: var(--st-soft); color: var(--st-ink); }
.st-head__link.is-current { color: var(--st-green-text-2); }
.st-head__count {
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--st-green);
  color: #fff;
  font-size: .68rem;
  font-weight: 700;
  line-height: 18px;
  text-align: center;
}
.st-head__count[hidden] { display: none; }

/* ── Shell ──────────────────────────────────────────────────────────────── */
.st-main { flex: 1; width: 100%; max-width: 1180px; margin: 0 auto; padding: 20px 16px 64px; }
.st-foot {
  border-top: 1px solid var(--st-border);
  background: var(--st-surface);
  padding: 20px 16px;
  text-align: center;
  font-size: .8rem;
  color: var(--st-muted);
  line-height: 1.7;
}
.st-foot a { color: var(--st-green-text-2); text-decoration: none; }

.st-preview-banner {
  background: #fff7ed;
  border-bottom: 1px solid #fed7aa;
  color: #9a3412;
  font-size: .82rem;
  font-weight: 600;
  padding: 9px 16px;
  text-align: center;
}
.st-preview-banner[hidden] { display: none; }

/* ── Typography ─────────────────────────────────────────────────────────── */
.st-title { font-size: 1.5rem; line-height: 1.2; color: var(--st-ink); margin: 0 0 6px; font-weight: 800; letter-spacing: -.01em; }
.st-sub { font-size: .95rem; color: var(--st-muted); margin: 0 0 20px; line-height: 1.6; }
.st-section-title { font-size: 1.05rem; color: var(--st-ink); margin: 28px 0 12px; font-weight: 700; }

/* ── Toolbar ────────────────────────────────────────────────────────────── */
.st-toolbar { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 16px; }
.st-search { flex: 1 1 200px; }
.st-input,
.st-select {
  width: 100%;
  padding: 11px 13px;
  border: 1px solid var(--st-border);
  border-radius: 11px;
  background: var(--st-surface);
  font: inherit;
  font-size: .92rem;
  color: var(--st-ink);
}
.st-input:focus,
.st-select:focus { outline: none; box-shadow: 0 0 0 3px rgba(0, 160, 72, .28); border-color: var(--st-green); }
.st-select { min-width: 150px; }

.st-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 18px; }
.st-chip {
  border: 1px solid var(--st-border);
  background: var(--st-surface);
  border-radius: 999px;
  padding: 7px 14px;
  font-size: .84rem;
  font-weight: 600;
  color: var(--st-text);
  cursor: pointer;
  font-family: inherit;
}
.st-chip.is-on { background: var(--st-green); border-color: var(--st-green); color: #fff; }

/* ── The grid ───────────────────────────────────────────────────────────── */
.st-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.st-card {
  background: var(--st-surface);
  border: 1px solid var(--st-border);
  border-radius: var(--st-radius);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  position: relative;
}
.st-card__media {
  position: relative;
  display: block;
  aspect-ratio: 1 / 1;
  background: var(--st-soft);
  overflow: hidden;
}
.st-card__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.st-card__placeholder {
  width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center;
  color: #c3cbc6;
}
.st-card__placeholder svg { width: 42%; height: 42%; }
.st-card__body { padding: 11px 12px 13px; display: flex; flex-direction: column; gap: 6px; flex: 1; }
.st-card__name { font-size: .92rem; font-weight: 700; color: var(--st-ink); margin: 0; line-height: 1.35; }
.st-card__name a { color: inherit; text-decoration: none; }
.st-card__price { font-size: .98rem; font-weight: 800; color: var(--st-ink); }
.st-card__was { font-size: .8rem; color: var(--st-muted); text-decoration: line-through; font-weight: 500; margin-left: 6px; }
.st-card__foot { margin-top: auto; display: flex; gap: 6px; align-items: center; }

.st-badges { position: absolute; top: 8px; left: 8px; display: flex; flex-direction: column; gap: 4px; z-index: 2; }
.st-badge {
  font-size: .64rem; font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
  color: #fff; background: var(--st-green); border-radius: 999px; padding: 3px 8px;
}
.st-badge--sale { background: #b91c1c; }
.st-badge--out { background: #6b7280; }
.st-badge--dark { background: var(--st-ink-solid); }

.st-heart {
  position: absolute; top: 8px; right: 8px; z-index: 2;
  width: 34px; height: 34px; border-radius: 50%;
  border: none; background: rgba(255,255,255,.94);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; box-shadow: var(--st-shadow);
}
.st-heart svg { width: 18px; height: 18px; fill: none; stroke: var(--st-muted); stroke-width: 2; }
.st-heart.is-on svg { fill: #e11d48; stroke: #e11d48; }

.st-stock { font-size: .74rem; font-weight: 700; }
.st-stock--in { color: var(--st-green-text-2); }
.st-stock--out { color: var(--st-danger); }
.st-stock--back { color: #b45309; }

/* ── Buttons ────────────────────────────────────────────────────────────── */
.st-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  border: 1px solid var(--st-green); background: var(--st-green); color: #fff;
  border-radius: 11px; padding: 11px 16px;
  font: inherit; font-size: .9rem; font-weight: 700;
  cursor: pointer; text-decoration: none;
  transition: background .15s ease;
}
.st-btn:hover { background: var(--st-green-dark); border-color: var(--st-green-dark); }
.st-btn--block { width: 100%; }
.st-btn--sm { padding: 9px 12px; font-size: .82rem; }
.st-btn--light { background: var(--st-surface); color: var(--st-ink); border-color: var(--st-border); }
.st-btn--light:hover { background: var(--st-soft); }
.st-btn--danger { background: var(--st-surface); color: var(--st-danger); border-color: var(--st-danger-border); }
.st-btn--danger:hover { background: var(--st-danger-soft); }
.st-btn[disabled] { opacity: .55; cursor: not-allowed; }

.st-link-btn {
  border: none; background: none; padding: 0;
  color: var(--st-green-text-2); font: inherit; font-size: .85rem; font-weight: 600;
  cursor: pointer; text-decoration: underline;
}

/* ── States ─────────────────────────────────────────────────────────────── */
.st-state {
  background: var(--st-surface); border: 1px solid var(--st-border);
  border-radius: var(--st-radius); padding: 40px 22px; text-align: center;
}
.st-state__title { font-size: 1.05rem; font-weight: 700; color: var(--st-ink); margin: 0 0 6px; }
.st-state__text { font-size: .9rem; color: var(--st-muted); margin: 0 auto 18px; max-width: 460px; line-height: 1.65; }
.st-state__icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 46px; height: 46px; border-radius: 50%;
  background: var(--st-soft); color: var(--st-muted);
  font-size: 1.3rem; font-weight: 800; margin-bottom: 14px;
}
.st-spinner {
  display: inline-block; width: 26px; height: 26px; margin-bottom: 14px;
  border: 3px solid var(--st-border); border-top-color: var(--st-green);
  border-radius: 50%; animation: st-spin .8s linear infinite;
}
@keyframes st-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .st-spinner { animation-duration: 2.4s; } }

.st-coming {
  background: var(--st-surface); border: 1px solid var(--st-border);
  border-radius: var(--st-radius); padding: 48px 24px; text-align: center;
}
.st-coming__kicker {
  font-size: .72rem; font-weight: 800; letter-spacing: .12em; text-transform: uppercase;
  color: var(--st-green-text-2); margin: 0 0 10px;
}
.st-coming__title { font-size: 1.4rem; font-weight: 800; color: var(--st-ink); margin: 0 0 10px; }
.st-coming__text { font-size: .95rem; color: var(--st-muted); max-width: 480px; margin: 0 auto 20px; line-height: 1.7; }

/* ── Product page ───────────────────────────────────────────────────────── */
.st-product { display: grid; grid-template-columns: 1fr; gap: 20px; }
.st-gallery { background: var(--st-surface); border: 1px solid var(--st-border); border-radius: var(--st-radius); overflow: hidden; }
.st-gallery__main { aspect-ratio: 1 / 1; background: var(--st-soft); position: relative; }
.st-gallery__main img { width: 100%; height: 100%; object-fit: cover; display: block; }
.st-gallery__thumbs { display: flex; gap: 8px; padding: 10px; overflow-x: auto; }
.st-gallery__thumb {
  width: 62px; height: 62px; flex: 0 0 auto; border-radius: 9px; overflow: hidden;
  border: 2px solid transparent; padding: 0; background: var(--st-soft); cursor: pointer;
}
.st-gallery__thumb.is-on { border-color: var(--st-green); }
.st-gallery__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

.st-buy { background: var(--st-surface); border: 1px solid var(--st-border); border-radius: var(--st-radius); padding: 18px; }
.st-buy__price { font-size: 1.6rem; font-weight: 800; color: var(--st-ink); }
.st-buy__was { font-size: 1rem; color: var(--st-muted); text-decoration: line-through; margin-left: 8px; font-weight: 500; }
.st-buy__brand { font-size: .78rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--st-muted); margin: 0 0 6px; }
.st-buy__short { font-size: .92rem; color: var(--st-text); line-height: 1.65; margin: 10px 0 0; }

.st-attr { margin-top: 18px; }
.st-attr__label { display: block; font-size: .8rem; font-weight: 700; color: var(--st-ink); margin-bottom: 8px; }
.st-attr__label span { font-weight: 500; color: var(--st-muted); }
.st-swatches { display: flex; flex-wrap: wrap; gap: 8px; }
.st-swatch {
  display: inline-flex; align-items: center; gap: 7px;
  border: 1.5px solid var(--st-border); background: var(--st-surface);
  border-radius: 10px; padding: 7px 12px;
  font: inherit; font-size: .85rem; font-weight: 600; color: var(--st-ink);
  cursor: pointer;
}
.st-swatch.is-on { border-color: var(--st-green); box-shadow: inset 0 0 0 1px var(--st-green); }
.st-swatch[disabled] { opacity: .38; cursor: not-allowed; text-decoration: line-through; }
.st-swatch__dot { width: 16px; height: 16px; border-radius: 50%; border: 1px solid rgba(0,0,0,.12); display: inline-block; }
.st-swatch__img { width: 20px; height: 20px; border-radius: 5px; object-fit: cover; display: inline-block; }
.st-swatch--dot { padding: 6px; border-radius: 50%; }
.st-swatch--dot .st-swatch__dot { width: 24px; height: 24px; }

.st-buy__row { display: flex; gap: 10px; align-items: flex-end; margin-top: 18px; }
.st-qty {
  display: inline-flex;
  align-items: stretch;
  /* The buttons are stretched to the height of the figure between them, which was 23px — under
     the 24px a target is allowed to be. The floor is set here so the + and − are hittable on
     every page that carries the component, mouse or thumb. */
  min-height: 34px;
  border: 1px solid var(--st-border);
  border-radius: 11px;
  overflow: hidden;
  background: var(--st-surface);
}
.st-qty button {
  width: 38px; border: none; background: var(--st-surface); color: var(--st-ink);
  font-size: 1.1rem; font-weight: 700; cursor: pointer; font-family: inherit;
}
.st-qty button:hover { background: var(--st-soft); }

/* The stepper shares its row with a full-width button, and `width: 100%` on that button made
   the row wider than the row: every child shrank by its own basis, so the stepper came out
   99px instead of 126 and its two buttons were squeezed to 24px — the − and + a thumb has to
   hit, at a size under the 24px a target is allowed to be. The stepper keeps the size it was
   drawn at and the block button takes the rest of the line, which is what a block button is
   for. Found on a 390px phone and at 1440px both: it was never a phone bug. */
.st-buy__row .st-qty { flex: 0 0 auto; }
/* And the same floor under the button's own words as the quick view's: the stepper keeps the width
   it was drawn at, the button gives, and "Add to cart" ellipsizes rather than wrapping into a
   second line on top of the first.
   The ellipsis is `overflow: hidden`, and the shop's buttons are set in the display face, whose
   ink is drawn well outside its own line box: a label the size this row draws was cut at the top
   of its letters and under its own baseline. The span is given the room to hold them — the leading
   the checkout's own button already uses, and a pixel of padding on each side of the line. */
.st-buy__row .st-btn { min-width: 0; }
.st-buy__row .st-btn > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1.5; padding-block: 1px; }
.st-qty button { flex: 0 0 auto; }
.st-qty input {
  width: 48px; border: none; border-left: 1px solid var(--st-border); border-right: 1px solid var(--st-border);
  text-align: center; font: inherit; font-size: .92rem; font-weight: 700; color: var(--st-ink);
  -moz-appearance: textfield;
}
.st-qty input::-webkit-outer-spin-button,
.st-qty input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

.st-meta { margin-top: 22px; font-size: .82rem; color: var(--st-muted); line-height: 1.8; }
.st-meta strong { color: var(--st-ink); }
.st-desc { background: var(--st-surface); border: 1px solid var(--st-border); border-radius: var(--st-radius); padding: 18px; margin-top: 20px; }
.st-desc h2 { font-size: 1rem; margin: 0 0 10px; color: var(--st-ink); }
.st-desc p { font-size: .92rem; line-height: 1.75; margin: 0 0 10px; }
.st-tags { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 14px; }
.st-tag { font-size: .74rem; font-weight: 600; color: var(--st-muted); background: var(--st-soft); border-radius: 999px; padding: 4px 10px; }

/* ── Lines (cart / wishlist / summary) ──────────────────────────────────── */
.st-panel { background: var(--st-surface); border: 1px solid var(--st-border); border-radius: var(--st-radius); overflow: hidden; }
.st-line { display: flex; gap: 12px; padding: 13px 14px; border-bottom: 1px solid var(--st-border); align-items: center; }
.st-line:last-child { border-bottom: none; }
.st-line__media { width: 66px; height: 66px; flex: 0 0 auto; border-radius: 10px; overflow: hidden; background: var(--st-soft); }
.st-line__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.st-line__body { flex: 1; min-width: 0; }
.st-line__name { font-size: .9rem; font-weight: 700; color: var(--st-ink); margin: 0 0 3px; }
.st-line__name a { color: inherit; text-decoration: none; }
.st-line__variant { font-size: .8rem; color: var(--st-muted); margin: 0 0 4px; }
.st-line__price { font-size: .88rem; font-weight: 700; color: var(--st-ink); }
.st-line__warn { font-size: .8rem; color: var(--st-danger); font-weight: 600; margin: 3px 0 0; }
.st-line__actions { display: flex; flex-direction: column; align-items: flex-end; gap: 8px; }

.st-lines-empty { padding: 40px 22px; text-align: center; }

/* ── Totals ─────────────────────────────────────────────────────────────── */
.st-totals { padding: 14px; border-top: 1px solid var(--st-border); }
.st-total-row { display: flex; justify-content: space-between; font-size: .9rem; padding: 5px 0; color: var(--st-text); }
.st-total-row--due { font-size: 1.02rem; font-weight: 800; color: var(--st-ink); border-top: 1px solid var(--st-border); margin-top: 6px; padding-top: 11px; }
.st-total-row__off { color: var(--st-green-text-2); font-weight: 700; }

.st-aside { display: grid; gap: 14px; }

/* ── Forms (checkout) ───────────────────────────────────────────────────── */
.st-field { display: block; margin-bottom: 14px; }
.st-field__label { display: block; font-size: .82rem; font-weight: 700; color: var(--st-ink); margin-bottom: 6px; }
.st-field__label span { font-weight: 500; color: var(--st-muted); }
.st-field__error { font-size: .78rem; color: var(--st-danger); font-weight: 600; margin: 6px 0 0; }
.st-field__error[hidden] { display: none; }
.st-hint { font-size: .78rem; color: var(--st-muted); line-height: 1.6; }
.st-card-block { background: var(--st-surface); border: 1px solid var(--st-border); border-radius: var(--st-radius); padding: 16px; margin-bottom: 14px; }
.st-card-block h2 { font-size: 1rem; margin: 0 0 12px; color: var(--st-ink); }
.st-kicker { font-size: .7rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--st-green-text-2); margin: 0 0 4px; }

.st-radio { display: flex; gap: 10px; align-items: flex-start; border: 1.5px solid var(--st-border); border-radius: 11px; padding: 12px; margin-bottom: 8px; cursor: pointer; background: var(--st-surface); }
.st-radio.is-on { border-color: var(--st-green); box-shadow: inset 0 0 0 1px var(--st-green); }
.st-radio input { margin-top: 2px; accent-color: var(--st-green); }
.st-radio__body { flex: 1; }
.st-radio__title { font-size: .9rem; font-weight: 700; color: var(--st-ink); }
.st-radio__text { font-size: .8rem; color: var(--st-muted); margin-top: 2px; line-height: 1.5; }
.st-radio__fee { font-size: .9rem; font-weight: 800; color: var(--st-ink); white-space: nowrap; }

.st-saved { display: grid; gap: 8px; margin-bottom: 12px; }
.st-saved__item { text-align: left; border: 1.5px solid var(--st-border); background: var(--st-surface); border-radius: 11px; padding: 10px 12px; cursor: pointer; font: inherit; }
.st-saved__item.is-on { border-color: var(--st-green); }
.st-saved__label { font-size: .8rem; font-weight: 800; color: var(--st-green-text-2); display: block; }
.st-saved__addr { font-size: .82rem; color: var(--st-text); }

.st-checkbox { display: flex; gap: 9px; align-items: flex-start; font-size: .84rem; color: var(--st-text); line-height: 1.55; margin: 12px 0; }
.st-checkbox input { margin-top: 2px; accent-color: var(--st-green); }

.st-note { font-size: .78rem; color: var(--st-muted); line-height: 1.65; }
.st-error { background: var(--st-danger-soft); border: 1px solid var(--st-danger-border); color: var(--st-danger); border-radius: 10px; padding: 11px 13px; font-size: .85rem; font-weight: 600; margin-bottom: 12px; }
.st-error[hidden] { display: none; }
.st-ok { background: #f0fdf4; border: 1px solid #bbf7d0; color: #166534; border-radius: 10px; padding: 11px 13px; font-size: .85rem; font-weight: 600; margin-bottom: 12px; }
.st-ok[hidden] { display: none; }

.st-methods { display: grid; gap: 9px; }
.st-method { display: flex; gap: 11px; align-items: center; border: 1.5px solid var(--st-border); border-radius: 11px; padding: 12px; cursor: pointer; background: var(--st-surface); }
.st-method.is-on { border-color: var(--st-green); box-shadow: inset 0 0 0 1px var(--st-green); }
.st-method input { accent-color: var(--st-green); }
.st-method__logo { width: 34px; height: 34px; border-radius: 8px; object-fit: contain; background: var(--st-soft); }
.st-method__title { font-size: .9rem; font-weight: 700; color: var(--st-ink); }
.st-method__text { font-size: .78rem; color: var(--st-muted); line-height: 1.5; }

.st-toast {
  position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%);
  background: var(--st-ink-solid); color: #fff; border-radius: 11px;
  padding: 12px 18px; font-size: .88rem; font-weight: 600;
  box-shadow: 0 10px 30px rgba(0,0,0,.25); z-index: 90;
  max-width: min(92vw, 420px); text-align: center; line-height: 1.5;
}
.st-toast[hidden] { display: none; }
.st-toast--warn { background: #9a3412; }

.st-sticky {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
  background: var(--st-surface); border-top: 1px solid var(--st-border);
  padding: 10px 16px calc(10px + env(safe-area-inset-bottom, 0px));
  display: flex; align-items: center; gap: 12px;
}
.st-sticky[hidden] { display: none; }
.st-sticky__label { display: block; font-size: .7rem; color: var(--st-muted); font-weight: 600; }
.st-sticky__value { font-size: 1.05rem; font-weight: 800; color: var(--st-ink); }
.st-sticky .st-btn { margin-left: auto; }

.st-visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ── Wider screens ──────────────────────────────────────────────────────── */
@media (min-width: 640px) {
  .st-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
  .st-head__brand img { width: 132px; }
}

@media (min-width: 900px) {
  .st-main { padding: 28px 24px 72px; }
  .st-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 18px; }
  .st-title { font-size: 1.85rem; }
  .st-product { grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr); gap: 28px; align-items: start; }
  .st-sticky { display: none !important; }
  .st-two { display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); gap: 22px; align-items: start; }
  .st-aside { position: sticky; top: 84px; }
}

@media (min-width: 1100px) {
  .st-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
/* ── Smooth scrolling and the locked page ───────────────────────────────────
   Only the five store pages load this sheet, so the html-level rules land on the shop alone.
   `st-noscroll` is put on <html> while the drawer or the quick view is open: the page behind
   stops moving, but its scroll position is kept, unlike a position:fixed lock which loses it. */
html { scroll-behavior: smooth; }
html.st-noscroll { overflow: hidden; }
html.st-noscroll body { overflow: hidden; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

/* A pinned header that only earns its shadow once the page has moved under it — a bar with a
   shadow at the top of a page reads as a seam, not as a layer. The shadow is the only thing the
   pinned state changes: the rows above the bar are given back by the scroll itself (see the top of
   this sheet), and nothing about the bar's own size moves, so the page under it never reflows. */
.st-head { transition: box-shadow .18s ease, border-color .18s ease, transform .18s cubic-bezier(.2, .9, .25, 1); }
.st-head.is-stuck {
  box-shadow: 0 1px 2px rgba(31, 47, 45, .05), 0 10px 24px -14px rgba(31, 47, 45, .28);
  border-color: transparent;
}
/* And the bar itself, once the customer is reading: down is the one gesture that never asks for the
   header, so past the first screenful it stands down — slid out of the way, transform only, so the
   whole move is handled by the compositor and the page under it does not move a pixel. A finger
   that moves up gets it back on the first pixel of the move, because up is the direction that means
   "the top". It travels its own visible height and no more: the rows above it are already off the
   top of the screen, so a `-100%` would spend a third of the move sliding empty space.
   The class is written by js/store.js, which every store page loads. */
.st-head.is-away { transform: translateY(calc(-100% + var(--st-rows-h, 90px))); }
.st-head__nav { min-width: 0; overflow-x: auto; scrollbar-width: none; }
.st-head__nav::-webkit-scrollbar { display: none; }
.st-head__link.is-current { background: rgba(10, 143, 60, .1); }

/* ── The card, as a thing you can act on in place ───────────────────────── */
.st-card { transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease; }
.st-card:hover { transform: translateY(-2px); box-shadow: var(--st-shadow); border-color: #d8dee3; }
.st-card__media { display: block; position: relative; }
.st-card__cover { display: block; width: 100%; height: 100%; }
/* The card's two marks, in one column: save it, or look inside it. The eye is the same disc as the
   heart above it — 34px, 6px apart, 8px in from the corner — so the pair reads as a stack rather
   than as two controls that happened to land near each other, and it is drawn at the heart's own
   icon size and weight.

   It used to be a pill on the picture's floor reading "Quick view", and it kept `opacity: 0` until
   the card was hovered: the one control that answers "what is this?" was the one control a thumb
   could never find, and on the featured tile it arrived as a bare underlined word over a
   photograph. The eye is always there, carries its label in `aria-label` and `title` instead of in
   letters, and says what it does without taking a line of the picture. */
.st-card__quick {
  position: absolute;
  top: 48px;
  right: 8px;
  z-index: 2;
  width: 34px;
  height: 34px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(255, 255, 255, .94);
  color: var(--st-muted);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: var(--st-shadow);
  transition: color .18s ease, background-color .18s ease, transform .18s ease;
}
.st-card__quick svg { width: 18px; height: 18px; }
.st-card__quick:hover { background: var(--st-surface); color: var(--st-green-text-2); transform: scale(1.06); }
@media (prefers-reduced-motion: reduce) {
  .st-card, .st-head, .st-head__inner { transition: none; }
  .st-card:hover { transform: none; }
}

/* ── The floating basket ────────────────────────────────────────────────── */
.st-fab {
  position: fixed;
  right: 14px;
  bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  z-index: 60;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 9px 15px 9px 12px;
  border: 1px solid var(--st-green-dark);
  border-radius: 999px;
  background: var(--st-green);
  color: #fff;
  font: inherit;
  cursor: pointer;
  box-shadow: 0 10px 26px -8px rgba(10, 143, 60, .55);
  animation: st-fab-in .22s ease both;
}
.st-fab:hover { background: var(--st-green-dark); }
.st-fab[hidden] { display: none; }
.st-fab__icon svg { width: 20px; height: 20px; display: block; }
.st-fab__text { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.15; }
.st-fab__label { font-size: .64rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; opacity: .82; }
.st-fab__total { font-size: .95rem; font-weight: 800; }
.st-fab__count {
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--st-surface);
  color: var(--st-green-text);
  font-size: .7rem;
  font-weight: 800;
  line-height: 20px;
  text-align: center;
}
.st-fab__count[hidden] { display: none; }
@keyframes st-fab-in { from { opacity: 0; transform: translateY(10px) scale(.96); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .st-fab { animation: none; } }

/* ── The basket drawer ──────────────────────────────────────────────────── */
.st-drawer { position: fixed; inset: 0; z-index: 80; }
.st-drawer[hidden] { display: none; }
.st-drawer__scrim {
  position: absolute;
  inset: 0;
  background: rgba(16, 26, 21, .48);
  opacity: 0;
  transition: opacity .22s ease;
}
.st-drawer.is-open .st-drawer__scrim { opacity: 1; }
.st-drawer__panel {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(430px, 92vw);
  display: flex;
  flex-direction: column;
  background: var(--st-surface);
  border-left: 1px solid var(--st-border);
  box-shadow: -18px 0 48px -22px rgba(16, 26, 21, .45);
  transform: translateX(100%);
  transition: transform .24s cubic-bezier(.22, .8, .3, 1);
  outline: none;
}
.st-drawer.is-open .st-drawer__panel { transform: none; }
.st-drawer__head {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 16px;
  border-bottom: 1px solid var(--st-border);
}
.st-drawer__title { margin: 0; font-size: 1.02rem; font-weight: 800; color: var(--st-ink); }
.st-drawer__sub { margin: 3px 0 0; font-size: .78rem; color: var(--st-muted); }
.st-drawer__close {
  margin-left: auto;
  width: 36px;
  height: 36px;
  flex: 0 0 auto;
  border: 1px solid var(--st-border);
  border-radius: 10px;
  background: var(--st-surface);
  color: var(--st-ink);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.st-drawer__close:hover { background: var(--st-soft); }
.st-drawer__close svg { width: 18px; height: 18px; }
.st-drawer__body { flex: 1; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
.st-drawer__empty { padding: 44px 22px; text-align: center; }
.st-drawer__foot { border-top: 1px solid var(--st-border); padding: 14px 16px calc(14px + env(safe-area-inset-bottom, 0px)); }
.st-drawer__totals { margin-bottom: 12px; }
.st-drawer__totals .st-total-row--due { margin-top: 4px; padding-top: 9px; }
.st-drawer__actions { display: grid; gap: 8px; }
.st-drawer__foot .st-note { margin: 10px 0 0; }
.st-line--gone { background: #fffbfb; }
.st-line__each { font-size: .72rem; font-weight: 500; color: var(--st-muted); }
@media (prefers-reduced-motion: reduce) {
  .st-drawer__panel, .st-drawer__scrim { transition: none; }
}
/* ── Quick view ───────────────────────────────────────────────────────────
   The window the eye on a card opens. It was one column of a fixed width, so on a laptop it was a
   panel 880px wide holding a picture 880px tall and the price somewhere under it: the shape of a
   poster rather than of a place to decide. It is two shapes now, drawn from the same markup — a
   sheet that rises from the foot of a phone, and a panel in the middle of anything wider with the
   picture on one side and the buy panel on the other, at the height it is given. The details
   column scrolls inside that height while the picture stays where it is; the price, the stock and
   the button are set the same way the product page sets them (`.st-buy__figure` and the row under
   it), because the one place a customer decides on should not be a second dialect of the shop.
   ────────────────────────────────────────────────────────────────────────── */
/* The sheet hangs from the top of the screen rather than sitting on its foot, which sounds like the
   wrong way round and is the opposite. A fixed layer is laid out against the viewport the page was
   laid out in, and on a phone that is the taller one — the one the address bar is allowed to sit
   over. A sheet pinned to the foot of it is a sheet with its own buy row 26px below the screen,
   which is what a handset showed. The top edge is the one both viewports share, so the sheet is
   measured down from there: 8vh of shop above it, 92vh of itself. */
.st-qv { position: fixed; inset: 0; z-index: 85; display: flex; align-items: flex-start; justify-content: center; padding-top: 8vh; }
.st-qv[hidden] { display: none; }
.st-qv__scrim {
  position: absolute;
  inset: 0;
  background: rgba(16, 26, 21, .52);
  /* Two pixels of blur: enough that the shelf behind reads as behind, not so much that the panel
     looks like it is floating on frosted glass. */
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  opacity: 0;
  transition: opacity .22s ease;
}
.st-qv.is-open .st-qv__scrim { opacity: 1; }
.st-qv__panel {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  /* A height of its own rather than the height of whatever is written inside it: the details scroll
     within the sheet, and a panel left to grow to its content is sized by the content and not by
     the screen it is meant to sit on. */
  height: 92vh;
  max-height: 92vh;
  overflow: hidden;
  background: var(--st-surface);
  border-radius: 22px 22px 0 0;
  box-shadow: 0 -20px 60px -26px rgba(16, 26, 21, .5);
  transform: translateY(26px);
  opacity: 0;
  transition: transform .26s cubic-bezier(.22, .8, .3, 1), opacity .2s ease;
  outline: none;
}
.st-qv.is-open .st-qv__panel { transform: none; opacity: 1; }
/* The bar a sheet is pulled by. It is drawn here rather than added to the markup — it is a phone's
   own grammar, and a panel that is not rising from the floor of the screen has nothing to pull. */
.st-qv__panel::before {
  content: "";
  position: absolute;
  top: 9px;
  left: 50%;
  z-index: 4;
  width: 42px;
  height: 4px;
  margin-left: -21px;
  border-radius: 999px;
  background: rgba(16, 26, 21, .18);
  pointer-events: none;
}
.st-qv__close {
  position: absolute;
  top: 12px;
  right: 12px;
  z-index: 5;
  width: 38px;
  height: 38px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(255, 255, 255, .93);
  color: var(--st-ink);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 8px 22px -12px rgba(16, 26, 21, .6);
}
.st-qv__close:hover { background: var(--st-surface); }
.st-qv__close svg { width: 18px; height: 18px; }
/* The body is the scroller: one column on a phone, where the whole window travels, and two on a
   wider screen, where the halves travel separately.

   It is a plain block on a phone rather than a one-column grid, and that is the whole of the
   picture bug on Android: the picture sits in a box that clips it (`overflow: hidden`, so a zoom
   stays inside it), and a clipped box contributes no height of its own to an automatic grid track
   — a grid row asks what its content is worth, is told nothing, and then stretches whatever room
   is left over. At 390px that left the picture 173px of a 390px square, and on a phone whose text
   scale is turned up the details below grew into the space the picture was standing in and the row
   came out 0px: a 390px photograph showing as a sliver, or not at all. As a block the box takes
   its height from the picture's own `aspect-ratio`, so `375 x 375` is 375 tall at any text size,
   and the body scrolls past it. */
.st-qv__body {
  display: block;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.st-qv__media { position: relative; overflow: hidden; background: var(--st-soft); }
.st-qv__media > img { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; display: block; transition: transform .25s ease-out; }
.st-qv__media > .st-card__placeholder { aspect-ratio: 1 / 1; display: flex; }
/* The picture answers the pointer, the way the product page's does, and for the same reason: a
   store picture is the thing the customer is judging, and the pointer's own position is the origin,
   so whatever is under the hand is what grows. The magnifier is drawn for a fine pointer only — a
   touch screen has pinch and no pointer to hover with — and a reader who asked for less motion
   keeps the plain picture. */
.st-qv__media.is-zoom > img {
  transform: scale(2.2);
  transform-origin: var(--st-zoom-x, 50%) var(--st-zoom-y, 50%);
}
.st-qv__zoom {
  position: absolute;
  top: 12px;
  left: 12px;
  z-index: 3;
  display: none;
  width: 36px;
  height: 36px;
  place-items: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, .93);
  color: var(--st-ink);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  box-shadow: 0 8px 22px -12px rgba(16, 26, 21, .6);
  pointer-events: none;
}
.st-qv__zoom svg { width: 18px; height: 18px; }
@media (hover: hover) and (pointer: fine) {
  .st-qv__zoom { display: grid; }
}
/* The way through the shelf without closing the window. The pair is drawn over the picture's own
   middle, and it is in the markup only when the page behind has more than one product to step
   through — a catalogue of one gets no arrows promising a neighbour it does not have. */
.st-qv__nav { position: absolute; inset: 0; display: block; pointer-events: none; }
.st-qv__step {
  position: absolute;
  top: 50%;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  margin-top: -20px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(255, 255, 255, .93);
  color: var(--st-ink);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  cursor: pointer;
  pointer-events: auto;
  box-shadow: 0 10px 24px -14px rgba(16, 26, 21, .7);
}
.st-qv__step svg { width: 18px; height: 18px; }
.st-qv__step--prev { left: 10px; }
.st-qv__step--next { right: 10px; }
.st-qv__step:hover { background: var(--st-surface); }
/* The other pictures lie along the foot of this one as chips with a little of the shop showing
   through them: a strip of thumbnails under the picture took a band of the panel that the picture
   could have had, and on the sheet it was the band the buy row needed. */
.st-qv__thumbs {
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: 12px;
  display: flex;
  gap: 8px;
  overflow-x: auto;
  scrollbar-width: none;
}
.st-qv__thumbs::-webkit-scrollbar { display: none; }
.st-qv__thumb {
  width: 52px;
  height: 52px;
  flex: 0 0 auto;
  padding: 2px;
  border: 1px solid rgba(255, 255, 255, .7);
  border-radius: 13px;
  overflow: hidden;
  background: rgba(255, 255, 255, .86);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  cursor: pointer;
  box-shadow: 0 8px 18px -12px rgba(16, 26, 21, .7);
}
.st-qv__thumb.is-on { border-color: var(--st-green); box-shadow: 0 0 0 1px var(--st-green); }
.st-qv__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: 10px; }
/* The column's own gutters travel as one figure, because the buy row below has to bleed back over
   them to be a bar rather than a block floating in the middle of the text. */
.st-qv__info { --st-qv-pad: 20px; display: flex; flex-direction: column; padding: 22px var(--st-qv-pad) 0; }
.st-qv__name { font-size: 1.42rem; font-weight: 800; color: var(--st-ink); margin: 0 0 12px; line-height: 1.3; text-wrap: balance; }
.st-qv__stock { margin: 0 0 4px; }
.st-qv__options .st-attr { margin-top: 16px; }
/* The row of decision stands on the foot of the window and stays there. The auto margin puts it on
   the floor when the column has room to spare; the stick keeps it on the floor while the details
   above it travel past, which is the difference between a phone customer scrolling down to find the
   button and a button that was under their thumb the moment the product opened. It bleeds back over
   the column's own gutters so nothing scrolls past the sides of it, and the soft shadow says the
   bar is over the text rather than the end of it. */
.st-qv__actions {
  position: sticky;
  bottom: 0;
  margin-top: auto;
  margin-left: calc(var(--st-qv-pad) * -1);
  margin-right: calc(var(--st-qv-pad) * -1);
  padding: 16px var(--st-qv-pad) calc(18px + env(safe-area-inset-bottom, 0px));
  background: var(--st-surface);
  box-shadow: 0 -16px 20px -18px rgba(16, 26, 21, .3);
  display: grid;
  gap: 9px;
}
.st-qv__buy { display: flex; gap: 10px; align-items: stretch; }
/* The button shares its line with the stepper and the heart, so it is the one that gives way: it
   may be narrower than its own words, and the words ellipsize inside it. A label left to wrap put
   "Add to" over "cart" the moment the phone's own text scale grew — two lines in a button with
   room for one, and the two of them painted into each other. The checkout bar's button is set the
   same way, and so is the product page's. */
.st-qv__buy .st-btn { flex: 1; min-width: 0; }
/* The quick view's button carries the same words under the same ellipsis, so its span is given the
   same room: the display face's ink needs a line box taller than itself, or the letters are cut. */
.st-qv__buy .st-btn > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1.5; padding-block: 1px; }
/* Saving it, in the same row as buying it: the two things a customer does with a product are the
   two things on the row they are looking at. */
.st-qv__save {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  flex: 0 0 auto;
  padding: 0;
  border: 1px solid var(--st-border);
  border-radius: 12px;
  background: var(--st-surface);
  color: var(--st-muted);
  cursor: pointer;
}
.st-qv__save:hover { background: var(--st-soft); }
.st-qv__save svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.9; }
.st-qv__save.is-on { border-color: #f9c6d9; color: #e0245e; }
.st-qv__save.is-on svg { fill: #e0245e; stroke: #e0245e; }
.st-qv__more { justify-self: center; text-align: center; }
.st-qv__loading { display: flex; align-items: center; justify-content: center; min-height: 260px; }
.st-swatch--static { cursor: default; }
.st-swatch--static:hover { border-color: var(--st-border); }
@media (prefers-reduced-motion: reduce) {
  .st-qv__panel, .st-qv__scrim { transition: none; }
  .st-qv__media > img { transition: none; }
  .st-qv__media.is-zoom > img { transform: none; }
}

/* ── Back to the top ──────────────────────────────────────────────────────────── */
/*
   It sat in the left corner, alone, while the rest of the site keeps this control in the
   right one, directly under AMMA — so on the store it was in a different place from every
   other page, and in the one corner a thumb does not travel to. It is on the right now, below
   AMMA, at the site's own geometry and in the site's own clothes: filled green, 48px, and one
   soft pulse when it arrives, because a control that fades in silently is a control nobody
   notices is there.

   The store has a third tenant in that corner that the rest of the site does not — the
   floating basket — so the three stack bottom-up instead of fighting for the same 24px: the
   basket pill on the floor, the way back up above it, and AMMA above that. Only while the
   basket is actually on screen, which is what the `:has()` below is asking.
 */
.st-top {
  position: fixed;
  /* Centred under AMMA, not merely in the same corner: AMMA is 68px at 24px in, so a 48px
     control sits in the middle of it at 34px — which is the site's own number for this button,
     arrived at the same way. Same corner was not enough; the user could see the two centres
     were not on one line. */
  right: max(34px, calc(env(safe-area-inset-right) + 22px));
  bottom: max(20px, calc(env(safe-area-inset-bottom) + 12px));
  z-index: 55;
  width: 48px;
  height: 48px;
  border: none;
  border-radius: 50%;
  background: var(--st-green);
  color: #fff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 8px 20px -6px rgba(10, 143, 60, .55);
  opacity: 0;
  pointer-events: none;
  transform: translateY(10px);
  transition: opacity .22s ease, transform .22s ease, background-color .18s ease;
}
.st-top svg { width: 20px; height: 20px; }
@media (max-width: 560px) { .st-top { width: 44px; height: 44px; } }
.st-top.is-on { opacity: 1; pointer-events: auto; transform: none; animation: st-top-pulse .6s ease-out; }
.st-top:hover { background: var(--st-green-dark); }
.st-top:active { transform: scale(.92); }
@keyframes st-top-pulse {
  0% { box-shadow: 0 8px 20px -6px rgba(10, 143, 60, .55), 0 0 0 0 rgba(10, 143, 60, .45); }
  70% { box-shadow: 0 8px 20px -6px rgba(10, 143, 60, .55), 0 0 0 12px rgba(10, 143, 60, 0); }
  100% { box-shadow: 0 8px 20px -6px rgba(10, 143, 60, .55), 0 0 0 0 rgba(10, 143, 60, 0); }
}

/* The basket pill's own height plus the gap it keeps — 52 and 10. Hard numbers, because the
   two controls are sized here and a stack computed from a variable would drift the moment
   either one is restyled. */
body:has(.st-fab:not([hidden])) .st-top { bottom: calc(76px + env(safe-area-inset-bottom, 0px)); }
body:has(.st-fab:not([hidden])) .amma-chat { bottom: max(138px, calc(env(safe-area-inset-bottom) + 130px)); }

/* ── Nothing floats over a panel ────────────────────────────────────────────
   The quick view and the basket drawer are sheets drawn over the page, with a photograph and a buy
   row of their own, and the three controls above belong to the page underneath: the shop's basket
   pill, the way back to the top, and AMMA's green circle all stood on top of the sheet — over the
   picture, over the price, over the button the customer opened the sheet to press. A panel that is
   open has the customer; the page's own controls stand down until it closes, which is what the two
   `:has()` rules below say. AMMA stays for the basket drawer, which is a panel about one order
   rather than a page of its own, and where a question is still worth asking. */
body:has(#st-qv.is-open) .st-fab,
body:has(#st-qv.is-open) .st-top,
body:has(#st-qv.is-open) .amma-chat,
body:has(#st-drawer.is-open) .st-fab,
body:has(#st-drawer.is-open) .st-top {
  visibility: hidden;
}

/* On a hand the way back up keeps the corner column and hangs directly under AMMA, the way it does
   on a desk — the same column, the same alignment, one control under the other. It does not move to
   the middle of the screen: a control that changes sides between two widths is a control a person
   has to look for twice, and the two floating buttons read as one stack of tools only while they
   share an edge.
   "Under AMMA" means the two centres are the same, so what is aligned is the middle of each button
   rather than its edge. On a desk `.st-top` is `right: max(34px, safe + 22px)` against AMMA's
   `max(24px, safe + 12px)`, and the 10px between them is exactly half of 68 minus 48 — AMMA's
   width less this one's. The rule below is the same sum for a hand: AMMA is 16px from the edge and
   58px across below 560px, this button is 44px across, so 16 + (58 - 44) / 2 = 23px.
   Vertically it hangs from AMMA, not from the basket: AMMA's own floor is 138px while the basket is
   on screen (the rule just above) and the site's 96px when it is not, the gap right under AMMA is
   the 14px the desk keeps there, and the button is 44px tall. 138 - 14 - 44 = 80, and 96 - 14 - 44
   = 38. The size is stated here as well as at 560px below, because half of 44 is the 22px the
   arithmetic turns on. */
@media (max-width: 640px) {
  .st-top { width: 44px; height: 44px; bottom: calc(38px + env(safe-area-inset-bottom, 0px)); }
  body:has(.st-fab:not([hidden])) .st-top { bottom: calc(80px + env(safe-area-inset-bottom, 0px)); }
}
/* AMMA's own phone position, mirrored: it is 16px from the edge and 58px across there, so the
   button that centres under it is 23px from the same edge.

   Its floor is lower on a hand as well — 84px rather than the desk's 96 — and this button hangs
   from AMMA's floor, so the same 14px gap under it puts the button at 84 − 14 − 44 = 26px. The
   desk's 38px was the arithmetic for a 96px AMMA: measured at 390px the two controls were 2px
   apart, which reads as the way back up sitting on AMMA rather than under it. The step for a
   visible basket is above and still wins, because `body:has(…) .st-top` is the more specific
   rule. */
@media (max-width: 560px) {
  .st-top { right: 23px; bottom: calc(26px + env(safe-area-inset-bottom, 0px)); }
}

@media (prefers-reduced-motion: reduce) { .st-top { transition: none; animation: none; } }

/* ── Focus is a glow, and a press is a press ─────────────────────────────
   The shop used to answer a press with a green rectangle: `outline: 2px solid` around every control
   this sheet invents. An outline is a box drawn around a shape that is not a box — around a pill,
   around a disc, around a section of the page — and it arrived the moment somebody pressed the
   thing they had already decided to press, which reads as the interface flinching rather than as an
   answer to a question. It is a soft halo now: one ring of the brand's own green, no corners, no
   second edge, drawn with `box-shadow` so it hugs whatever shape the control actually has. Every
   `outline` in this sheet was rewritten the same way, in place, so the ring follows each control's
   own radius instead of describing a box around it.

   And because a mouse press is not a focus event, a press is answered separately and visibly by the
   press effect at the foot of this sheet — a soft flash from the point the finger landed, and a
   dip. The browser's own tap highlight is switched off there too, so a thumb on a phone gets the
   shop's answer rather than the platform's grey rectangle. */
.st-fab:focus-visible,
.st-top:focus-visible,
.st-card__quick:focus-visible,
.st-drawer__close:focus-visible,
.st-qv__close:focus-visible,
.st-qv__thumb:focus-visible {
  outline: none;
  box-shadow: 0 0 0 4px rgba(0, 160, 72, .28);
}

/* ── The drawer and the quick view, on a wider screen ───────────────────── */
@media (min-width: 720px) {
  /* The window stops rising off the floor and centres itself, at a size of its own rather than the
     size of whatever is written inside it: the picture has a height to fill and the details have a
     foot to be pushed towards, and both of them are the same on every product in the shop. */
  .st-qv { align-items: center; padding: 24px; }
  .st-qv__panel {
    width: min(940px, 100%);
    height: min(88vh, 700px);
    max-height: none;
    border-radius: 22px;
    box-shadow: 0 46px 90px -34px rgba(16, 26, 21, .6);
    transform: translateY(14px) scale(.99);
  }
  .st-qv__panel::before { display: none; }
  .st-qv__body { display: grid; grid-template-columns: minmax(0, 1.06fr) minmax(0, 1fr); overflow: hidden; }
  .st-qv__media { height: 100%; }
  /* The picture fills its half rather than squaring itself off inside it, so a tall panel is a
     photograph and not a photograph with a band of soft green under it. */
  .st-qv__media > img { height: 100%; aspect-ratio: auto; }
  .st-qv__info { --st-qv-pad: 28px; padding: 30px var(--st-qv-pad) 0; overflow-y: auto; overscroll-behavior: contain; }
  .st-qv__actions { padding: 20px var(--st-qv-pad) 26px; }
  .st-fab { right: 20px; bottom: calc(20px + env(safe-area-inset-bottom, 0px)); }
}
/* ══════════════════════════════════════════════════════════════════════════
   The shop
   ──────────────────────────────────────────────────────────────────────────
   The storefront is a catalogue you narrow, not a page you scroll: a search
   in the header, a rail of the things the catalogue can honestly be asked
   about, and a grid that answers. Headings are set in the brand's display
   face (Boldonse, the same one the rest of the site draws its titles with);
   everything you read at length stays in the body face, because a display
   face is a poster, not a paragraph.
   ══════════════════════════════════════════════════════════════════════════ */

.st { --st-display: var(--font-display, "Boldonse", sans-serif); }

/* ── The header, in three zones ─────────────────────────────────────────── */
.st-head__inner {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 14px;
}
.st-head__search {
  position: relative;
  display: flex;
  align-items: center;
  max-width: 520px;
  width: 100%;
  justify-self: center;
}
.st-head__searchicon {
  position: absolute;
  left: 13px;
  width: 17px;
  height: 17px;
  color: var(--st-muted);
  pointer-events: none;
}
.st-head__search input {
  width: 100%;
  border: 1px solid var(--st-border);
  border-radius: 999px;
  background: var(--st-soft);
  padding: 10px 14px 10px 38px;
  font: inherit;
  font-size: .88rem;
  color: var(--st-ink);
}
.st-head__search input::placeholder { color: #9aa6a0; }
.st-head__search input:focus {
  outline: none;
  box-shadow: 0 0 0 3px rgba(0, 160, 72, .28);
  background: var(--st-surface);
  border-color: var(--st-green);
}
.st-head__label { display: inline; }
.st-head__link svg { width: 17px; height: 17px; }
.st-head__link.is-current svg { color: var(--st-green-text-2); }
.st-head__account {
  width: 36px;
  height: 36px;
  justify-content: center;
  border: 1px solid var(--st-border);
  background: var(--st-surface);
  padding: 0;
}
.st-head__account svg { width: 20px; height: 20px; }

/* ── The intro ──────────────────────────────────────────────────────────── */
.st-intro { margin: 4px 0 18px; }
.st-kicker {
  font-family: var(--st-display);
  font-size: .68rem;
  font-weight: 400;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--st-green-text-2);
  margin: 0 0 8px;
}
.st-title {
  font-family: var(--st-display);
  font-weight: 400;
  letter-spacing: 0;
  font-size: 1.42rem;
  line-height: 1.18;
}
.st-sub { max-width: 62ch; }

/* ── The category row ───────────────────────────────────────────────────── */
.st-cats {
  display: flex;
  flex-wrap: nowrap;
  gap: 8px;
  margin: 0 0 18px;
  padding: 6px;
  background: var(--st-surface);
  border: 1px solid var(--st-border);
  border-radius: 999px;
  overflow-x: auto;
  scrollbar-width: none;
}
.st-cats::-webkit-scrollbar { display: none; }
.st-cats .st-chip {
  border: none;
  background: transparent;
  white-space: nowrap;
  padding: 8px 16px;
}
.st-cats .st-chip.is-on {
  background: var(--st-green);
  color: #fff;
  box-shadow: 0 6px 16px -8px rgba(10, 143, 60, .8);
}

/* ── The shop: rail beside grid ─────────────────────────────────────────── */
.st-shop { display: grid; grid-template-columns: 1fr; gap: 18px; align-items: start; }

.st-rail {
  background: var(--st-surface);
  border: 1px solid var(--st-border);
  border-radius: var(--st-radius);
  box-shadow: var(--st-shadow);
  overflow: hidden;
}
.st-rail__toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  border: none;
  background: var(--st-surface);
  padding: 15px 16px;
  font-family: var(--st-display);
  font-size: .82rem;
  color: var(--st-ink);
  cursor: pointer;
}
.st-rail__toggle svg { width: 18px; height: 18px; transition: transform .2s ease; }
.st-rail__toggle[aria-expanded="true"] svg { transform: rotate(180deg); }
.st-rail__body {
  display: none;
  padding: 0 16px 16px;
  border-top: 1px solid var(--st-border);
}
.st-rail.is-open .st-rail__body { display: block; }

.st-filter { padding: 15px 0; border-bottom: 1px solid var(--st-border); }
.st-filter:last-of-type { border-bottom: none; }
.st-filter__title {
  font-family: var(--st-display);
  font-size: .76rem;
  font-weight: 400;
  letter-spacing: .02em;
  color: var(--st-ink);
  margin: 0 0 4px;
}
.st-filter__hint { font-size: .74rem; color: var(--st-muted); margin: 0 0 12px; line-height: 1.5; }

/* Price: two fields, and a bar that shows where the range sits in the catalogue. */
.st-price__inputs { display: flex; gap: 8px; margin-bottom: 12px; }
.st-price__field { flex: 1; display: block; font-size: .68rem; font-weight: 700; color: var(--st-muted); }
.st-price__field span { display: block; margin-bottom: 4px; letter-spacing: .06em; text-transform: uppercase; }
.st-price__field input {
  width: 100%;
  border: 1px solid var(--st-border);
  border-radius: 10px;
  padding: 9px 10px;
  font: inherit;
  font-size: .84rem;
  color: var(--st-ink);
  background: var(--st-soft);
}
.st-price__field input:focus { outline: none; box-shadow: 0 0 0 3px rgba(0, 160, 72, .28); background: var(--st-surface); }
.st-price__bar {
  position: relative;
  height: 6px;
  border-radius: 999px;
  background: var(--st-soft);
  overflow: hidden;
}
.st-price__fill {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--st-green), #2ac76e);
  transition: left .2s ease, right .2s ease;
}

/* Brand checklist */
.st-brands { display: grid; gap: 2px; max-height: 210px; overflow-y: auto; }
.st-brand {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 8px;
  border-radius: 9px;
  font-size: .82rem;
  color: var(--st-text);
  cursor: pointer;
}
.st-brand:hover { background: var(--st-soft); }
.st-brand input { accent-color: var(--st-green); }
.st-brand__name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.st-brand__count { font-size: .72rem; color: var(--st-muted); }

.st-checks { display: grid; gap: 2px; }
.st-check {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 8px;
  border-radius: 9px;
  font-size: .82rem;
  color: var(--st-text);
  cursor: pointer;
}
.st-check:hover { background: var(--st-soft); }
.st-check input { accent-color: var(--st-green); }

/* The two ways an order arrives, as the basket would ask — chosen once, here. */
.st-methods { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 10px; }
.st-method {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  border: 1.5px solid var(--st-border);
  background: var(--st-surface);
  border-radius: 12px;
  padding: 12px;
  font: inherit;
  font-size: .8rem;
  font-weight: 700;
  color: var(--st-text);
  cursor: pointer;
  text-align: left;
}
.st-method > input { flex: 0 0 auto; margin-top: 2px; }
.st-method > span { min-width: 0; }
.st-method__title { display: block; }
.st-method__text { display: block; margin-top: 2px; }
.st-method:hover { border-color: #cfd8d3; }

/* ── One payment method per row on a phone ───────────────────────────────────
   The chooser is two columns on a desk, and on a phone that is two 153px tiles: 24px of padding,
   84px of gateway mark, an arrow and about 20px left for the name. The name does not fit and the
   tile says so by overflowing — measured at 390px on the checkout page, the document scrolled
   sideways by 24px and the page had a horizontal scrollbar of its own. One column gives the name
   the row it needs, and `overflow-wrap` is what keeps a long gateway name inside the tile rather
   than leaving it to the same accident. */
@media (max-width: 640px) {
  .st-methods { grid-template-columns: minmax(0, 1fr); }
  .st-method > span { min-width: 0; }
  .st-method__title,
  .st-method__text { overflow-wrap: anywhere; }
}
.st-method.is-on {
  border-color: var(--st-green);
  background: rgba(10, 143, 60, .08);
  color: var(--st-green-text);
  box-shadow: inset 0 0 0 1px var(--st-green);
}
.st-rail__clear { display: block; width: 100%; text-align: center; padding: 10px 0 2px; }

@media (min-width: 900px) {
  .st-shop { grid-template-columns: 264px minmax(0, 1fr); gap: 22px; }
  .st-rail { position: sticky; top: 84px; }
  .st-rail__toggle { pointer-events: none; cursor: default; }
  .st-rail__toggle svg { display: none; }
  .st-rail__body { display: block; }
  .st-title { font-size: 1.72rem; }
}
/* ── The result bar ─────────────────────────────────────────────────────── */
.st-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
}
.st-bar__count {
  font-family: var(--st-display);
  font-size: .74rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--st-muted);
  margin: 0;
}
.st-select--sm { width: auto; min-width: 176px; padding: 9px 12px; font-size: .84rem; }

/* ── The card ───────────────────────────────────────────────────────────── */
.st-card {
  background: var(--st-surface);
  border: 1px solid var(--st-border);
  border-radius: 18px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  position: relative;
}
.st-card__media {
  position: relative;
  display: block;
  aspect-ratio: 1 / 1;
  background: linear-gradient(160deg, var(--st-soft), #eceff1);
  overflow: hidden;
  /* The card clips the picture with its own rounded corners, and the picture is the one thing
     inside a card that moves: on hover it leans in at 1.075. A transformed layer is composited
     outside the ancestor's rounded clip — that is a Chromium behaviour, not a preference — so the
     zoomed frame came back as a rectangle with a square corner laid over the card's arc, which is
     exactly the rounded rectangle the eye read as wrong. Two things fix it, and both are needed:
     the media carries the card's radius as a clip of its own (top corners only, because its foot
     meets the card's body on a straight edge), and the picture inherits that radius, so the corner
     is round even in the frame Chromium composites outside the clip. `isolation` keeps the layer
     and its clip in one stacking context. */
  border-radius: var(--st-radius) var(--st-radius) 0 0;
  isolation: isolate;
}
.st-card__cover { display: block; width: 100%; height: 100%; border-radius: inherit; }
.st-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  border-radius: inherit;
  transition: transform .35s cubic-bezier(.22, .8, .3, 1);
}
.st-card:hover .st-card__media img { transform: scale(1.045); }
.st-card__body { padding: 13px 14px 15px; display: flex; flex-direction: column; gap: 5px; flex: 1; }
/* A name that wraps takes two even lines rather than one long line and a word — the same shape the
   buy panel's name takes, and the reason the card's second line no longer stands half empty.
   `balance` is a hint: a browser that does not know it sets the line breaks it always did. */
.st-card__name { font-size: .93rem; font-weight: 700; color: var(--st-ink); margin: 0; line-height: 1.35; text-wrap: balance; }
.st-card__name a { color: inherit; text-decoration: none; }
.st-card__name a:hover { color: var(--st-green-text-2); }
.st-card__meta {
  font-size: .74rem;
  color: var(--st-muted);
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The price and the way to buy it, in one band — the band the buy panel prints (see
   `.st-buy__figure`): the figure in first and the way to buy it at the end of that line, with the
   spacing the panel gives them. One shape in the shop and in the card, so a price reads as a price
   wherever it stands.

   The band is the price and the button, and nothing else. The price it beats stands under it, on
   the card's own white: inside, on the band's green and inside the band's own box, a struck-through
   figure read as part of what the thing costs rather than as what it used to — and it cost the band
   a second line, which took the button off the price's own line exactly as often as the card was
   narrow. Outside, the band is one line on every card, the figure and the button together, and the
   offer underneath reads as the note it is.

   The band does not wrap, and that is deliberate: with one line to fill, the figure gives back
   pixels with an ellipsis rather than pushing the button onto a line of its own — a price with no
   way to buy it beside it is the one arrangement this band must not print.

   The radius is a rounded rectangle rather than the stadium it was: a stadium's arc is half the
   band's height, so the arc cut into the figure's own ₦. */
.st-card__row {
  /* The band's own left inset, and the price it beats is indented by the same figure — so the two
     prices stand in one column instead of the old price lining up with the edge of the box. It is
     declared here rather than on the band because the band and the struck-through price are
     siblings: a custom property only inherits down the tree, and the price it beats is not a child
     of the box it stands under. The fallback is the design's own 12px, so a card built by some
     later template without this wrapper still indents the old price instead of losing the inset. */
  --st-band-pad: 12px;
  margin-top: auto;
  padding-top: 8px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 8px;
}
.st-card__pill {
  display: inline-flex;
  align-items: center;
  column-gap: 8px;
  flex: 1 1 auto;
  min-width: 0;
  padding: 6px 6px 6px var(--st-band-pad, 12px);
  border-radius: 12px;
  background: rgba(10, 143, 60, .09);
}
.st-card__price {
  font-family: var(--st-display);
  font-size: .84rem;
  color: var(--st-green-text);
  white-space: nowrap;
  /* The floor under a card too narrow for even the figure and the button: the price gives back
     pixels with an ellipsis rather than overflowing the pill onto whatever stands beside it. */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.st-card__price--range { font-family: inherit; font-weight: 800; font-size: .84rem; }
.st-card__was {
  font-size: .74rem;
  color: var(--st-muted);
  text-decoration: line-through;
  margin-left: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* A line of its own under the band — 100% of the row asks for the wrap whatever the card is, so
     no figure and no phone's own text scale can talk it out of it. */
  flex: 0 0 100%;
  padding-left: var(--st-band-pad, 12px);
}
.st-card__add {
  margin-left: auto;
  width: 32px;
  height: 32px;
  flex: 0 0 auto;
  border: none;
  border-radius: 50%;
  background: var(--st-green);
  color: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 6px 14px -6px rgba(10, 143, 60, .9);
  transition: transform .15s ease, background .15s ease;
}
.st-card__add svg { width: 16px; height: 16px; }
.st-card__add:hover { background: var(--st-green-dark); transform: scale(1.06); }
.st-card__add[disabled], .st-card__add.is-off { background: #c3cbc6; box-shadow: none; cursor: not-allowed; }
.st-card__add--opts { background: var(--st-ink-solid); box-shadow: 0 6px 14px -6px rgba(31, 47, 45, .8); }
.st-card__add--opts:hover { background: #000; }
.st-card__stock { font-size: .72rem; font-weight: 700; }

/* Badges and the ways in, over the picture */
.st-badge--top { background: #f5b301; color: #3a2c00; }

/* ── The featured card ──────────────────────────────────────────────────── */
/* One product, twice the width, with its name on the picture: the shelf's own
   headline. Set in the display face because it is a headline, not a label. */
.st-card.is-feature { grid-column: span 2; }
.st-card.is-feature .st-card__media { aspect-ratio: 16 / 9; }
.st-card.is-feature .st-card__cover::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 78%;
  background: linear-gradient(180deg, rgba(6, 48, 25, 0) 0%, rgba(6, 48, 25, .52) 58%, rgba(6, 48, 25, .88) 100%);
  pointer-events: none;
}
.st-card__over {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 16px;
  z-index: 3;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.st-card.is-feature .st-card__name {
  font-family: var(--st-display);
  font-weight: 400;
  font-size: 1.05rem;
  color: #fff;
  text-shadow: 0 2px 12px rgba(0, 0, 0, .45);
  max-width: 22ch;
}
.st-card.is-feature .st-card__name a:hover { color: #d8ffe7; }
.st-card__over .st-card__row { padding-top: 0; }
.st-card.is-feature .st-card__pill { background: rgba(255, 255, 255, .94); flex: 0 1 auto; }
.st-card.is-feature .st-card__meta { color: rgba(255, 255, 255, .82); }
.st-card.is-feature .st-card__stock { color: #d8ffe7; }
/* The rest of the words that sit on the picture. The rating and the struck-through old price wore
the card's own ink and muted grey — right on white paper, and on the featured card's photograph a
dark figure on a dark picture: "4.7" and "(3)" had to be leaned into to be read, and the amount
the price is measured against could not be seen at all. Everything the overlay writes is the
picture's own white now, so the line reads at one weight. */
.st-card.is-feature .st-rating { color: rgba(255, 255, 255, .86); }
.st-card.is-feature .st-rating__text strong { color: #fff; }
.st-card.is-feature .st-card__was { color: rgba(255, 255, 255, .8); }

@media (prefers-reduced-motion: reduce) {
  .st-card__media img, .st-card__add, .st-card__quick, .st-price__fill { transition: none; }
  .st-card:hover .st-card__media img { transform: none; }
}

/* A two-column card needs a two-column shelf to sit on. */
@media (max-width: 519px) {
  .st-card.is-feature .st-card__name { font-size: .92rem; }
  .st-card__over { padding: 12px; }
}

/* The column the grid and its result bar share. `min-width: 0` is what lets a long
   product name wrap instead of stretching the grid past the rail beside it. */
.st-shop__main { min-width: 0; }
/* ══════════════════════════════════════════════════════════════════════════
   The reskin — three header rows, a brighter ground, the GoFillAm palette worn
   ──────────────────────────────────────────────────────────────────────────
   The colours are the marketing site's own (green, lime, orange, cream),
   restated as `--st-*` so the store can be read without the other sheet. Only
   headings are set in Boldonse; everything you read at length stays in the
   body face, because a display face is a poster, not a paragraph.
   ══════════════════════════════════════════════════════════════════════════ */

.st {
  --st-green: #00a048;
  --st-green-dark: #00803a;
  --st-green-bright: #00c65c;
  --st-lime: #b4fc2c;
  --st-orange: #dc781c;
  --st-orange-dark: #c1660f;
  --st-cream: #fdf6e8;
  --st-ink: #132b1d;
  --st-text: #37453c;
  --st-muted: #6d7d71;
  --st-border: #e3e9e1;
  --st-radius: 18px;
  /* Two shadows, always: a close one that reads as an edge and a far one that reads as a lift. */
  --st-shadow: 0 1px 2px rgba(19, 43, 29, .06), 0 10px 28px -14px rgba(19, 43, 29, .22);
  --st-glow: 0 18px 40px -20px rgba(0, 160, 72, .6);
  background:
    radial-gradient(1000px 480px at 8% -10%, rgba(0, 198, 92, .16), transparent 60%),
    radial-gradient(820px 420px at 100% 0%, rgba(180, 252, 44, .2), transparent 58%),
    linear-gradient(180deg, #fbfdf7 0%, var(--st-cream) 100%);
  background-attachment: fixed;
}

/* The page column, shared by all three header rows so the brand, the search and the categories
   line up on one axis rather than three. */
.st-wrap {
  width: 100%;
  max-width: 1280px;
  margin: 0 auto;
  padding-left: 16px;
  padding-right: 16px;
}

/* ── Row 1 — where you are, and the ways to reach a human ────────────────── */
.st-util {
  background:
    radial-gradient(600px 200px at 0% 0%, rgba(0, 198, 92, .5), transparent 70%),
    linear-gradient(100deg, #123f26, #1c7a44 55%, var(--st-green-bright));
  color: #eaf9ef;
}
.st-util__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 40px;
  padding-top: 6px;
  padding-bottom: 6px;
  font-size: .78rem;
}
.st-loc {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  border: 1px solid rgba(255, 255, 255, .34);
  background: rgba(255, 255, 255, .14);
  color: inherit;
  border-radius: 999px;
  padding: 5px 12px;
  font: inherit;
  font-size: .78rem;
  font-weight: 700;
  cursor: pointer;
  transition: background .16s ease;
}
.st-loc:hover { background: rgba(255, 255, 255, .24); }
.st-loc__pin { width: 14px; height: 14px; }
.st-loc__text { letter-spacing: .01em; }
.st-loc__caret { width: 12px; height: 12px; opacity: .75; }
.st-util__links { display: flex; align-items: center; gap: 20px; min-width: 0; }
.st-util__links a {
  color: inherit;
  text-decoration: none;
  font-weight: 500;
  opacity: .86;
  white-space: nowrap;
}
.st-util__links a:hover { opacity: 1; text-decoration: underline; text-underline-offset: 3px; }
.st-util__tel { font-weight: 800; opacity: 1; }

/* ── Row 2 — the brand, the search and the three things you carry away ───── */
.st-head {
  background: rgba(255, 255, 255, .82);
  -webkit-backdrop-filter: saturate(1.5) blur(14px);
  backdrop-filter: saturate(1.5) blur(14px);
  border-bottom: 1px solid rgba(19, 43, 29, .08);
}
.st-head__bar { position: relative; }
.st-head__inner {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 16px;
  padding-top: 12px;
  padding-bottom: 12px;
}
.st-head__brand img { width: 124px; }

.st-searchbar {
  display: flex;
  align-items: center;
  justify-self: center;
  width: 100%;
  max-width: 660px;
  padding: 4px 4px 4px 8px;
  border: 1px solid rgba(19, 43, 29, .1);
  border-radius: 999px;
  background: var(--st-surface);
  box-shadow: 0 12px 30px -22px rgba(19, 43, 29, .7);
  transition: border-color .16s ease, box-shadow .16s ease;
}
.st-searchbar:focus-within {
  border-color: var(--st-green);
  box-shadow: 0 0 0 4px rgba(0, 160, 72, .14), 0 14px 34px -22px rgba(0, 160, 72, .8);
}
.st-searchbar__cat {
  max-width: 158px;
  border: none;
  background: transparent;
  color: var(--st-text);
  font: inherit;
  font-size: .82rem;
  font-weight: 700;
  padding: 9px 6px 9px 6px;
  cursor: pointer;
}
.st-searchbar__cat:focus { outline: none; }
.st-searchbar__div { width: 1px; height: 22px; background: rgba(19, 43, 29, .12); flex: 0 0 auto; }
.st-searchbar__input {
  flex: 1;
  min-width: 0;
  border: none;
  background: transparent;
  color: var(--st-ink);
  font: inherit;
  font-size: .88rem;
  padding: 9px 10px;
}
.st-searchbar__input::placeholder { color: #9aa89d; }
.st-searchbar__input:focus { outline: none; }
.st-searchbar__go {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  flex: 0 0 auto;
  border: none;
  border-radius: 999px;
  padding: 10px 18px;
  background: linear-gradient(120deg, var(--st-green), var(--st-green-bright));
  color: #fff;
  font: inherit;
  font-size: .82rem;
  font-weight: 800;
  cursor: pointer;
  box-shadow: var(--st-glow);
  transition: filter .16s ease, transform .16s ease;
}
.st-searchbar__go svg { width: 15px; height: 15px; }
.st-searchbar__go:hover { filter: brightness(1.07); transform: translateY(-1px); }

.st-head__nav { margin-left: 0; gap: 4px; }
.st-ia {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 5px 12px 5px 5px;
  border-radius: 999px;
  color: var(--st-text);
  text-decoration: none;
  transition: background .16s ease, color .16s ease;
}
.st-ia:hover { background: rgba(0, 160, 72, .1); color: var(--st-ink); }
.st-ia__icon {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--st-surface);
  border: 1px solid rgba(19, 43, 29, .09);
  box-shadow: inset 0 -6px 14px -10px rgba(19, 43, 29, .5);
}
.st-ia__icon svg { width: 18px; height: 18px; }
.st-ia__text { display: none; flex-direction: column; line-height: 1.16; }
.st-ia__label { font-size: .8rem; font-weight: 800; color: var(--st-ink); }
.st-ia__sub { font-size: .68rem; font-weight: 600; color: var(--st-muted); }
.st-ia .st-head__count {
  position: absolute;
  top: -4px;
  right: -4px;
  min-width: 17px;
  height: 17px;
  padding: 0 4px;
  line-height: 17px;
  font-size: .64rem;
  background: linear-gradient(140deg, var(--st-orange), var(--st-orange-dark));
  color: #3a2200;
  box-shadow: 0 2px 6px -2px rgba(19, 43, 29, .6);
}
@media (min-width: 900px) { .st-ia__text { display: flex; } }

/* ── Row 3 — the categories, the shelf's own index ───────────────────────── */
.st-catnav { border-top: 1px solid rgba(19, 43, 29, .07); background: rgba(255, 255, 255, .5); }
.st-catnav__inner {
  display: flex;
  align-items: center;
  gap: 8px;
  overflow-x: auto;
  scrollbar-width: none;
  padding-top: 9px;
  padding-bottom: 9px;
}
.st-catnav__inner::-webkit-scrollbar { display: none; }
.st-catnav .st-chip {
  border: 1px solid rgba(19, 43, 29, .1);
  background: var(--st-surface);
  border-radius: 999px;
  padding: 8px 17px;
  white-space: nowrap;
  font-size: .82rem;
  font-weight: 700;
  color: var(--st-text);
  box-shadow: 0 6px 16px -14px rgba(19, 43, 29, .8);
  transition: transform .16s ease, border-color .16s ease, color .16s ease;
}
.st-catnav .st-chip:hover { transform: translateY(-1px); border-color: rgba(0, 160, 72, .4); color: var(--st-ink); }
.st-catnav .st-chip.is-on {
  background: linear-gradient(120deg, var(--st-green), var(--st-green-bright));
  border-color: transparent;
  color: #fff;
  box-shadow: var(--st-glow);
}

/* ── The ground the shop sits on ─────────────────────────────────────────── */
.st-main { max-width: 1280px; }
.st-intro { margin: 14px 0 20px; }
.st-kicker { color: var(--st-green-text); }
.st-sub { color: var(--st-text); }

.st-cats .st-chip.is-on { background: linear-gradient(120deg, var(--st-green), var(--st-green-bright)); box-shadow: var(--st-glow); }

/* ── The price histogram — the shape of the shelf, not just its ends ─────── */
.st-price__hist {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 56px;
  margin: 2px 0 8px;
}
.st-price__bar-el {
  flex: 1 1 0;
  min-height: 4px;
  border-radius: 4px 4px 2px 2px;
  background: rgba(0, 160, 72, .16);
  transition: background .18s ease;
}
.st-price__bar-el.is-on { background: linear-gradient(180deg, var(--st-green-bright), var(--st-green)); }
.st-price__bar { height: 5px; background: rgba(19, 43, 29, .08); }
.st-price__fill { background: linear-gradient(90deg, var(--st-green), var(--st-green-bright), var(--st-lime)); }
.st-price__field input:focus { border-color: var(--st-green); }

/* ── The cards, with a little life in them ───────────────────────────────── */
.st-card {
  border-color: rgba(19, 43, 29, .08);
  box-shadow: 0 1px 2px rgba(19, 43, 29, .04), 0 14px 30px -26px rgba(19, 43, 29, .8);
}
.st-card:hover { transform: translateY(-3px); border-color: rgba(0, 160, 72, .35); box-shadow: var(--st-shadow), var(--st-glow); }
.st-card__pill { background: rgba(0, 160, 72, .1); }
.st-card__add { background: linear-gradient(140deg, var(--st-green), var(--st-green-bright)); }
.st-badge { background: linear-gradient(140deg, var(--st-green), var(--st-green-bright)); }
.st-badge--top { background: linear-gradient(140deg, #ffd34d, var(--st-orange)); color: #3a2600; box-shadow: 0 4px 12px -6px rgba(220, 120, 28, .9); }
.st-badge--sale { background: linear-gradient(140deg, #f0644f, #c62f2f); }
.st-heart.is-on svg { fill: #e0245e; stroke: #e0245e; }
.st-badge--top { animation: st-pop 2.4s ease-in-out infinite; }
@keyframes st-pop {
  0%, 100% { transform: none; }
  50% { transform: translateY(-1.5px) scale(1.03); }
}

/* ── The shelf, at three columns beside the rail ─────────────────────────── */
.st-grid { gap: 14px; }
@media (min-width: 640px) { .st-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; } }
/* The rail arrives at 900px, which takes the column back off the grid; the third column returns
   once the page is wide enough to pay for it. */
@media (min-width: 900px) { .st-shop .st-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; } }
@media (min-width: 1140px) { .st-shop .st-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* ── Buttons, pills and panels, worn in the house colours ────────────────── */
.st-btn {
  background: linear-gradient(120deg, var(--st-green), var(--st-green-bright));
  border-color: transparent;
  box-shadow: var(--st-glow);
}
.st-btn:hover { background: linear-gradient(120deg, var(--st-green-dark), var(--st-green)); filter: brightness(1.03); }
.st-btn--light { background: var(--st-surface); border-color: rgba(19, 43, 29, .12); box-shadow: none; }
.st-btn--light:hover { background: var(--st-surface); border-color: rgba(0, 160, 72, .4); }
.st-method.is-on { background: rgba(0, 160, 72, .1); }
.st-radio.is-on { background: rgba(0, 160, 72, .06); }
.st-rail { box-shadow: var(--st-shadow); }
.st-fab { background: linear-gradient(140deg, var(--st-green), var(--st-green-bright)); border-color: transparent; }
.st-fab:hover { filter: brightness(1.05); }
.st-preview-banner {
  background: linear-gradient(100deg, #fff5da, #eefadf);
  border-bottom-color: rgba(220, 120, 28, .5);
  color: #7a4a06;
}
.st-foot { background: var(--st-surface); border-top: 1px solid rgba(19, 43, 29, .08); }
.st-foot a { color: var(--st-green-text); font-weight: 600; }
.st-state__title, .st-coming__title { font-family: var(--st-display); font-weight: 400; }
.st-section-title { font-family: var(--st-display); font-weight: 400; }
.st-qv__name, .st-drawer__title { font-family: var(--st-display); font-weight: 400; }
.st-buy__price { font-family: var(--st-display); font-weight: 400; }

@media (prefers-reduced-motion: reduce) {
  .st-card:hover { transform: none; }
  .st-badge--top { animation: none; }
  .st-catnav .st-chip, .st-searchbar__go { transition: none; }
}

/* A featured tile spans two columns, so its height is whatever the row beside it asks for — and a
   picture with a ratio of its own inside a taller box leaves the band of white the eye reads as a
   broken card. So there the picture carries no ratio and is laid over the whole tile instead, with
   a floor under it so a lone result cannot collapse to nothing. */
.st-card.is-feature .st-card__media { aspect-ratio: auto; flex: 1; min-height: 260px; }
.st-card.is-feature .st-card__cover { position: absolute; inset: 0; }

/* The utility row carries the phone number and the way to a human, which matter at the top of a
   page and not once you are reading. Pinned over a long catalogue it gives its height back — with
   the advert row above it, by the scroll itself and not by an animation; see the note on `.st-head`
   at the top of this sheet. Its height is measured into `--st-rows-h` by js/store.js, which is what
   the header's sticky offset points past, so this row is only ever as tall as the words in it. */
.st-util { overflow: hidden; }


/* ── The header, on a phone and on a tablet ─────────────────────────────────
   Three columns cannot hold a brand, a search field and three controls at 390px — the field was
   squeezed to eighteen pixels and the row pushed the page thirteen pixels wider than the screen.
   So below 860px the bar becomes two rows: the brand and the controls, then the search across the
   full width. The utility row sheds the links that lead to another page rather than to a person,
   which is what stops it overflowing, and keeps its own scrolling as a last resort. */
.st-util__links { min-width: 0; overflow-x: auto; scrollbar-width: none; }
.st-util__links::-webkit-scrollbar { display: none; }
@media (max-width: 980px) {
  .st-util__links a[href="/terms-and-conditions"] { display: none; }
}
@media (max-width: 860px) {
  .st-head__inner {
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas: "brand nav" "search search";
    row-gap: 10px;
  }
  .st-head__brand { grid-area: brand; }
  .st-head__nav { grid-area: nav; justify-self: end; }
  .st-searchbar { grid-area: search; max-width: none; }
}
@media (max-width: 760px) {
  .st-util__links { gap: 14px; }
  .st-util__links a[href="/#track-order"],
  .st-util__links a[href="/cart"] { display: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   Flat, not gradient
   ──────────────────────────────────────────────────────────────────────────
   The house palette is a handful of solid colours — green, lime, amber,
   cream. A gradient behind a button changes the colour the brand actually is,
   and on a 30px circle it reads as a smudge rather than a finish. So the shop
   is painted in the flat fills, and the only gradient left in the sheet is the
   scrim under the featured tile's name, which is contrast, not decoration.
   ══════════════════════════════════════════════════════════════════════════ */
.st { background: var(--st-cream); background-attachment: scroll; }
.st-util { background: #14663a; }
.st-searchbar__go,
.st-btn,
.st-card__add,
.st-badge,
.st-catnav .st-chip.is-on,
.st-cats .st-chip.is-on,
.st-fab,
.st-price__fill,
.st-price__bar-el.is-on { background: var(--st-green); }
.st-card__media { background: var(--st-soft); }
.st-badge--top { background: #f5b301; color: #3a2c00; }
.st-badge--sale { background: #c62f2f; }
.st-ia .st-head__count { background: var(--st-orange); color: #3a2200; }
.st-preview-banner { background: #fff7e6; }

/* ── The rail: advanced filters, flat paint ──────────────────────────────── */
/* Two things make a long filter rail usable rather than a scroll you lose your place in: the
   section headings stay at the top of the rail's own scroll, and each section can be emptied
   without hunting for the box you ticked. */
.st-rail__body { max-height: calc(100vh - 140px); overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; }
.st-filter__title {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--st-surface);
  margin: 0 0 8px;
  padding: 10px 0 8px;
  display: flex;
  align-items: center;
  gap: 8px;
}
.st-filter__title::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--st-border);
}
/* The chips become real check rows: a drawn box, a full-width hit area, and the count pushed to
   the far edge so the column of numbers reads as a column. */
.st-brand, .st-check {
  padding: 8px 10px;
  border-radius: 10px;
  border: 1px solid transparent;
  transition: background .14s ease, border-color .14s ease;
}
.st-brand:hover, .st-check:hover { background: rgba(0, 160, 72, .07); border-color: rgba(0, 160, 72, .22); }
.st-brand input[type="checkbox"], .st-check input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
  border: 1.5px solid #b9c6bd;
  border-radius: 5px;
  background: var(--st-surface);
  display: inline-grid;
  place-content: center;
  cursor: pointer;
  transition: background .14s ease, border-color .14s ease;
}
.st-brand input[type="checkbox"]::after, .st-check input[type="checkbox"]::after {
  content: "";
  width: 9px;
  height: 5px;
  border-left: 2px solid #fff;
  border-bottom: 2px solid #fff;
  transform: rotate(-45deg) scale(0);
  transition: transform .14s ease;
}
.st-brand input[type="checkbox"]:checked, .st-check input[type="checkbox"]:checked { background: var(--st-green); border-color: var(--st-green); }
.st-brand input[type="checkbox"]:checked::after, .st-check input[type="checkbox"]:checked::after { transform: rotate(-45deg) scale(1); }
.st-brand input[type="checkbox"]:focus-visible, .st-check input[type="checkbox"]:focus-visible { outline: none; box-shadow: 0 0 0 4px rgba(0, 160, 72, .28); }
.st-brand { border: 1px solid var(--st-border); background: var(--st-surface); }
.st-brand__count { font-variant-numeric: tabular-nums; }
.st-price__hist { gap: 2px; }
.st-price__bar-el { border-radius: 3px 3px 0 0; }
.st-methods { gap: 8px; }
.st-rail__clear { border-top: 1px solid var(--st-border); margin-top: 6px; }

/* ══════════════════════════════════════════════════════════════════════════
   Phones
   ──────────────────────────────────────────────────────────────────────────
   A grid's implicit column is sized to its widest child's min-content, and in
   the basket that made the side column 398px inside a 341px page — the page
   scrolled sideways by 24px. An explicit `minmax(0, 1fr)` track cannot do
   that, and `min-width: 0` lets the panels inside it shrink instead of push.
   ══════════════════════════════════════════════════════════════════════════ */
.st-aside { grid-template-columns: minmax(0, 1fr); }
.st-aside > *, .st-two > * { min-width: 0; }
.st-panel, .st-card-block, .st-totals, .st-lines { max-width: 100%; }

@media (max-width: 560px) {
  .st-wrap { padding-left: 14px; padding-right: 14px; }
  .st-main { padding: 16px 14px 56px; }
  .st-intro { margin: 12px 0 16px; }
  .st-title { font-size: 1.22rem; }
  .st-sub { font-size: .9rem; margin-bottom: 16px; }
  .st-bar { flex-direction: column; align-items: stretch; gap: 8px; }
  .st-select--sm { width: 100%; min-width: 0; }
  .st-grid { gap: 12px; }
  .st-card__body { padding: 11px 12px 13px; }
  .st-card__name { font-size: .88rem; }
  /* The band keeps the button's own phone target and gives itself a hair less padding: the two
     numbers are one figure, the row's inset, so the price it beats still lines up with the price. */
  .st-card__row { --st-band-pad: 11px; }
  .st-card__pill { padding: 3px 3px 3px var(--st-band-pad, 11px); }
  .st-line { padding: 11px 12px; gap: 10px; }
  .st-line__media { width: 56px; height: 56px; }
  .st-line__actions { gap: 6px; }
  .st-totals, .st-card-block, .st-panel { padding: 14px; }
  .st-sticky { padding: 10px 14px; }
  .st-filter { padding: 12px 0; }
  .st-hero, .st-intro, .st-shop { margin-top: 0; }
}
/* ── A heading is drawn, not read ───────────────────────────────────────────
   Every size on this page is stated in `rem`, which is right for text that is read: a customer who
   has turned their phone's text scale up gets bigger words, everywhere, and that is the point of
   the setting. It is the wrong unit for a heading, because a heading is a *shape* — set to fill a
   width, in a display face whose whole character is its proportions — and on a phone the smallest
   of a `clamp()`'s three terms is the one that decides, because the `vw` term is small there. So
   `clamp(2rem, 4vw, 3rem)` came out at 32px on a phone at 1x and at 56px on a phone at 1.75x,
   which took four lines where it took two and ran into the sentence underneath it.

   So in every `clamp()` below the floor is a number of pixels and the ceiling is a number of rem.
   The floor is the size the page draws that heading at on the narrowest screen it is read on —
   the rem floor converted — so at a normal text scale nothing here changes by as much as a
   rounding error at any width, and at 1.75x the heading keeps the shape it has at 1x while the
   paragraphs under it still answer the customer's own setting. */
/* ── The shop's hero band ───────────────────────────────────────────────────
   The band the store opens on: a green kicker with a lime tick beside it, the
   headline in the display face, and one grey line under it. It is the shop's
   own introduction, so it is the one place on the storefront that speaks in the
   display type at a size that fills the width and then stops. */
.st-intro {
  position: relative;
  padding: 6px 0 4px;
}
.st-intro .st-kicker {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-size: .76rem;
  font-weight: 800;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--st-green-text-2);
  margin: 0 0 12px;
}
.st-intro .st-kicker::before {
  content: "";
  width: 26px;
  height: 3px;
  border-radius: 999px;
  background: var(--st-lime);
}
.st-intro .st-title {
  font-family: var(--st-display);
  font-size: clamp(30px, 5.2vw, 3.35rem);
  line-height: 1.02;
  letter-spacing: -.02em;
  color: var(--st-ink);
  margin: 0 0 14px;
  max-width: 22ch;
}
.st-intro .st-sub {
  font-size: clamp(15px, 1.4vw, 1.08rem);
  color: var(--st-muted);
  max-width: 62ch;
  margin: 0;
}

/* ── The checkout page ──────────────────────────────────────────────────────
   Two columns: the form on the left, the order's own summary held down the
   right so the figure being paid is always on screen. The colour discipline is
   the site's: green for anything that moves the order forward, the dark ink for
   the button that spends the money, and nothing else competing. */
.st-ck-page { background: var(--st-surface); background-image: none; }

.st-ck__top {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 18px;
  margin: 2px 0 18px;
  flex-wrap: wrap;
}
.st-ck__top .st-title { font-size: clamp(26px, 3.4vw, 2.1rem); }
.st-ck__top .st-sub { margin: 6px 0 0; max-width: 52ch; }
.st-ck__secure {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  padding: 9px 15px;
  border: 1px solid var(--st-border);
  border-radius: 999px;
  background: var(--st-surface);
  color: var(--st-ink);
  font-size: .82rem;
  font-weight: 700;
  white-space: nowrap;
}
.st-ck__secure svg { width: 16px; height: 16px; color: var(--st-green-text-2); }

/* The step rail: four marks, the one being worked on filled in ink. */
.st-steps {
  list-style: none;
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0 0 20px;
  padding: 12px 16px;
  border: 1px solid var(--st-border);
  border-radius: 999px;
  background: var(--st-surface);
  overflow-x: auto;
  scrollbar-width: none;
}
.st-steps::-webkit-scrollbar { display: none; }
.st-step {
  display: flex;
  align-items: center;
  gap: 9px;
  flex: 1 1 0;
  min-width: 0;
  color: var(--st-muted);
  font-size: .82rem;
  font-weight: 700;
  white-space: nowrap;
}
.st-step:not(:last-child)::after {
  content: "";
  flex: 1;
  height: 1.5px;
  border-radius: 999px;
  background: var(--st-border);
}
.st-step__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  flex: 0 0 auto;
  border-radius: 50%;
  border: 1.5px solid var(--st-border);
  background: var(--st-surface);
  font-size: .78rem;
  font-weight: 800;
  color: var(--st-muted);
}
.st-step__num svg { width: 14px; height: 14px; }
.st-step.is-on { color: var(--st-ink); }
.st-step.is-on .st-step__num { background: var(--st-ink-solid); border-color: var(--st-ink); color: #fff; }
.st-step.is-done { color: var(--st-green-text-2); }
.st-step.is-done .st-step__num { background: var(--st-green); border-color: var(--st-green); color: #fff; }

/* The two columns. The rail is real estate the form does not get, so it only
   appears once there is room for both. */
.st-ck {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 18px;
  align-items: start;
}
.st-ck > * { min-width: 0; }
.st-ck__form { display: grid; gap: 18px; }
.st-ck__rail { display: grid; gap: 18px; }

.st-ck__card {
  background: var(--st-surface);
  border: 1px solid var(--st-border);
  border-radius: var(--st-radius);
  padding: 20px 22px;
  box-shadow: var(--st-shadow);
}
.st-ck__card-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 12px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--st-border);
}
.st-ck__card-title {
  font-size: 1.02rem;
  font-weight: 800;
  color: var(--st-ink);
  margin: 0;
}
.st-ck__card-step {
  font-size: .68rem;
  font-weight: 800;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--st-muted);
}
.st-ck .st-field { margin-bottom: 14px; }
.st-ck .st-field:last-child { margin-bottom: 0; }
.st-ck__grid { display: grid; gap: 14px; }
.st-ck__grid--2 { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.st-ck__grid .st-field { margin-bottom: 0; }

/* Door or pickup as two cards rather than two lines: the choice is a shape you
   can see from across the page, and the fee is inside the card that costs it. */
.st-radio--pick {
  align-items: center;
  gap: 12px;
  padding: 16px;
  border-radius: 16px;
  border-width: 1.5px;
  transition: border-color .16s ease, background-color .16s ease, transform .16s ease;
}
.st-radio--pick:hover { border-color: var(--st-green-bright); transform: translateY(-1px); }
.st-radio--pick.is-on {
  border-color: var(--st-green);
  background: rgba(0, 160, 72, .06);
  box-shadow: 0 0 0 3px rgba(0, 160, 72, .12);
}
.st-radio--pick input { width: 18px; height: 18px; margin-top: 0; flex: 0 0 auto; }
.st-radio--pick .st-radio__title { display: flex; align-items: center; gap: 8px; font-size: .95rem; font-weight: 800; }
.st-radio__flag {
  font-size: .62rem;
  font-weight: 800;
  letter-spacing: .08em;
  text-transform: uppercase;
  background: var(--st-lime);
  color: #2a3200;
  border-radius: 999px;
  padding: 3px 8px;
}

/* The payment cards: the mark leads, the words follow. */
.st-ck .st-methods { margin-bottom: 12px; }
.st-ck .st-method {
  border-radius: 14px;
  padding: 14px;
  transition: border-color .16s ease, box-shadow .16s ease, background-color .16s ease;
}
.st-ck .st-method:hover { border-color: var(--st-green-bright); }
.st-ck .st-method.is-on {
  border-color: var(--st-green);
  background: rgba(0, 160, 72, .06);
  box-shadow: 0 0 0 3px rgba(0, 160, 72, .12);
}
.st-ck .st-method__logo { width: 40px; height: 40px; }

/* The summary rail: held in view, with the button that spends the money the
   last thing in it. */
.st-ck__card--sum { padding: 20px 20px 18px; }
.st-ck__sum-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 12px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--st-border);
}
.st-ck__sum-head .st-ck__card-title { margin: 0; }
.st-ck__edit {
  font-size: .8rem;
  font-weight: 700;
  color: var(--st-green-text-2);
  text-decoration: none;
  border-bottom: 1px solid transparent;
}
.st-ck__edit:hover { border-bottom-color: var(--st-green); }
.st-ck__lines .st-line { padding: 14px 0; }
.st-ck__lines .st-line + .st-line { border-top: 1px solid var(--st-border); border-bottom: none; }
.st-ck__lines .st-line__media { width: 58px; height: 58px; border-radius: 12px; }
.st-ck__lines .st-line:last-child { border-bottom: none; }
.st-ck__totals { padding: 12px 0 4px; border-top: 1px solid var(--st-border); }
.st-ck__totals .st-total-row { font-size: .88rem; padding: 6px 0; }
.st-ck__totals .st-total-row--due { font-size: 1.08rem; padding-top: 12px; }

/* The checkout's pay control was an ink slab of its own — a second button in a second colour. It
   is the shop's button now, which is the site's button, and all it adds is the room above it and
   the mark that names it. */
.st-btn--pay { margin-top: 14px; }
.st-btn--pay svg { width: 16px; height: 16px; }
.st-ck__legal {
  font-size: .73rem;
  line-height: 1.6;
  color: var(--st-muted);
  margin: 12px 0 0;
  text-align: center;
}
.st-ck__legal a { color: var(--st-green-text-2); font-weight: 700; text-decoration: none; }
.st-ck__legal a:hover { text-decoration: underline; }

.st-ck__help .st-ck__card-title { margin: 0 0 14px; }
.st-ck__help-list { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
.st-ck__help-item {
  display: grid;
  gap: 3px;
  padding: 12px 11px;
  border: 1px solid var(--st-border);
  border-radius: 13px;
  background: var(--st-surface);
  text-decoration: none;
  transition: border-color .16s ease, transform .16s ease;
}
.st-ck__help-item:hover { border-color: var(--st-green); transform: translateY(-1px); }
.st-ck__help-label { font-size: .8rem; font-weight: 800; color: var(--st-ink); }
.st-ck__help-text { font-size: .72rem; color: var(--st-muted); overflow-wrap: anywhere; }

/* The phone's bar: the total and the button, pinned to the bottom edge where a
   thumb already is. It is a phone's affordance and is taken out of the desktop
   layout entirely, so a wide screen never sees it. */
.st-ck__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 60;
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px));
  background: var(--st-surface);
  border-top: 1px solid var(--st-border);
  box-shadow: 0 -10px 30px -18px rgba(19, 43, 29, .5);
}
.st-ck__bar[hidden] { display: none; }
.st-ck__bar-total { display: grid; gap: 1px; min-width: 0; }
.st-ck__bar-label { font-size: .68rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--st-muted); }
.st-ck__bar-total strong { font-size: 1.1rem; font-weight: 800; color: var(--st-ink); }
.st-ck__bar .st-btn { flex: 0 1 auto; min-width: 0; }
.st-ck__bar .st-btn svg { width: 16px; height: 16px; }
/* The label may be shortened, never clipped. `overflow: hidden` is what gives the ellipsis its
   edge, but on a label set in the display face at `line-height: 1` the box it hides in is shorter
   than the ink inside it: measured on a phone here, the box was 14px around 19px of letters, so
   the top and the bottom of "Place order — ₦9,000" were being cut off by the very rule meant to
   tidy it up. The line box is given the height the face actually draws in, the button a little
   more air of its own, and the label is allowed to shorten with an ellipsis from there. */
.st-ck__bar .st-btn span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1.5; padding-block: 1px; }
@media (max-width: 560px) { .st-ck__bar .st-btn { padding: 13px 20px; } }
/* The bar's own room, given to the words on the button. Measured at 390px the total, the gap and
   the button were 7px more than "Place order — ₦46,000" needed inside it, so the ellipsis was
   eating the last two digits of the amount — the one figure on the bar that must never be guessed
   at. The total keeps its place; the gap between the two loses 4px and the bar loses 8px of its
   side padding, which is 12px back to the label and the whole order price read out in full. */
@media (max-width: 560px) { .st-ck__bar { padding-inline: 12px; gap: 10px; } }

/* ── The rewards card ───────────────────────────────────────────────────────
   The shop's loyalty, in the card where the money is decided. It is a soft panel rather than a
   coloured one, because it sits above the total and a second green block there would compete with
   the button the customer is trying to reach; the green is kept for the two things it means on this
   card — the balance, and the figure that is coming off the bill.

   The whole offer is one label rather than a checkbox with a row of words beside it, so the tap
   target is the card and not the 16px box inside it, and the card itself lights when it is ticked
   (`:has(input:checked)`) so the choice is visible without reading the small print underneath. */
.st-rewards {
  margin: 0 0 14px;
  padding: 13px 14px;
  border: 1px solid var(--st-border);
  border-radius: var(--st-radius);
  background: var(--st-soft);
}
.st-rewards[hidden] { display: none; }
.st-rewards__head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.st-rewards__kicker { font-size: .68rem; font-weight: 800; letter-spacing: .12em; text-transform: uppercase; color: var(--st-muted); }
.st-rewards__balance {
  display: inline-flex;
  align-items: center;
  padding: 4px 10px;
  border-radius: 999px;
  background: rgba(0, 160, 72, .13);
  color: var(--st-green-text);
  font-size: .74rem;
  font-weight: 800;
  line-height: 1.4;
}
.st-rewards__apply {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin-top: 11px;
  padding: 10px 11px;
  border: 1px solid var(--st-border);
  border-radius: 12px;
  background: var(--st-surface);
  cursor: pointer;
  transition: border-color .18s ease, background-color .18s ease;
}
.st-rewards__apply:hover { border-color: var(--st-green); }
.st-rewards__apply:has(input:checked) { border-color: var(--st-green); background: rgba(0, 160, 72, .07); }
.st-rewards__apply input { flex: 0 0 auto; width: 16px; height: 16px; margin-top: 1px; accent-color: var(--st-green); cursor: pointer; }
.st-rewards__copy { display: grid; gap: 2px; min-width: 0; }
.st-rewards__label { font-size: .84rem; font-weight: 700; color: var(--st-ink); }
.st-rewards__value { font-size: .78rem; font-weight: 700; color: var(--st-green-text); }
.st-rewards__note { margin: 9px 0 0; font-size: .76rem; line-height: 1.5; color: var(--st-muted); }

/* ── Checkout, side by side ───────────────────────────────────────────────── */
@media (min-width: 980px) {
  .st-ck { grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); gap: 26px; }
  .st-ck__rail { position: sticky; top: 96px; }
}

/* ── Checkout, on a phone ─────────────────────────────────────────────────── */
@media (max-width: 860px) {
  .st-ck__bar { display: flex; }
  .st-main { padding-bottom: 104px; }
}
@media (min-width: 861px) {
  .st-ck__bar { display: none; }
}
@media (max-width: 620px) {
  .st-ck__top { align-items: flex-start; }
  .st-ck__secure { align-self: flex-start; }
  .st-ck__card { padding: 17px 15px; border-radius: 16px; }
  .st-ck__grid--2 { grid-template-columns: minmax(0, 1fr); }
    /* The checkout's rail at 390px. It used to be one row of steps as wide as they wanted to be:
     measured here at 399px of steps in a 345px window, so the fourth name — "Review" — sat behind
     the pill's own right edge and the rail read as broken rather than as scrollable. Four names
     will not divide a phone's width in one line, so they are not asked to: each step becomes a
     cell of a four-column grid, the number above its name, and the names stop being cut at all.
     At 390px a cell has 75px of room and the longest name measures 52px; at 320px the cell still
     has 63px. The connectors between the numbers are drawn as hairlines across the gaps instead of
     as flex items that would take width the names need. */
  .st-steps {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 6px;
    padding: 11px 10px;
    border-radius: 22px;
  }
  .st-step { position: relative; flex-direction: column; align-items: center; text-align: center; gap: 5px; font-size: .7rem; }
  .st-step:not(:last-child)::after {
    position: absolute;
    top: 10px;
    left: calc(50% + 15px);
    right: calc(-50% + 15px);
    height: 1.5px;
  }
  .st-step__num { width: 22px; height: 22px; font-size: .7rem; }
  .st-step__num svg { width: 12px; height: 12px; }
  /* Doubled, because the sheet names `.st-step__label` again further down to put the ellipsis back
     for the wide rail, and that rule is written after this one. In a cell with room for the whole
     name there is nothing to ellipsize. */
  .st-steps .st-step__label { overflow: visible; text-overflow: clip; }
  .st-ck__help-list { grid-template-columns: minmax(0, 1fr); }
}

/* The two labels the checkout's rail and heading carry. */
.st-ck__top-text { min-width: 0; }
.st-step__label { overflow: hidden; text-overflow: ellipsis; }

/* The fee a delivery card carries sits at the card's far edge, so the two cards
   read as a column of prices rather than two blocks of prose. */
.st-method .st-radio__fee { margin-left: auto; }

/* ══════════════════════════════════════════════════════════════════════════
   The shop on white
   ──────────────────────────────────────────────────────────────────────────
   The storefront is a shop, and a shop's shelf is white: the cream the site
   warms its marketing pages with made every product photograph fight the page
   it sat on. The page is white here and the only colour left is the brand's —
   the green that means "this moves", the ink that means "this spends", and the
   lime that means "new".
   ══════════════════════════════════════════════════════════════════════════ */
.st,
.st-ck-page {
  background: var(--st-surface);
  background-image: none;
  background-attachment: scroll;
}

/* ── The hero band, sized to be read ──
   The band is an introduction, not a poster. The headline sets two lines at a
   size a phone can hold without the words running off the right of the screen,
   and stops growing once it has filled the measure. */
.st-intro .st-title {
  font-size: clamp(25px, 3.6vw, 2.45rem);
  line-height: 1.06;
  max-width: 24ch;
}
.st-intro .st-kicker { font-size: .7rem; letter-spacing: .14em; }
.st-intro .st-sub { font-size: 1rem; }

/* ── One naira, one shape ──
   Every figure the shop prints is body type. Boldonse draws the naira with a
   heavy double bar that reads as a struck-through N at small sizes, so the one
   place a price was set in it — the buy panel — is the one place the currency
   looked like a different currency. Prices are set in the body face at a
   weight strong enough to carry the number, which is what a price wants. */
.st-buy__price,
.st-card__price,
.st-line__price,
.st-total-row--due,
.st-radio__fee,
.st-ck__totals .st-total-row--due,
.st-ck__bar-total strong {
  font-family: var(--font-body, "Figtree", sans-serif);
  font-variant-numeric: tabular-nums;
}
.st-buy__price { font-weight: 800; }

/* ── Flat where it was painted ──
   Two fills survived the flat-paint pass in gradient form; both are one green. */
.st-price__bar-el.is-on { background: var(--st-green); }
.st-price__fill { background: var(--st-green); }

/* ── The two delivery cards are rows ──
   Side by side in a basket's narrow column each card was ~150px wide, and the
   sentence inside it wrapped to three lines beside two figures. A row gives the
   sentence the whole column and puts the fee where the eye already is. */
.st-card-block .st-methods { grid-template-columns: minmax(0, 1fr); }
.st-card-block .st-method { align-items: center; }
.st-card-block .st-method__text { max-width: 46ch; }

/* ── Scrolling ──
   The rail is the one scrollable pane in the shop, so it gets the thin, drawn
   scrollbar rather than the operating system's chrome, and every anchored jump
   lands below the sticky header instead of under it.

   The room a jump has to leave is the header's *own* height, and that is not a number this sheet
   can know: the header is three or four rows deep depending on the page, its rows stand down as
   the customer scrolls, and a phone whose text scale is turned up makes each of them taller. The
   guess that stood here — 104px — was smaller than the header is on a phone even collapsed, so
   `#st-checkout` and every other anchored heading landed 90px under the bar they were scrolled to.
   The figure is measured and written to `--st-head-h` by js/store.js, which every store page loads
   (the cart and the checkout do not load the chrome script, which is why the measurement lives
   there), and the number here is only the stand-in for the first paint, before the script runs. */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}
.st [id] { scroll-margin-top: calc(var(--st-head-h, 90px) + 14px); }
.st-rail__body { scroll-behavior: smooth; }
.st-rail__body::-webkit-scrollbar { width: 8px; }
.st-rail__body::-webkit-scrollbar-track { background: transparent; }
.st-rail__body::-webkit-scrollbar-thumb {
  background: rgba(31, 47, 45, .18);
  border-radius: 999px;
}
.st-rail__body::-webkit-scrollbar-thumb:hover { background: rgba(31, 47, 45, .3); }
.st-rail__body { scrollbar-width: thin; scrollbar-color: rgba(31, 47, 45, .28) transparent; }

/* ══════════════════════════════════════════════════════════════════════════
   The shop's front page
   ──────────────────────────────────────────────────────────────────────────
   Three bands above and below the shelf, so the storefront is a place rather
   than a list: a row of the things the shop is proudest of, three flyers in the
   brand's own colours, a second row of suggestions, and the account band that
   says the shop and the website are one account. Nothing here is painted from
   anywhere but the catalogue this page already holds.
   ══════════════════════════════════════════════════════════════════════════ */

.st-show { display: block; margin: 26px 0 6px; }
.st-show[hidden] { display: none; }
.st-show__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
  margin-bottom: 14px;
}
.st-show__title {
  margin: 0;
  font-family: var(--st-display);
  font-size: 1.15rem;
  font-weight: 400;
  letter-spacing: -.01em;
  color: var(--st-ink);
}
.st-show__nav { display: flex; align-items: center; gap: 8px; }
.st-show__step {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border: 1px solid var(--st-border);
  border-radius: 999px;
  background: var(--st-surface);
  color: var(--st-ink);
  cursor: pointer;
  transition: border-color .16s ease, background-color .16s ease, color .16s ease;
}
.st-show__step svg { width: 17px; height: 17px; }
.st-show__step:hover { border-color: var(--st-green); color: var(--st-green-text-2); background: rgba(0, 160, 72, .06); }
.st-show__more {
  font-size: .8rem;
  font-weight: 700;
  color: var(--st-green-text-2);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  white-space: nowrap;
}
.st-show__more:hover { border-bottom-color: var(--st-green); }

/* The row itself: a scroll container that snaps card to card, with the scrollbar hidden because
   the arrows above it are the affordance — a bar under a row of pictures is clutter. */
.st-scroller {
  display: flex;
  gap: 16px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-left: 2px;
  padding: 2px 2px 12px;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.st-scroller::-webkit-scrollbar { display: none; }
.st-scroller:focus-visible { outline: none; box-shadow: 0 0 0 5px rgba(0, 160, 72, .28); border-radius: 18px; }
.st-scroller > .st-card {
  flex: 0 0 auto;
  width: 246px;
  scroll-snap-align: start;
}

/* The featured row, moving — the class js/store.js puts on the scroller it drives. A snap is a
   row that insists on one card at a time, and the row that now glides a pixel a frame would be
   dragged back to the nearest card between every one of them, so the snap comes off. The
   overflow, the hidden scrollbar and the two arrows are all untouched: with the class absent —
   reduced motion, or two cards or fewer — this is exactly the scroller it was. */
.st-scroller--marquee {
  scroll-snap-type: none;
}

/* A copy of a card is there to fill the belt, never to be read twice: where the movement is off,
   the copies are not shown at all and the row is the originals alone. */
@media (prefers-reduced-motion: reduce) {
  .st-scroller > .st-card[data-clone] { display: none; }
}

/* ── The flyers ──
   Three bands, each one colour the brand owns, set in the display face at a size that reads as
   a poster without shouting, and a link that is part of the card rather than under it. */
.st-flyer {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
  margin: 30px 0 8px;
}
.st-flyer__card {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 24px 22px 22px;
  border-radius: var(--st-radius);
  border: 1px solid transparent;
  min-height: 210px;
}
.st-flyer__kicker {
  margin: 0;
  font-size: .68rem;
  font-weight: 800;
  letter-spacing: .14em;
  text-transform: uppercase;
  opacity: .78;
}
/* Boldonse draws well above its own line box, so the title carries the clearance as padding:
   the boxes already clear each other, and it is the ink that needs the room.

   An advertisement's headline is the one thing on it that has to be read before anything else, and
   at 1.34 leading over two or three lines it was setting as a block rather than as a headline — the
   lines closing each other up above a body copy that had more air between its lines than the
   heading did. It leads at 1.42 now and is given a little more clear space above it, and it balances
   its own last line (`text-wrap: balance`) instead of leaving one word stranded on a line of its
   own, which is what made the three cards look unevenly filled when their headings ran to different
   lengths. */
.st-flyer__title {
  margin: 0;
  padding-top: 14px;
  font-family: var(--st-display);
  font-size: 1.24rem;
  font-weight: 400;
  line-height: 1.42;
  letter-spacing: -.01em;
  text-wrap: balance;
}
.st-flyer__text { margin: 0; font-size: .86rem; line-height: 1.6; opacity: .86; }
/* The flyer's call to action used to be its own translucent pill, which meant the shop had two
   buttons again. It is the shop's shared button now, so this class carries only where the button
   sits in the card's grid — the pill, its display face and its arrow circle all come from `.st-btn`,
   written once at the foot of this sheet. The card paints the button in the variant its own colour
   asks for: `--light` on the green and ink cards, the plain green pill on the white one. */
.st-flyer__cta { margin-top: auto; align-self: flex-start; }
.st-flyer__card--green { background: var(--st-green); color: #fff; }
.st-flyer__card--ink { background: var(--st-ink-solid); color: #fff; }
.st-flyer__card--lime { background: var(--st-lime); color: #23300a; }

/* ── The account band ──
   The shop asks for the website's account, so this is the website's invitation: what an account
   is worth, said in the shop's own terms, and two ways in. */
/* ── The account band, as the shop's one dark panel ──
   Everything else on this page is white, which is what makes the invitation read: the account is
   the only thing the shop is asking for, so it is the only band painted in the brand's ink. The
   glow is a soft radial, not a gradient wash — it sits under the headline and fades before the
   buttons. The three perks are the answer to "why would I", and the tick is drawn rather than
   printed, so the mark and the words share one colour.

   `st-signin__glow` is `aria-hidden` in the markup: it is light, not content. */
.st-signin {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 26px;
  flex-wrap: wrap;
  margin: 32px 0 10px;
  padding: 32px 34px;
  border-radius: 20px;
  overflow: hidden;
  background:
    radial-gradient(120% 160% at 12% 0%, rgba(0, 192, 92, .34) 0%, rgba(0, 160, 72, .12) 42%, rgba(0, 0, 0, 0) 70%),
    linear-gradient(135deg, #14261d 0%, #1f2f2d 58%, #234034 100%);
  box-shadow: 0 22px 46px -26px rgba(20, 38, 29, .8);
}
/* The account band is the one dark panel on the page, and a plain dark panel beside a shop full of
   pictures reads as a form. It carries a picture of the thing an account is for — somebody holding
   the cylinder they ordered — laid into the right-hand end and faded out well before the words, so
   the invitation still lands on flat ink. It is a background image rather than an `<img>` because
   nothing about it is content: it is the same picture at every width, it carries no caption, and it
   needs no description for a screen reader, which is exactly what `background-image` is for. */
.st-signin::before {
  content: "";
  position: absolute;
  inset: 0 0 0 auto;
  width: min(46%, 520px);
  background-image: url("/assets/img/hero-portrait.webp");
  background-size: cover;
  background-position: center 22%;
  opacity: .22;
  -webkit-mask-image: linear-gradient(to right, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 62%);
  mask-image: linear-gradient(to right, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 62%);
  pointer-events: none;
}
.st-signin__glow {
  position: absolute;
  right: -60px;
  top: -80px;
  width: 320px;
  height: 320px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(180, 252, 44, .22) 0%, rgba(180, 252, 44, 0) 62%);
  pointer-events: none;
}
.st-signin__body { position: relative; min-width: 0; max-width: 60ch; z-index: 1; }
.st-signin .st-kicker { margin-bottom: 10px; color: var(--st-lime); letter-spacing: .14em; }
.st-signin__title {
  margin: 0 0 10px;
  font-family: var(--st-display);
  font-size: clamp(22px, 2.3vw, 1.75rem);
  font-weight: 400;
  line-height: 1.12;
  letter-spacing: -.01em;
  color: #fff;
  max-width: 34ch;
}
.st-signin__text { margin: 0 0 14px; font-size: .94rem; line-height: 1.65; color: rgba(255, 255, 255, .74); max-width: 62ch; }
.st-signin__perks {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 8px;
}
.st-signin__perks li {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  font-size: .88rem;
  line-height: 1.45;
  color: rgba(255, 255, 255, .9);
}
.st-signin__tick {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  margin-top: 1px;
  border-radius: 50%;
  background: rgba(180, 252, 44, .18);
  color: var(--st-lime);
}
.st-signin__tick svg { width: 12px; height: 12px; }
.st-signin__actions { position: relative; display: flex; gap: 10px; flex-wrap: wrap; align-items: center; z-index: 1; }
.st-signin__actions .st-btn { text-decoration: none; }
/* The primary is the lime, because on the ink panel the brand's green is nearly invisible — and
   the lime is the shop's own "new", which is what an account is. */
.st-signin__signin { background: var(--st-lime) !important; color: #14261d !important; }
.st-signin__signin:hover { background: #a8ef20 !important; }
.st-signin__create {
  background: transparent !important;
  color: #fff !important;
  border: 1px solid rgba(255, 255, 255, .38) !important;
}
.st-signin__create:hover { background: rgba(255, 255, 255, .1) !important; border-color: rgba(255, 255, 255, .6) !important; }

@media (max-width: 900px) {
  .st-flyer { grid-template-columns: minmax(0, 1fr); }
  .st-flyer__card { min-height: 0; }
}
@media (max-width: 620px) {
  .st-scroller > .st-card { width: 214px; }
  .st-show { margin-top: 20px; }
  .st-signin { padding: 20px 18px; }
  /* In a hand there is no right-hand end to lay a picture into, so the picture becomes the band
     behind the whole panel, dimmed further and faded downward — the words are the panel here, and a
     picture beside them would be a picture under them. */
  .st-signin::before {
    width: 100%;
    background-position: center 18%;
    opacity: .16;
    -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0) 88%);
    mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0) 88%);
  }
  .st-signin__actions { width: 100%; }
  .st-signin__actions .st-btn { flex: 1 1 auto; text-align: center; }
}

/* ── The shop's questions, as the last section of the page ──
   The site's own FAQ section pads 120px above and below its list, because on the marketing pages
   another section sits under it and that padding is the room between two of them. The shop's
   questions are the last thing before the footer, so the padding under them is a band of white
   120px deep with the page's own 72px under that — two section gaps stacked where one is meant.
   The room above stays (the account band is the section before it), and the room below is the
   page's own. */
.st-main #st-faqs { padding-top: 84px; padding-bottom: 0; }

/* ── A row's own remove is a mark ──
   The × replaces the word that used to sit under every line. Its hover takes the error red the
   store already owns, because this is the one control in a row that destroys something. */
.st-x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  flex: 0 0 auto;
  padding: 0;
  border: 1px solid var(--st-border);
  border-radius: 999px;
  background: var(--st-surface);
  color: var(--st-muted);
  cursor: pointer;
  font: inherit;
  transition: border-color .16s ease, background-color .16s ease, color .16s ease;
}
.st-x svg { width: 15px; height: 15px; }
.st-x:hover { border-color: var(--st-danger-border); background: var(--st-danger-soft); color: var(--st-danger); }
.st-x:focus-visible { outline: none; box-shadow: 0 0 0 4px rgba(0, 160, 72, .28); }

/* ── The flyers, as three ads of one size ──
   They were three long strips stacked, which is three advertisements shown one after another —
   and the third was below the fold by the time the first had been read. They are three across on
   a desk instead: equal columns, equal height, the same four parts in the same order in every
   one, so the row reads as one advertisement carrying three offers rather than as three notices
   of different lengths. So one advertisement is one row again — the full width of the page, its
   words on the left in a measure they can be read at, and its way in directly under them on the
   card's own floor. Three rows read as three advertisements, which is what they are:
   advertisements are read one at a time and a row is something the eye can finish. The three cards
   keep the same height whether or not their words run the same length, because the middle of the
   row takes up the slack (`1fr`) and the button is pinned to the bottom — a stack of three rows of
   three different heights is a stack that looks unfinished.

   Each row carries a picture of the thing it is offering, and it is a picture rather than a
   photograph because the card has to stay readable: the image is laid into the empty right-hand
   half, faded into the card's own colour from the middle outwards, so the words sit on flat paint
   and the eye gets a subject. The pictures are the site's own — the rider from the home page, the
   burner from the shop's hero, the cylinder handover from the gallery — so a flyer can never show
   something the shop does not sell or has never shown anywhere else. */
.st-flyer {
  grid-template-columns: minmax(0, 1fr);
  gap: 16px;
  /* One row track per advertisement, and every track the same height. The three cards say
     different amounts of words, and the first one is the shortest, so without this the top of the
     stack sits 22px shorter than the two under it and the whole block looks untidy. Equal rows
     give every advertisement the same canvas — the words keep their own line count, the card
     simply has room to breathe to the tallest one. */
  grid-auto-rows: 1fr;
  align-items: stretch;
}
.st-flyer__card {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto auto 1fr auto;
  align-items: start;
  column-gap: 0;
  /* More air between the four parts, and more of it inside the card: the eyebrow, the headline,
     the sentence and the button were reading as one grey block at 10px apart, and a poster's parts
     have to be separable at a glance. The extra room is on the sides as well, so the headline stops
     at a margin rather than at the picture's own edge. */
  row-gap: 14px;
  min-height: 208px;
  padding: 30px 32px 28px;
}
.st-flyer__kicker { grid-column: 1; grid-row: 1; }
.st-flyer__title { grid-column: 1; grid-row: 2; max-width: 30ch; }
.st-flyer__text { grid-column: 1; grid-row: 3; max-width: 56ch; }
.st-flyer__cta { grid-column: 1; grid-row: 4; justify-self: start; align-self: end; margin-top: 12px; }

/* The picture in the row's right-hand half. It is decorative, so it is `aria-hidden` and takes no
   space in the flow: absolutely placed in the half the words do not use, faded from fully there to
   nothing by the middle, which is what keeps the headline on flat colour. `isolate` on the card
   keeps the negative stacking level inside the card's own paint rather than behind the page. */
.st-flyer__bg {
  position: absolute;
  inset: 0 0 0 auto;
  z-index: -1;
  width: 46%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  opacity: .34;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(to right, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 46%);
  mask-image: linear-gradient(to right, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 46%);
}
/* The white card cannot carry a photograph at the same strength as the two dark ones — white at a
   third opacity over a bright picture is a grey wash — so it is the lights that come down, not the
   card's own paint. */
.st-flyer__card--lime .st-flyer__bg { opacity: .17; }

@media (max-width: 860px) {
  .st-flyer__card { min-height: 0; padding: 24px 22px 22px; }
  .st-flyer__bg { width: 58%; opacity: .26; }
  .st-flyer__card--lime .st-flyer__bg { opacity: .18; }
  .st-flyer__title { max-width: 34ch; }
}
@media (max-width: 560px) {
  .st-flyer { gap: 12px; }
  .st-flyer__card { padding: 22px 18px 20px; }
  /* In a hand the picture goes above the words rather than beside them: at this width a 58% slice
     of the row is a sliver of somebody's shoulder, which is a picture that costs height and says
     nothing. It becomes a band across the top of the card instead, faded downward into the card's
     own colour so the headline still lands on flat paint. */
  .st-flyer__bg {
    inset: 0 0 auto 0;
    width: 100%;
    height: 108px;
    opacity: .3;
    -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0) 100%);
    mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0) 100%);
  }
  .st-flyer__card--lime .st-flyer__bg { opacity: .2; }
}

/* ── An advertisement between the shelves ──
   The band is a row of the catalogue's own grid, spanning every column of it, which is what puts
   it between two rows of products rather than beside one of them — the shelf is a grid of cards and
   an advertisement in it is the one thing that takes the whole width. It is the flyer's own markup
   and the flyer's own paint, so an advertisement cannot look like anything except an advertisement
   the shop runs.

   It keeps no minimum height of its own. Standing between the shelves it is part of the shelf's
   rhythm, and a poster stretched to a fixed height opens a hole in a row of cards that are their own
   size. The margin is the shelf's own gap and a little more, because what separates an advertisement
   from the products around it has to be plainly more than the space between two products. */
.st-ad {
  grid-column: 1 / -1;
  margin: 10px 0 6px;
}
.st-ad .st-flyer__card { min-height: 0; }

/* ── The shop's own flyer ────────────────────────────────────────────────────
   One of the three advertisements is the shop's artwork rather than a card of words: a banner the
   operator drew, with its headline already in the picture. It keeps the class the other two carry —
   the shelf deals it the same way — and marks itself with `data-flyer-art`, so the sheet can tell
   the drawn one from the written ones without a fourth class the dealing counts on.

   The picture is shown as it was drawn, whole, on the shop's own white: not cropped to fill a band,
   not tinted, nothing laid over it. The artwork brings its own white around its own green, so where
   the file ends and the page begins there is no seam at all — the band simply is the flyer, at its
   own ratio, the whole band the link, and the words travelling in the file's own alt text for anyone
   who cannot see it. */
.st-flyer__card[data-flyer-art] { display: block; padding: 0; min-height: 0; background: var(--st-surface); }
.st-flyer__card[data-flyer-art] .st-flyer__art { display: block; }
.st-flyer__card[data-flyer-art] .st-flyer__art img { width: 100%; height: auto; display: block; border-radius: inherit; }

/* ── A wider rail ──
   The filter rail is the one column a customer works in, and 264px was narrow enough that a
   brand's name and its count sat on top of each other. */
@media (min-width: 900px) {
  .st-shop { grid-template-columns: 300px minmax(0, 1fr); }
}

/* ── What a card says about itself: the stars, and the colours ──
   Two figures a shopper uses to choose without opening the product: what other people thought of
   it, and whether it comes in more than one. Both sit on one line under the name so a grid reads
   down a column of prices rather than through a paragraph per card. */
.st-card__flags {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  min-height: 20px;
  margin: 6px 0 2px;
}

/* The stars are drawn twice — a grey five and the same five clipped to the average — so a 4.5
   shows half a star rather than being rounded up to a 5 the shop did not earn. */
.st-rating {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: .78rem;
  color: var(--st-muted);
  font-family: var(--font-body, inherit);
  white-space: nowrap;
}
.st-stars { position: relative; display: inline-block; line-height: 1; letter-spacing: 1px; }
.st-stars__base { color: #d9ddd9; }
.st-stars__fill {
  position: absolute;
  left: 0;
  top: 0;
  overflow: hidden;
  white-space: nowrap;
  color: #f0a028;
}
.st-rating__text strong { color: var(--st-ink); font-weight: 700; }
.st-rating--none .st-rating__text { color: #9aa8a0; }

/* The dots, at card size: small enough to sit beside a price without asking for a decision. */
.st-swatches--mini { display: inline-flex; align-items: center; gap: 4px; }
.st-swatches--mini .st-swatch__dot { width: 13px; height: 13px; box-shadow: 0 0 0 1px rgba(31,47,45,.14) inset; }
.st-swatch__dot--img { background-size: cover; background-position: center; }
/* A colour with no hex yet reads as an empty ring, not as a missing dot — a placeholder for a
   value the shop still has to fill in, and a visible one. */
.st-swatch__dot--blank {
  background:
    repeating-linear-gradient(45deg, #e6eae6 0 3px, #f6f8f5 3px 6px) !important;
}
.st-swatch__more {
  font-size: .68rem;
  font-weight: 700;
  color: var(--st-muted);
  font-family: var(--font-body, inherit);
}

/* ── Ratings and reviews on the product page ── */
.st-reviews {
  margin-top: 26px;
  padding: 22px 24px;
  border: 1px solid var(--st-border);
  border-radius: var(--st-radius);
  background: var(--st-surface);
}
.st-reviews h2 { font-size: 1.05rem; margin: 0 0 14px; color: var(--st-ink); }
.st-reviews__score {
  display: flex;
  align-items: center;
  gap: 22px;
  flex-wrap: wrap;
  padding-bottom: 16px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--st-border);
}
.st-reviews__average {
  font-size: 2.4rem;
  font-weight: 800;
  line-height: 1;
  color: var(--st-ink);
  font-family: var(--font-body, inherit);
}
.st-reviews__count { font-size: .85rem; color: var(--st-muted); }
.st-reviews__bars { display: grid; gap: 4px; min-width: 240px; flex: 1 1 240px; }
.st-reviews__bar { display: grid; grid-template-columns: 44px 1fr 30px; align-items: center; gap: 10px; font-size: .78rem; color: var(--st-muted); }
.st-reviews__bar-star { white-space: nowrap; color: #b9a06a; }
.st-reviews__track { height: 8px; border-radius: 999px; background: var(--st-soft); overflow: hidden; }
.st-reviews__fill { display: block; height: 100%; background: #f0a028; border-radius: 999px; }
.st-reviews__bar-count { text-align: right; font-variant-numeric: tabular-nums; }
.st-reviews__empty {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 14px 16px;
  border: 1px dashed var(--st-border);
  border-radius: var(--st-radius);
  background: var(--st-soft);
  margin-bottom: 16px;
}
.st-reviews__empty p { margin: 0; font-size: .88rem; color: var(--st-muted); }
.st-reviews__list { list-style: none; margin: 0 0 18px; padding: 0; display: grid; gap: 14px; }
.st-review { padding: 14px 16px; border: 1px solid var(--st-border); border-radius: var(--st-radius); background: var(--st-soft); }
.st-review__head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 6px; }
.st-review__author { font-weight: 700; font-size: .86rem; color: var(--st-ink); }
.st-review__stars { color: #f0a028; letter-spacing: 1px; font-size: .8rem; }
.st-review__date { font-size: .74rem; color: var(--st-muted); margin-left: auto; font-family: var(--font-body, inherit); }
.st-review__body { margin: 0; font-size: .9rem; line-height: 1.6; color: var(--st-text); }

.st-review-form {
  display: grid;
  gap: 10px;
  padding: 16px 18px;
  border: 1px solid var(--st-border);
  border-radius: var(--st-radius);
  background: var(--st-soft);
}
.st-review-form__title { margin: 0; font-weight: 700; font-size: .92rem; color: var(--st-ink); }
.st-review-form__stars { display: inline-flex; gap: 2px; }
.st-review-form__star {
  border: 0;
  background: none;
  padding: 0 2px;
  font-size: 1.5rem;
  line-height: 1;
  color: #d9ddd9;
  cursor: pointer;
}
.st-review-form__star.is-on { color: #f0a028; }
.st-review-form__star:focus-visible { outline: none; box-shadow: 0 0 0 4px rgba(0, 160, 72, .28); border-radius: 4px; }
.st-review-form textarea {
  width: 100%;
  border: 1px solid var(--st-border);
  border-radius: 12px;
  padding: 12px 14px;
  font: inherit;
  font-size: .9rem;
  color: var(--st-text);
  background: var(--st-surface);
  resize: vertical;
}
.st-review-form textarea:focus { outline: none; box-shadow: 0 0 0 3px rgba(0, 160, 72, .28); border-color: var(--st-green); }
.st-review-form .st-btn { justify-self: start; }
.st-review-form__note { margin: 0; font-size: .84rem; color: var(--st-muted); }
.st-review-form__signin { font-size: .88rem; color: var(--st-muted); margin: 0; }
.st-review-form__signin a { color: var(--st-green-text); font-weight: 700; }

/* ── One line, read the way a shop's line is read ────────────────────────
   A basket line is four facts — what it is, what one costs, how many, and what that comes to —
   and one way out. It was a row of three boxes with the × unrolled to the front of it, which put
   the way out in the first place the eye lands while the two figures a customer actually checks
   sat furthest apart, and the way out still had to be looked for. Every shop worth copying draws
   this line the same way instead: the picture on the left, the name and what one costs beside it,
   the way out in the top-right corner of the line — where a way out is looked for, and where it
   cannot be pressed by accident on the way to the quantity — and then the two numbers that answer
   the line, how many and what that comes to, on a second row under the name, at the two ends.

   The boxes already existed; they only needed placing. This is a grid of three columns and two
   rows: the picture spans both rows of the first column, the words take the first row, the × takes
   the third column of that same row, and the second row carries the stepper under the words with
   the line's own total at the far edge. The actions column is still unrolled with
   `display: contents`, so the ×, the stepper and the total stay the children they already were
   and not one line of markup — on the basket page, in the drawer or in the summary — has to move
   to be read in a better order.

   Scoped to the lines that carry a quantity (the basket and the drawer) plus the lines that are
   gone, because the wishlist's line carries a button and the checkout's own summary carries no
   controls at all: each of those keeps the row it was drawn with. */
.st-line:has(.st-qty),
.st-line--gone {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: start;
  column-gap: 14px;
  row-gap: 12px;
}
.st-line:has(.st-qty) > .st-line__actions,
.st-line--gone > .st-line__actions { display: contents; }
.st-line:has(.st-qty) > .st-line__media,
.st-line--gone > .st-line__media { grid-column: 1; grid-row: 1 / span 2; }
.st-line:has(.st-qty) > .st-line__body,
.st-line--gone > .st-line__body { grid-column: 2; grid-row: 1; min-width: 0; }
/* The way out, in the corner it is looked for. It stops being a row item with an `order` on it and
   becomes a cell of its own, so the two numbers below never shuffle when it is pressed. */
.st-line:has(.st-qty) > .st-line__actions > .st-x,
.st-line--gone > .st-line__actions > .st-x {
  grid-column: 3;
  grid-row: 1;
  justify-self: end;
  align-self: start;
  margin: 0;
  order: 0;
}
.st-line:has(.st-qty) > .st-line__actions > .st-qty {
  grid-column: 2;
  grid-row: 2;
  justify-self: start;
  align-self: center;
  margin: 0;
}
/* The line's own money answers in the same cell whatever the page calls it: the basket writes
   `.st-line__price` for the line total and the drawer writes `.st-line__total`. */
.st-line:has(.st-qty) > .st-line__actions > .st-line__price,
.st-line:has(.st-qty) > .st-line__actions > .st-line__total {
  grid-column: 3;
  grid-row: 2;
  justify-self: end;
  align-self: center;
}

/* ── And the milk band above the footer ──
   The footer's wave band is painted cream on the marketing pages, where the page above it is
   cream too. In the shop the page is white, so that band stopped being a transition and became a
   strip of milk sitting between the shop and its own footer. The wave itself is untouched: it is
   still the page meeting the footer, drawn in the footer's own dark. */
.site-footer .footer-wave { background: var(--st-surface); }

/* ── The product page uses both its columns ──
   `.st-product` is a two-column grid, but the block the product paints into (#st-book) is a single
   child of it, so the grid held one item in its first column and left the whole second column
   empty — the page's own space, unused. `display: contents` takes that wrapper out of the box
   model, so the gallery, the buy box, the description and the reviews become the grid's own items.

   The photograph and what it takes to buy stand side by side. The buy box carries the decision:
   the name, the price, the short line the operator wrote to sell it, the choices, the quantity and
   the button. The long description is not part of a decision, and set in a half-width column
   beside a portrait photograph it ran forty-odd lines down the page while the whole width of the
   page under both columns went unused. It spans that width now, where a paragraph is read, and
   the reviews keep the full width beneath it. On one column nothing changes — they simply
   stack, which is also the order they are painted in. */
#st-book { display: contents; }
@media (min-width: 900px) {
  .st-product .st-gallery { grid-column: 1; grid-row: 1; }
  .st-product .st-buy { grid-column: 2; grid-row: 1; }
  .st-product .st-desc { grid-column: 1 / -1; grid-row: 2; }
  .st-product .st-reviews { grid-column: 1 / -1; grid-row: 3; }
}

/* The line the operator wrote to sell it, standing with the price rather than in a block of its
   own at the bottom of the column. */
.st-buy__intro { font-size: .95rem; line-height: 1.6; margin: 12px 0 0; color: var(--st-ink); }

/* A description read across a page and not down a column: the measure is wide now, so the type is
   a step larger and the leading a step looser than the half-width card it used to sit in. */
.st-desc { padding: 22px 24px; }
.st-desc h2 { font-size: 1.12rem; }
.st-desc p { font-size: .98rem; line-height: 1.8; }

/* ── The photograph answers the pointer ──
   A store photograph is the thing a customer is judging, and at 1:1 in a card they get the shape of
   it and not a great deal else — the weave, the stitching and the print are as small as the card
   allows. Pointing at it magnifies it, with the pointer's own position as the origin, so whatever
   is under the hand is what grows. Nothing here is a fade, a filter or a second picture: it is the
   same photograph at a larger scale, which is the only magnification that can be trusted to show
   what the customer is buying. The magnifier is drawn only for a fine pointer, so a touch screen
   keeps its pinch and its scroll and is never told about a control it does not have, and a reader
   who has asked for less motion keeps the plain photograph. */
.st-gallery__main { overflow: hidden; }
.st-gallery__main img { transition: transform .2s ease-out; }
.st-gallery__main.is-zoom img {
  transform: scale(2.2);
  transform-origin: var(--st-zoom-x, 50%) var(--st-zoom-y, 50%);
}
.st-gallery__zoom {
  position: absolute; top: 12px; right: 12px; display: none;
  width: 36px; height: 36px; place-items: center;
  border-radius: 50%; background: rgba(255, 255, 255, .94); color: var(--st-ink);
  border: 1px solid var(--st-border); box-shadow: 0 2px 8px rgba(20, 30, 25, .12);
  pointer-events: none;
}
.st-gallery__zoom svg { width: 18px; height: 18px; }
@media (hover: hover) and (pointer: fine) {
  .st-gallery__zoom { display: grid; }
}
@media (prefers-reduced-motion: reduce) {
  .st-gallery__main img { transition: none; }
  .st-gallery__main.is-zoom img { transform: none; }
}

/* ── The store sign-in panel's heading ──
   The eyebrow, the headline and the line under it were three things set as one lump. The heading
   keeps its own air: the kicker clears it, its lines are not packed, and the body text is a step
   away from it. The headline carries a little more room between its own lines than it did, because
   it wraps to two lines on a laptop and read as one crowded block. */
.st-signin .st-kicker { margin-bottom: 20px; }
.st-signin__title { margin: 0 0 18px; line-height: 1.32; }

/* ── The account band reads down the page, not across it ──
   The panel was a row: the copy on the left and the two buttons pushed to the far right, so the
   pair floated at the end of the line the eye never reached. The buttons belong under the reasons
   to make an account, where the eye has just finished — so the band is one column and the actions
   follow the bullets. `align-items: flex-start` keeps both children flush left at every width. */
.st-signin { flex-direction: column; align-items: flex-start; justify-content: flex-start; gap: 22px; }
.st-signin__body { max-width: 72ch; }

/* ── The account band's own air ──
   The band is the one place on the page that asks for something rather than showing something, and
   it was reading as a cluster: the kicker, the headline, the sentence and the three perks were
   sitting 14 to 20px apart inside 32px of padding, which is closer than the shelf's own rhythm and
   made the panel look like a form to be got through. Every step now has room to be read as its own
   statement — the eyebrow above the headline, the headline above the sentence, the sentence above
   the perks, and the perks apart from each other — and the panel itself is given the wider padding
   the rest of the shop's cards use, so the invitation breathes inside its own ink.

   The phone keeps the site's own tighter rhythm (the rules further down set 12/12/20 there when a
   hand is watching) and takes only the padding and the gap, because a panel 42px in on a 360px
   screen is a panel the words have to fight for room inside. */
.st-signin { padding: 40px 42px; gap: 30px; }
.st-signin .st-kicker { margin-bottom: 22px; }
.st-signin__title { margin-bottom: 20px; }
.st-signin__text { margin-bottom: 22px; }
.st-signin__perks { gap: 12px; }
.st-signin__perks li { line-height: 1.5; }
.st-signin__actions { gap: 12px; }
@media (max-width: 620px) {
  .st-signin { padding: 26px 22px; gap: 24px; }
}

/* ── The card's swatches, a little more than a hint ──
   At 13px the dots read as part of the rating beside them. A touch larger, with a clear ring,
   and they are unmistakably swatches — the answer to "does this come in another colour?". */
.st-swatches--mini { gap: 5px; }
.st-swatches--mini .st-swatch__dot { width: 15px; height: 15px; box-shadow: 0 0 0 1.5px rgba(31, 47, 45, .2) inset; }

/* ── Where the shop delivers, dressed as the one control it is ──
   The location was a flat translucent pill: readable, and indistinguishable from the links
   beside it. It is the shop's only "who is this for" control, so it is given the weight that
   asks for a click — a glass pill on the dark bar, a lime pin that breathes, a tracked uppercase
   label the eye lands on, and a light that sweeps across it on hover. Everything is drawn with
   the two pseudo-elements and the existing spans, so the markup keeps the one contract the rest
   of the shop reads (`st-loc` and `st-loc__text`). */
.st-loc {
  position: relative;
  overflow: hidden;
  gap: 8px;
  padding: 6px 13px 6px 11px;
  border: 1px solid rgba(180, 252, 44, .45);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, .2) 0%, rgba(255, 255, 255, .02) 100%),
    rgba(7, 24, 15, .6);
  color: #fff;
  font-size: .71rem;
  font-weight: 800;
  letter-spacing: .1em;
  text-transform: uppercase;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .24), 0 8px 20px -12px rgba(0, 0, 0, .8);
  transition: border-color .2s ease, box-shadow .2s ease, transform .12s ease;
}
.st-loc:hover {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, .26) 0%, rgba(255, 255, 255, .05) 100%),
    rgba(7, 24, 15, .7);
  border-color: rgba(180, 252, 44, .85);
  transform: translateY(-1px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .3), 0 12px 26px -14px rgba(0, 0, 0, .9);
}
.st-loc:focus-visible { outline: none; box-shadow: 0 0 0 4px rgba(180, 252, 44, .45); }
.st-loc__pin {
  width: 15px;
  height: 15px;
  color: var(--st-lime);
  filter: drop-shadow(0 0 6px rgba(180, 252, 44, .55));
  animation: st-loc-breathe 2.6s ease-in-out infinite;
}
.st-loc__text { letter-spacing: inherit; }
.st-loc__caret { width: 12px; height: 12px; opacity: .85; transition: transform .2s ease; }
.st-loc:hover .st-loc__caret { transform: translateY(1px); }
/* The sheen: a band of light that crosses the pill on hover, and never intercepts a click. */
.st-loc::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(105deg, transparent 32%, rgba(255, 255, 255, .38) 50%, transparent 68%);
  transform: translateX(-130%);
}
.st-loc:hover::after { animation: st-loc-sheen .75s ease; }
@keyframes st-loc-sheen { to { transform: translateX(130%); } }
@keyframes st-loc-breathe {
  0%, 100% { filter: drop-shadow(0 0 4px rgba(180, 252, 44, .35)); }
  50% { filter: drop-shadow(0 0 9px rgba(180, 252, 44, .8)); }
}
@media (prefers-reduced-motion: reduce) {
  .st-loc__pin { animation: none; filter: drop-shadow(0 0 6px rgba(180, 252, 44, .55)); }
  .st-loc:hover::after { animation: none; }
}

/* ── The announcement bar, above the whole shop ──
   The first thing on every store page is what the shop wants to say: what is free, what is fresh,
   how to reach the team. It scrolls on its own, without stopping, because a line that waits to be
   dragged is a line most customers never read.

   The loop is seamless by arithmetic rather than by a script: the same row of adverts is drawn
   twice and the track slides exactly half its own width, so the moment the second copy fills the
   frame the first is back where it started. The second copy is `aria-hidden` — a screen reader
   has already heard the first, and the numbers are the same. Nothing is fetched and nothing is
   timed to a viewport, so it reads the same on a phone and on a shop's big screen. */
.st-adbar {
  background: #0b0b0b;
  color: #fff;
  overflow: hidden;
}
.st-adbar__track {
  display: flex;
  width: max-content;
  animation: st-adbar-scroll 46s linear infinite;
  will-change: transform;
  backface-visibility: hidden;
}
/* Neither copy may ever shrink. If a flex item were allowed to compress, the arithmetic that makes
   the two copies identical would stop holding and the loop would show a seam; `flex: 0 0 auto`
   plus `min-width: max-content` pins each group to exactly its content width at any font size. */
.st-adbar__group {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  min-width: max-content;
  padding: 9px 0;
  white-space: nowrap;
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .01em;
}
/* The gap lives on the item, not on the group, so the distance from the last advert of one copy to
   the first of the next is the same as every other gap — which is what makes the loop invisible. */
.st-adbar__item { display: inline-flex; align-items: center; gap: 8px; margin-right: 30px; }
.st-adbar__item::before {
  content: "";
  flex: 0 0 auto;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--st-lime);
}
.st-adbar__item strong { color: var(--st-lime); font-weight: 700; }
@keyframes st-adbar-scroll {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(-50%, 0, 0); }
}
/* A reader who asked the system for less motion gets the adverts standing still rather than gone. */
@media (prefers-reduced-motion: reduce) {
  .st-adbar__track { animation: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   The shop as a place: the gate, the room behind it, the basket carried
   around it, and the shape it all takes on a phone
   ──────────────────────────────────────────────────────────────────────────
   None of this is about a product. It is about arriving: whether a closed
   shop can be caught open, what a customer finds while it is shut, how the
   basket follows them through it, and whether any of it holds together in a
   hand rather than on a desk.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── The launch gate, before the first paint ───────────────────────────────
   js/store-boot.js runs from <head> before the body is drawn and leaves
   `st-boot` on the document while it asks whether the shop is public. While
   that class is on, the page's own content is not painted — so a customer
   refreshing a shop that has not opened can never catch the real store in the
   one frame before the coming-soon panel takes its place. The class comes off
   the moment the answer does, after the shop or the panel is already standing.

   It fails closed on purpose: if nothing ever gets to take it down, the
   header and the footer still read and the shop stays out of sight, which is
   the safer of the two mistakes for a shop that has not opened. */
html.st-boot main.st-main,
html.st-boot .st-sticky,
html.st-boot .st-fab { visibility: hidden; }
/* A closed shop keeps its basket out of the way entirely, not just while the
   answer is in flight: there is nothing to check out of a shop that is shut. */
html.st-closed .st-sticky,
html.st-closed .st-fab { display: none !important; }

/* ── The coming-soon room ──────────────────────────────────────────────────
   What a customer meets while the shop is being built, and the only thing on
   the page, so it is allowed to be a room rather than a notice: a drawn mark,
   the shop's own words, what is coming, and two ways on. Flat fills — the one
   moving part is the dot in the pill, and it stops for a reader who asked for
   less motion. */
.st-coming {
  position: relative;
  overflow: hidden;
  max-width: 720px;
  margin: 6px auto 0;
  padding: 44px 30px 38px;
  border: 1px solid var(--st-border);
  border-radius: 24px;
  background: var(--st-surface);
  text-align: center;
  box-shadow: var(--st-shadow);
}
.st-coming__art {
  /* Block-level on purpose, and the kicker below depends on it. An inline-flex here made the
     mark and the "Coming soon" pill two inline boxes in one line box, so the pill sat beside the
     icon — vertically centred on it — instead of under it. A block box ends the line, and
     `margin: 0 auto` centres it exactly as it did before. */
  display: flex;
  align-items: center;
  justify-content: center;
  width: 64px;
  height: 64px;
  margin: 0 auto 18px;
  border-radius: 20px;
  background: rgba(0, 160, 72, .1);
  color: var(--st-green-text-2);
}
.st-coming__art svg { width: 32px; height: 32px; }
.st-coming__kicker {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 16px;
  padding: 7px 15px;
  border-radius: 999px;
  background: var(--st-lime);
  color: #22300c;
  font-size: .69rem;
  font-weight: 800;
  letter-spacing: .11em;
  text-transform: uppercase;
}
.st-coming__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--st-green);
  animation: st-coming-pulse 1.9s ease-in-out infinite;
}
@keyframes st-coming-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: .4; transform: scale(.75); }
}
.st-coming__title {
  margin: 0 0 14px;
  font-family: var(--st-display);
  font-size: clamp(24px, 4vw, 2.2rem);
  font-weight: 400;
  line-height: 1.16;
  letter-spacing: -.01em;
  color: var(--st-ink);
}
.st-coming__text {
  max-width: 54ch;
  margin: 0 auto 26px;
  font-size: .98rem;
  line-height: 1.7;
  color: var(--st-muted);
}
.st-coming__perks {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 10px;
  margin: 0 0 28px;
  padding: 0;
  text-align: left;
}
.st-coming__perk {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 13px 15px;
  border: 1px solid var(--st-border);
  border-radius: 14px;
  background: var(--st-soft);
  font-size: .85rem;
  font-weight: 600;
  line-height: 1.45;
  color: var(--st-ink);
}
.st-coming__perk svg { width: 17px; height: 17px; flex: 0 0 auto; margin-top: 2px; color: var(--st-green-text-2); }
.st-coming__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
}
.st-coming__actions .st-btn { text-decoration: none; }
@media (prefers-reduced-motion: reduce) { .st-coming__dot { animation: none; } }
@media (max-width: 560px) {
  .st-coming { padding: 32px 20px 28px; border-radius: 20px; }
  .st-coming__title { max-width: none; }
  .st-coming__actions .st-btn { flex: 1 1 auto; }
}

/* ── The basket in the corner ──────────────────────────────────────────────
   The one control a shopper looks for on every page, so it says all three
   things at once — the basket, what is in it, what it costs — and the count
   rides on the pill rather than taking a column of its own. */
.st-fab {
  right: 16px;
  bottom: calc(16px + env(safe-area-inset-bottom, 0px));
  gap: 10px;
  padding: 9px 18px 9px 9px;
  border: none;
  border-radius: 999px;
  background: var(--st-green);
  box-shadow: 0 14px 30px -12px rgba(10, 143, 60, .62), inset 0 0 0 1px rgba(255, 255, 255, .16);
  transition: transform .16s ease, box-shadow .16s ease, background-color .16s ease;
}
.st-fab:hover {
  background: var(--st-green-dark);
  transform: translateY(-2px);
  box-shadow: 0 20px 38px -14px rgba(10, 143, 60, .7), inset 0 0 0 1px rgba(255, 255, 255, .2);
}
.st-fab:active { transform: translateY(0) scale(.97); }
.st-fab__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: rgba(255, 255, 255, .18);
}
.st-fab__label { font-size: .61rem; letter-spacing: .11em; opacity: .88; }
.st-fab__total { font-size: 1rem; font-variant-numeric: tabular-nums; }
.st-fab__count {
  min-width: 21px;
  height: 21px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--st-surface);
  color: var(--st-green-text);
  font-size: .7rem;
  font-weight: 800;
  line-height: 21px;
  box-shadow: 0 2px 6px -2px rgba(19, 47, 29, .5);
}

/* ── The basket drawer ─────────────────────────────────────────────────────
   A panel rather than a page: it slides over the shop, holds the shop behind
   a softened scrim, and keeps its foot still while the lines scroll. The
   panel is rounded on the side it opens from, so it reads as a drawer rather
   than as the page growing a second edge. */
.st-drawer__scrim {
  background: rgba(12, 22, 16, .5);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
.st-drawer__panel {
  width: min(452px, 94vw);
  border-left: none;
  border-top-left-radius: 24px;
  border-bottom-left-radius: 24px;
  box-shadow: -30px 0 70px -30px rgba(12, 22, 16, .6);
}

/* The drawer is where a basket is read, so it is built as a small page of its own rather than as a
   list inside a panel: a head that says what is in it, lines with room for a picture and a name, a
   strip of the promises that are true of every order, and a foot that holds the money and the way
   out. One figure belongs at the top — how many things — because the money belongs where the total
   is, and two figures in the same panel make a person look for the one that matters. Every number
   in the foot is the server's; nothing in this drawer is priced here. */
.st-drawer__head { align-items: center; padding: 20px 20px 16px; }
.st-drawer__title { font-family: var(--st-display); font-size: 1.22rem; font-weight: 400; }
.st-drawer__count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 26px;
  height: 26px;
  margin-left: 8px;
  padding: 0 9px;
  border-radius: 999px;
  background: var(--st-green);
  color: #fff;
  font-size: .78rem;
  font-weight: 700;
  line-height: 1;
  vertical-align: middle;
}
.st-drawer__count[hidden] { display: none; }
.st-drawer__sub { margin-top: 6px; font-size: .8rem; }
.st-drawer__close {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border-color: transparent;
  background: var(--st-soft);
}
.st-drawer__close:hover { background: #e9efe9; }
.st-drawer__close svg { width: 17px; height: 17px; }
.st-drawer__body { padding: 4px 0 10px; }
.st-drawer__empty { padding: 52px 26px 44px; }
.st-drawer__empty .st-state__text { max-width: 32ch; }
.st-drawer__empty-actions { display: grid; gap: 9px; max-width: 250px; margin: 0 auto; }

/* A line in the drawer's own proportions. The basket page's rows carry the same parts at their own
   sizes and are left alone: this is that row with a bigger picture, because a drawer is read at a
   glance and a picture is what a glance goes to first. */
.st-drawer .st-line { align-items: flex-start; gap: 14px; padding: 15px 20px; }
.st-drawer .st-line__media { width: 72px; height: 72px; border-radius: 13px; }
.st-drawer .st-line__name { font-size: .95rem; margin-bottom: 4px; }
.st-drawer .st-line__variant { font-size: .81rem; margin-bottom: 5px; }
.st-drawer .st-line__price { font-size: .84rem; font-weight: 600; color: var(--st-muted); }
.st-drawer .st-line__actions { gap: 10px; }
.st-drawer .st-line__total { font-size: .98rem; font-weight: 800; color: var(--st-ink); }
/* The × is no longer the last thing in an actions column that has to be pushed to a corner by
   hand: it is a cell of the line's own grid now, in the top-right of the line, and the rule that
   used to hold it at the foot of that column is gone with the column. */
.st-drawer .st-line { row-gap: 13px; }

/* The promises that are true of every order, said once, where the basket is read. They are the
   shop's own three, and not one of them is a price: the price depends on the zone and is answered
   at checkout, which is why the drawer states no delivery figure it cannot stand behind. */
.st-drawer__promises {
  margin: 14px 20px 6px;
  padding: 15px 16px;
  border: 1px solid var(--st-border);
  border-radius: var(--st-radius);
  background: var(--st-soft);
}
.st-drawer__promise {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  font-size: .82rem;
  line-height: 1.5;
  color: var(--st-text);
}
.st-drawer__promise + .st-drawer__promise { margin-top: 10px; }
.st-drawer__promise svg { width: 16px; height: 16px; flex: 0 0 auto; margin-top: 1px; color: var(--st-green-text-2); }

.st-drawer__foot {
  padding: 16px 20px calc(20px + env(safe-area-inset-bottom, 0px));
  box-shadow: 0 -18px 36px -30px rgba(12, 22, 16, .7);
}
.st-drawer__totals {
  margin-bottom: 14px;
  padding: 14px 15px 12px;
  border: 1px solid var(--st-border);
  border-radius: var(--st-radius);
  background: var(--st-soft);
}
.st-drawer__totals .st-total-row { font-size: .87rem; padding: 4px 0; }
.st-drawer__totals .st-total-row--due { margin-top: 8px; padding-top: 10px; }
.st-drawer__actions { gap: 9px; }
@media (max-width: 560px) {
  .st-drawer__panel { width: 100%; border-radius: 0; }
  .st-drawer .st-line__media { width: 64px; height: 64px; }
}

/* ── The basket pages, headed like pages ───────────────────────────────────
   The basket, the wishlist and the checkout each open on a plain h1 in the
   page's own body rather than in the shop's hero band, so they are set in the
   display face and given the air a page's title wants — the only heading of
   its size on the page, and it should look like it. */
.st-main > .st-title {
  font-family: var(--st-display);
  font-weight: 400;
  font-size: clamp(26px, 3.4vw, 2.15rem);
  line-height: 1.14;
  letter-spacing: -.01em;
  margin: 6px 0 8px;
}
.st-main > .st-sub { max-width: 62ch; margin: 0 0 22px; color: var(--st-muted); }

/* The wishlist's own aside has no cards in it, so it is given the card look
   itself — one panel of explanation rather than a heading and three loose
   paragraphs floating beside the list. The basket's aside holds priced cards
   and is left exactly as it is. */
.st-aside:not(:has(.st-card-block)) {
  padding: 20px 20px 22px;
  border: 1px solid var(--st-border);
  border-radius: var(--st-radius);
  background: var(--st-surface);
}
.st-aside:not(:has(.st-card-block)) .st-section-title { margin: 0 0 10px; }
.st-aside:not(:has(.st-card-block)) .st-btn { justify-self: start; margin-top: 2px; }

/* ── The shop in a hand ────────────────────────────────────────────────────
   A phone is where most of this shop is read, and a 390px screen is not a
   narrow desk: the columns have to be full width, the headings have to stop
   growing, the rows have to give each other room, and nothing may be wider
   than the screen it is sitting in. Every rule here is a size or a gap —
   nothing is hidden and nothing is rearranged, because a phone should get the
   same shop, only set for the space it has. */
@media (max-width: 640px) {
  .st-main { padding: 16px 13px 52px; }
  .st-intro { margin: 6px 0 16px; }
  .st-intro .st-title { font-size: clamp(24px, 7.6vw, 2rem); max-width: none; }
  .st-intro .st-sub { font-size: .93rem; margin-bottom: 16px; }
  .st-main > .st-title { font-size: clamp(23px, 7vw, 1.9rem); }
  .st-main > .st-sub { font-size: .92rem; margin-bottom: 18px; }
  .st-section-title { margin: 22px 0 10px; font-size: 1rem; }

  /* The featured row's heading and its two arrows and its "see everything"
     link are four things on one line at 390px, so the controls take the line
     under the title and the link keeps to the right of them. */
  .st-show { margin-top: 20px; }
  .st-show__head { flex-wrap: wrap; gap: 10px; margin-bottom: 12px; }
  .st-show__title { font-size: 1.05rem; }
  .st-show__nav { width: 100%; }
  .st-show__more { margin-left: auto; }
  .st-scroller { gap: 12px; }
  .st-scroller > .st-card { width: 204px; }
  .st-shop { gap: 14px; }

  /* A basket line, at three hundred and something pixels.
     The line is built as three boxes — the picture, the words, and the controls — and on a desk
     the controls are unrolled into the row itself, which is what puts the quantity beside the
     price. On a phone that is five things competing for 317px: the picture kept its 54, the words
     were squeezed to 42 and the rest wrapped underneath. So the controls are given back their own
     box and their own row, the words get the width they need above it, and the row reads in the
     order a thumb moves — the way out first, then the quantity, then the money, at the far edge. */
  .st-line { padding: 12px 12px 13px; gap: 10px; row-gap: 10px; flex-wrap: wrap; align-items: flex-start; }
  .st-line .st-line__actions {
    display: flex;
    flex: 1 0 100%;
    flex-direction: row;
    align-items: center;
    gap: 10px;
    margin-left: 0;
  }
  .st-line .st-line__actions .st-x { order: -1; }
  .st-line .st-line__actions .st-qty { margin-left: 0; }
  .st-line .st-line__actions .st-line__price { margin-left: auto; }
  .st-line__media { width: 54px; height: 54px; border-radius: 9px; }
  .st-line__name { font-size: .85rem; }
  .st-line__price { font-size: .84rem; }
  .st-line .st-btn { margin-left: auto; }
  .st-qty button { width: 34px; }
  .st-qty input { width: 40px; }

  .st-panel { border-radius: 14px; }
  .st-totals { padding: 13px; }
  .st-sticky { padding: 10px 13px calc(10px + env(safe-area-inset-bottom, 0px)); }
  .st-sticky__value { font-size: 1.05rem; }
  .st-toast { left: 13px; right: 13px; bottom: 16px; transform: none; max-width: none; }
}

/* The basket pill sits in the same corner as the way back to the top and the
   chat, so at the narrowest widths it drops the word it does not need and
   keeps the two figures that matter. */
@media (max-width: 420px) {
  .st-fab { right: 12px; bottom: calc(12px + env(safe-area-inset-bottom, 0px)); padding: 8px 14px 8px 8px; }
  .st-fab__icon { width: 28px; height: 28px; }
  .st-fab__label { display: none; }
}

/* ── The phone, once more: a narrow card and a thumb ──────────────────────
   A two-column phone card is 168px wide, which leaves 142px for the flags row, and the
   unrated row — five stars and the words "No reviews yet" — is 147px of `nowrap`. The card
   clips its own overflow, so those words were cut in half on the shop's front page. The row
   is now allowed to put its words under the stars, and the figure is drawn a shade tighter so
   that at 390px it still reads on one line: the wrap is the guarantee, the tightening is the
   tidy up. The same pass gives the small things a thumb can find — a 15px tally of a link in
   the utility bar was a link a finger misses. */
@media (max-width: 640px) {
  .st-rating {
    gap: 5px;
    font-size: .74rem;
    min-width: 0;
    white-space: normal;
    flex-wrap: wrap;
    row-gap: 2px;
  }
  .st-stars { letter-spacing: .5px; }
  .st-rating__text { min-width: 0; }

  /* Touch targets: 40px for the bar's own links and the rail's arrows, 44px for the one text
     link that opens the whole shelf, 36px for the pill on a card. */
  .st-util__links a {
    display: inline-flex;
    align-items: center;
    min-height: 40px;
    padding: 0 2px;
  }
  .st-util__inner { padding-top: 2px; padding-bottom: 2px; }
  .st-show__step { width: 40px; height: 40px; }
  .st-show__more { display: inline-flex; align-items: center; min-height: 44px; }
  /* A dialog's close control is a thumb's target and takes the site's own floor for one, which is
     what the sheet it closes is already written to. */
  .st-qv__close { width: 44px; height: 44px; }
  /* The eye is a disc in the card's own corner stack rather than a line of text now, and a thumb
     gets 36px of it — the same floor as the rest of the row, and the disc is drawn at that size
     rather than stretched to it, so a floor two pixels over its height does not turn the circle
     beside the heart into a small ellipse. */
  .st-card__quick { width: 36px; height: 36px; min-height: 36px; }

  /* The stepper's buttons are stretched to the height of the figure between them, and that
     figure was 23px — under the 24px a target is allowed to be, let alone a thumb's 44. The
     product page and the cart line carry the same component, so it grows in one place. */
  .st-qty { min-height: 40px; }
  .st-qty button { width: 40px; }
}

/* ════════════════════════════════════════════════════════════════════════
   The shop's hover language
   ──────────────────────────────────────────────────────────────
   Every hoverable thing in the shop used to answer the pointer in its own way — a card lifted 2px,
   a chip jumped 1px, a button changed colour — and each chose its own duration and its own easing,
   so moving the cursor across the grid read as a series of unrelated twitches rather than one shop
   answering. This is that answer, once, for all of them:

     · the surface rises and takes a ring, so it is plain what the pointer is over;
     · the picture inside it grows, slowly, which is the shop leaning in;
     · and what was hidden arrives — the view that opens, the label that names the action.

   One decelerating curve is shared, so a hover that crosses three cards reads as a single
   movement, and the durations are long enough to be seen and short enough not to be waited for.
   None of it is a gradient, a blur or a shadow over the whole surface: what changes is scale,
   position and ink. A touch screen gets none of it — there is no pointer to hover with, and a
   state that sticks after a tap is worse than no state at all — and a reader who has asked for
   less motion keeps the colour change and nothing else. */
@media (hover: hover) and (pointer: fine) {
  /* The card: the surface rises, the picture leans in, and the shop warms its own border rather
     than dropping a bigger shadow on itself. */
  .st-card {
    transition: transform .34s cubic-bezier(.22, .68, .24, 1), border-color .34s cubic-bezier(.22, .68, .24, 1),
      box-shadow .34s cubic-bezier(.22, .68, .24, 1);
  }
  .st-card:hover {
    transform: translateY(-5px);
    border-color: rgba(0, 160, 72, .38);
    box-shadow: 0 22px 44px -30px rgba(19, 43, 29, .55);
  }
  .st-card__media { position: relative; overflow: hidden; }
  .st-card__media img { transition: transform .62s cubic-bezier(.22, .68, .24, 1); }
  .st-card:hover .st-card__media img { transform: scale(1.075); }
  /* The name keeps its ink. A card that turned its own headline green under the pointer was
     answering twice: the surface had already risen and the picture had already leaned in, and a
     third change of colour on the words read as a link being hovered rather than as a card.
     So the hover is movement here, and the colour is left to the ink the card was designed in. */
  .st-card__name a { transition: none; }
  .st-card:hover .st-card__name a { color: inherit; }

  /* The chips: a ring and a lift rather than a jump, so the chosen one still reads as chosen. */
  .st-chip { transition: transform .22s cubic-bezier(.22, .68, .24, 1), border-color .22s cubic-bezier(.22, .68, .24, 1), box-shadow .22s cubic-bezier(.22, .68, .24, 1); }
  .st-catnav .st-chip:hover {
    transform: translateY(-2px);
    border-color: rgba(0, 160, 72, .55);
    box-shadow: 0 10px 22px -16px rgba(19, 43, 29, .9), 0 0 0 3px rgba(0, 160, 72, .12);
  }

  /* The buttons: the colour deepens, the button rises a hair and takes a ring. No sheen. */
  .st-btn { transition: background-color .22s cubic-bezier(.22, .68, .24, 1), transform .22s cubic-bezier(.22, .68, .24, 1), box-shadow .22s cubic-bezier(.22, .68, .24, 1); }
  .st-btn:hover { transform: translateY(-1px); box-shadow: 0 12px 26px -18px rgba(19, 43, 29, .9), 0 0 0 3px rgba(0, 160, 72, .14); }
  .st-btn:active { transform: translateY(0); }

  /* The product's own actions, with the mark that names them. */
  .st-buy .st-btn { display: inline-flex; align-items: center; justify-content: center; gap: 9px; }
  .st-btn__icon { width: 17px; height: 17px; flex: 0 0 auto; }
}

/* A reader who asked for less motion gets the ink and not the movement. */
@media (prefers-reduced-motion: reduce) {
  .st-card, .st-card__media img, .st-card__name a, .st-card__quick, .st-chip, .st-btn {
    transition: none !important;
    transform: none !important;
  }
}

/* ── Row 3, when the categories outnumber the width ──
   The row scrolls sideways on a narrow screen, and a row that only clips its last pill reads as a
   row that is broken rather than one that moves — the screenshot that came in showed the last
   category sliced in half with nothing at all to say there was more behind it. Two things say it
   now: the edge fades, and only while there is genuinely something behind it, because a permanent
   fade on a row that fits is its own small lie. The classes are set from the pixels by the script
   that paints the chips. */
.st-catnav__inner { scroll-snap-type: x proximity; scroll-padding-inline: 16px; }
.st-catnav .st-chip { scroll-snap-align: start; }
.st-catnav__inner.is-more-right { -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 42px), transparent 100%); mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 42px), transparent 100%); }
.st-catnav__inner.is-more-left { -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 42px); mask-image: linear-gradient(90deg, transparent 0, #000 42px); }
.st-catnav__inner.is-more-left.is-more-right {
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 42px, #000 calc(100% - 42px), transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 42px, #000 calc(100% - 42px), transparent 100%);
}

/* ── The shop's opening is a window, not a heading ───────────────────────────
   An eyebrow, a headline and a lede said what the shop is in three lines of type and showed
   nothing at all, and the headline was the only thing on the page set larger than the products —
   a strange thing for a shop to shout about itself. It is a window now: a dark green panel with the
   one sentence that says what this place is and a way in, and the shop's own featured product
   beside it, painted from the catalogue the page has already read. Nothing here is a stock
   photograph of something the shop does not sell. The panel keeps the full width on its own until
   a picture is actually painted into the shot, which is what `has-shot` marks — so a failed read
   leaves a panel and not a gap where a picture should have been. */
.st-hero { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; margin: 22px 0 28px; }
.st-hero__panel {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  padding: 30px 26px;
  border: 1px solid rgba(180, 252, 44, .18);
  border-radius: 22px;
  background: #103f26;
  color: #eaf9ef;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .07), 0 26px 50px -36px rgba(19, 43, 29, .95);
}
.st-hero__kicker {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 14px;
  font-size: .71rem;
  font-weight: 800;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--st-lime);
}
/* The eyebrow takes no rule. A short line before a label is a useful mark when the label stands
   on white and needs separating from the page; on its own panel, with its own colour, it was a
   dash explaining nothing. */
.st-hero__title {
  font-family: var(--st-display);
  font-weight: 400;
  font-size: clamp(24px, 3.1vw, 2.15rem);
  line-height: 1.2;
  letter-spacing: -.02em;
  margin: 0 0 14px;
  max-width: 26ch;
  color: #fff;
}
.st-hero__sub { margin: 0 0 22px; font-size: 1rem; line-height: 1.7; max-width: 54ch; color: rgba(234, 249, 239, .84); }
.st-hero__actions { display: flex; align-items: center; flex-wrap: wrap; gap: 18px; }
.st-hero__aside {
  padding-bottom: 3px;
  border-bottom: 1px solid rgba(180, 252, 44, .5);
  color: #eaf9ef;
  font-size: .88rem;
  font-weight: 700;
  text-decoration: none;
  transition: color .18s ease, border-color .18s ease;
}
.st-hero__aside:hover { color: #fff; border-bottom-color: var(--st-lime); }
.st-hero__shot {
  position: relative;
  display: block;
  overflow: hidden;
  border: 1px solid var(--st-border);
  border-radius: 22px;
  background: var(--st-soft);
  aspect-ratio: 4 / 3;
  text-decoration: none;
}
.st-hero__shot[hidden] { display: none; }
/* The card's own corner problem, in the hero: this picture leans in at 1.05 under the pointer and
   the 22px arc around it is the ancestor's clip, which the composited layer escapes — so the
   picture carries the radius itself and the arc survives the zoom. */
.st-hero__shot img { display: block; width: 100%; height: 100%; object-fit: cover; border-radius: inherit; transition: transform .6s cubic-bezier(.22, .68, .24, 1); }
.st-hero__tag {
  position: absolute;
  left: 14px;
  right: 14px;
  bottom: 14px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 14px;
  border-radius: 14px;
  background: rgba(255, 255, 255, .95);
  color: var(--st-ink);
  font-size: .9rem;
  font-weight: 800;
  box-shadow: 0 14px 30px -22px rgba(19, 43, 29, .95);
}
.st-hero__name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.st-hero__more { flex: 0 0 auto; color: var(--st-green-text); font-size: .78rem; font-weight: 700; white-space: nowrap; }
@media (hover: hover) and (pointer: fine) {
  .st-hero__shot:hover img { transform: scale(1.05); }
}
@media (min-width: 900px) {
  .st-hero.has-shot { grid-template-columns: minmax(0, 1.25fr) minmax(0, .85fr); gap: 18px; }
  .st-hero__panel { padding: 38px 34px; }
  .st-hero__shot { height: 100%; aspect-ratio: auto; min-height: 330px; }
}
@media (max-width: 640px) {
  .st-hero__panel { padding: 24px 20px; border-radius: 18px; }
  .st-hero__title { font-size: clamp(22px, 6.2vw, 1.8rem); }
  .st-hero__shot { border-radius: 18px; }
}

/* ── The dots as controls ────────────────────────────────────────────────────
   A dot that moves the price is a button, and it has to look pressable without shouting: the ring
   around the chosen one is the shop's own green, and a colour nothing sellable offers is dimmed
   rather than hidden — "we sell that one and it is out" is a different thing from never having
   offered it, and a shelf that deletes the difference is telling the customer a smaller truth. */
.st-swatches--pick { gap: 7px; }
.st-swatches--pick .st-swatch__dot {
  width: 15px; height: 15px; padding: 0; cursor: pointer;
  transition: transform .22s cubic-bezier(.22,.68,.24,1), box-shadow .22s ease;
}
.st-swatches--pick .st-swatch__dot.is-on {
  box-shadow: 0 0 0 2px #fff, 0 0 0 3.5px var(--st-green);
}
.st-swatches--pick .st-swatch__dot[disabled] { opacity: .38; cursor: not-allowed; }
.st-swatches--pick .st-swatch__pick:focus-visible { outline: none; box-shadow: 0 0 0 5px rgba(0, 160, 72, .28); }

@media (hover: hover) and (pointer: fine) {
  .st-swatches--pick .st-swatch__pick:hover:not([disabled]) { transform: scale(1.16); }
}
@media (prefers-reduced-motion: reduce) {
  .st-swatches--pick .st-swatch__dot { transition: none; }
  .st-swatches--pick .st-swatch__pick:hover:not([disabled]) { transform: none; }
}

/* ── The top of the shop, dressed as one piece ───────────────────────────────
   Three rows of the header had three ideas of an edge: a black ticker, a flat green band and a
   white bar, each stopping wherever its own padding stopped. They read as one header now. Both
   dark rows carry the same lime hairline on their underside — the ticker's a whisper, the band's
   a shade louder — so the colour deepens in steps rather than restarting, and the white bar keeps
   its own hairline so the three stack with two visible seams and no gaps between them.

   The row of ways to reach a person is where this shows most. Its links were flat paragraphs at
   86% opacity with a browser underline on hover, which is the treatment of body copy, not of a
   menu. They are a menu now: a shade brighter, 600 rather than 500, more air between them, and a
   lime rule that grows out from the left of the link the pointer is over. The number is the one
   thing on the row that is not a destination in the shop but a way to a human, so it is the one
   thing set in white at 800 — unboxed, because a box on the end of that row is a box against the
   edge, and an edge is where a scroll container cuts. */
.st-adbar { border-bottom: 1px solid rgba(180, 252, 44, .13); }
.st-adbar__group { padding: 10px 0; font-size: .79rem; letter-spacing: .015em; color: rgba(255, 255, 255, .84); }
.st-adbar__item { margin-right: 38px; }
.st-adbar__item::before { width: 6px; height: 6px; box-shadow: 0 0 9px rgba(180, 252, 44, .75); }
.st-adbar__item strong { font-weight: 800; letter-spacing: .01em; }

.st-util { border-bottom: 1px solid rgba(180, 252, 44, .16); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .06); }
.st-util__inner { min-height: 46px; gap: 20px; padding-top: 5px; padding-bottom: 5px; }
.st-util .st-util__links a {
  position: relative;
  color: rgba(255, 255, 255, .8);
  font-weight: 600;
  letter-spacing: .015em;
  opacity: 1;
  text-decoration: none;
  transition: color .18s ease;
}
.st-util .st-util__links a:hover { color: #fff; text-decoration: none; }
.st-util .st-util__links a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 1px;
  background: var(--st-lime);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform .26s cubic-bezier(.22, .68, .24, 1);
}
.st-util .st-util__links a:hover::after,
.st-util .st-util__links a:focus-visible::after { transform: scaleX(1); }
.st-util .st-util__links a:focus-visible { outline: none; box-shadow: 0 0 0 6px rgba(180, 252, 44, .45); }
@media (min-width: 641px) {
  .st-util .st-util__links a { padding: 4px 0; }
  /* The narrower the row gets, the less air it has to spend — the phone rules below take it to 14px. */
  .st-util__links { gap: 24px; }
}

/* The number was given a lime-outlined pill in the first pass. On a bar that already ends in the
   row's own edge, the pill's box was the thing nearest that edge, and a scroll container clipping
   one pixel of it read as a cut-off button. It is the plainest thing on the row now, and the
   loudest: white, 800, a hair of tracking, and nowrap so it can never be broken across two lines.
   Its affordance is the same lime rule the links beside it use, so nothing about it is underlined
   twice or boxed for no reason. */
.st-util__links a.st-util__tel {
  color: #fff;
  font-weight: 800;
  letter-spacing: .025em;
  white-space: nowrap;
}

/* The white bar earns a warmer edge once the page has moved under it, so a pinned header reads as
   a layer of the shop rather than as a seam across it. */
.st-head.is-stuck { border-bottom-color: rgba(0, 160, 72, .2); box-shadow: 0 1px 2px rgba(19, 43, 29, .06), 0 16px 36px -28px rgba(19, 43, 29, .95); }

@media (prefers-reduced-motion: reduce) {
  .st-util .st-util__links a::after { transition: none; }
}

/* ── The product's small print, as a spec sheet ──────────────────────────────
   SKU, shelf and delivery were three grey sentences stacked under the buttons, where the eye
   reads them as one loose paragraph and skips all of it. Each fact is a row now, with a glyph in
   a rounded chip on the left and a tracked label over its value, which is the shape a
   specification has everywhere else and the shape a person scans rather than reads. The rules
   between the rows are the 1px gaps in the grid showing the border colour through, so nothing
   is drawn twice and a row that the shop did not fill in leaves no empty line behind it. */
.st-meta {
  display: grid;
  gap: 1px;
  margin-top: 22px;
  border: 1px solid var(--st-border);
  border-radius: 14px;
  overflow: hidden;
  background: var(--st-border);
  line-height: 1.3;
}
.st-spec { display: flex; align-items: center; gap: 11px; padding: 11px 13px; background: var(--st-surface); }
.st-spec__icon {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  border-radius: 9px;
  background: rgba(0, 160, 72, .09);
  color: var(--st-green-text);
}
.st-spec__icon svg { width: 16px; height: 16px; }
.st-spec__body { display: flex; flex-direction: column; min-width: 0; }
.st-spec__label { font-size: .665rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--st-muted); }
.st-spec__value { font-size: .845rem; font-weight: 600; color: var(--st-ink); }

/* ── The one product's name, at the size of a name ───────────────────────────
   A shop's front-page heading and a product's name are both `.st-title`, and the display face
   makes the difference plain: at the front page's size the name of one hose wrapped to two lines
   and took more of the panel than the price, the photograph and the button together. The name is
   set for its own column — a size that fits one line when it can, with enough leading that two
   lines read as two lines rather than as one struck-through block. */
.st-buy .st-title {
  font-size: clamp(21px, 2.05vw, 1.72rem);
  line-height: 1.36;
  letter-spacing: -.004em;
  margin: 0 0 15px;
}
.st-buy__brand { margin: 0 0 8px; }

/* ── The figure, as the answer the page exists to give ───────────────────────
   The price was already the largest thing in the panel and still read as a caption, because it
   was set like every other line — same colour, same margins, nothing to say that this is the one
   number the page is about. It has a panel of its own now — a tint of the shop's own green, a
   hairline round it, a lit top edge — with the amount set large and tight, the price it
   replaced struck through beside it, and the difference stated as a saving in a pill at the end
   of the row rather than left to the customer to work out. It is kept small on purpose: the
   first pass gave it a green bar down one side, which read as a warning stripe rather than as a
   figure, and the panel spent more of the column than the price did. Nothing is invented: the
   pill and the struck-through figure appear only when the shop has actually set a price to beat. */
.st-buy__figure {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 11px;
  margin: 4px 0 0;
  padding: 10px 14px;
  border: 1px solid rgba(0, 160, 72, .16);
  border-radius: 12px;
  background: rgba(0, 160, 72, .05);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .8);
}
.st-buy__price {
  font-size: clamp(25px, 2.3vw, 1.85rem);
  line-height: 1.08;
  letter-spacing: -.018em;
  color: var(--st-ink);
}
.st-buy__was { margin-left: 0; font-size: .9rem; }
.st-buy__off {
  margin-left: auto;
  align-self: center;
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--st-green);
  color: #fff;
  font-size: .7rem;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* ── A review, with room to be read ──────────────────────────────────────────
   The first review used to start on the line the rating bars finished on, and inside the card the
   name, the stars and the body were 6px apart, which is the spacing of a list of conditions rather
   than of something a person wrote. The list takes a little air above its first row, the card
   takes a little more inside itself, and the body is set on the same leading as the description
   it sits under, so the two read as the same kind of writing. */
.st-reviews__list { margin: 14px 0 18px; gap: 16px; }
.st-review { padding: 16px 18px; }
.st-review__head { gap: 12px; margin-bottom: 10px; }
.st-review__body { line-height: 1.72; }

/* ── The search, as the one thing in the bar that is a shape ──────────────────
   The field was a rectangle with a labelled green button inside it, which read as a form — the
   submit was a destination of its own rather than the end of the field. It is one control now: the
   field taller and rounder, and the submit a circle drawn inside its right end, which is the shape
   the eye already knows means "go". The word is still there for a screen reader; sighted shoppers
   get the magnifier, which is the word this control is written in everywhere. */
.st-searchbar { padding: 5px 5px 5px 16px; }
.st-searchbar__go {
  width: 40px;
  height: 40px;
  padding: 0;
  justify-content: center;
  border-radius: 50%;
  flex: 0 0 auto;
}
.st-searchbar__go span {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.st-searchbar__go svg { width: 17px; height: 17px; }

/* ── The panel's own background, and how faint it is ─────────────────────────
   The panel is the shop's window, so the thing inside it is the picture the shop sells by: a
   burner lit, blue flame and all. It is the panel's background image at 35% — a third of the way
   out of the dark green ground rather than painted on top of it — which is faint enough that the
   white heading crosses it unread, and present enough that the panel is a photograph rather than a
   colour. The green the panel is filled with is what the picture is blended against, so the copy's
   contrast is measured against that dark ground on any crop: even on the brightest flame, three
   parts of ten leaves the panel dark.
   The path is absolute, from the site root, and that is not a detail: a url() inside a stylesheet
   is resolved against the STYLESHEET's own directory, so the relative form asked for
   /css/assets/store/hero-gas-burner.png and the panel has been a flat colour with the picture it
   was given nowhere on it.
   The one rule this keeps is that nothing is painted UNDER the words. The photograph is a
   background of the panel, the copy is a child of it, and the two can never change places.
   Two declarations, lightest first: the picture was a 640x360 PNG at 396KB, which is a third of a
   megabyte of the shop's first screen spent on a band that is drawn at 35% opacity. The WebP of
   exactly the same artwork is 30KB — the browser takes the second declaration when it can read
   WebP, which is every browser this shop supports, and the PNG stays under it for anything that
   cannot. */
.st-hero__panel::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: url("/assets/store/hero-gas-burner.png");
  background-image: url("/assets/store/hero-gas-burner.webp");
  background-size: cover;
  background-position: center;
  opacity: .35;
}
.st-hero__panel > * { position: relative; }

/* ── Two rows of a heading are two rows ──────────────────────────────────────
   The display face is drawn with a low x-height and tall ascenders, so two lines set at the
   leading a body face wants read as one struck-through block — which is what a two-row heading
   looked like wherever a product or a section name was long enough to wrap. Every display heading
   in the shop is given the same open leading here, once, rather than per heading. */
.st-title,
.st-section-title,
.st-hero__title,
.st-qv__name,
.st-drawer__title,
.st-coming__title,
.st-state__title { line-height: 1.28; }
.st-hero__title { line-height: 1.3; }

/* ── One button, one hover, and the light one actually arrives ───────────────
   Two hovers were competing: an early pair of rules that darkened the fill, and a later one that
   swapped it for a gradient, which the flat-paint pass then took away again by naming `.st-btn` in
   the group that sets the green. The result was a button whose hover could change nothing at all.
   The store's button answers in one way now — the fill steps down to the darker green, the border
   follows it, and nothing else moves — and the light variant, which had been quietly losing to that
   same group rule since the flat pass, is finally white with ink on it. A button that has to be a
   different shape (the search's circle) still is; this is what the shop means by "a button". */
.st-btn { transition: background .18s ease, border-color .18s ease, color .18s ease; }
.st-btn:hover { background: var(--st-green-dark); border-color: var(--st-green-dark); filter: none; }
.st-btn--light { background: var(--st-surface); color: var(--st-ink); border-color: var(--st-border); box-shadow: none; }
.st-btn--light:hover { background: var(--st-soft); border-color: var(--st-border); }
.st-btn:focus-visible { outline: none; box-shadow: 0 0 0 4px rgba(0, 160, 72, .28); }

/* ── The panel on a phone ────────────────────────────────────────────────────
   Stacked, the panel and the picture each need their own air rather than the gap a two-column
   layout was using: the copy takes the padding of a card, the actions sit directly under the words
   with a full-width call to action as the obvious next step, and the picture keeps a wide ratio so
   a tall window is not spent on one product. */
@media (max-width: 640px) {
  .st-hero { gap: 12px; margin: 18px 0 24px; }
  .st-hero__panel { padding: 26px 20px 24px; }
  .st-hero__kicker { margin-bottom: 12px; }
  .st-hero__title { margin-bottom: 12px; }
  .st-hero__sub { margin-bottom: 20px; }
  .st-hero__actions { gap: 14px; width: 100%; }
  .st-hero__cta { flex: 1 1 auto; justify-content: center; text-align: center; }
}

/* ── One roll, every card ────────────────────────────────────────────────────
   The panel above sets the shop's phone rhythm: eyebrow, headline, sentence, and the button
   beneath them — 12px apart down to the last of the words, and 20px above the button. It was the
   panel's rhythm alone: the flyers ran their four parts at 14px with 26px above the button, the
   page intros set 12/14/16, and the sign-in band set 20/18/14, so no two sections stacked alike
   and a page of them read as a dozen different hands. On a phone they all take the panel's
   rhythm now, so the air between a section's own parts is the same air wherever the section is.
   Only the space between the parts is set here — every section keeps the sizes and the widths it
   draws, and the desk layout is untouched. */
@media (max-width: 640px) {
  .st-intro .st-title { margin-bottom: 12px; }
  .st-intro .st-sub { margin-bottom: 20px; }
  .st-main > .st-title { margin-bottom: 12px; }
  .st-main > .st-sub { margin-bottom: 20px; }
  .st-flyer__card { row-gap: 12px; }
  /* The card's own gap is 12 now, so the button adds the 8 that makes its 20. */
  .st-flyer__cta { margin-top: 8px; }
  .st-signin .st-kicker { margin-bottom: 12px; }
  .st-signin__title { margin-bottom: 12px; }
  .st-signin__text { margin-bottom: 20px; }
}


/* ── The flyer that was lemon ────────────────────────────────────────────────
   The second card of the pair was painted in the shop's lemon, on a page whose own ground is a
   warm cream: a lemon card on cream is a card that reads as a highlight with nothing to highlight,
   and dark ink on lemon is one of the lowest-contrast pairings in the sheet. It is a white card
   now — the same card as any other panel in the shop, with the lemon kept for the small marks that
   are meant to be seen (the ticker's dots, the location pin, the eyebrow on the hero) rather than
   for a whole surface. The kicker takes the shop's own green so the card still has a colour of its
   own, and the call to action is a real button instead of a black wash. */
.st-flyer__card--lime {
  background: var(--st-surface);
  color: var(--st-ink);
  border: 1px solid rgba(19, 43, 29, .1);
}
.st-flyer__card--lime .st-flyer__kicker { color: var(--st-green-text); }
.st-flyer__card--lime .st-flyer__text { color: var(--st-text); opacity: 1; }

/* ── The gateway mark, in a tile ─────────────────────────────────────────────
   A payment brand is a wordmark far wider than it is tall, so a square slot shrinks it to a
   smudge. It gets the shape it actually has — a wide white tile, which is also the shape these
   marks are drawn on everywhere a card is chosen — and a gateway whose logo could not be sourced
   gets its initial on the same tile rather than an empty square. The selector carries `.st-method`
   because the checkout page already sizes this tile at 40x40 through `.st-ck .st-method__logo`,
   which a bare class cannot outrank however late it is written. */
.st-method .st-method__logo {
  flex: 0 0 auto;
  width: 84px;
  height: 38px;
  padding: 5px 7px;
  border: 1px solid var(--st-border);
  border-radius: 9px;
  background: var(--st-surface);
  object-fit: contain;
}
.st-method .st-method__logo--chip {
  display: grid;
  place-items: center;
  padding: 0;
  background: var(--st-soft);
  color: var(--st-ink);
  font-size: .95rem;
  font-weight: 800;
}
/* ── The menu, made from the dashboard's own categories ──────────────────────
   The row of pills was one pill per category the operator had made, which is a filing system read
   aloud: a dozen names in the order they were typed, with nothing to say that "Domestic Cylinders"
   and "Commercial Cylinders" are the same kind of thing. It is the shop's menu now — Shop all,
   then the group each category belongs to — and a group whose category has subcategories under it
   carries a caret and opens them.
   A row with children is one control in two halves: the pill filters to the whole group, the caret
   opens the list, and neither does the other's job. The list is a strip under the row and not a
   card inside it, because the row scrolls sideways and a scroll container clips what it holds —
   the one place a dropdown can be drawn is outside the thing that scrolls. */
.st-catnav { position: relative; }
.st-catnav .st-menu { display: inline-flex; align-items: stretch; flex: 0 0 auto; scroll-snap-align: start; }
.st-catnav .st-menu__link { border-top-right-radius: 7px; border-bottom-right-radius: 7px; }
.st-catnav .st-menu__toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 32px;
  margin-left: -1px;
  padding: 0;
  border: 1px solid rgba(19, 43, 29, .1);
  border-left-color: rgba(19, 43, 29, .18);
  border-top-right-radius: 999px;
  border-bottom-right-radius: 999px;
  background: var(--st-surface);
  color: var(--st-ink);
  cursor: pointer;
  font-family: inherit;
  transition: border-color .16s ease, color .16s ease, box-shadow .16s ease;
}
.st-catnav .st-menu__toggle:hover { border-color: rgba(0, 160, 72, .45); color: var(--st-green-text); }
.st-catnav .st-menu__caret { width: 15px; height: 15px; transition: transform .2s cubic-bezier(.22, .68, .24, 1); }
.st-catnav .st-menu.is-open .st-menu__caret { transform: rotate(180deg); }
.st-catnav .st-menu.is-open .st-menu__toggle {
  color: var(--st-green-text);
  border-color: rgba(0, 160, 72, .45);
  box-shadow: 0 0 0 3px rgba(0, 160, 72, .12);
}
/* A group the shelf is being read through, when what was chosen is one of its subcategories: the
   row says so without pretending to BE the chosen pill. */
.st-catnav .st-menu__link.is-child {
  border-color: rgba(0, 160, 72, .55);
  background: var(--st-surface);
  color: var(--st-green-text);
  box-shadow: inset 0 0 0 1px rgba(0, 160, 72, .3), 0 6px 16px -14px rgba(19, 43, 29, .8);
}

/* ── The strip a group opens is a strip of the page, not a card over it ───────
   It used to hang absolutely under the row, which meant the first thing it covered was the top of
   the shelf — the customer chose "Domestic Cylinders" and the products it had just filtered to were
   hidden behind the panel that asked the question. It sits in the flow now: the same column, right
   under the row, pushing the shelf down and leaving every product the choice produced in view. It
   is the row's own drawer opening, so it arrives with one short drop rather than appearing whole. */
.st-catnav__panel {
  border-top: 1px solid rgba(19, 43, 29, .08);
  border-bottom: 1px solid rgba(19, 43, 29, .1);
  background: var(--st-surface);
  box-shadow: 0 26px 44px -34px rgba(19, 43, 29, .95);
  animation: st-menu-drop .2s cubic-bezier(.22, .68, .24, 1) both;
}
.st-catnav__panel[hidden] { display: none; }
@keyframes st-menu-drop {
  from { opacity: 0; transform: translateY(-8px); }
  to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .st-catnav__panel { animation: none; }
}
.st-catnav__panel-inner {
  display: flex;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: 12px 22px;
  padding-top: 14px;
  padding-bottom: 16px;
}
.st-catnav__panel-head { flex: 0 0 auto; min-width: 190px; max-width: 260px; }
.st-catnav__panel-title { margin: 0 0 3px; font-family: var(--st-display); font-size: 1rem; line-height: 1.3; color: var(--st-ink); }
.st-catnav__panel-blurb { margin: 0; font-size: .8rem; line-height: 1.5; color: var(--st-muted); }
.st-catnav__panel-list { display: flex; flex-wrap: wrap; gap: 8px; flex: 1 1 240px; min-width: 0; }
.st-catnav__panel-item {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 8px 13px;
  border: 1px solid rgba(19, 43, 29, .12);
  border-radius: 999px;
  background: var(--st-surface);
  color: var(--st-text);
  font-family: inherit;
  font-size: .82rem;
  font-weight: 600;
  cursor: pointer;
  transition: border-color .2s cubic-bezier(.22, .68, .24, 1), transform .2s cubic-bezier(.22, .68, .24, 1), box-shadow .2s cubic-bezier(.22, .68, .24, 1);
}
.st-catnav__panel-item:hover {
  transform: translateY(-2px);
  border-color: rgba(0, 160, 72, .55);
  box-shadow: 0 10px 22px -16px rgba(19, 43, 29, .9), 0 0 0 3px rgba(0, 160, 72, .12);
}
.st-catnav__panel-item.is-on { background: var(--st-green); border-color: transparent; color: #fff; }
.st-catnav__panel-item.is-on .st-menu__count { border-color: rgba(255, 255, 255, .45); color: #fff; }
.st-catnav__panel-all {
  align-self: center;
  margin-left: auto;
  padding: 9px 15px;
  border: 1px solid var(--st-green);
  border-radius: 999px;
  background: var(--st-green);
  color: #fff;
  font-family: inherit;
  font-size: .82rem;
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
}
.st-catnav__panel-all:hover { background: var(--st-green-dark); border-color: var(--st-green-dark); }
/* The subcategory's name and the number of products behind it, shared by the strip's pills. */
.st-menu__name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.st-menu__count {
  flex: 0 0 auto;
  min-width: 21px;
  padding: 1px 6px;
  border: 1px solid rgba(19, 43, 29, .12);
  border-radius: 999px;
  color: var(--st-muted);
  font-size: .69rem;
  font-weight: 700;
  text-align: center;
}

@media (max-width: 640px) {
  .st-catnav__panel-inner { gap: 10px; padding-top: 12px; padding-bottom: 14px; }
  .st-catnav__panel-head { min-width: 0; max-width: none; }
  .st-catnav__panel-list { flex: 1 1 100%; }
  .st-catnav__panel-item { min-height: 38px; }
  .st-catnav__panel-all { flex: 1 1 100%; margin-left: 0; min-height: 42px; }
  .st-catnav .st-menu__toggle { width: 44px; }
  /* The same floor for the card's own "+", which is a thumb's target on a phone. */
  .st-card__add { width: 40px; height: 40px; }
  .st-card__add svg { width: 18px; height: 18px; }
}
@media (prefers-reduced-motion: reduce) {
  .st-catnav .st-menu__caret, .st-catnav__panel-item { transition: none !important; transform: none !important; }
}
/* ── One button, and it is the site's button, to the pixel ───────────────────
   The shop had grown its own button: a small rounded rectangle with the body face in it, while
   every other page on the site draws the brand's own — a pill in the display face with a circle
   holding the arrow, the pair reading as one control. Two buttons for one brand is a shop that
   looks like a different company. So the shop's button is not a likeness of the site's button any
   more, it is the site's button: every number below was read off the hero's own call to action in
   `css/styles.css` — `.btn`, `.btn__label` and `.btn__icon` — and written down here unchanged.
   It is written once, after everything else, because the flat-paint pass and the hover pass both
   name `.st-btn` and the last word is the one that wins.

   The site's button is two pieces: a pill of `16px 28px` at `.85rem` in the display face, which is
   45.6px tall, and a 48px disc painted the same green and pulled back over the pill's edge by 2px.
   Because the disc is the taller of the two it stands proud of the pill above and below it, and
   that outer circle is what the end of the button reads as — the pill looks like it runs into it.
   The shop's `.st-btn--go` is two pieces the same way: the label span is the pill and
   `.st-btn__go` is the disc, pulled back by the same 2px. It was not always. The green used to be
   painted on the button element itself, so the pill came out as tall as the disc and the disc had
   nothing to stand proud of — the shape was one flat capsule, and put beside the hero's button it
   was the flat one. Nothing about a button should have to be measured to be told apart. Measured
   against the hero's button now, the two agree on the pill's height, the disc's size, the 2px
   overlap and the 48px total.
   Three controls keep their own shape and say so where they are written: the search's submit,
   which is a circle inside a field, the "Shop all" chip inside the menu's dropdown, which is one
   pill in a list of pills, and `--sm`, which is the same button one step down inside a data row. */
.st-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: none;
  border-radius: 999px;
  padding: 16px 28px;
  background: var(--st-green);
  color: #fff;
  font-family: var(--st-display);
  font-weight: 400;
  font-size: .85rem;
  line-height: 1;
  letter-spacing: .01em;
  cursor: pointer;
  text-decoration: none;
  box-shadow: none;
  transition: background-color .2s ease, border-color .2s ease, transform .2s ease;
}
.st-btn:hover { background: var(--st-green-dark); border-color: var(--st-green-dark); transform: translateY(-1px); box-shadow: none; }
.st-btn:active { transform: translateY(0); }
.st-btn:focus-visible { outline: none; box-shadow: 0 0 0 4px rgba(0, 160, 72, .28); }
.st-btn[disabled] { opacity: .55; cursor: not-allowed; transform: none; }
.st-btn--block { width: 100%; }
.st-btn--sm { padding: 11px 18px; font-size: .78rem; }
.st-btn--light { background: var(--st-surface); color: var(--st-ink); box-shadow: inset 0 0 0 1px var(--st-border); }
.st-btn--light:hover { background: #f2ead9; color: var(--st-ink); box-shadow: inset 0 0 0 1px var(--st-border); }
.st-btn--danger { background: var(--st-surface); color: var(--st-danger); box-shadow: inset 0 0 0 1px #fecaca; }
.st-btn--danger:hover { background: var(--st-danger-soft); box-shadow: inset 0 0 0 1px #fecaca; }
/* The circle that says "this takes you somewhere", on the buttons that do. It is the site's own:
   48px across, the green of the pill it is set into so the two read as one shape, an arrow at
   16px, and it darkens with the pill rather than on its own. */
/* The pill is the label, so the button element paints nothing on this variant: the two pieces have
   to be able to differ in height for the disc to stand proud of the pill the way the site's does. */
.st-btn--go { padding: 0; gap: 0; background: none; box-shadow: none; }
.st-btn--go:hover { background: none; box-shadow: none; }
.st-btn--go > span:not(.st-btn__go) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 1 1 auto;
  text-align: center;
  padding: 16px 28px;
  border-radius: 999px;
  background: var(--st-green);
  color: #fff;
  line-height: 1;
  transition: background-color .2s ease;
}
.st-btn--go:hover > span:not(.st-btn__go) { background: var(--st-green-dark); }
/* The light and danger variants paint the pill, and the disc follows them, the way the site's
   `--light` is a white pill with a white disc and ink in both. */
.st-btn--light.st-btn--go { box-shadow: none; }
.st-btn--light.st-btn--go:hover { background: none; box-shadow: none; }
.st-btn--light.st-btn--go > span:not(.st-btn__go) { background: var(--st-surface); color: var(--st-ink); }
.st-btn--light.st-btn--go:hover > span:not(.st-btn__go) { background: #f2ead9; }
.st-btn--danger.st-btn--go > span:not(.st-btn__go) { background: var(--st-surface); color: var(--st-danger); }
.st-btn--danger.st-btn--go:hover > span:not(.st-btn__go) { background: var(--st-danger-soft); }
/* A mark drawn inside the pill rather than in a circle, at the site's own 16px, so a button
   with one is the same height as a button with a circle. */
.st-btn__icon { width: 16px; height: 16px; flex: 0 0 auto; }
.st-btn__go {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 48px;
  height: 48px;
  margin-left: -2px;
  border-radius: 50%;
  background: var(--st-green);
  color: inherit;
  transition: background-color .2s ease;
}
.st-btn__go svg { width: 16px; height: 16px; }
.st-btn:hover .st-btn__go { background: var(--st-green-dark); }
.st-btn--light .st-btn__go { background: var(--st-surface); }
.st-btn--light:hover .st-btn__go { background: #f2ead9; }
/* The hero's call to action was its own white button, kept because the flat-paint pass once named
   `.st-btn` late enough to hand a `--light` element the green. It is the light button now, the
   same one the site draws on a dark panel, and the class stays in the markup only to size it on a
   phone. */

/* ── Sliding the row of groups ───────────────────────────────────────────────
   Seven groups are wider than a phone, so the row is a window with more behind its edge. A finger
   already slides it; a mouse now does too, because a menu whose last two groups can only be
   reached with a scrollbar that has been hidden is a menu that hides two groups. The drag sets a
   flag so the press that ends it is not read as a choice, and snapping is off while the row is
   being dragged or it fights the hand. */
@media (hover: hover) {
  .st-catnav__inner { cursor: grab; }
}
.st-catnav__inner.is-dragging {
  cursor: grabbing;
  scroll-snap-type: none;
  user-select: none;
}

/* ── The strip's own button is a button, not a hero ──────────────────────────
   "Shop all Gas Cylinders" carries the shop's shared button, which is sized to lead a page. Inside
   a dropdown it sat beside pills half its height and read as the loudest thing in a strip that is
   meant to be read as a list. It is sized to the row it lives in now: the same pill and the same
   face, a shorter body and a smaller circle. The selector is doubled because the shared `.st-btn`
   block above is written last on purpose and a single class would lose to it. */
.st-catnav__panel .st-catnav__panel-all { font-size: .78rem; }
.st-catnav__panel .st-catnav__panel-all > span:not(.st-btn__go) { padding: 9px 12px 9px 15px; }
.st-catnav__panel .st-catnav__panel-all .st-btn__go { width: 24px; height: 24px; margin-left: 0; }
.st-catnav__panel .st-catnav__panel-all .st-btn__go svg { width: 13px; height: 13px; }

/* ── On a phone the button is the same button ────────────────────────────────
   The promo bands used to bring their buttons down a size so two of them could share a phone's
   width. The site's button does not do that — the hero stacks its pills on a phone and leaves each
   one its own size and its own circle — so the shop's does not either: the two bands read as the
   same ad at every width, which is the point of having one button. */
/* ── The page's own scroll ───────────────────────────────────────────────────
   The shop is a long page read downward, so the two things that make a long page legible are here:
   an anchor stops below the header rather than under it, and the page's scrollbar is drawn in the
   shop's own ink rather than the browser's default grey — a thin thumb that warms to the brand's
   green under the pointer. A shop that owns its colours owns its scroll, too. */
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
/* The store pages write their header's own measured height to `--st-head-h` (see the note on
   `.st [id]` above); everywhere else the stand-in is what the header was measured at here, 96px
   from the floor to the top of the target. */
[id] { scroll-margin-top: calc(var(--st-head-h, 82px) + 14px); }
.st-catnav__inner { -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain; }
@media (min-width: 900px) {
  html { scrollbar-color: rgba(19, 43, 29, .26) transparent; scrollbar-width: thin; }
  ::-webkit-scrollbar { width: 11px; height: 11px; }
  ::-webkit-scrollbar-track { background: transparent; }
  ::-webkit-scrollbar-thumb {
    background: rgba(19, 43, 29, .22);
    background-clip: padding-box;
    border: 3px solid transparent;
    border-radius: 999px;
  }
  ::-webkit-scrollbar-thumb:hover { background: rgba(0, 160, 72, .55); background-clip: padding-box; }
}

/* ══════════════════════════════════════════════════════════════════════════
   The press
   ──────────────────────────────────────────────────────────────────────────
   A shop is pressed. Every other thing the interface does — the drawer sliding out, the basket
   count climbing, the toast arriving — is a reply to somebody having pressed something, and until
   this section the press itself was the one moment with nothing drawn on it. What a browser draws
   is its own: a grey flash on a phone, a rectangle on a desk. This is the shop's own answer, and it
   is the same answer for every control it invents:

     * a soft flash of light where the finger actually landed, opening outward and fading — so the
       press is acknowledged at the point it happened rather than somewhere inside the button's own
       box;
     * a small dip, so the control reads as pressed rather than as merely highlighted;
     * and a glow instead of an outline when the focus has to be shown, which is what the sheet's
       `outline`s were rewritten into.

   Nothing here needs markup: the flash is the control's own `::after`, drawn in the element's own
   radius, and the pointer's position arrives as two custom properties written by `js/store.js` on
   the press. A control that never gets pressed — a heading, a card — never gets a flash, because
   the list below is the shop's controls and nothing else. `color`-aware it is not: a white flash
   over the green, and the same flash in the green over the white ones, which is the one pair of
   colours every button in the shop is already made of. */
.st,
.st-ck-page,
.st-drawer,
.st-qv { -webkit-tap-highlight-color: transparent; }

/* The controls that sit in a flow and need something to hang their flash on. Two of the shop's
   controls are missing from this list on purpose: the card's eye and the dialog's close place
   themselves with `position: absolute`, and an absolutely positioned element is already a
   containing block for its own `::after`. `position: relative` here was the last word and it took
   their placement away — the eye fell into the card as a full-width ellipse over the picture, and
   the close button landed in the dialog's flow at the top of the panel, shoving the photograph
   down. Nothing is needed to make the flash work on either of them; the flash is aimed at their
   own box wherever that box ends up. */
.st-btn,
.st-x,
.st-qty button,
.st-drawer__close,
.st-searchbar__go,
.st-loc { position: relative; }

.st-btn::after,
.st-x::after,
.st-qty button::after,
.st-card__quick::after,
.st-drawer__close::after,
.st-qv__close::after,
.st-searchbar__go::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(circle at var(--st-press-x, 50%) var(--st-press-y, 50%),
    var(--st-press, rgba(255, 255, 255, .5)) 0%, rgba(255, 255, 255, 0) 60%);
  opacity: 0;
  pointer-events: none;
}
/* The white controls get the flash in the brand's green, which is the same mark drawn the other way
   round: a white flash on white is nothing at all. */
.st-btn--light::after,
.st-x::after,
.st-qty button::after,
.st-card__quick::after,
.st-drawer__close::after,
.st-qv__close::after,
.st-searchbar__go::after { --st-press: rgba(0, 160, 72, .26); }

.st-btn.is-pressed::after,
.st-x.is-pressed::after,
.st-qty button.is-pressed::after,
.st-card__quick.is-pressed::after,
.st-drawer__close.is-pressed::after,
.st-qv__close.is-pressed::after,
.st-searchbar__go.is-pressed::after { animation: st-press .55s cubic-bezier(.22, .61, .36, 1) both; }
@keyframes st-press {
  from { opacity: 1; transform: scale(.4); }
  to { opacity: 0; transform: scale(1); }
}

/* The dip. `.st-btn` already lifts by a pixel under the pointer, so the press takes that lift back
   and folds it a hair smaller: the button comes down to meet the finger. */
.st-btn:active,
.st-card__quick:active,
.st-drawer__close:active,
.st-searchbar__go:active { transform: scale(.97); }
.st-drawer__close:active svg,
.st-card__quick:active svg { transform: scale(.94); }

/* The white and outlined buttons wear a ring inside as well as out, so the glow does not erase the
   hairline that is what makes them buttons in the first place. */
.st-btn--light:focus-visible,
.st-btn--danger:focus-visible,
.st-btn--light.st-btn--go:focus-visible {
  box-shadow: inset 0 0 0 1px var(--st-border), 0 0 0 4px rgba(0, 160, 72, .28);
}

@media (prefers-reduced-motion: reduce) {
  .st-btn.is-pressed::after,
  .st-x.is-pressed::after,
  .st-qty button.is-pressed::after,
  .st-card__quick.is-pressed::after,
  .st-drawer__close.is-pressed::after,
  .st-qv__close.is-pressed::after,
  .st-searchbar__go.is-pressed::after { animation-duration: .001s; }
  .st-btn:active,
  .st-card__quick:active,
  .st-drawer__close:active,
  .st-searchbar__go:active { transform: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   One rhythm down the front page
   ────────────────────────────────────────────────────────────────────────
   Every band of the shop's front page was spaced by its own margin: 24px under the hero, 6px above
   the shelf, 30px above the adverts, 20px under them, 32px above the account band. Measured in a
   browser at 390px, those five bands produced five different gaps — 23, 6, 30, 20 and 32 — so the
   page read as a stack of unrelated blocks instead of as one page being read downward, and the 6px
   above the shelf read as a mistake because it was one.

   They are given one gap now, named once, and the bottom margins that used to add themselves to the
   next band's top margin are zeroed so what stands between two bands is that figure and nothing
   else. The page's own floor is deliberately not part of this: `.st-main`'s padding-bottom is the
   distance to the footer, not the distance between two sections, and it stays as it was.

   Scoped with `:has(> .st-hero)`, because the hero band exists on this page and no other: the
   product page shares `.st-show`, and the basket pages share the main element itself, so both keep
   the spacing they were drawn with. */
.st-main:has(> .st-hero) { --st-section-gap: 34px; }
.st-main:has(> .st-hero) > section,
.st-main:has(> .st-hero) > .st-shop { margin-bottom: 0; }
.st-main:has(> .st-hero) > * + * { margin-top: var(--st-section-gap); }


/* ── One leading, every card ────────────────────────────────────────────────
   The reference the shop is set against leads its headline at about 1.3 of its own size and its
   body at about 1.33, and the numbers it was given for them — 65 and 20 — are those ratios read
   off a layout at a larger scale. The shop had one leading per section instead: the flyers led at
   1.42, the account band at 1.32, the page intros at 1.02, the product names at 1.35, and the
   subtexts ran from 1.6 to 1.7 — so a card did not stack like the card beside it, and a row of
   cards stacked like three different hands. Every card heading and every card subtext now takes
   the one leading, so the air inside a card is the same air wherever the card is. Only the leading
   is set here: each element keeps its own face, size, colour and measure.

   The flyer headings are set in the display face, which draws its ink well above its own line box —
   the same property that had to be given room on the hero wordmark. They take the one leading with
   every other card, and the check that matters is not this comment but the ink: the store markup
   suite measures nothing here, so the leading was stood against the face on the page and the two
   lines of a wrapped flyer heading were left clear of each other before it was kept. */
.st-flyer__title,
.st-signin__title,
.st-show__title,
.st-intro .st-title,
.st-main > .st-title,
.st-card__name,
.st-title,
.st-section-title,
.st-qv__name,
.st-drawer__title,
.st-coming__title,
.st-state__title,
.st-hero__title {
  line-height: 1.3;
}

/* ── The name of the product, in the two narrow columns it has to wrap in ────
   A product's name is the one string in the shop that is both set in the display face and long:
   "Gaslight Propane Cylinder Refillable 10kg" is a sentence folded into a panel the width of a
   wrist. The face draws its descenders nearly half a line below the letters beside them, so two
   rows of a name at the leading every heading here takes — 1.3 — touch: the 'g' of "Gaslight" and
   the 'y' of "Cylinder" run into the row under them, and the name reads as one struck-through
   block. The name is met in two places — under the quick view's picture and at the head of the
   single product's own panel — and both are given the leading its ink asks for rather than the
   leading the face's headings take. 1.45 is the first value at which the descender of one row and
   the ascender of the row under it clear each other; the shop asked for a step more than that, so
   the two rows are led at 1.55 — a hair of air on both sides of every row, and still a name. */
.st-qv__name,
.st-buy .st-title { line-height: 1.55; }

.st-hero__sub,
.st-flyer__text,
.st-signin__text,
.st-intro .st-sub,
.st-main > .st-sub,
.st-card__meta {
  line-height: 1.33;
}


/* ── The type on a phone, held to the size it is drawn at ───────────────────
   The heads of the page are handled above, in every `clamp()`: the floor is a number of pixels.
   The rows under a head are the other half of it. A card's price, the name under its picture, the
   label inside a button, the chips along the top of the shop — each of them shares its line with
   something else, and each is stated in `rem`, so a phone whose own text scale is turned up grew
   the row without growing the row's width. That is the half of this that the wrapping and the
   ellipsis cannot reach: the + ended up painted on the struck-through price, the cart button's
   label wrapped into a second line on top of its first, and a card's name ran to five lines where
   it took two.

   So on a phone they are held to the size the design draws them at, which is their size at 1x —
   the same pixels, so a phone at a normal text scale does not move by a rounding error — while the
   sentences around them (`.st-text`, the descriptions, the answers) keep answering the customer's
   own setting, which is what the setting is for. */
@media (max-width: 560px) {
  /* Two rows' worth of air between what a card says and what it costs, without spending a phone's
     width on the sides of it: the name, the one line of meta under it and the price band were 5px
     apart, which is the spacing of a list, and the band is what a thumb is aiming at. */
  .st-card__body { padding: 12px 14px 14px; gap: 7px; }
  .st-title { font-size: 19.5px; }
  .st-sub { font-size: 14.5px; }
  .st-section-title { font-size: 17px; }
  .st-adbar__group { font-size: 13px; }
  .st-card__name { font-size: 14px; }
  .st-card__meta { font-size: 12px; }
  /* The two figures in the card's money band are the one place in the shop where a number is asked to
     be a phone's own type: the card is 174px wide and the band is the smallest stage on it. The band
     itself is sized by the sheet's phone rules — its padding and the thumb's 40px target, so it is one
     line and 46px deep — and what is held here is only the type: the figure at the size the design
     draws it (13.5px, which is what .84rem resolves to at the design's own root) and the price it
     beat, on the card's white under it, a step under that. Left to `rem`, a phone at 20px root
     prints the figure at 16.8px and the strike at 14.8px in a 174px card: the band's one line and
     the two of them compete for it, and the price that has to give is the one the customer is
     buying at. */
  .st-card__price,
  .st-card__price--range { font-size: 13.5px; }
  .st-card__was { font-size: 12px; }
  .st-line__name { font-size: 14.5px; }
  .st-line__variant,
  .st-line__warn { font-size: 13px; }
  .st-line__price { font-size: 14px; }
  .st-line__each { font-size: 11.5px; }
  .st-chip { font-size: 13.5px; }
  /* The button is named `.st .st-btn` rather than `.st-btn` on purpose: scripts/store-markup.test.js
     reads the *last* bare `.st-btn` rule in the sheet and compares its numbers with the site's own
     pill, and this is a phone-only size rather than the button's size. A scoped selector keeps that
     rule the last one while still outranking it. */
  .st .st-btn { font-size: 14.5px; }
  .st .st-btn--sm { font-size: 13px; }
  .st-qty button { font-size: 17.5px; }
  .st-qty input { font-size: 14.5px; }
  .st-qv__name { font-size: 22.5px; }
  .st-show__title { font-size: 17px; }
  /* The "See everything" link beside a section's name is `.8rem`: 16px on a phone with its text
     turned up, which is the section heading's own size spent on a link. It is held to the size the
     design draws it at, like every other row here. */
  .st-show__more { font-size: 13px; }
  .st-flyer__title { font-size: 20px; }
  .st-flyer__text { font-size: 14px; }
  .st-sticky__label { font-size: 11px; }
  .st-sticky__value { font-size: 17px; }

  /* ── The two names a card and a cart row are read by ─────────────────────────
     A card's name and a cart line's name are set in the body face, and a name is
     a name and not a paragraph: at the one leading the desk uses — 1.3 — a name
     that takes two rows in a 174px card spends air the card has not got. Both
     close to 1.12 on a phone, which is the tightest a row of a name goes while
     the row under it is still a row: the body face draws its ink inside its own
     line box, so two rows close without either touching the other.
     The display face is the other half of this and is left where it is set: it
     draws its ink well above its own line box, so a heading keeps the 1.3 the
     desk gives it — every one of those was stood against the face on the page
     before it was kept — and a product's own name, the one string in that face
     long enough to wrap around descenders, takes the 1.55 the note above
     `.st-qv__name` sets. What closes on a phone is a name; what stays open is a
     heading whose own face has already spent the air. */
  .st-card__name,
  .st-line__name { line-height: 1.12; }
}

/* ── The rows above the bar, given back by the scroll itself ───────────────
   The adverts row is worth its 43px at the top of a page and not once somebody is reading one, and
   neither is the phone number under it: together they are 95px on a phone — a third of the screen
   — of a header standing over whatever the customer is actually reading, and every heading a link
   was scrolled to passed under them. They are given back by the header’s own sticky offset (see the
   note on .st-head at the top of this sheet): the browser pins the header from a point past these
   two rows, so they leave the screen as the page scrolls and come back with it as it returns, at the
   speed of the finger, with nothing animated and nothing under them reflowed.

   The advert loop stops while the row is off the top of the screen, because a marquee nobody can
   see is a repaint nobody asked for. */
.st-head.is-stuck .st-adbar__track { animation-play-state: paused; }
