/* ──────────────────────────────────────────────────────────────────────
   Storefront — consumer shop paradigm, `.sf` scope.

   v3 "awning" pass: every store gets its OWN identity. A tone class
   (`.sf-tone-0…7`, seeded from the store's sequential id) or the seller's
   chosen `brand_color` sets `--sf-accent`; everything commerce-colored
   keys off that var with a fallback to the platform `--brand-primary`.
   The result: a soft gradient cover band per store, lettermark tiles
   instead of "no photo" ghosts, accent CTAs/pills/prices — two stores
   never look identical, and a store with zero photos still looks styled.

   Shared by storefront, product_detail, cart, checkout, browse, wishlist,
   order_success, store_not_found. Pages WITHOUT a tone class simply fall
   back to platform violet — never remove a class here without checking
   all eight templates.

   Restraint rules still hold: hairline borders, no shadow stacks, weight/
   size hierarchy, tabular numerals. Tokens from global design-variables.css.
   ────────────────────────────────────────────────────────────────────── */

.sf {
  /* Per-store identity tokens. Tone classes / inline brand_color override these. */
  --sf-accent: var(--brand-primary);          /* surfaces: buttons, pills, marks   */
  --sf-accent-2: var(--brand-primary);        /* companion hue for cover gradients */
  --sf-ink: var(--brand-primary);             /* accent-colored TEXT (dark-aware)  */
  /* Label colour for a filled --sf-accent surface. Defaults to the THEME-FLIPPING
     inverted text because the default accent IS --brand-primary, which is dark in
     the light theme (#675CFF) but LIGHT in the dark theme (#8291F8) — a hardcoded
     `color:#fff` on .sf-btn gave 2.86:1 in dark mode on every storefront CTA
     (cart, wishlist, compare, addresses). The eight store tones below are all
     dark hues, so they pin it back to white. */
  --sf-accent-ink: var(--theme-text-inverted);
  /* Buy-box CTAs. These were literally Amazon's pair (#ffd814 / #ffa41c),
     which put THREE competing primaries on one PDP: those two plus the store's
     own --sf-accent, which the mobile buy bar was already using correctly — and
     none of the three was a brand token. The buy box now speaks the store's
     identity like every other storefront surface, and the second action steps
     down to a hairline: hierarchy in weight, never a second saturated fill. */
  /* CTA fill is the PLATFORM brand, not the store tone. Eight seeded tones
     meant the most important control on every storefront — Follow, Add to
     cart, the mobile buy bar — came out ochre on store #1, terracotta on #0,
     slate on #7: a random, often muddy primary, while the marketplace chrome
     around it stayed purple. Marketplaces (Etsy, Amazon, Faire) keep one CTA
     colour and let the store's identity live in its mark, cover and tints —
     which is exactly what --sf-accent still drives. A seller's explicit
     brand_color moves the CTA too (store_brand_style sets --sf-cta), so a
     themed storefront is still possible; the DEFAULT is no longer a lottery.
     --brand-fill is white-ink safe in both themes (design-variables.css). */
  --sf-cta: var(--brand-fill, var(--brand-primary));
  --sf-cta-ink: #fff;
  /* ONE ACCENT PER SCREEN. The store tone (--sf-accent/-2) is a WASH: the
     mark, the cover, the page floor, placeholder tints, hover borders. Every
     accent that carries meaning — link ink, selected chip, active tab,
     focus ring, checked option, verified tick — is the platform brand, the
     same colour as the CTA beside it. Before this the tone also set --sf-ink
     and every selection state, so a store #1 page showed ochre links and an
     ochre selected chip next to a purple Follow: two accents, neither the
     user's. A seller's brand_color still overrides both (store_brand_style). */
  --sf-ink: var(--brand-text);
  --sf-buybox-primary: var(--sf-cta);
  --sf-buybox-ink: var(--sf-cta-ink);
  /* ONE HEIGHT PER ROW. Every button family and every toolbar control takes
     its height from a token, not from padding + font metrics + a per-family
     min-height floor. Measured 2026-09-05 at 375px before this: the identity
     row was 44 / 42 / 40 (Follow, Message, "···") and the filter toolbar was
     33 / 31 / 40 / 40 (pill, Filters, sort, hide) with two radii — the user's
     phone screenshot: "why can't one line have one size? the whole app looks
     clunky". Floors never agree; a set height does. Coarse pointers lift both
     tokens to the WCAG 2.5.5 target size, still one value per row. */
  --sf-control-h: 40px;
  --sf-toolbar-h: 36px;

  background: var(--theme-bg);
  color: var(--theme-text);
  font-family: var(--font-family);
  -webkit-font-smoothing: antialiased;
}
@media (pointer: coarse) { .sf { --sf-control-h: 44px; --sf-toolbar-h: 40px; } }
.sf a { color: inherit; text-decoration: none; }
.sf button { font-family: inherit; cursor: pointer; }
.sf .sf-wrap { max-width: 1200px; margin: 0 auto; padding: 0 var(--space-6); }
@media (max-width: 640px) { .sf .sf-wrap { padding: 0 var(--space-4); } }

/* ─── Store tones — 8 curated palettes, seeded by sequential_id % 8 ────
   Accents are deep enough for white button text in BOTH modes; --sf-ink
   swaps to a lighter cousin in dark mode so accent text stays legible.
   The class works on the page root (.sf.sf-tone-N) AND on any element
   inside it (.sf .sf-tone-N) so mixed-store surfaces — browse grid,
   wishlist — can tone each card independently.
   Python mirror for PNG/JSON surfaces: app/utils/store_identity.py. */
.sf.sf-tone-0, .sf .sf-tone-0 { --sf-accent: #b3441e; --sf-accent-2: #d97706; --sf-accent-ink: #fff; } /* terracotta */
.sf.sf-tone-1, .sf .sf-tone-1 { --sf-accent: #8a6400; --sf-accent-2: #b45309; --sf-accent-ink: #fff; } /* ochre      */
.sf.sf-tone-2, .sf .sf-tone-2 { --sf-accent: #2e7d32; --sf-accent-2: #4d7c0f; --sf-accent-ink: #fff; } /* moss       */
.sf.sf-tone-3, .sf .sf-tone-3 { --sf-accent: #0f766e; --sf-accent-2: #0e7490; --sf-accent-ink: #fff; } /* teal       */
.sf.sf-tone-4, .sf .sf-tone-4 { --sf-accent: #1d4ed8; --sf-accent-2: #4338ca; --sf-accent-ink: #fff; } /* cobalt     */
.sf.sf-tone-5, .sf .sf-tone-5 { --sf-accent: #6d28d9; --sf-accent-2: #9333ea; --sf-accent-ink: #fff; } /* plum       */
.sf.sf-tone-6, .sf .sf-tone-6 { --sf-accent: #be185d; --sf-accent-2: #e11d48; --sf-accent-ink: #fff; } /* raspberry  */
.sf.sf-tone-7, .sf .sf-tone-7 { --sf-accent: #475569; --sf-accent-2: #334155; --sf-accent-ink: #fff; } /* slate      */

/* Store-page floor: a 4% accent wash that breathes out into the page bg —
   kills the "clinical white admin list" feel without coloring content. */
.sf.sf-store {
  background: var(--theme-bg);
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--sf-accent) 4%, var(--theme-bg)) 0%,
    var(--theme-bg) 560px
  );
}

/* ─── Buttons ─────────────────────────────────────────────────────────── */
.sf .sf-btn,
.sf .sf-btn-pri {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: var(--sf-control-h); padding: 0 18px;
  background: var(--sf-cta); color: var(--sf-cta-ink);
  border: 1px solid var(--sf-cta); border-radius: var(--radius-md);
  font-size: 13.5px; font-weight: 600; cursor: pointer;
  transition: filter var(--transition-fast), background-color var(--transition-fast);
}
.sf .sf-btn:hover, .sf .sf-btn-pri:hover { filter: brightness(1.08); }
/* The coarse-pointer 44px floor that used to sit HERE, above .sf-btn-sec, lost
   to that later rule's own min-height:40px at equal specificity (a media query
   adds none) — measured 42px on a phone. The height now comes from
   --sf-control-h, which the coarse media rule on .sf lifts to 44px. */
.sf .sf-btn:disabled { opacity: 0.5; cursor: not-allowed; filter: none; }
.sf .sf-btn-lg { height: calc(var(--sf-control-h) + 8px); padding: 0 22px; font-size: 14.5px; }
.sf .sf-btn-block { width: 100%; }
/* 40x40 minimum hit area (UI_DESIGN_RULES §12) — padding alone computed to
   38px; min-height makes the floor explicit. */
.sf .sf-btn-sec {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: var(--sf-control-h); padding: 0 18px;
  background: var(--theme-card-bg); color: var(--theme-text);
  border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  font-size: 13.5px; font-weight: 500;
  transition: border-color var(--transition-fast), background-color var(--transition-fast);
}
.sf .sf-btn-sec:hover { border-color: var(--border-strong); background: var(--theme-hover); }
.sf .sf-btn-ghost {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 12px; font-size: 13px; font-weight: 500;
  color: var(--theme-text-secondary); border-radius: var(--radius-md);
  transition: color var(--transition-fast), background-color var(--transition-fast);
}
.sf .sf-btn-ghost:hover { color: var(--theme-text); background: var(--theme-hover); }
/* Compact density with WCAG §2.5.5-class 40px minimum touch target.
   Visual padding can look denser; the hit box is at least 40×40. */
.sf .sf-btn-sm {
  min-height: 40px;
  min-width: 40px;
  padding: 8px 16px;
  font-size: 13px;
}
.sf .sf-btn-xs {
  min-height: 40px;
  min-width: 40px;
  padding: 8px 12px;
  font-size: 12.5px;
}

/* ─── Lettermark — the store/product identity tile ────────────────────
   Used wherever an avatar/photo is missing: the mark IS the brand, not an
   apology. Accent gradient + initials, square-ish like an app icon. */
.sf .sf-mark {
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0; overflow: hidden; user-select: none;
  border-radius: 26%;
  background: var(--sf-accent);
  background: linear-gradient(
    140deg,
    color-mix(in srgb, var(--sf-accent) 82%, #fff) 0%,
    var(--sf-accent) 52%,
    color-mix(in srgb, var(--sf-accent-2) 72%, #000) 125%
  );
  color: #fff;
  font-family: var(--font-family-heading);
  font-weight: 700; letter-spacing: 0.01em; line-height: 1;
}
.sf .sf-mark img { width: 100%; height: 100%; object-fit: cover; }
.sf .sf-mark--xs { width: 26px; height: 26px; font-size: 11px; border-radius: 8px; }
.sf .sf-mark--sm { width: 40px; height: 40px; font-size: 15px; }
.sf .sf-mark--md { width: 56px; height: 56px; font-size: 20px; }
.sf .sf-mark--lg { width: 88px; height: 88px; font-size: 30px; }
@media (max-width: 640px) { .sf .sf-mark--lg { width: 72px; height: 72px; font-size: 25px; } }

/* ─── Cover band — the store's awning ─────────────────────────────────
   Pure atmosphere: layered soft radials in the store tone. No text inside;
   the identity block overlaps its bottom edge. banner_url replaces it. */
.sf .sf-cover {
  position: relative;
  /* Collapsed awning — the cover was ~370px of identity chrome before a single
     product appeared. A thin accent band keeps the store's brand moment while
     surfacing products near the top (Amazon-style "all product" above the fold). */
  height: clamp(56px, 7.5vw, 92px);
  overflow: hidden;
  /* De-expressed (Geist restraint): one near-neutral surface with a faint
     accent tint replaces the prior 4-layer radial-gradient awning, matching the
     rest of the app's "content on near-neutral surfaces" rule. */
  background: color-mix(in srgb, var(--sf-accent) 6%, var(--theme-bg-secondary));
}
.dark .sf .sf-cover {
  background: color-mix(in srgb, var(--sf-accent) 11%, var(--theme-bg-secondary));
}
.sf .sf-cover-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* Soft fade into the page floor so cover and content feel continuous. */
.sf .sf-cover::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 18px;
  background: linear-gradient(180deg, transparent, color-mix(in srgb, var(--sf-accent) 4%, var(--theme-bg)));
}

/* Owner-only empty-banner affordance: the blank awning becomes a click target
   to add a banner, so it never reads as a finished-but-empty banner. State
   changes color/bg only — never geometry. z-index clears the ::after fade. */
.sf .sf-cover-edit {
  position: absolute; inset: 0; z-index: 2;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  font-size: 13px; font-weight: 600; letter-spacing: 0.01em;
  color: color-mix(in srgb, var(--sf-accent) 78%, var(--theme-text-primary));
  text-decoration: none; background: transparent;
  transition: background-color 0.15s ease, color 0.15s ease;
}
.sf .sf-cover-edit:hover {
  background: color-mix(in srgb, var(--sf-accent) 12%, transparent);
  color: color-mix(in srgb, var(--sf-accent) 92%, var(--theme-text-primary));
}

/* ─── Identity — name, one line of numbers, bio, ONE primary action ──────
   position+z-index: the cover is position:relative (banner img), so it would
   otherwise paint OVER this static block and clip the overlapping mark. */
.sf .sf-identity { position: relative; z-index: 1; padding-bottom: var(--space-5); border-bottom: 1px solid var(--border-divider); }
/* The mark overlaps the cover by 24px and the row bottom-aligns, so the name
   sits on the cover's edge. The identity block owns a 340px floor so the
   actions wrap to their own line as soon as both cannot fit — measured, not a
   guessed breakpoint (between 640 and ~900px the buttons used to squeeze the
   name into two lines and strand the avatar three lines below it). */
.sf .sf-id-bar { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-4); margin-top: -24px; }
.sf .sf-id-bar .sf-mark { flex-shrink: 0; box-shadow: 0 0 0 4px color-mix(in srgb, var(--sf-accent) 4%, var(--theme-bg)); }
.sf .sf-id-main { flex: 1 1 340px; min-width: 0; }
.sf .sf-id-namerow { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 4px; }
.sf .sf-shop-name {
  font-family: var(--font-family-heading);
  font-size: clamp(20px, 2.5vw, 24px); font-weight: 650; letter-spacing: -0.02em;
  color: var(--theme-text); line-height: 1.15;
}
/* Crisp verified chip — the old self-drawn seal rendered as a smudge in dark. */
.sf .sf-shop-verified {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; border-radius: 50%; flex-shrink: 0;
  background: color-mix(in srgb, var(--sf-cta) 14%, transparent);
  color: var(--sf-ink);
}
.sf .sf-shop-verified svg { width: 11px; height: 11px; }
/* One tertiary line: @handle, catalogue size, rating, tenure. Column gap is
   the only separator — a "·" drawn inside a stat travels with it on wrap and
   lands as the first character of the next line (journal 2026-08). The
   handle is sans like the numbers: it is a name, not a code, and the mono
   face read as an ID beside "#0001" (now gone from buyer surfaces). */
.sf .sf-shop-stats {
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px 14px;
  font-size: 13px; color: var(--theme-text-tertiary); font-variant-numeric: tabular-nums;
}
.sf .sf-shop-handle { font-size: 13px; color: var(--theme-text-secondary); }
.sf .sf-shop-stat { display: inline-flex; align-items: baseline; gap: 4px; }
.sf .sf-shop-stat strong { font-weight: 600; color: var(--theme-text); }
.sf .sf-shop-bio {
  margin-top: var(--space-2); font-size: 14px; line-height: 1.5;
  color: var(--theme-text-secondary); max-width: 62ch;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}
.sf .sf-id-actions { display: inline-flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; flex: 0 0 auto; padding-bottom: 2px; }
.sf .sf-id-actions > * { flex-shrink: 0; }
/* "···" overflow: a native <details>. The summary IS the button — same
   register and height as the secondary beside it, 40px square. */
.sf .sf-more { position: relative; }
.sf .sf-more-btn { list-style: none; width: var(--sf-control-h); padding: 0; }
.sf .sf-more-btn::-webkit-details-marker { display: none; }
.sf .sf-more-menu {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 30;
  min-width: 168px; padding: 4px; display: flex; flex-direction: column;
  background: var(--theme-card-bg); border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
}
.sf .sf-more-item {
  display: block; width: 100%; text-align: left; padding: 8px 12px; border: 0;
  border-radius: var(--radius-sm); font-size: 13.5px; font-weight: 500;
  color: var(--theme-text); background: transparent;
}
.sf .sf-more-item:hover { background: var(--theme-hover); }
.sf .sf-more-item.is-danger { color: var(--danger-text); }
/* Facts — delivery, payment, policies, contact — one quiet row under the
   identity. Tertiary glyphs (an accent glyph per fact made five things pop);
   a policy VALUE is clamped to a line, the full text sits on the product page. */
.sf .sf-facts { display: flex; flex-wrap: wrap; align-items: center; gap: 6px var(--space-4); margin-top: var(--space-3); font-size: 13px; color: var(--theme-text-secondary); }
.sf .sf-fact { display: inline-flex; align-items: center; gap: 6px; min-width: 0; max-width: 100%; }
.sf .sf-fact svg { flex-shrink: 0; color: var(--theme-text-tertiary); }
.sf .sf-fact-v { color: var(--theme-text); font-weight: 500; max-width: 28ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A contact fact is a link in the same row — no chip chrome (.sf-contact below
   still dresses the PDP's contact chips). */
.sf .sf-fact.sf-contact { padding: 0; border: 0; font-size: 13px; color: var(--theme-text-secondary); }
.sf .sf-fact.sf-contact:hover { color: var(--theme-text); }
/* Vacation: a status LINE in warning ink, not a 48px callout wedged between
   the store and its shelf. */
.sf .sf-note { display: flex; align-items: center; gap: 8px; margin: var(--space-3) 0 0; font-size: 13px; font-weight: 500; color: var(--warning-text); }
.sf .sf-note::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; flex-shrink: 0; }
.sf .sf-shop-map { margin-top: var(--space-4); }
@media (max-width: 640px) {
  .sf .sf-id-bar { margin-top: -20px; }
  /* Follow and Message split the row EQUALLY; "···" keeps its 40px. A grid,
     not flex: 1 1 0 — measured at 375px the flex split came out 125 / 163
     (the form's content-sized button leaked into the basis), the grid 144 / 144.
     The owner row (Edit store + Share) has no overflow, so two columns. */
  .sf .sf-id-actions { display: grid; grid-template-columns: 1fr 1fr 40px; width: 100%; padding-top: var(--space-2); }
  .sf .sf-id-actions:not(:has(.sf-more)) { grid-template-columns: 1fr 1fr; }
  .sf .sf-id-actions form { min-width: 0; }
  .sf .sf-id-actions form > button { width: 100%; }
}

/* ─── Result bar — Amazon-style real count over the current filter ─────── */
.sf .sf-resultbar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-2) 0 var(--space-3);
  border-bottom: 1px solid var(--border-divider); margin-bottom: var(--space-4);
}
.sf .sf-result-count { font-size: 12.5px; color: var(--theme-text-secondary); font-variant-numeric: tabular-nums; }
.sf .sf-result-q { color: var(--theme-text); font-weight: 600; }

/* ─── Store filter rail injected into the shell sidebar ───────────────────
   These live OUTSIDE .sf (the shell aside), so they are intentionally
   unscoped. Theme tokens flip for light/dark; active uses the platform brand
   (the sidebar is platform chrome, not inside the store's accent scope). */
.sf-side-filters { padding: 6px 16px 14px; border-top: 1px solid var(--border-subtle); margin-top: 6px; }
.sf-side-group { margin-top: 14px; }
.sf-side-group:first-child { margin-top: 2px; }
.sf-side-h { font-size: 11px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--theme-text-tertiary); margin: 0 0 7px; }
.sf-side-h-gap { margin-top: 14px; }
.sf-side-filters ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 1px; }
.sf-side-link { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: var(--radius-md); font-size: 13px; color: var(--theme-text-secondary); text-decoration: none; transition: background-color 0.12s ease, color 0.12s ease; }
.sf-side-link:hover { background: var(--theme-bg-secondary); color: var(--theme-text); }
.sf-side-link.is-active { background: color-mix(in srgb, var(--brand-primary) 13%, transparent); color: var(--brand-text); font-weight: 600; }
.sf-side-emoji { font-size: 13px; line-height: 1; flex-shrink: 0; }
.sf-side-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sf-side-n { font-size: 11.5px; color: var(--theme-text-tertiary); font-variant-numeric: tabular-nums; flex-shrink: 0; }
.sf-side-back { color: var(--brand-text); font-weight: 600; }
.sf-side-price { display: flex; align-items: center; gap: 5px; }
/* Same 40px as the apply button beside it (measured 38 vs 40 on one row). */
.sf-side-input { width: 100%; min-width: 0; height: 40px; padding: 0 8px; font-size: 12.5px; color: var(--theme-text); background: var(--theme-input-bg, var(--theme-bg)); border: 1px solid var(--theme-input-border, var(--border-control)); border-radius: var(--radius-md); font-variant-numeric: tabular-nums; }
.sf-side-input:focus { outline: none; border-color: var(--brand-primary); }
.sf-side-dash { color: var(--theme-text-tertiary); }
/* 40x40 minimum hit area (UI_DESIGN_RULES §12) — the two price inputs have
   min-width:0 and share the remaining flex space, so this doesn't overflow
   the rail. */
.sf-side-go { flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: var(--radius-md); border: 1px solid var(--border-control); background: var(--theme-bg-secondary); color: var(--theme-text-secondary); cursor: pointer; }
.sf-side-go:hover { border-color: var(--brand-primary); color: var(--brand-text); }
.sf-side-check { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--theme-text-secondary); cursor: pointer; padding: 3px 0; }
.sf-side-check input { accent-color: var(--brand-primary); width: 15px; height: 15px; }
.sf-side-clear { display: inline-block; margin-top: 10px; font-size: 12px; color: var(--brand-text); text-decoration: none; }
.sf-side-clear:hover { text-decoration: underline; }

/* One catalog canvas. Filters share the toolbar at every width, so a
   store never reserves a column for missing sidebar content. */
.sf .sf-catalog {
  max-width: 1200px;
  margin: 0 auto;
  padding: var(--space-5) var(--space-6) 0;
}
.sf .sf-catalog-body { min-width: 0; }
.sf .sf-catalog-title { font-size: var(--wm-type-section); font-weight: 600; color: var(--theme-text); }
.sf .sf-identity-no-cover { padding-top: var(--space-6); }
.sf .sf-identity-no-cover .sf-id-bar { margin-top: 0; align-items: center; }
.sf .sf-store-benefits { padding-bottom: var(--space-5); }
.sf .sf-store-benefits > div:not(.hidden) > div {
  background: var(--theme-bg-secondary);
  color: var(--theme-text-secondary);
  border-color: var(--border-subtle);
  font-size: var(--wm-type-body);
}
@media (max-width: 640px) {
  .sf .sf-catalog { padding: var(--space-4) var(--space-4) 0; }
  .sf .sf-identity-no-cover .sf-id-main { flex-basis: calc(100% - 80px); }
}

/* ─── Compact sticky bar (appears on scroll; liquid-glass) ────────────── */
.sf .sf-shophead { position: sticky; top: 0; z-index: 20; }
.sf .sf-shophead-compact {
  padding: var(--space-2) 0;
  /* De-expressed: opaque sticky chrome — no translucent liquid-glass +
     backdrop-blur (which forced a full-width repaint on every scroll frame). */
  background: var(--theme-card-bg);
  border-bottom: 1px solid var(--border-subtle);
}
.sf .sf-shophead-row { display: flex; align-items: center; gap: var(--space-3); }
.sf .sf-shop-id { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }
.sf .sf-shophead-compact .sf-shop-name {
  font-size: 14.5px; font-weight: 600; letter-spacing: -0.01em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sf .sf-shop-actions { display: inline-flex; gap: var(--space-2); align-items: center; margin-left: auto; }

/* Chips (badges / status) */
.sf .sf-chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 10px; border-radius: var(--radius-full);
  border: 1px solid var(--border-subtle);
  font-size: 11.5px; font-weight: 600; color: var(--theme-text-secondary);
}
.sf .sf-chip.is-good { color: var(--success-text); border-color: var(--success-border); }
.sf .sf-chip.is-info { color: var(--sf-ink); border-color: var(--border-strong); }
.sf .sf-chip.is-warn { color: var(--warning-text); border-color: var(--warning-border); }
.sf .sf-contact {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12.5px; color: var(--theme-text-secondary);
  padding: 5px 10px; border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  transition: border-color var(--transition-fast), color var(--transition-fast);
}
.sf .sf-contact:hover { border-color: var(--border-strong); color: var(--theme-text); }
.sf .sf-avatar-lg { width: 96px; height: 96px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }

/* ─── Filter / sort bar ───────────────────────────────────────────────── */
.sf .sf-filters { padding: var(--space-5) 0 var(--space-3); }
.sf .sf-search { position: relative; }
.sf .sf-search-icon {
  position: absolute;
  left: 14px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--theme-text-tertiary);
  pointer-events: none;
}
.sf .sf-search input {
  width: 100%; padding: 11px 14px 11px 40px;
  font-family: inherit; font-size: 14px;
  color: var(--theme-text); background: var(--theme-input-bg);
  border: 1px solid var(--theme-input-border); border-radius: var(--radius-lg);
  transition: border-color var(--transition-fast);
}
.sf .sf-search input:focus { outline: none; border-color: var(--sf-cta); }
.sf .sf-search input::placeholder { color: var(--theme-text-tertiary); }

/* ─── Copy-field: a read-only value beside an action ──────────────────── */
/* `.sf-copyfield` was used in market/order_success.html with NO rule anywhere in
   the codebase, so its <input type="text" readonly> fell straight through to the
   user-agent default — white background, black text — and rendered as a bright
   white box on the dark receipt with a barely legible tracking URL. On the page
   that confirms a purchase.
   Borrows .sf-search input's tokens verbatim rather than introducing a second
   field style: same background, border, radius and focus treatment, so the two
   stay in sync. Monospace because the value is a URL (see the project rule:
   monospace for codes and identifiers). No geometry changes on state. */
.sf .sf-copyfield {
  display: flex;
  gap: var(--space-2);
  align-items: stretch;
}
.sf .sf-copyfield input {
  flex: 1;
  /* Without min-width:0 a long URL refuses to shrink and overflows the card. */
  min-width: 0;
  padding: 11px 14px;
  font-family: var(--font-family-mono, ui-monospace, monospace);
  font-size: 13px;
  color: var(--theme-text);
  background: var(--theme-input-bg);
  border: 1px solid var(--theme-input-border);
  border-radius: var(--radius-lg);
  text-overflow: ellipsis;
}
.sf .sf-copyfield input:focus {
  outline: none;
  border-color: var(--sf-accent);
}
.sf .sf-filter-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); margin-top: var(--space-3); }
.sf .sf-cats { display: flex; gap: var(--space-2); overflow-x: auto; scrollbar-width: none; padding: 2px; }
.sf .sf-cats::-webkit-scrollbar { display: none; }
.sf .sf-pill {
  padding: 6px 14px; border-radius: var(--radius-full); white-space: nowrap;
  font-size: 12.5px; font-weight: 500;
  border: 1px solid var(--border-subtle); color: var(--theme-text-secondary);
  transition: border-color var(--transition-fast), background-color var(--transition-fast), color var(--transition-fast);
}
.sf .sf-pill:hover { border-color: var(--border-strong); }
.sf .sf-pill.is-active { background: var(--sf-cta); border-color: var(--sf-cta); color: var(--sf-cta-ink); }
/* Count badge inside a chip; muted, tabular. */
.sf .sf-pill-n {
  margin-left: 6px; font-size: 11px; font-variant-numeric: tabular-nums;
  color: var(--theme-text-tertiary);
}
.sf .sf-pill.is-active .sf-pill-n { color: color-mix(in srgb, #fff 75%, transparent); }
/* "All categories" toggle chip — brand-tinted so it reads as the entry point. */
.sf .sf-pill-allcats {
  flex-shrink: 0; cursor: pointer; display: inline-flex; align-items: center;
  font-weight: 600; color: var(--sf-accent);
  border-color: color-mix(in srgb, var(--sf-accent) 35%, var(--border-subtle));
  background: color-mix(in srgb, var(--sf-accent) 6%, transparent);
}
.sf .sf-pill-allcats .sf-pill-n { color: var(--sf-accent); }
/* Department chips — the top-level browse. Slightly bolder, icon-led. */
.sf .sf-pill-dept {
  flex-shrink: 0; font-weight: 600; color: var(--theme-text);
  border-color: var(--border-subtle); background: var(--theme-card-bg);
}
.sf .sf-pill-dept:hover { border-color: var(--sf-accent); background: color-mix(in srgb, var(--sf-accent) 5%, var(--theme-card-bg)); }
/* Breadcrumb back out of a department. */
.sf .sf-pill-back {
  flex-shrink: 0; font-weight: 600; color: var(--sf-accent);
  border-color: color-mix(in srgb, var(--sf-accent) 35%, var(--border-subtle));
  background: color-mix(in srgb, var(--sf-accent) 6%, transparent);
}
.sf .sf-pill-allcats.is-active { background: var(--sf-cta); border-color: var(--sf-cta); color: var(--sf-cta-ink); }
.sf .sf-pill-allcats.is-active .sf-pill-n { color: color-mix(in srgb, #fff 80%, transparent); }

/* All-categories overview panel. */
.sf .sf-cat-panel {
  margin-top: var(--space-3); padding: var(--space-3);
  border: 1px solid var(--border-subtle); border-radius: var(--radius-lg);
  background: var(--theme-bg-secondary);
}
.sf .sf-cat-grid {
  display: grid; gap: var(--space-2);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 640px)  { .sf .sf-cat-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .sf .sf-cat-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.sf .sf-cat-card {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 9px 12px; border-radius: var(--radius-md);
  border: 1px solid var(--border-subtle); background: var(--theme-card-bg);
  font-size: 12.5px; color: var(--theme-text); text-decoration: none;
  transition: border-color var(--transition-fast), background-color var(--transition-fast);
}
.sf .sf-cat-card:hover { border-color: var(--sf-accent); background: color-mix(in srgb, var(--sf-accent) 4%, var(--theme-card-bg)); }
.sf .sf-cat-card.is-active { border-color: var(--sf-cta); background: color-mix(in srgb, var(--sf-cta) 8%, transparent); }
.sf .sf-cat-card-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sf .sf-cat-card-n {
  flex-shrink: 0; font-size: 11px; font-weight: 600; font-variant-numeric: tabular-nums;
  color: var(--theme-text-tertiary);
  background: var(--theme-bg-tertiary); border-radius: var(--radius-full); padding: 1px 8px;
}
.sf .sf-sort {
  flex-shrink: 0; appearance: none;
  padding: 7px 30px 7px 14px; border-radius: var(--radius-full);
  font-size: 12.5px; font-weight: 500; cursor: pointer;
  background: var(--theme-card-bg); color: var(--theme-text-secondary);
  border: 1px solid var(--border-subtle);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath fill='%23888' d='M0 0l5 6 5-6z'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 12px center;
}
.sf .sf-sort:focus { outline: none; border-color: var(--sf-cta); }
.sf .sf-filter-summary { display: flex; align-items: center; gap: var(--space-2); margin-top: var(--space-2); font-size: 12px; color: var(--theme-text-tertiary); }
.sf .sf-clear-link { color: var(--sf-ink); font-weight: 500; margin-left: 4px; }

/* ── Advanced filters ─────────────────────────────────────────────────── */
.sf .sf-filter-tools { display: inline-flex; align-items: center; gap: var(--space-2); }
.sf .sf-filters-btn {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 14px; line-height: 1;
  border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  background: var(--theme-card-bg); color: var(--theme-text-secondary);
  font-size: 13px; font-weight: 500; cursor: pointer;
  transition: background var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
}
.sf .sf-filters-btn:hover { background: var(--theme-hover); }
.sf .sf-filters-btn.is-open,
.sf .sf-filters-btn.is-active { color: var(--theme-text); border-color: var(--border-strong); }
.sf .sf-filters-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 0 5px; border-radius: var(--radius-full);
  background: var(--sf-accent); color: #fff;
  font-size: 11px; font-weight: 600; font-variant-numeric: tabular-nums;
}
.sf .sf-advanced {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-5);
  margin-top: var(--space-3); padding: var(--space-4);
  border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  background: var(--theme-bg-secondary);
}
.sf .sf-adv-group { display: flex; flex-direction: column; gap: 8px; }
.sf .sf-adv-label {
  font-size: 11px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--theme-text-tertiary);
}
.sf .sf-adv-check { display: inline-flex; align-items: center; gap: 8px; font-size: 13.5px; color: var(--theme-text-secondary); cursor: pointer; }
.sf .sf-adv-check input { width: 15px; height: 15px; accent-color: var(--sf-cta); cursor: pointer; }
.sf .sf-adv-price { display: inline-flex; align-items: center; gap: 8px; }
.sf .sf-adv-input {
  width: 96px; padding: 8px 10px;
  border: 1px solid var(--border-control); border-radius: var(--radius-md);
  background: var(--theme-card-bg); color: var(--theme-text);
  font-size: 13.5px; font-variant-numeric: tabular-nums;
}
.sf .sf-adv-input:focus { outline: none; border-color: var(--sf-cta); }
.sf .sf-adv-input::placeholder { color: var(--theme-text-tertiary); }
.sf .sf-adv-dash { color: var(--theme-text-tertiary); }
.sf .sf-adv-actions { display: inline-flex; align-items: center; gap: var(--space-4); margin-left: auto; }
.sf .sf-adv-reset { font-size: 13px; color: var(--theme-text-tertiary); }
.sf .sf-adv-reset:hover { color: var(--theme-text-secondary); }

/* ── Collapsed toolbar ────────────────────────────────────────────────── */
.sf .sf-toolbar-bar { display: flex; justify-content: flex-end; }
.sf .sf-filters.sf-collapsed { padding-top: var(--space-2); padding-bottom: var(--space-2); }
.sf .sf-toolbar-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  /* 40x40 minimum hit area (UI_DESIGN_RULES §12). */
  width: 40px; height: 40px; padding: 0;
  border: 1px solid transparent; border-radius: var(--radius-md);
  background: transparent; color: var(--theme-text-tertiary); cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast);
}
.sf .sf-toolbar-toggle:hover { background: var(--theme-hover); color: var(--theme-text-secondary); }
.sf .sf-toolbar-toggle.is-active { color: var(--sf-ink); }
.sf .sf-toolbar-collapse {
  display: inline-flex; align-items: center; justify-content: center;
  /* 40x40 minimum hit area (UI_DESIGN_RULES §12). */
  width: 40px; height: 40px; padding: 0;
  border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  background: var(--theme-card-bg); color: var(--theme-text-tertiary); cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast);
}
.sf .sf-toolbar-collapse:hover { background: var(--theme-hover); color: var(--theme-text-secondary); }

/* ── One height per toolbar row ───────────────────────────────────────────
   Category pill, Filters, sort, hide: exactly --sf-toolbar-h tall, one
   radius, one font size, horizontal padding only. This block sits BELOW the
   four family rules it overrides — same specificity, so source order is the
   whole mechanism (journal 2026-09-03: a media query adds no specificity; a
   later rule with equal specificity wins). */
.sf .sf-pill, .sf .sf-filters-btn, .sf .sf-sort, .sf .sf-toolbar-collapse {
  box-sizing: border-box; height: var(--sf-toolbar-h); min-height: 0;
  display: inline-flex; align-items: center;
  padding-top: 0; padding-bottom: 0; line-height: 1;
  border-radius: var(--radius-full); font-size: 13px;
}
.sf .sf-toolbar-collapse { width: var(--sf-toolbar-h); justify-content: center; }
/* Phones: the chip strip owns a full row and scrolls; the three tools take
   the next row with the sort growing between Filters and hide. On one row the
   Mongolian tools (Шүүлтүүр + Онцлох + hide ≈ 300px) crushed the chips to a
   40px sliver at 375px. */
@media (max-width: 640px) {
  .sf .sf-filter-row { flex-wrap: wrap; }
  .sf .sf-cats { flex: 1 1 100%; min-width: 0; }
  .sf .sf-filter-tools { width: 100%; }
  .sf .sf-filter-tools .sf-sort { flex: 1 1 auto; min-width: 0; }
}

/* Always-visible search and sort; optional refinements open below them. */
.sf-store .sf-filters { padding: var(--space-3) 0; }
.sf-store .sf-catalog-tools { display: flex; gap: var(--space-3); align-items: center; }
.sf-store .sf-catalog-tools .sf-search { flex: 1; min-width: 0; }
.sf-store .sf-catalog-tools .sf-search input { height: var(--sf-control-h); }
.sf-store .sf-catalog-tools .sf-filter-tools { width: auto; flex-shrink: 0; }
.sf-store .sf-cats { margin-top: var(--space-3); }
.sf-store .sf-cats:empty { display: none; }
.sf-store .sf-catalog-tools .sf-sort,
.sf-store .sf-catalog-tools .sf-filters-btn { height: var(--sf-control-h); border-radius: var(--radius-md); }
.sf.sf-store .sf-card-quickadd { opacity: 1; pointer-events: auto; }
@media (max-width: 640px) {
  .sf-store .sf-catalog-tools { flex-wrap: wrap; }
  .sf-store .sf-catalog-tools .sf-search { flex-basis: 100%; }
  .sf-store .sf-catalog-tools .sf-filter-tools { width: 100%; }
}

/* ─── Product grid + cards ────────────────────────────────────────────── */
.sf .sf-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(228px, 100%), 1fr));
  gap: var(--space-5) var(--space-4);
  padding-bottom: var(--space-8);
}
@media (max-width: 640px) { .sf .sf-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4) var(--space-3); } }
.sf .sf-card { position: relative; }
.sf .sf-card-media {
  position: relative; aspect-ratio: 1 / 1; overflow: hidden;
  border-radius: var(--radius-lg);
  /* Intelligent fill for tiny/small-subject photos: white bg, object-fit: cover + center
     automatically crops excess whitespace (the "tiny in white box" problem) and enlarges
     the actual product to fill the card square as much as possible. This is the standard,
     zero-config way to make product images "zoom" in the grid without manual crops or AI.
     Full uncropped photo is still available in quick-view and PDP (contain there).
     No hover transform per design rules. */
  background: #ffffff;
  border: 1px solid var(--border-subtle);
  transition: border-color var(--transition-fast);
}
.sf .sf-card:hover .sf-card-media { border-color: color-mix(in srgb, var(--sf-accent) 35%, var(--border-subtle)); }
.sf .sf-card-media img { width: 100%; height: 100%; object-fit: cover; object-position: center; }
/* Hover feedback = border-color only (rule above). No image scale: state must
   not change geometry (UI_DESIGN_RULES §5 — hover never transforms). */

/* ─── Store location map — graceful loading/empty state ───────────────────
   The shared pod_map macro hands us a fixed-height MapLibre container. While
   tiles stream in (or in any environment where the tile backend is briefly
   unreachable) that box would otherwise read as a stark empty rectangle right
   under the store name. Render a soft accent wash + a centered pin watermark
   instead, so the location panel always looks intentional. The opaque map
   canvas paints over all of this the moment tiles arrive. */
.sf .sf-shop-map [data-pod-map],
.sf .sf-shop-map [id^="sf-store-map"] {
  /* A storefront leads with products, so the location panel is a compact strip
     (~150px) rather than the macro's tall 220px default — the bestsellers rail
     surfaces above the laptop fold. MapLibre's per-map ResizeObserver handles
     the smaller container. */
  height: 150px;
  min-height: 150px;
  background:
    radial-gradient(150px 100px at 50% 44%,
      color-mix(in srgb, var(--sf-accent) 16%, transparent), transparent 72%),
    color-mix(in srgb, var(--sf-accent) 6%, var(--theme-card-bg));
  position: relative;
}
.sf .sf-shop-map [data-pod-map]::before,
.sf .sf-shop-map [id^="sf-store-map"]::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 30px;
  height: 30px;
  transform: translate(-50%, -60%);
  background: var(--sf-accent);
  -webkit-mask: var(--sf-pin-mask) center / contain no-repeat;
  mask: var(--sf-pin-mask) center / contain no-repeat;
  opacity: 0.55;
  pointer-events: none;
}
.sf {
  --sf-pin-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2C8 2 5 5 5 9c0 5 7 13 7 13s7-8 7-13c0-4-3-7-7-7zm0 9.5A2.5 2.5 0 1 1 12 6a2.5 2.5 0 0 1 0 5.5z'/%3E%3C/svg%3E");
}

/* Micro-craft (§12): balanced headings, pretty body — set once for the storefront. */
.sf h1, .sf h2, .sf h3, .sf .sf-pdp-title, .sf .sf-section-title, .sf .sf-card-name { text-wrap: balance; }
.sf p, .sf .sf-pdp-desc, .sf .sf-collapse-body { text-wrap: pretty; }

/* Preserve structured rich descriptions from the YKT seed pipeline (pack + Состав + nutrition etc. with \n\n).
   pre-line turns \n into breaks without forcing <br> in the template. */
.sf .sf-pdp-desc,
.sf .sf-collapse-body.sf-pdp-desc {
  white-space: pre-line;
}

/* Legacy ghost placeholder — still used by wishlist (cross-store grid). */
.sf .sf-card-noimg {
  width: 100%; height: 100%;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
  color: var(--theme-text-tertiary);
  background: var(--theme-bg-secondary);
  background:
    radial-gradient(130% 100% at 50% -10%, color-mix(in srgb, var(--sf-accent) 7%, transparent), transparent 55%),
    var(--theme-bg-secondary);
}
.sf .sf-card-noimg svg { width: 34px; height: 34px; opacity: 0.4; }
.sf .sf-card-noimg span { font-size: 11px; font-weight: 500; letter-spacing: 0.01em; opacity: 0.8; }

/* Lettermark tile — the no-photo state as merchandising, not apology.
   Product initial on a store-tinted wash; angle varies by .v1/.v2 so a
   photo-less shelf still has rhythm. */
.sf .sf-card-mark {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  background:
    radial-gradient(120% 130% at 18% -10%, color-mix(in srgb, var(--sf-accent) 17%, transparent), transparent 62%),
    linear-gradient(158deg, color-mix(in srgb, var(--sf-accent) 9%, var(--theme-bg-secondary)), var(--theme-bg-secondary) 78%);
}
.sf .sf-card-mark.v1 {
  background:
    radial-gradient(120% 130% at 84% -12%, color-mix(in srgb, var(--sf-accent-2) 15%, transparent), transparent 60%),
    linear-gradient(202deg, color-mix(in srgb, var(--sf-accent) 8%, var(--theme-bg-secondary)), var(--theme-bg-secondary) 76%);
}
.sf .sf-card-mark.v2 {
  background:
    radial-gradient(130% 140% at 50% 112%, color-mix(in srgb, var(--sf-accent) 13%, transparent), transparent 62%),
    linear-gradient(180deg, var(--theme-bg-secondary), color-mix(in srgb, var(--sf-accent) 7%, var(--theme-bg-secondary)));
}
/* Contained monogram chip, NOT a billboard letter. The initial used to be
   clamp(44px, 5.4vw, 64px) and filled the tile — fine as an occasional accent,
   but 0 of 95 catalogue products carry a photo, so every buyer surface
   (/market/catalog, /new, /browse) became a wall of giant A B B C D. A bounded
   chip on the same tinted wash reads as deliberate identity at any density,
   and keeps the per-card rhythm the .v1/.v2 angles were added for. */
.sf .sf-card-mark span {
  display: flex; align-items: center; justify-content: center;
  width: 27%; aspect-ratio: 1 / 1; max-width: 64px; min-width: 34px;
  border-radius: 22%;
  font-family: var(--font-family-heading);
  font-size: clamp(15px, 2.2vw, 24px); font-weight: 650; letter-spacing: -0.01em;
  background: color-mix(in srgb, var(--sf-accent) 13%, var(--theme-bg-secondary));
  border: 1px solid color-mix(in srgb, var(--sf-accent) 24%, transparent);
  color: color-mix(in srgb, var(--sf-accent) 78%, var(--theme-text));
}

.sf .sf-card-badge {
  position: absolute; top: 10px; left: 10px; z-index: 2;
  padding: 3px 9px; border-radius: var(--radius-full);
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.02em;
  background: color-mix(in srgb, var(--theme-card-bg) 88%, transparent);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  border: 1px solid var(--warning-border); color: var(--warning-text);
}
.sf .sf-card-badge.is-out { border-color: var(--danger-border); color: var(--danger-text); }

/* hover action rail (wishlist + quick view). Fade only — never translateY on
   hover/focus-within (UI_DESIGN_RULES §1/§5: state must not change geometry). */
.sf .sf-card-rail {
  position: absolute; top: 10px; right: 10px; z-index: 3;
  display: inline-flex; flex-direction: column; gap: 8px;
  opacity: 0; pointer-events: none;
  transition: opacity var(--transition-fast);
}
.sf .sf-card:hover .sf-card-rail,
.sf .sf-card:focus-within .sf-card-rail { opacity: 1; pointer-events: auto; }
.sf .sf-icon-btn {
  /* 40x40 minimum hit area (UI_DESIGN_RULES §12) — the icon glyph inside is a
     separately-sized SVG, so growing the box doesn't grow the visible glyph. */
  width: 40px; height: 40px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--theme-card-bg); color: var(--theme-text-secondary);
  border: 1px solid var(--border-subtle);
  transition: color var(--transition-fast), border-color var(--transition-fast), background-color var(--transition-fast);
}
.sf .sf-icon-btn:hover { color: var(--theme-text); border-color: var(--border-strong); }
.sf .sf-icon-btn.is-fav { color: var(--danger-text); border-color: var(--danger-border); }
@media (max-width: 640px) {
  .sf .sf-card-rail { opacity: 1; pointer-events: auto; }
}

/* quick-add — fades in on hover, always shown on touch. Opacity only — never
   translateY on hover/focus-within (same rule as the action rail above). */
.sf .sf-card-quickadd {
  position: absolute; bottom: 0; left: 0; right: 0; z-index: 2;
  padding: 10px;
  opacity: 0; pointer-events: none;
  transition: opacity var(--transition-fast);
}
.sf .sf-card:hover .sf-card-quickadd,
.sf .sf-card:focus-within .sf-card-quickadd { opacity: 1; pointer-events: auto; }
/* Touch: no hover, so the quick-add stays as a persistent glass bar pinned to
   the media bottom (static would fall into the tile's flow and overlap it). */
@media (max-width: 640px) { .sf .sf-card-quickadd { opacity: 1; pointer-events: auto; position: absolute; padding: 8px; } }
.sf .sf-card-quickadd .sf-btn-sec { -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); }

/* In-cart quantity stepper — replaces the quick-add button once the item is in
   the cart, and stays visible (.is-active) so the shopper always sees their qty,
   not only on hover. Green segmented −/qty/+, same footprint as the button. */
.sf .sf-card-quickadd.is-active { opacity: 1; pointer-events: auto; }
.sf .sf-card-qty {
  display: grid; grid-template-columns: 38px 1fr 38px; align-items: center;
  height: 36px; border-radius: var(--radius-md); overflow: hidden;
  background: var(--sf-accent); color: #fff;
  font-variant-numeric: tabular-nums;
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}
.sf .sf-card-qty.is-busy { opacity: 0.6; pointer-events: none; }
.sf .sf-card-qty-btn {
  height: 100%; border: none; background: transparent; color: #fff;
  font-size: 19px; line-height: 1; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background-color var(--transition-fast);
}
.sf .sf-card-qty-btn:hover { background: rgba(255, 255, 255, 0.16); }
.sf .sf-card-qty-btn:disabled { opacity: 0.45; cursor: not-allowed; background: transparent; }
.sf .sf-card-qty-val { text-align: center; font-size: 14px; font-weight: 700; }

.sf .sf-card-info { padding: var(--space-3) 2px 0; }
.sf .sf-card-eyebrow {
  /* Sans, tertiary. This was mono in the store's accent ink, which made
     category labels compete with prices, stars and CTAs for the same color —
     four things shouting the accent means no hierarchy at all. A category is
     metadata; mono is reserved for codes (SKUs, references). */
  font-size: var(--font-size-xxs); font-weight: 600; letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--theme-text-tertiary); margin-bottom: 1px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sf .sf-card-name {
  font-size: var(--wm-type-body); font-weight: 500; color: var(--theme-text); line-height: 1.4;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2;
  overflow: hidden; min-height: 0; margin: 0;
  /* A product name is user-supplied and need contain no space at all — a SKU,
     a concatenated title, a pasted URL. Measured on /market/low-stock at a
     375px viewport: a 110-character unbroken name stretched this element to
     845px and the page to 941px, so the whole card grid scrolled sideways.
     The line clamp does not save it; clamping limits LINES, and an unbreakable
     token is one very long line. anywhere, not break-word: break-word leaves
     the element's min-content width at the full token, which is what makes the
     container grow in the first place. */
  overflow-wrap: anywhere;
}

/* The card's one real link lives on its title, and its ::after stretches over
   the whole tile so the entire card stays clickable by mouse — the standard
   way to give a "clickable card" keyboard reach and link semantics without
   nesting the card's own buttons inside an <a>.
   z-index 1 deliberately: .sf-card-quickadd is 2 and .sf-card-rail is 3, so
   the wishlist and add-to-cart buttons stay above the overlay and keep taking
   their own clicks. .sf-card is already position: relative. */
.sf .sf-card-link {
  color: inherit;
  text-decoration: none;
}
.sf .sf-card-link::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: var(--radius-lg);
}
/* The focus ring has to be drawn on the overlay, not on the clipped title:
   .sf-card-name is a -webkit-box with overflow:hidden, so an outline on the
   text itself is cut off at the clamp. */
.sf .sf-card-link:focus-visible {
  outline: none;
}
.sf .sf-card-link:focus-visible::after {
  outline: 2px solid var(--border-accent);
  outline-offset: 2px;
}

/* Media-less navigation tile (hub / store-hub grids).
   .sf-card on its own is just `position: relative` — the visible surface comes
   from .sf-card-media. A tile with no image therefore rendered as naked text on
   the page background, so /market/hub and /market/store-hub looked like a bare
   list of 14 links rather than a grid of destinations.
   Per the design rules: hairline border, no shadow, and hover changes COLOUR
   only — no padding/border-width/transform shift, so nothing reflows. */
.sf .sf-card--tile {
  background: var(--theme-card-bg);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  transition: border-color var(--transition-fast), background var(--transition-fast);
}
.sf .sf-card--tile:hover {
  border-color: color-mix(in srgb, var(--sf-accent) 35%, var(--border-subtle));
  background: color-mix(in srgb, var(--sf-accent) 4%, var(--theme-card-bg));
}
/* The tile's name is a heading, not a clamped product title. */
.sf .sf-card--tile .sf-card-name {
  font-size: 13.5px; font-weight: 600;
  display: block; min-height: 0; white-space: normal;
}
.sf .sf-card-price { display: flex; align-items: baseline; gap: 4px; margin-top: var(--space-1); }
.sf .sf-card-price .sf-price { font-size: var(--wm-type-body); font-weight: 600; color: var(--theme-text); font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
.sf .sf-price-cur { font: inherit; vertical-align: baseline; }
.sf .sf-card-price .sf-price-cents { font-size: 11px; font-weight: 600; vertical-align: 0.5em; margin-left: 1px; }
.sf .sf-card-price .sf-price-was { font-size: 12px; color: var(--theme-text-tertiary); text-decoration: line-through; }
.sf .sf-card-cat { font-size: 11.5px; color: var(--theme-text-tertiary); margin-top: 2px; }

/* Grocery unit price ("206 ₽ / 100 г") — a quiet shelf-comparison caption under
   the price. Tabular so figures align down the column; tertiary so it never
   competes with the headline price. */
.sf .sf-card-meta {
  /* Wholesale facts: origin · minimum · availability. Sans, tertiary,
     tabular — and it WRAPS; a card line that clips the country is worse
     than a card that is 14px taller. */
  font-size: 11.5px;
  color: var(--theme-text-tertiary);
  font-variant-numeric: tabular-nums;
  margin-top: 3px;
  white-space: normal;
}
.sf .sf-unit-price {
  display: block;
  font-size: 11.5px;
  color: var(--theme-text-tertiary);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
  margin-top: 3px;
  /* "1200 per container · ₮1,208 per piece" was clipped mid-word at phone
     width; the two facts may take two lines. */
  white-space: normal;
  overflow-wrap: anywhere;
}
/* PDP carries it just under the big price row, a step up in size. */
.sf .sf-pdp-unit { margin: 4px 0 0; }
.sf .sf-pdp-unit .sf-unit-price { font-size: 13px; }
.sf .sf-pdp-unit:empty { display: none; }

/* ─── Shared rating stars (_sf_stars macro) ─────────────────────────────
   Moved to components/macros_v2.css. The macro calls itself a shared atom and
   is used on /market/products, which is not inside .sf — so scoping its rules
   to the storefront shell left that page rendering ten identical grey stars in
   a row instead of a five-star track with a gold overlay. #starsmoved */

/* ─── Card engagement lines (rating / options / sold) ─────────────────── */
.sf .sf-card-rating { margin-top: 4px; }
.sf .sf-card-options { font-size: 11.5px; color: var(--theme-text-secondary); margin-top: 4px; }
.sf .sf-card-sold { font-size: 11.5px; color: var(--theme-text-tertiary); margin-top: 4px; font-variant-numeric: tabular-nums; }

/* Bestseller chip — dark chip + accent text (no Amazon orange; honest top-N). */
/* --sf-ink, not --sf-accent. The two exist precisely so a store's colour can
   be a SURFACE and a TEXT tone independently: every tone pins a lighter
   --sf-ink for dark mode, and the accent stays deep enough to carry white
   button text. Using the accent as the label colour skipped that — on the
   moss tone it read 2.92:1 against the elevated card in dark. The accent
   still draws the border, where 3:1 for a non-text boundary applies. */
.sf .sf-card-badge.is-bestseller { background: var(--theme-bg-elevated); color: var(--sf-ink); border: 1px solid color-mix(in srgb, var(--sf-accent) 30%, transparent); font-weight: 650; }

/* ─── Card density variants (rail = also-viewed, fbt = bought-together) ── */
.sf .sf-card--rail .sf-card-name,
.sf .sf-card--fbt .sf-card-name { -webkit-line-clamp: 2; min-height: 0; font-size: var(--wm-type-body); }
.sf .sf-card--rail .sf-card-price .sf-price,
.sf .sf-card--fbt .sf-card-price .sf-price { font-size: var(--wm-type-body); }
.sf .sf-card--fbt .sf-card-info { padding-top: 6px; }

/* ─── Empty / coming-soon states ──────────────────────────────────────── */
/* Centred in the available space, not top-anchored: the shell is a 100vh
   sticky-footer column, so a top-anchored empty state left ~900px of dead
   canvas below it and read as a broken page. Self-contained min-height so it
   does not depend on any parent having a resolved height. */
/* Empty states: templates must set role="status" aria-live="polite" on this
   container so filter/load empties are announced once (not on the title child). */
.sf .sf-empty {
  text-align: center;
  padding: var(--space-12) var(--space-4) var(--space-16);
  min-height: min(58vh, 520px);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
/* The min-height above exists to stop a lone empty state leaving dead canvas in
   the 100vh sticky-footer column. When something real follows it — the empty
   cart's recently-viewed / featured rails — that same 520px becomes the problem
   instead of the fix, burying the merchandising below the fold. Same component,
   no geometry change on state, just no reserved void when it isn't needed. */
.sf .sf-empty--compact {
  min-height: 0;
  padding: var(--space-8) var(--space-4) var(--space-6);
}
.sf .sf-empty .sf-mark { margin: 0 auto var(--space-5); }
.sf .sf-empty-title { font-family: var(--font-family-heading); font-size: 19px; font-weight: 650; letter-spacing: -0.01em; color: var(--theme-text); margin-bottom: var(--space-2); }
.sf .sf-empty-text { font-size: 13.5px; color: var(--theme-text-tertiary); max-width: 400px; margin: 0 auto var(--space-5); line-height: 1.55; }

/* ─── Pagination ──────────────────────────────────────────────────────── */
/* ─── View tabs — primary one-tap browse switch ───────────────────────── */
.sf .sf-tabs {
  display: flex; gap: var(--space-5); align-items: center;
  border-bottom: 1px solid var(--border-divider);
  margin-bottom: var(--space-4);
}
.sf .sf-tab {
  position: relative; padding: 10px 2px;
  /* Persistent 2px rail — only its COLOR changes on state, never geometry. */
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  font-size: 14px; font-weight: 600; color: var(--theme-text-tertiary);
  text-decoration: none; white-space: nowrap;
  transition: color 160ms ease, border-color 160ms ease;
}
@media (hover: hover) and (pointer: fine) {
  .sf .sf-tab:hover { color: var(--theme-text); }
}
.sf .sf-tab.is-active { color: var(--theme-text); border-bottom-color: var(--sf-cta); }

/* ─── Pager — bespoke, store-accent, tactile ──────────────────────────── */
.sf .sf-pager {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-3);
  padding: var(--space-8) 0 var(--space-12);
}
.sf .sf-pager-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; justify-content: center; }
.sf .sf-pg {
  display: inline-flex; align-items: center; justify-content: center;
  /* 32px quiet controls: pagination is wayfinding, not a call to action —
     the 40px radius-12 blocks with a solid accent square for the current
     page were the loudest thing on the whole storefront. */
  min-width: var(--control-h-sm); height: var(--control-h-sm); padding: 0 10px;
  border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  background: var(--theme-card-bg); color: var(--theme-text-secondary);
  font-size: 13px; font-weight: 550; font-variant-numeric: tabular-nums;
  text-decoration: none; cursor: pointer; user-select: none;
  /* Specific props only — never `all`. :active below is opacity-only (no
     geometry change), so no transform is transitioned here either. */
  transition: background-color 160ms ease, border-color 160ms ease, color 160ms ease;
}
.sf .sf-pg-nav { padding: 0; color: var(--theme-text-tertiary); }
.sf .sf-pg-nav svg { display: block; }
@media (hover: hover) and (pointer: fine) {
  .sf a.sf-pg:hover {
    background: color-mix(in srgb, var(--sf-accent) 8%, var(--theme-card-bg));
    border-color: color-mix(in srgb, var(--sf-accent) 42%, var(--border-subtle));
    color: var(--theme-text);
  }
}
.sf a.sf-pg:active { opacity: 0.7; }  /* press cue — opacity, not geometry (house rule: state never changes geometry) */
.sf .sf-pg.is-active {
  /* Current page = ink on a light accent tint, not a solid accent block.
     Solid fills are reserved for the ONE primary action on a page; "you are
     on page 1" is a fact, not an action. */
  background: color-mix(in srgb, var(--sf-accent) 12%, var(--theme-card-bg));
  border-color: color-mix(in srgb, var(--sf-accent) 45%, var(--border-subtle));
  color: var(--theme-text);
  font-weight: 650;
  cursor: default;
}
.sf .sf-pg.is-disabled { opacity: 0.4; pointer-events: none; }
.sf .sf-pg-gap {
  display: inline-flex; align-items: flex-end; justify-content: center;
  min-width: 20px; height: 32px; padding-bottom: 6px;
  color: var(--theme-text-tertiary); user-select: none;
}
.sf .sf-pager-summary {
  font-size: 12.5px; color: var(--theme-text-tertiary); margin: 0;
}
.sf .sf-pager-summary strong {
  color: var(--theme-text-secondary); font-weight: 600; font-variant-numeric: tabular-nums;
}

/* ─── Quick-view modal ────────────────────────────────────────────────── */
/* Native dialog: the element is the layer, .sf-qv-dialog is the card. */
/* position/inset/margin spelled out: a global reset zeroes the UA's
   `margin: auto`, which is what centres a modal dialog — without it the card
   sat at the top-left corner (measured 0,0 on 2026-09-05). */
dialog.sf-qv { position: fixed; inset: 0; margin: auto; padding: 0; border: 0; background: transparent; width: min(92vw, 720px); max-width: none; max-height: 90vh; overflow: visible; }
dialog.sf-qv .sf-qv-dialog { max-height: 90vh; overflow: auto; }
dialog.sf-qv::backdrop { background: rgb(0 0 0 / 0.5); }
.sf .sf-qv-grid { display: grid; grid-template-columns: 1fr 1fr; }
@media (max-width: 640px) { .sf .sf-qv-grid { grid-template-columns: 1fr; } .sf .sf-qv-media { aspect-ratio: 4 / 3; } }
.sf .sf-qv-close { position: absolute; top: 8px; right: 8px; z-index: 1; background: var(--theme-card-bg); border: 1px solid var(--border-subtle); }
.sf .sf-qv-dialog { position: relative; }
.sf .sf-qv-dialog { background: var(--theme-card-bg); border-radius: var(--radius-lg); overflow: hidden; max-width: 680px; }
.sf .sf-qv-media { aspect-ratio: 1 / 1; background: #ffffff; }
.sf .sf-qv-media img { width: 100%; height: 100%; object-fit: contain; }
.sf .sf-qv-body { padding: var(--space-5); display: flex; flex-direction: column; }
.sf .sf-qv-name { font-family: var(--font-family-heading); font-size: 18px; font-weight: 600; color: var(--theme-text); margin-bottom: var(--space-2); }
.sf .sf-qv-price { font-size: 20px; font-weight: 700; color: var(--sf-ink); font-variant-numeric: tabular-nums; }
.sf .sf-qv-desc { font-size: 13px; color: var(--theme-text-tertiary); line-height: 1.55; margin: var(--space-3) 0; flex: 1; }

/* ─── PDP (product detail) ────────────────────────────────────────────── */
.sf .sf-crumbs {
  border-bottom: 1px solid var(--border-subtle);
  position: sticky; top: 0; z-index: 15;
  /* De-expressed: opaque sticky chrome — no translucent liquid-glass +
     backdrop-blur (which forced a full-width repaint on every scroll frame). */
  background: var(--theme-card-bg);
}
.sf .sf-crumbs-row { display: flex; flex-wrap: nowrap; align-items: center; justify-content: space-between; gap: var(--space-3); padding: var(--space-3) 0; }
.sf .sf-crumb-nav { display: flex; flex-wrap: nowrap; align-items: center; gap: 6px; font-size: 12.5px; color: var(--theme-text-tertiary); min-width: 0; }
/* WCAG 2.5.8 target floor (24px) for STANDALONE links. These are navigation
   targets, not words inside a sentence, so the inline exception does not
   apply — they measured 16–20px tall. Height only; no geometry change on
   state, and the text position is unaffected. */
.sf .sf-crumb-nav a,
.sf .sf-section-link {
    display: inline-flex;
    align-items: center;
    min-height: 24px;
}

.sf .sf-crumb-nav a { white-space: nowrap; }
.sf .sf-crumb-nav a:hover { color: var(--theme-text-secondary); }
.sf .sf-crumb-sep { color: var(--border-strong); }
.sf .sf-crumb-cur { color: var(--theme-text); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 220px; }
.sf .sf-crumb-back { white-space: nowrap; flex-shrink: 0; }
.sf .sf-crumb-back .sf-crumb-back-store { display: none; }
/* Mobile: the full trail + a wrapping "Back to store" is two lines of clutter.
   Collapse to ONE link carrying the store name — the page title says the rest. */
@media (max-width: 640px) {
  .sf .sf-crumb-nav { display: none; }
  .sf .sf-crumbs-row { justify-content: flex-start; }
  .sf .sf-crumb-back .sf-crumb-back-store { display: inline; max-width: 70vw; overflow: hidden; text-overflow: ellipsis; }
  .sf .sf-crumb-back .sf-crumb-back-label { display: none; }
}

/* Product identity, price and purchase controls share one reading column. */
.sf .sf-pdp { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--space-7); align-items: start; padding: var(--space-7) 0 var(--space-5); }
@media (max-width: 1023px) {
  /* align-items: stretch is LOAD-BEARING — the desktop grid rule above sets
     align-items: start, and in a column flexbox that makes every child
     shrink to content width (~256px), hugging the left edge with a giant
     void beside it. The whole mobile PDP rendered broken because of this
     one inherited property (2026-07-07: "UI is clearly broken"). */
  .sf .sf-pdp { display: flex; flex-direction: column; align-items: stretch; gap: var(--space-5); padding: var(--space-5) 0 var(--space-2); }
  /* Mobile-first reading order: image → title/details → buy. Never price before the title. */
  .sf .sf-pdp-gallery { order: 1; } .sf .sf-pdp-info { order: 2; } .sf .sf-pdp-buybox-col { order: 3; }
  /* A square gallery at full phone width devours the viewport — cap and
     center it; info/buybox stay full-width. */
  .sf .sf-pdp-gallery { width: 100%; max-width: 440px; margin-inline: auto; }
  /* Let flex children shrink so long titles/words wrap instead of forcing horizontal overflow. */
  .sf .sf-pdp-gallery, .sf .sf-pdp-info, .sf .sf-pdp-buybox-col { min-width: 0; }
}

.sf .sf-pdp-gallery { position: sticky; top: var(--space-8); }
@media (max-width: 900px) { .sf .sf-pdp-gallery { position: static; } }
.sf .sf-pdp-stage {
  /* A square stage suits the mostly portrait catalog (300-photo sample,
     median width/height 0.78). Preserve the full product with contain. */
  position: relative; aspect-ratio: 1 / 1; border-radius: var(--radius-lg); overflow: hidden;
  /* Blend with the product photos' white backgrounds. */
  background: #ffffff; border: 1px solid var(--border-subtle);
}
.sf .sf-pdp-stage img, .sf .sf-pdp-stage video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
/* Minimal padding so the full photo fills the stage without being marooned. */
.sf .sf-pdp-stage img { padding: var(--space-2); }

/* Mobile overrides follow the base rules: media queries add no specificity.
   Use the full phone width and keep the entire product visible. */
@media (max-width: 640px) {
  .sf .sf-pdp-stage {
    width: calc(100% + 2 * var(--space-4));
    margin: 0 calc(-1 * var(--space-4));
    border: none;
    border-radius: 0;
    /* Keep the white stage so photo margins blend into its background. */
  }
  .sf .sf-pdp-stage img {
    padding: 0;
    object-fit: contain;
  }
  /* Tighten content below for efficiency, full width. */
  .sf .sf-pdp-info {
    padding-left: var(--space-4);
    padding-right: var(--space-4);
  }
}
/* Cap the image card so a single small photo isn't stranded in a giant square
   on wide screens; it stays left-aligned in its sticky column. */
.sf .sf-pdp-gallery { width: 100%; max-width: 520px; }
@media (max-width: 1023px) { .sf .sf-pdp-gallery { max-width: none; } }
.sf .sf-pdp-fav { position: absolute; top: 14px; right: 14px; z-index: 4; }
.sf .sf-pdp-arrow {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 4;
  /* 40x40 minimum hit area (UI_DESIGN_RULES §12). */
  width: 40px; height: 40px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--theme-card-bg); border: 1px solid var(--border-subtle); color: var(--theme-text-secondary);
}
.sf .sf-pdp-arrow.is-prev { left: 12px; } .sf .sf-pdp-arrow.is-next { right: 12px; }
.sf .sf-pdp-thumbs { display: flex; gap: 8px; overflow-x: auto; scrollbar-width: none; margin-top: var(--space-3); }
.sf .sf-pdp-thumbs::-webkit-scrollbar { display: none; }
.sf .sf-pdp-thumb { flex-shrink: 0; width: 64px; height: 64px; border-radius: var(--radius-md); overflow: hidden; border: 2px solid transparent; background: var(--theme-bg-secondary); }
.sf .sf-pdp-thumb img { width: 100%; height: 100%; object-fit: cover; }

/* ── Image zoom — click the photo for a focused, backdropped larger view ── */
.sf .sf-pdp-stage img { cursor: zoom-in; }
.sf .sf-pdp-zoom-hint {
  position: absolute; bottom: 10px; right: 10px; z-index: 4; pointer-events: none;
  width: 30px; height: 30px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--theme-card-bg) 82%, transparent);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  border: 1px solid var(--border-subtle); color: var(--theme-text-secondary);
}
.sf .sf-pdp-zoom-hint svg { width: 16px; height: 16px; }
/* The overlay is appended to <body>, OUTSIDE the .sf scope — keep it unscoped. */
body.sf-zoom-lock { overflow: hidden; }
.sf-zoom-overlay {
  position: fixed; inset: 0; z-index: 1000;
  display: none; align-items: center; justify-content: center; padding: 4vmin;
  background: rgba(10, 12, 14, 0.86);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
}
.sf-zoom-overlay.is-open { display: flex; }
.sf-zoom-img {
  max-width: 92vw; max-height: 92vh;
  border-radius: var(--radius-lg, 12px); background: #f4f4f5;
}
.sf-zoom-close {
  position: absolute; top: 18px; right: 22px;
  width: 42px; height: 42px; border-radius: 50%; border: none; cursor: pointer;
  background: rgba(255, 255, 255, 0.14); color: #fff; font-size: 26px; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
}
.sf-zoom-close:hover { background: rgba(255, 255, 255, 0.24); }

/* PDP no-photo: full-stage lettermark, same merchandising language as cards. */
.sf .sf-stage-mark {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-2);
  background:
    radial-gradient(120% 130% at 18% -10%, color-mix(in srgb, var(--sf-accent) 16%, transparent), transparent 62%),
    radial-gradient(130% 140% at 88% 118%, color-mix(in srgb, var(--sf-accent-2) 11%, transparent), transparent 60%),
    linear-gradient(165deg, color-mix(in srgb, var(--sf-accent) 8%, var(--theme-bg-secondary)), var(--theme-bg-secondary) 80%);
}
/* Bounded monogram, scaled for the PDP stage. Same family as .sf-card-mark span
   — was clamp(72px, 14vw, 128px), a letter big enough to read as a rendering
   fault rather than a placeholder. The category label below it carries the
   real information. */
.sf .sf-stage-mark .sf-stage-initial {
  display: flex; align-items: center; justify-content: center;
  width: clamp(72px, 15vw, 112px); aspect-ratio: 1 / 1;
  border-radius: 22%;
  font-family: var(--font-family-heading);
  font-size: clamp(28px, 5.6vw, 44px); font-weight: 650; letter-spacing: -0.01em; line-height: 1;
  background: color-mix(in srgb, var(--sf-accent) 12%, var(--theme-bg-secondary));
  border: 1px solid color-mix(in srgb, var(--sf-accent) 22%, transparent);
  color: color-mix(in srgb, var(--sf-accent) 76%, var(--theme-text));
}
.sf .sf-stage-mark .sf-stage-cat {
  font-size: 11px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase;
  color: color-mix(in srgb, var(--sf-accent) 52%, var(--theme-text-tertiary));
}

/* Share tools — quiet row under the gallery, not floating chrome. */
.sf .sf-pdp-share {
  display: flex; align-items: center; gap: var(--space-2);
  margin-top: var(--space-3);
}
/* Sans, like every other label: "Share" is a word, not a code (UI rule §7). */
.sf .sf-pdp-share-label {
  font-size: 12.5px; font-weight: 500;
  color: var(--theme-text-tertiary); margin-right: var(--space-1);
}

.sf .sf-pdp-info { display: flex; flex-direction: column; }
.sf .sf-pdp-cat { align-self: flex-start; font-size: 11px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; color: var(--sf-ink); margin-bottom: var(--space-3); }
.sf .sf-pdp-title { font-family: var(--font-family-heading); font-size: var(--wm-type-page); font-weight: 650; letter-spacing: -0.022em; line-height: 1.12; color: var(--theme-text); margin-bottom: var(--space-2); overflow-wrap: anywhere; }
.sf .sf-pdp-rating { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--theme-text-secondary); margin-bottom: var(--space-3); }
.sf .sf-stars { display: inline-flex; gap: 1px; color: var(--rating-star); }
.sf .sf-stars .is-empty { color: var(--border-strong); }
.sf .sf-pdp-price-row { display: flex; align-items: baseline; gap: var(--space-4); flex-wrap: wrap; }
.sf .sf-pdp-price { font-family: var(--font-family-heading); font-size: var(--wm-type-page); font-weight: 600; letter-spacing: -0.01em; color: var(--theme-text); font-variant-numeric: tabular-nums; }
.sf .sf-pdp-divider { height: 1px; background: var(--border-divider); margin: var(--space-5) 0; }
.sf .sf-pdp-desc { font-size: 14px; color: var(--theme-text-secondary); line-height: 1.6; margin-bottom: var(--space-5); }
.sf .sf-pdp-label { font-size: 11px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; color: var(--theme-text-tertiary); margin-bottom: var(--space-2); display: block; }

/* "Visit the X store" brand link above the title. */
.sf .sf-pdp-brandlink { display: inline-flex; align-items: center; min-height: 24px; font-size: 13px; font-weight: 600; color: var(--sf-ink); text-decoration: none; margin-bottom: 6px; }
.sf .sf-pdp-brandlink:hover { text-decoration: underline; }

/* Buy-box rail — sticky so it stays in view as the details column scrolls. */
.sf .sf-pdp-buybox-col { position: sticky; top: var(--space-6); }
@media (max-width: 1023px) { .sf .sf-pdp-buybox-col { position: static; } }

/* Real delivery row inside the buy box. */
.sf .sf-buybox-deliv { display: flex; gap: 9px; align-items: flex-start; padding: 4px 0 10px; margin-bottom: var(--space-3); color: var(--theme-text-secondary); }
.sf .sf-buybox-deliv svg { flex-shrink: 0; margin-top: 1px; color: var(--sf-ink); }
.sf .sf-buybox-deliv strong { display: block; font-size: 13px; color: var(--theme-text); font-weight: 600; }
.sf .sf-buybox-deliv span { font-size: 12px; color: var(--theme-text-tertiary); }

/* Sold-by + payment rows. */
.sf .sf-buybox-meta { margin-top: var(--space-4); display: flex; flex-direction: column; gap: 6px; }
.sf .sf-buybox-row { display: flex; gap: 10px; font-size: 12px; line-height: 1.35; }
.sf .sf-buybox-k { color: var(--theme-text-tertiary); flex-shrink: 0; min-width: 64px; }
.sf .sf-buybox-v { color: var(--theme-text-secondary); }
.sf .sf-buybox-seller { color: var(--sf-ink); font-weight: 600; text-decoration: none; }
.sf .sf-buybox-seller:hover { text-decoration: underline; }

/* Mobile sticky buy bar — price + add-to-cart pinned to the bottom so they stay
   reachable while scrolling the long PDP. Desktop hides it (the sticky buy-box
   rail handles it). Reuses the rail's data-action handler — no extra JS. */
.sf .sf-buybar { display: none; }
@media (max-width: 1023px) {
  .sf .sf-buybar {
    display: flex; align-items: center; gap: var(--space-3);
    position: fixed; inset: auto 0 0 0; z-index: 60;
    padding: 10px var(--space-4) calc(10px + env(safe-area-inset-bottom, 0px));
    background: var(--theme-card-bg); border-top: 1px solid var(--border-subtle);
  }
  .sf .sf-buybar-price { display: flex; flex-direction: column; line-height: 1.15; flex-shrink: 0; }
  .sf .sf-buybar-amount { font-family: var(--font-family-heading); font-size: 19px; font-weight: 700; letter-spacing: -0.02em; color: var(--theme-text); font-variant-numeric: tabular-nums; }
  .sf .sf-buybar-stock { font-size: 11px; font-weight: 600; color: var(--success, var(--sf-accent)); }
  .sf .sf-buybar-cta { flex: 1; margin: 0; }
  /* Reserve space so the fixed bar never hides the last content (reviews, seller). */
  .sf.sf-store { padding-bottom: calc(76px + env(safe-area-inset-bottom, 0px)); }
}

/* "Sell this in my store" — the universal-catalog one-click stocking CTA. */
.sf .sf-sell-this { margin-top: var(--space-4); padding: var(--space-4); border: 1px dashed color-mix(in srgb, var(--sf-accent) 42%, var(--border-subtle)); border-radius: var(--radius-lg); background: color-mix(in srgb, var(--sf-accent) 4%, transparent); }
.sf .sf-sell-this-h { font-size: 13.5px; font-weight: 650; color: var(--theme-text); margin-bottom: 3px; }
.sf .sf-sell-this-sub { font-size: 12px; color: var(--theme-text-secondary); line-height: 1.45; margin: 0 0 10px; }

/* "Also at N other stores" — the same universal product, compared across stores. */
.sf .sf-offers-sub { font-size: 13px; color: var(--theme-text-secondary); margin: -4px 0 var(--space-4); }
.sf .sf-offers-list { display: flex; flex-direction: column; gap: 6px; max-width: 560px; }
.sf .sf-offer { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); padding: 12px 14px; border: 1px solid var(--border-subtle); border-radius: var(--radius-md); text-decoration: none; transition: border-color var(--transition-fast), background-color var(--transition-fast); }
.sf .sf-offer:hover { border-color: color-mix(in srgb, var(--sf-accent) 40%, var(--border-subtle)); background: color-mix(in srgb, var(--sf-accent) 3%, transparent); }
.sf .sf-offer-store { display: inline-flex; align-items: center; gap: 9px; min-width: 0; }
.sf .sf-offer-name { display: flex; flex-direction: column; gap: 1px; min-width: 0; font-size: 13.5px; font-weight: 600; color: var(--theme-text); }
.sf .sf-offer-near { font-size: 11px; font-weight: 600; color: var(--sf-accent, var(--brand-primary)); font-variant-numeric: tabular-nums; }
.sf .sf-offer-price { font-size: var(--wm-type-body); font-weight: 600; color: var(--theme-text); font-variant-numeric: tabular-nums; flex-shrink: 0; }
/* Success-green marks the offer the buy-box actually CHOSE, never merely the
   first row. Keyed on :first-child, a US shop's "$1.43" rendered green above
   a "₽87.80" on a page priced in ₮ — a cheapest-signal invented by sort
   order and applied across currencies that are never converted. */
.sf .sf-offer-best .sf-offer-price { color: var(--success-text); }

/* ISO code shown when a shop prices in a different currency than this page. */
.sf .sf-offer-cur {
    margin-left: var(--space-1, 4px);
    color: var(--theme-text-tertiary);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.04em;
}
.sf .sf-offer-best { border-color: color-mix(in srgb, var(--sf-accent) 55%, var(--border-subtle)); background: color-mix(in srgb, var(--sf-accent) 5%, transparent); }
.sf .sf-offer-tag { display: inline-block; font-size: var(--font-size-xxs); font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--sf-accent, var(--brand-primary)); }

/* Buy box — the auto-picked best offer, in the buybox rail under the CTA. */
.sf .sf-bb { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; margin-top: var(--space-3); padding: 10px 12px; border-radius: var(--radius-md); border: 1px solid var(--border-subtle); }
.sf .sf-bb-badge { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--sf-accent, var(--brand-primary)); }
.sf .sf-bb-why { font-size: 12px; color: var(--theme-text-secondary); font-variant-numeric: tabular-nums; }
.sf .sf-bb-here { background: color-mix(in srgb, var(--success) 8%, transparent); border-color: color-mix(in srgb, var(--success) 35%, var(--border-subtle)); }
.sf .sf-bb-here .sf-bb-badge { color: var(--success-text); }
.sf a.sf-bb-switch { text-decoration: none; background: color-mix(in srgb, var(--sf-accent) 6%, transparent); border-color: color-mix(in srgb, var(--sf-accent) 45%, var(--border-subtle)); transition: border-color var(--transition-fast), background-color var(--transition-fast); }
.sf a.sf-bb-switch:hover { border-color: var(--sf-accent); background: color-mix(in srgb, var(--sf-accent) 11%, transparent); }
.sf .sf-bb-store { font-size: 12.5px; font-weight: 600; color: var(--theme-text); }
.sf .sf-bb-price { margin-left: auto; font-size: var(--wm-type-body); font-weight: 600; color: var(--theme-text); font-variant-numeric: tabular-nums; }

/* Collapsible product info (<details>) — About / Specifications. */
.sf .sf-pdp-collapse-group { margin-top: var(--space-4); }
.sf .sf-collapse { border-top: 1px solid var(--border-subtle); }
.sf .sf-collapse:last-child { border-bottom: 1px solid var(--border-subtle); }
.sf .sf-collapse > summary { display: flex; align-items: center; justify-content: space-between; cursor: pointer; padding: 13px 2px; font-size: 14px; font-weight: 600; color: var(--theme-text); list-style: none; }
.sf .sf-collapse > summary::-webkit-details-marker { display: none; }
.sf .sf-collapse > summary::after { content: "+"; font-size: 18px; font-weight: 400; color: var(--theme-text-tertiary); }
.sf .sf-collapse[open] > summary::after { content: "–"; }
.sf .sf-collapse-body { padding: 0 2px 14px; font-size: 13.5px; color: var(--theme-text-secondary); line-height: 1.6; }
.sf .sf-specs-dl { display: flex; flex-direction: column; gap: 6px; }
.sf .sf-specs-dl > div { display: flex; justify-content: space-between; gap: 12px; }
.sf .sf-specs-dl dt { color: var(--theme-text-tertiary); } .sf .sf-specs-dl dd { color: var(--theme-text); margin: 0; font-variant-numeric: tabular-nums; }

/* Buy box — the transaction surface, gently lifted off the page. */
.sf .sf-buybox {
  padding: var(--space-3) 0;
  margin-bottom: var(--space-3);
}
.sf .sf-buybox > .sf-pdp-price-row { margin-bottom: var(--space-2); }
.sf .sf-buybox .sf-pdp-unit-inline {
  font-size: 13px;
  color: var(--theme-text-tertiary);
  font-weight: 400;
  margin-left: 6px;
  vertical-align: baseline;
}

/* Quantity and purchase actions share one alignment and spacing rhythm. */
.sf .sf-buy-qty-ctas {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}
.sf .sf-qty-wrap {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.sf .sf-qty-wrap > .sf-pdp-label {
  margin: 0;
  font-size: var(--wm-type-body);
  font-weight: var(--wm-weight-medium);
  line-height: var(--wm-leading-text);
  letter-spacing: normal;
  text-transform: none;
}
.sf .sf-qty-select {
  min-width: 70px;
  padding: 6px 10px;
  border: 1px solid var(--border-control);
  border-radius: var(--radius-md);
  background: var(--theme-input-bg);
  color: var(--theme-text);
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  appearance: none;
  cursor: pointer;
}
.sf .sf-ctas-stack {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.sf .sf-buybox .sf-btn-buy {
  /* --radius-md, matching the mobile buy bar and every other control in the
     system. The 9999px pill was the only one on the page. */
  border-radius: var(--radius-md);
  font-weight: 600;
  padding: 11px 0;
  font-size: 14px;
  text-align: center;
}
.sf .sf-buybox .sf-btn-primary {
  background: var(--sf-buybox-primary);
  border-color: var(--sf-buybox-primary);
  color: var(--sf-buybox-ink);
}
.sf .sf-buybox .sf-btn-secondary {
  background: transparent;
  border-color: var(--border-strong);
  color: var(--theme-text);
}
/* Hover tints only — border-width and padding stay put so nothing reflows. */
.sf .sf-buybox .sf-btn-secondary:hover {
  background: color-mix(in srgb, var(--sf-accent) 7%, transparent);
  border-color: var(--sf-accent);
}

/* The buy box holds four actions. They must read as a LADDER, not four peers:
   filled accent (add to cart) → hairline (buy now) → ghost → ghost. The wishlist
   button shipped as .sf-btn-sec — a white hairline box the same size and weight
   as "Buy now", so the two outranked the pair below them and the eye had no
   idea which of the four mattered. Scoped to .sf-buybox: .sf-btn-sec is a
   shared storefront class and the seller shell depends on its usual look. */
.sf .sf-buybox .sf-btn-sec {
  background: transparent;
  border-color: transparent;
  color: var(--theme-text-secondary);
  font-weight: 500;
}
.sf .sf-buybox .sf-btn-sec:hover {
  background: color-mix(in srgb, var(--sf-accent) 6%, transparent);
  color: var(--theme-text);
}

/* "Also sell this?" is a cross-sell prompt aimed at other sellers, not this
   page's job. Filled in the store accent it was a second loud CTA competing
   with Add-to-cart — two identical magenta buttons, one page. One filled
   button per screen; this one states itself with a hairline. */
.sf .sf-sell-this .sf-btn {
  background: transparent;
  border-color: var(--sf-accent);
  color: var(--sf-ink);
}
.sf .sf-sell-this .sf-btn:hover {
  background: color-mix(in srgb, var(--sf-accent) 8%, transparent);
}

/* Quantity group: label tightly above the stepper, so it doesn't float over the whole row */
.sf .sf-buy-row .sf-qty-group {
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.sf .sf-buy-row .sf-qty-group .sf-pdp-label {
  font-size: 10.5px;
  font-weight: 500;
  color: var(--theme-text-tertiary);
  letter-spacing: 0.02em;
}

/* Equal cells, one outer border, and no native spinner shifting the numeral. */
.sf .sf-buybox .wm-quantity-stepper {
  display: inline-grid;
  grid-template-columns: repeat(3, 44px);
  align-items: stretch;
  border: 1px solid var(--border-control);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--theme-card-bg);
}
.sf .sf-buybox .wm-quantity-stepper .quantity-btn,
.sf .sf-buybox .wm-quantity-stepper-input {
  box-sizing: border-box;
  width: 44px;
  height: 44px;
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  font-family: inherit;
  font-size: var(--wm-type-body);
  line-height: 1;
  text-align: center;
  background: transparent;
  color: var(--theme-text);
}
.sf .sf-buybox .wm-quantity-stepper .quantity-btn {
  display: grid;
  place-items: center;
  font-size: 18px;
  font-weight: var(--wm-weight-regular);
  color: var(--theme-text-secondary);
  transition: background-color var(--transition-fast), color var(--transition-fast);
}
.sf .sf-buybox .wm-quantity-stepper .quantity-btn:hover {
  background: var(--theme-hover);
  color: var(--theme-text);
}
.sf .sf-buybox .wm-quantity-stepper-input {
  appearance: textfield;
  -moz-appearance: textfield;
  border-inline: 1px solid var(--border-control);
  font-variant-numeric: tabular-nums;
  font-weight: var(--wm-weight-medium);
}
.sf .sf-buybox .wm-quantity-stepper-input::-webkit-inner-spin-button,
.sf .sf-buybox .wm-quantity-stepper-input::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.sf .sf-buybox .wm-quantity-stepper:focus-within {
  outline: 2px solid var(--border-accent);
  outline-offset: 2px;
}

/* variant chips */
.sf .sf-variants { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: var(--space-4); }
.sf .sf-variant {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 14px; border-radius: var(--radius-md);
  border: 1px solid var(--border-subtle); color: var(--theme-text-secondary);
  background: var(--theme-card-bg);
  font-size: 13px; font-weight: 500; transition: border-color var(--transition-fast), color var(--transition-fast), background-color var(--transition-fast);
}
.sf label:has(input:checked) .sf-variant { border-color: var(--sf-cta); color: var(--sf-ink); background: color-mix(in srgb, var(--sf-cta) 7%, var(--theme-card-bg)); }
.sf label:has(input:disabled) .sf-variant { color: var(--theme-text-tertiary); text-decoration: line-through; cursor: not-allowed; }

/* quantity stepper */
.sf .sf-qty { display: inline-flex; align-items: center; border: 1px solid var(--border-control); border-radius: var(--radius-md); overflow: hidden; background: var(--theme-card-bg); }
/* Width bumped to complete the 40x40 minimum hit area (UI_DESIGN_RULES §12) — height was already 44px. */
.sf .sf-qty button { width: 40px; height: 44px; display: inline-flex; align-items: center; justify-content: center; color: var(--theme-text-secondary); background: var(--theme-card-bg); font-size: 16px; transition: background-color var(--transition-fast); }
.sf .sf-qty button:hover { background: var(--theme-hover); }
.sf .sf-qty input { width: 52px; height: 44px; text-align: center; border: 0; border-left: 1px solid var(--border-control); border-right: 1px solid var(--border-control); background: var(--theme-input-bg); color: var(--theme-text); font-variant-numeric: tabular-nums; font-size: 14px; }
.sf .sf-qty input:focus { outline: none; }

.sf .sf-trust { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-3); padding-top: var(--space-4); font-size: 11.5px; color: var(--theme-text-tertiary); }
.sf .sf-trust span { display: inline-flex; align-items: center; gap: var(--space-3); }
.sf .sf-trust span + span::before { content: "·"; color: var(--border-strong); }
.sf .sf-specs { margin-top: var(--space-2); }
.sf .sf-specs dl > div { display: flex; justify-content: space-between; padding: 9px 0; border-bottom: 1px solid var(--border-divider); font-size: 13px; }
.sf .sf-specs dl > div:last-child { border-bottom: 0; }
.sf .sf-specs dt { color: var(--theme-text-tertiary); } .sf .sf-specs dd { font-weight: 500; color: var(--theme-text); font-variant-numeric: tabular-nums; }

/* reviews */
.sf .sf-section { margin-top: var(--space-8); padding-top: var(--space-6); border-top: 1px solid var(--border-divider); }
@media (max-width: 900px) { .sf .sf-section { margin-top: var(--space-4); padding-top: var(--space-5); } }
/* Landing bestsellers rail — like a section but no top divider (it sits right
   under the store identity, which already carries its own bottom border). */
.sf .sf-rail-section { margin: var(--space-5) 0 var(--space-6); }
.sf .sf-rail-section .sf-section-h { margin-bottom: var(--space-3); }

/* .sf-related-link is GONE. It existed to make rail cards "natively clickable"
   by wrapping sf_product_card in an <a> — but the macro emits its own link and
   stretches it across the card, so the wrapper was invalid nested-anchor markup
   that the parser reduced to an empty link occupying its own grid column. The
   comment here claimed it "must not add visual or layout cost"; it added both.
   Rail cards are clickable through .sf-card-link::after, as everywhere else. */
.sf .sf-section-link {
  font-size: 13px; font-weight: 600; color: var(--sf-accent);
  text-decoration: none; white-space: nowrap;
}
.sf .sf-section-link:hover { text-decoration: underline; }

/* "More from this store" — compact card row. State changes color/bg only. */
.sf .sf-rel-grid {
  display: grid; gap: var(--space-3);
  grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 640px)  { .sf .sf-rel-grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1024px) { .sf .sf-rel-grid { grid-template-columns: repeat(6, 1fr); } }
.sf .sf-rel-card {
  display: flex; flex-direction: column; gap: 6px;
  border: 1px solid var(--border-subtle); border-radius: var(--radius-lg);
  padding: var(--space-2); text-decoration: none; color: inherit;
  background: var(--theme-card-bg);
  transition: border-color 0.15s ease, background-color 0.15s ease;
}
.sf .sf-rel-card:hover {
  border-color: color-mix(in srgb, var(--sf-accent) 45%, var(--border-subtle));
  background: color-mix(in srgb, var(--sf-accent) 4%, var(--theme-card-bg));
}
.sf .sf-rel-media {
  position: relative; aspect-ratio: 1 / 1; border-radius: var(--radius-md);
  overflow: hidden; background: var(--theme-bg-secondary);
  display: flex; align-items: center; justify-content: center;
}
.sf .sf-rel-media img { width: 100%; height: 100%; object-fit: contain; padding: 8px; }
.sf .sf-rel-mark {
  font-family: var(--font-family-heading); font-size: 28px; font-weight: 650;
  color: color-mix(in srgb, var(--sf-accent) 70%, var(--theme-text-tertiary));
}
.sf .sf-rel-name {
  font-size: 12.5px; line-height: 1.3; color: var(--theme-text);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.sf .sf-rel-price { font-size: 13px; font-weight: 700; color: var(--theme-text); font-variant-numeric: tabular-nums; }
/* Empty state as an inviting panel, not two stray gray lines. */
.sf .sf-reviews-empty {
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2);
  border: 1px solid var(--border-subtle); border-radius: var(--radius-lg);
  background: var(--theme-bg-secondary);
  padding: var(--space-5); margin-bottom: var(--space-5);
}
.sf .sf-reviews-empty .sf-stars { color: var(--border-strong); font-size: 16px; letter-spacing: 2px; }
.sf .sf-reviews-empty p { font-size: 13.5px; color: var(--theme-text-secondary); line-height: 1.5; margin: 0; }
.sf .sf-section-h { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); margin-bottom: var(--space-5); flex-wrap: wrap; }
.sf .sf-section-title { font-family: var(--font-family-heading); font-size: 18px; font-weight: 650; letter-spacing: -0.01em; color: var(--theme-text); }
.sf .sf-review { border: 1px solid var(--border-subtle); border-radius: var(--radius-lg); padding: var(--space-4); margin-bottom: var(--space-3); }
.sf .sf-review-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); margin-bottom: 6px; }
.sf .sf-review-name { font-size: 13px; font-weight: 600; color: var(--theme-text); }
.sf .sf-review-date { font-size: 11.5px; color: var(--theme-text-tertiary); font-variant-numeric: tabular-nums; }
.sf .sf-review-title { font-size: 13px; font-weight: 600; color: var(--theme-text); margin-bottom: 3px; }
.sf .sf-review-body { font-size: 13px; color: var(--theme-text-secondary); line-height: 1.55; white-space: pre-line; }
/* Seller reply — a bg-tinted block (no left-accent rail, per the UI rules). */
.sf .sf-review-reply { margin-top: var(--space-2); padding: var(--space-2) var(--space-3); background: var(--theme-hover); border-radius: var(--radius-md); }
.sf .sf-review-reply .sf-review-name { font-size: 12px; }
.sf .sf-review-form { background: var(--theme-bg-secondary); border: 1px solid var(--border-subtle); border-radius: var(--radius-lg); padding: var(--space-4); }
.sf .sf-star-pick { display: inline-flex; gap: 4px; margin-bottom: var(--space-4); }
.sf .sf-star-pick i, .sf .sf-star-pick .sf-star { font-size: 22px; cursor: pointer; color: var(--border-strong); transition: color var(--transition-fast); }
.sf .sf-review-input { width: 100%; padding: 9px 12px; font-family: inherit; font-size: 13.5px; color: var(--theme-text); background: var(--theme-input-bg); border: 1px solid var(--theme-input-border); border-radius: var(--radius-md); margin-bottom: var(--space-3); }
.sf .sf-review-input:focus { outline: none; border-color: var(--sf-cta); }
.sf .sf-signin-link { color: var(--sf-ink); font-weight: 600; }

/* ─── Frequently bought together ──────────────────────────────────────── */
.sf .sf-fbt {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  background: var(--theme-card-bg);
}
.sf .sf-fbt-body { display: flex; flex-wrap: wrap; gap: var(--space-5); align-items: center; }
.sf .sf-fbt-row { display: flex; align-items: stretch; gap: 8px; flex-wrap: wrap; }
.sf .sf-fbt-cell { position: relative; width: 140px; }
.sf .sf-fbt-cell .sf-card { pointer-events: auto; }
.sf .sf-fbt-check { position: absolute; top: 6px; left: 6px; z-index: 3; }
.sf .sf-fbt-check input { width: 18px; height: 18px; accent-color: #3b82f6; cursor: pointer; }
.sf .sf-fbt-plus { display: flex; align-items: center; font-size: 20px; font-weight: 300; color: var(--theme-text-tertiary); }
.sf .sf-fbt-buy { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; margin-left: auto; min-width: 140px; }
.sf .sf-fbt-total { font-size: 13px; color: var(--theme-text-secondary); }
.sf .sf-fbt-total strong { font-size: 16px; color: var(--theme-text); font-weight: 700; font-variant-numeric: tabular-nums; }
.sf .sf-fbt-buy .sf-btn { padding: 10px 16px; font-size: 13px; }

/* ─── Horizontal carousel (also-viewed) ───────────────────────────────── */
.sf .sf-carousel { position: relative; }
.sf .sf-carousel-track { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(min(150px, 100%), 1fr); gap: var(--space-4); overflow-x: auto; scroll-snap-type: x proximity; scrollbar-width: none; padding-bottom: 4px; }
@media (min-width: 640px) { .sf .sf-carousel-track { grid-auto-columns: minmax(170px, 0.2fr); } }
.sf .sf-carousel-track::-webkit-scrollbar { display: none; }
/* Snap whatever the direct child is. This was keyed to `.sf-card` while the
   track's children were <a> wrappers, so it matched nothing and scroll-snap
   silently never worked; keeping it child-agnostic means it cannot rot again. */
.sf .sf-carousel-track > * { scroll-snap-align: start; }

/* The rail always cuts a card at the right edge (that is how a scroller says
   "there is more"), but a hard slice through price text just reads as broken.
   Fade the cut edge so it reads as continuation, and keep the fade clear of
   the arrows. */
.sf .sf-carousel-track {
    /* Trailing edge only. The left fade ran even at rest (scrollLeft 0), so
       the FIRST card's opening letter was ghosted ("reen Tea 100 Bags") on
       every store page — a fade should say "more this way", and at rest
       there is nothing to the left. */
    mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 28px), transparent 100%);
    scroll-padding-inline: var(--space-1, 4px);
}
/* Stationary control — hairline border only, no lift shadow (UI_DESIGN_RULES §4).
   40x40 minimum hit area (§12) — the arrow glyph is font-size, independent of the box. */
.sf .sf-carousel-arrow { position: absolute; top: 38%; transform: translateY(-50%); z-index: 2; width: 40px; height: 40px; border-radius: 50%; border: 1px solid var(--border-subtle); background: var(--theme-card-bg); color: var(--theme-text); font-size: 18px; line-height: 1; cursor: pointer; display: flex; align-items: center; justify-content: center; }
/* Sit in the page gutter, not on top of a product card. At -8px a 40px arrow
   covered 32px of the card beneath it — including its price. */
.sf .sf-carousel-arrow.is-prev { left: -22px; } .sf .sf-carousel-arrow.is-next { right: -22px; }
.sf .sf-carousel-arrow:disabled { opacity: 0; pointer-events: none; }
/* No gutter to sit in on a phone: at 375px the prev arrow landed 6px off the
   left edge of the viewport, so a third of a 40px tap target was unreachable.
   Moving it inward is not an option either — see the note above about it
   covering the price of the card beneath. The track snaps and scrolls by
   touch, and the edge mask already shows there is more, so the arrows are a
   pointer affordance and go away with the pointer. */
@media (max-width: 760px) {
  .sf .sf-carousel-arrow { display: none; }
}
.sf .sf-carousel-arrow:hover { border-color: var(--sf-accent); color: var(--sf-ink); }

/* ─── Reviews — two-column Amazon layout ──────────────────────────────── */
.sf .sf-reviews-grid { display: grid; grid-template-columns: minmax(220px, 0.8fr) 2fr; gap: var(--space-8); align-items: start; }
@media (max-width: 760px) { .sf .sf-reviews-grid { grid-template-columns: 1fr; gap: var(--space-5); } }
.sf .sf-reviews-summary { position: sticky; top: var(--space-6); }
@media (max-width: 760px) { .sf .sf-reviews-summary { position: static; } }
.sf .sf-reviews-avg { display: flex; align-items: center; gap: 8px; }
.sf .sf-reviews-avg-n { font-size: 16px; font-weight: 700; color: var(--theme-text); }
.sf .sf-reviews-count { font-size: 13px; color: var(--theme-text-tertiary); margin: 4px 0 var(--space-4); }
.sf .sf-breakdown { display: flex; flex-direction: column; gap: 6px; }
.sf .sf-breakdown-row { display: flex; align-items: center; gap: 8px; font-size: 12.5px; }
.sf .sf-breakdown-star { color: var(--theme-text-secondary); white-space: nowrap; font-variant-numeric: tabular-nums; min-width: 24px; }
.sf .sf-breakdown-track { flex: 1; height: 9px; border-radius: 5px; background: var(--theme-bg-secondary); overflow: hidden; }
.sf .sf-breakdown-fill { display: block; height: 100%; background: var(--warning); border-radius: 5px; }
.sf .sf-breakdown-pct { color: var(--theme-text-tertiary); font-variant-numeric: tabular-nums; min-width: 34px; text-align: right; }
.sf .sf-review-avatar { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: 50%; background: color-mix(in srgb, var(--sf-accent) 14%, var(--theme-bg-secondary)); color: var(--sf-ink); font-size: 12px; font-weight: 700; flex-shrink: 0; }
.sf .sf-review-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 6px 0 4px; }
.sf .sf-review-sub { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-size: 11.5px; color: var(--theme-text-tertiary); margin-bottom: 6px; }
.sf .sf-review-verified { color: var(--success-text); font-weight: 600; }
.sf .sf-review-gate { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--theme-text-secondary); padding: 12px 14px; border: 1px dashed var(--border-subtle); border-radius: var(--radius-md); }
.sf .sf-review-gate svg { color: var(--success-text); flex-shrink: 0; }

/* seller card — store identity footer on the PDP */
.sf .sf-seller {
  margin-top: var(--space-8); margin-bottom: var(--space-10, 40px);
  border: 1px solid var(--border-subtle); border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--sf-accent) 3%, var(--theme-card-bg));
  padding: var(--space-4) var(--space-5);
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); flex-wrap: wrap;
}
.sf .sf-seller-id { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }
.sf .sf-seller-name { font-size: 13.5px; font-weight: 600; color: var(--theme-text); }
.sf .sf-seller-sub { font-size: 12.5px; color: var(--theme-text-tertiary); margin-top: 2px; }
@media (max-width: 640px) { .sf .sf-seller > .sf-btn { width: 100%; } }
.sf .sf-callout { border: 1px solid var(--warning-border); border-radius: var(--radius-md); padding: 8px 12px; font-size: 12.5px; color: var(--theme-text-secondary); }

/* ─── Page header (cart / checkout / generic) ─────────────────────────── */
/* padding-BLOCK only: this class rides together with .sf-wrap (which owns
   the horizontal gutters), and the old 3-value shorthand's horizontal `0`
   clobbered them — cart/checkout content sat flush against the viewport
   edge on mobile (2026-07-07: "UI is clearly broken, no margins"). */
.sf .sf-page { padding-block: var(--space-6) var(--space-16); }
.sf .sf-page-head { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--space-4); margin-bottom: var(--space-5); flex-wrap: wrap; }
.sf .sf-page-title { font-family: var(--font-family-heading); font-size: 24px; font-weight: 600; letter-spacing: -0.022em; color: var(--theme-text); }
.sf .sf-page-sub { font-size: 13.5px; color: var(--theme-text-tertiary); margin-top: 4px; }

/* ─── Cart ────────────────────────────────────────────────────────────── */
.sf .sf-cart { display: grid; grid-template-columns: minmax(0,1fr) 360px; gap: var(--space-8); align-items: start; }
@media (max-width: 900px) { .sf .sf-cart { grid-template-columns: 1fr; gap: var(--space-5); } }
.sf .sf-cart-items { display: flex; flex-direction: column; }
.sf .sf-cart-item { display: grid; grid-template-columns: 64px minmax(0, 1fr) auto minmax(80px, auto); grid-template-areas: "image details quantity total"; gap: var(--space-4); padding: var(--space-5) 0; border-bottom: 1px solid var(--border-divider); align-items: center; }
.sf .sf-cart-item.is-warn { border-color: var(--warning-border); }
.sf .sf-cart-thumb { grid-area: image; width: 64px; height: 64px; border-radius: var(--radius-md); overflow: hidden; flex-shrink: 0; background: var(--theme-bg-secondary); border: 1px solid var(--border-subtle); }
.sf .sf-cart-thumb img { width: 100%; height: 100%; object-fit: cover; }
.sf .sf-cart-mid { grid-area: details; min-width: 0; }
.sf .sf-cart-name { overflow-wrap: anywhere; font-size: var(--wm-type-body); font-weight: 500; color: var(--theme-text); }
.sf .sf-cart-name a:hover { color: var(--sf-ink); }
.sf .sf-cart-meta { font-size: 12px; color: var(--theme-text-tertiary); margin-top: 2px; }
.sf .sf-cart-unit { font-size: 12.5px; color: var(--theme-text-secondary); margin-top: 2px; font-variant-numeric: tabular-nums; }
.sf .sf-cart-end { grid-area: total; text-align: right; white-space: nowrap; }
.sf .sf-cart-total { font-size: var(--wm-type-body); font-weight: 600; color: var(--theme-text); font-variant-numeric: tabular-nums; }
.sf .sf-cart-remove { font-size: 12px; color: var(--theme-text-tertiary); transition: color var(--transition-fast); }
.sf .sf-cart-remove:hover { color: var(--danger-text); }
.sf .sf-cart-qty { display: inline-flex; align-items: center; border: 1px solid var(--border-subtle); border-radius: var(--radius-md); overflow: hidden; }
/* 40x40 minimum hit area (UI_DESIGN_RULES §12) on the +/- buttons; the value
   cell is bumped to the same 40px height so the pill stays visually flush. */
.sf .sf-cart-qty button { width: 40px; height: 40px; display: inline-flex; align-items: center; justify-content: center; background: var(--theme-card-bg); color: var(--theme-text-secondary); font-size: 14px; }
.sf .sf-cart-qty button:hover:not(:disabled) { background: var(--theme-hover); }
.sf .sf-cart-qty button:disabled { opacity: 0.4; cursor: not-allowed; }
.sf .sf-cart-qty .sf-qty-val { min-width: 36px; text-align: center; font-size: 13.5px; font-variant-numeric: tabular-nums; border-left: 1px solid var(--border-subtle); border-right: 1px solid var(--border-subtle); height: 40px; display: inline-flex; align-items: center; justify-content: center; }
.sf .sf-cart-form { display: contents; }

/* ─── Order summary card ──────────────────────────────────────────────── */
.sf .sf-summary { position: sticky; top: var(--space-6); border: 1px solid var(--border-subtle); border-radius: var(--radius-lg); padding: var(--space-5); background: var(--theme-card-bg); }
@media (max-width: 900px) { .sf .sf-summary { position: static; } }
.sf .sf-summary-title { font-family: var(--font-family-heading); font-size: 16px; font-weight: 600; color: var(--theme-text); margin-bottom: var(--space-4); }
.sf .sf-summary-row { display: flex; justify-content: space-between; font-size: 13.5px; color: var(--theme-text-secondary); margin-bottom: var(--space-3); }
.sf .sf-summary-row strong { font-weight: 600; color: var(--theme-text); font-variant-numeric: tabular-nums; }
.sf .sf-summary-row .is-free { color: var(--success-text); font-weight: 600; }
/* Free-shipping nudge — sits on the shared wm-callout component; only the
   progress bar carries bespoke styling (fill width is the lone inline style). */
.sf .sf-freeship { margin: var(--space-2) 0 var(--space-4); }
.sf .sf-freeship-body { display: flex; flex-direction: column; gap: 7px; width: 100%; }
.sf .sf-freeship-label { font-size: 12.5px; font-weight: 500; }
.sf .sf-freeship-amount { font-variant-numeric: tabular-nums; }
.sf .sf-freeship-track { display: block; height: 5px; border-radius: 999px; background: var(--theme-hover); overflow: hidden; }
.sf .sf-freeship-fill { display: block; height: 100%; border-radius: 999px; background: var(--brand, #4f46e5); transition: width .35s ease; }
.sf .sf-freeship-fill.is-unlocked { background: var(--success); }
.sf .sf-summary-total { display: flex; justify-content: space-between; align-items: baseline; border-top: 1px solid var(--border-divider); padding-top: var(--space-4); margin-top: var(--space-2); }
.sf .sf-summary-total .lbl { font-size: 15px; font-weight: 600; color: var(--theme-text); }
.sf .sf-summary-total .amt { font-family: var(--font-family-heading); font-size: 22px; font-weight: 700; color: var(--theme-text); font-variant-numeric: tabular-nums; }
.sf .sf-secure { display: flex; align-items: center; justify-content: center; gap: 6px; font-size: 11.5px; color: var(--theme-text-tertiary); margin-top: var(--space-4); }
.sf .sf-ship-bar { border: 1px solid var(--border-subtle); border-radius: var(--radius-md); padding: var(--space-3) var(--space-4); margin-top: var(--space-4); font-size: 12.5px; }
.sf .sf-ship-bar.is-unlocked { border-color: var(--success-border); }
.sf .sf-ship-bar strong { display: block; color: var(--theme-text); margin-bottom: 2px; }

/* ─── Banner (stock warnings etc.) ────────────────────────────────────── */
.sf .sf-banner { border: 1px solid var(--warning-border); border-radius: var(--radius-md); padding: var(--space-3) var(--space-4); margin-bottom: var(--space-5); display: flex; justify-content: space-between; gap: var(--space-3); }
.sf .sf-banner-title { font-weight: 600; color: var(--theme-text); font-size: 13px; }
.sf .sf-banner-text { font-size: 12.5px; color: var(--theme-text-secondary); margin-top: 2px; }

/* ─── Checkout ────────────────────────────────────────────────────────── */
.sf .sf-checkout { display: grid; grid-template-columns: minmax(0,1fr) 360px; gap: var(--space-8); align-items: start; }
/* Single column below 900px — and the summary moves ABOVE the form. In DOM order
 * the form comes first (it owns the 360px rail on desktop), so on a phone the
 * buyer previously scrolled the whole form and passed "Place order" before ever
 * seeing the item list, the total, or the discount field. `order` flips the
 * visual sequence without touching the DOM, so the desktop two-column layout and
 * the form's own tab order are both unchanged. The summary's item list is
 * already height-capped (max-height:360px, overflow-y:auto), so putting it first
 * costs a bounded amount of scroll, not an unbounded one. */
@media (max-width: 900px) {
  .sf .sf-checkout { grid-template-columns: 1fr; gap: var(--space-5); }
  .sf .sf-checkout > .sf-summary { order: -1; }
}
/* Discount disclosure. The marker is an affordance icon, not decoration (§8),
 * and it swaps glyph rather than rotating — [open] is a state, and state may not
 * transform (§1). `+`/`−` share an advance width in the UI font, and the pseudo
 * is pinned to the row end by margin-left:auto, so nothing beside it shifts.
 * Hover changes colour only. */
.sf .sf-promo { margin: 0; }
.sf .sf-promo-toggle { display: flex; align-items: center; min-height: 40px; font-size: 13px; font-weight: 500; color: var(--theme-text-secondary); cursor: pointer; list-style: none; transition: color 120ms ease; }
.sf .sf-promo-toggle::-webkit-details-marker { display: none; }
.sf .sf-promo-toggle::after { content: '+'; margin-left: auto; font-size: 15px; line-height: 1; color: var(--theme-text-tertiary); }
.sf .sf-promo[open] .sf-promo-toggle::after { content: '\2212'; }
.sf .sf-promo-toggle:hover { color: var(--theme-text); }
.sf .sf-promo-toggle:focus-visible { outline: 2px solid var(--brand-primary); outline-offset: 2px; border-radius: var(--radius-sm); }
/* Two EQUAL columns instead of content + a 360px rail — the order receipt pairs
 * "Order information" with "Shipping" rather than a summary sidebar.
 *
 * This exists because the receipt previously carried
 * `style="grid-template-columns:1fr 1fr"` inline, and an inline declaration beats
 * every stylesheet rule INCLUDING the media query above it. So the receipt was
 * two columns at 390px — ~137px per card — and `format_order_datetime` produced a
 * 161px date that overflowed its cell and rendered ON TOP of the "Date" label.
 * The checkout page uses the same class with no inline style and stacked
 * correctly, which is why only one of the two pages was broken.
 *
 * Declared inside a min-width query rather than as a plain rule after the
 * max-width one: a same-specificity rule later in the file would win at every
 * width and silently restore the bug. The two queries cannot both match. */
@media (min-width: 901px) { .sf .sf-checkout--split { grid-template-columns: 1fr 1fr; } }
.sf .sf-checkout--split { gap: var(--space-4); }
.sf .sf-co-card { border: 1px solid var(--border-subtle); border-radius: var(--radius-lg); background: var(--theme-card-bg); margin-bottom: var(--space-4); overflow: hidden; }
.sf .sf-co-card-h { padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--border-divider); font-family: var(--font-family-heading); font-size: 14px; font-weight: 600; color: var(--theme-text); }
.sf .sf-co-body { padding: var(--space-4); }
.sf .sf-field { display: flex; flex-direction: column; gap: 6px; margin-bottom: var(--space-4); }
.sf .sf-field:last-child { margin-bottom: 0; }
.sf .sf-field-label { font-size: 12.5px; font-weight: 500; color: var(--theme-text-secondary); }
.sf .sf-input, .sf .sf-textarea, .sf .sf-select {
  width: 100%; padding: 10px 12px; font-family: inherit; font-size: 14px;
  color: var(--theme-text); background: var(--theme-input-bg);
  /* --border-control, not --theme-input-border: the latter measured 1.47:1 in
   * light and 1.22:1 in dark against the surface these sit on, and a control's
   * boundary needs 3:1 (WCAG 1.4.11). Same fix as the .wm-* control family got;
   * this storefront family is its sibling and was missed the first time. */
  border: 1px solid var(--border-control); border-radius: var(--radius-md);
  transition: border-color var(--transition-fast);
}
.sf .sf-input:focus, .sf .sf-textarea:focus, .sf .sf-select:focus { outline: none; border-color: var(--sf-cta); }
/* One height per row, inputs included (UI rule §14). A single-line field on a
   storefront page — .sf-input, .sf-select, or a shared .wm-input the checkout
   dresses with py-3.5 — is exactly --sf-control-h tall, the same as the button
   beside it. Measured 2026-09-05 on checkout: fields 54px, discount input 46px,
   Apply 44px, all on one page. Textareas keep their padding. */
.sf input.sf-input, .sf select.sf-select, .sf input.wm-input, .sf select.wm-input {
  box-sizing: border-box; height: var(--sf-control-h); padding-top: 0; padding-bottom: 0;
}
/* A code field may uppercase what the buyer types (the cart's voucher input
   does); its placeholder is a label and stays sentence case. */
.sf .sf-input::placeholder { text-transform: none; }
/* Field spacing has ONE owner per boundary.
 *
 * `.sf-field` owns its own bottom margin, and this grid owns a `gap` — so a
 * field INSIDE the grid used to get both (32px instead of 16px), while the
 * boundary AFTER a grid got NEITHER: the grid carried no bottom margin, and
 * `.sf-field:last-child` zeroed the margin of its own last child. Measured on
 * the checkout address form at a real 390px viewport, the gap from one field to
 * the next label ran 32, 0, 16, 16, 16, 32, 0 — and both zeros left a label
 * 0px below the field above it while sitting 6px above its own control, so
 * "Country/Region" and "Phone" were visually ambiguous about which input they
 * labelled. Programmatically correct (`for`/`id` are right); visually
 * mislabelled, which is what a person filling in an address actually reads.
 *
 * So: the grid's gap owns the boundaries between its children, and the grid's
 * own margin owns the boundary after it. Every gap on the form is --space-4.
 */
.sf .sf-fieldgrid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-4); margin-bottom: var(--space-4); }
.sf .sf-fieldgrid > .sf-field { margin-bottom: 0; }
.sf .sf-fieldgrid:last-child { margin-bottom: 0; }
@media (max-width: 520px) { .sf .sf-fieldgrid { grid-template-columns: 1fr; } }
.sf .sf-pay-opt { display: flex; align-items: flex-start; gap: var(--space-3); padding: var(--space-3) var(--space-4); border: 1px solid var(--border-subtle); border-radius: var(--radius-md); margin-bottom: var(--space-2); cursor: pointer; transition: border-color var(--transition-fast); }
.sf label:has(input:checked) > .sf-pay-opt, .sf .sf-pay-opt:has(input:checked) { border-color: var(--sf-cta); }

/* Building picker (Yakutsk delivery model) — same affordance as payment opts. */
.sf .sf-build-list { display: flex; flex-direction: column; gap: var(--space-2); margin-top: 8px; }
.sf .sf-build-opt {
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  cursor: pointer; transition: border-color var(--transition-fast), background-color var(--transition-fast);
}
.sf .sf-build-opt:hover { border-color: color-mix(in srgb, var(--sf-accent) 40%, var(--border-subtle)); }
.sf .sf-build-opt:has(input:checked) { border-color: var(--sf-cta); background: color-mix(in srgb, var(--sf-cta) 5%, transparent); }
.sf .sf-build-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.sf .sf-build-body strong { font-size: 13.5px; color: var(--theme-text); }
.sf .sf-build-sub { font-size: 12.5px; color: var(--theme-text-secondary); }
.sf .sf-co-line { display: flex; justify-content: space-between; gap: var(--space-3); font-size: 13px; padding: 6px 0; }
.sf .sf-co-line-name { color: var(--theme-text-secondary); min-width: 0; }
.sf .sf-co-line-amt { font-weight: 600; color: var(--theme-text); font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ─── Store browse grid ───────────────────────────────────────────────── */
/* 260px cards read as a list of chips rather than a shelf of storefronts —
   four of them left the browse page mostly empty. Wider tracks and a taller
   awning give each store real presence, which is the whole job of this page.
   272px, NOT 320. Measured, not guessed: .sf-wrap is max-width 1200 with
   --space-6 (24px) padding either side, so content is 1152px and the gap is
   16px. Four columns therefore need 4w + 48 <= 1152, i.e. w <= 276. At 320 (and
   even at 280) it drops to three columns and orphans the fourth store onto a
   row of its own — which is exactly what the light-mode survey caught. */
.sf .sf-store-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(272px, 100%), 1fr)); gap: var(--space-4); padding-bottom: var(--space-8); }
.sf .sf-store-card { border: 1px solid var(--border-subtle); border-radius: var(--radius-lg); overflow: hidden; background: var(--theme-card-bg); display: flex; flex-direction: column; transition: border-color var(--transition-fast); }
.sf .sf-store-card:hover { border-color: var(--border-strong); }
.sf .sf-store-top { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-4); }
/* Mini-awning: every browse card carries its store's tone — the same cover
   recipe as the store page, condensed to a strip. The lettermark overlaps it
   exactly like .sf-identity overlaps .sf-cover. */
.sf .sf-store-awn {
  height: 68px;
  background:
    radial-gradient(120% 200% at 10% -40%, color-mix(in srgb, var(--sf-accent) 42%, transparent), transparent 60%),
    radial-gradient(100% 180% at 90% -30%, color-mix(in srgb, var(--sf-accent-2) 30%, transparent), transparent 56%),
    linear-gradient(180deg, color-mix(in srgb, var(--sf-accent) 10%, var(--theme-bg-secondary)), var(--theme-bg-secondary)),
    var(--theme-bg-secondary);
}
.dark .sf .sf-store-awn {
  background:
    radial-gradient(120% 200% at 10% -40%, color-mix(in srgb, var(--sf-accent-2) 30%, transparent), transparent 60%),
    radial-gradient(100% 180% at 90% -30%, color-mix(in srgb, var(--sf-accent-2) 16%, transparent), transparent 56%),
    linear-gradient(180deg, color-mix(in srgb, var(--sf-accent-2) 9%, var(--theme-bg-secondary)), var(--theme-bg-secondary)),
    var(--theme-bg-secondary);
}
.sf .sf-store-awn + .sf-store-top { align-items: flex-start; padding-top: 0; }
.sf .sf-store-awn + .sf-store-top .sf-mark { margin-top: -26px; box-shadow: 0 0 0 3px var(--theme-card-bg); }
.sf .sf-store-awn + .sf-store-top > div:last-child { padding-top: var(--space-2); }

/* ─── Store identity row — cart / checkout headers ────────────────────── */
.sf .sf-id-row {
  display: inline-flex; align-items: center; gap: 8px;
  margin-bottom: var(--space-2); min-width: 0;
}
.sf .sf-id-row .sf-id-row-name { font-size: 13px; font-weight: 600; color: var(--theme-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sf .sf-id-row .sf-id-row-handle { font-size: 12px; color: var(--sf-ink, var(--theme-text-tertiary)); white-space: nowrap; }
a.sf-id-row:hover .sf-id-row-name { color: var(--sf-ink, var(--theme-text)); }
.sf .sf-store-name { font-size: 14.5px; font-weight: 600; color: var(--theme-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sf .sf-store-handle { font-size: 12.5px; color: var(--theme-text-tertiary); display: flex; align-items: center; gap: 6px; }
.sf .sf-store-bio { font-size: 11.5px; color: var(--theme-text-tertiary); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sf .sf-store-foot { margin-top: auto; padding: var(--space-3) var(--space-4); border-top: 1px solid var(--border-divider); }

/* ─── Wishlist card footer CTA ────────────────────────────────────────── */
.sf .sf-wish-foot { padding: var(--space-3) 2px 0; }
.sf .sf-wish-meta { font-size: 11.5px; color: var(--theme-text-tertiary); margin-top: 2px; }
/* Wishlist is a mixed-store shelf — each card names its store in the
   store's own ink, same eyebrow register as .sf-card-eyebrow. */
.sf .sf-wish-store {
  display: inline-flex; align-items: center; gap: 6px; max-width: 100%;
  font-size: 11px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--sf-ink, var(--theme-text-tertiary)); margin-bottom: 4px;
}
.sf .sf-wish-store .sf-mark { flex-shrink: 0; width: 18px; height: 18px; font-size: var(--font-size-xxs); border-radius: 6px; }
.sf .sf-wish-store span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sf .sf-card-media .sf-card-remove {
  position: absolute; top: 10px; right: 10px; z-index: 4;
  /* 40x40 minimum hit area (UI_DESIGN_RULES §12). */
  width: 40px; height: 40px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--theme-card-bg); border: 1px solid var(--border-subtle); color: var(--theme-text-tertiary);
  transition: color var(--transition-fast), border-color var(--transition-fast);
}
.sf .sf-card-media .sf-card-remove:hover { color: var(--danger-text); border-color: var(--danger-border); }

/* WCAG 2.5.5/2.5.8 touch floor — 40x40 is under the 44px minimum. Same rule as
   .wm-icon-btn (tailwind.css) and .sfx-icon-btn (storefront/chrome.css); coarse
   pointers only, so desktop density is unchanged. */
@media (pointer: coarse) {
  .sf .sf-icon-btn {
    min-height: var(--min-touch-target, 44px);
    min-width: var(--min-touch-target, 44px);
  }
}

/* ─── Hub groups ───────────────────────────────────────────────────────────
   /market/hub and /market/store-hub were a single flat grid of 14 identical
   tiles: "My orders" and "Saved addresses" rendered at exactly the same weight,
   so the eye had nowhere to land and the page read as a link dump. Three
   labelled groups now, and the first (the reason people open this page) gets
   wider tiles so it reads as primary without shouting. */
.sf .sf-hub-group { margin-bottom: var(--space-7); }
.sf .sf-hub-group:last-child { margin-bottom: 0; }
.sf .sf-hub-group-label {
  font-size: 11px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--theme-text-tertiary);
  margin: 0 0 var(--space-3);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--border-divider);
}
.sf .sf-hub-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(230px, 100%), 1fr));
  gap: var(--space-3);
}
/* Lead group — fewer, wider tiles. Same component, more room. */
.sf .sf-hub-grid--lead {
  grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr));
}
.sf .sf-hub-tile {
  display: flex; flex-direction: column; gap: 4px;
  padding: var(--space-4);
  text-decoration: none;
}
.sf .sf-hub-grid--lead .sf-hub-tile { padding: var(--space-5) var(--space-4); }
.sf .sf-hub-tile-top {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
}
.sf .sf-hub-tile .sf-card-name {
  font-size: 13.5px; font-weight: 600; color: var(--theme-text);
  display: block; min-height: 0; white-space: normal;
}
.sf .sf-hub-grid--lead .sf-hub-tile .sf-card-name { font-size: 15px; }
.sf .sf-hub-tile-sub {
  margin: 0; font-size: 12px; line-height: 1.45;
  color: var(--theme-text-tertiary);
}


/* Touch: "Save for later" / "Remove" are text links stacked at the cart
   row's right; at 12px each was a 14px target. Padding lifts each to the
   floor without changing the text. */
@media (pointer: coarse), (max-width: 767px) {
  .sf .sf-cart-remove, .sf .sf-cart-save {
    display: inline-flex; align-items: center;
    min-height: var(--min-touch-target, 44px);
  }
}

/* ─── Storefront-register controls inside the SELLER shell ─────────────────
   Nine seller tools (inbox, reviews, announcements, vacation, …) render in
   the seller shell but are built from .sf- parts; their brand-filled CTA
   read as a different product beside every other seller page's near-white
   primary. Until they migrate to the idx/dtl families, the shell decides
   the button register: seller primary/secondary metrics and colours. */
body.wm-shell-seller .sf .sf-btn,
body.wm-shell-seller .sf .sf-btn-pri {
  height: var(--control-h); padding: 0 16px;
  background: var(--theme-text); color: var(--theme-text-inverted);
  border: 1px solid transparent;
  font-size: 12.5px; font-weight: 600;
}
body.wm-shell-seller .sf .sf-btn:hover,
body.wm-shell-seller .sf .sf-btn-pri:hover { filter: none; opacity: 0.92; }
body.wm-shell-seller .sf .sf-btn-sec {
  height: var(--control-h); min-height: 0; padding: 0 14px;
  font-size: 12.5px; font-weight: 500;
}
@media (pointer: coarse), (max-width: 767px) {
  body.wm-shell-seller .sf .sf-btn,
  body.wm-shell-seller .sf .sf-btn-pri,
  body.wm-shell-seller .sf .sf-btn-sec { min-height: var(--min-touch-target, 44px); }
}

/* Shopping controls use the same density as their surrounding product text. */
.sf .sf-cart-controls { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); margin-top: var(--space-1); }
.sf .sf-cart-quantity { grid-area: quantity; }
.sf .sf-cart-quantity .wm-quantity-stepper { display: grid; width: max-content; grid-template-columns: repeat(3, 36px); align-items: stretch; overflow: hidden; border-radius: var(--radius-md); }
.sf .sf-cart-quantity .wm-quantity-stepper-button,
.sf .sf-cart-quantity .wm-quantity-stepper-value { display: grid; place-items: center; width: 36px; height: 36px; min-width: 0; padding: 0; margin: 0; border-radius: 0; font-size: var(--wm-type-body); line-height: 1; }
.sf .sf-cart-quantity .wm-quantity-stepper-value { border-inline: 1px solid var(--border-control); font-weight: var(--wm-weight-medium); font-variant-numeric: tabular-nums; }
.sf .sf-cart-controls .sf-cart-remove { display: inline-flex; align-items: center; min-height: 28px; padding: 0; font-size: var(--wm-type-label); line-height: var(--wm-leading-text); }
.sf .sf-cart-quantity .wm-quantity-stepper:focus-within { outline: 2px solid var(--border-accent); outline-offset: 2px; }
.sf .sf-cart-thumb { display: grid; place-items: center; color: var(--theme-text-secondary); font-size: var(--wm-type-page); }
.sf .sf-question-compose { margin-top: var(--space-3); }
.sf .sf-question-compose > summary { width: fit-content; cursor: pointer; }
.sf .sf-question-compose[open] > summary { margin-bottom: var(--space-3); }
.sf .sf-qa { padding-block: var(--space-4); margin-top: var(--space-5); }
.sf .sf-qa .sf-section-title { font-size: var(--wm-type-section); }
.sf .sf-qa .sf-page-sub { font-size: var(--wm-type-body); }
.sf .sf-pdp-info .sf-buybox { width: 100%; }
.sf .sf-pdp-info .sf-ctas-stack { display: flex; flex-direction: row; flex-wrap: wrap; gap: var(--space-2); }
.sf .sf-pdp-info .sf-ctas-stack > button { flex: 1 1 180px; }
.sf .sf-pdp-info .sf-ctas-stack > form { flex-basis: 100%; }
.sf .sf-pdp-info .sf-ctas-stack > form button { width: auto; padding-inline: 0; }
.sf #product-social-proof:empty { display: none; }
@media (max-width: 640px) {
  .sf .sf-cart-item { gap: var(--space-3); }
  .sf .sf-cart-thumb { width: 56px; height: 56px; }
  .sf .sf-cart-item { grid-template-columns: 56px minmax(0, 1fr) auto; grid-template-areas: "image details details" ". quantity total"; align-items: center; }
  .sf .sf-cart-end { text-align: left; }
}

/* Checkout uses a continuous form surface with quiet section divisions. */
.sf #checkoutForm { padding: var(--space-5); background: var(--theme-card-bg); border: 1px solid var(--border-subtle); border-radius: var(--radius-lg); }
.sf #checkoutForm > .sf-co-card:not(.wm-callout) { border: 0; border-radius: 0; background: transparent; margin-bottom: var(--space-5); }
.sf #checkoutForm .sf-co-card-h { padding: 0 0 var(--space-3); font-size: var(--wm-type-section); }
.sf #checkoutForm .sf-co-body { padding: var(--space-4) 0 0; }
.sf #checkoutForm .wm-input { padding-inline: var(--space-3); }
@media (max-width: 640px) {
  .sf #checkoutForm { padding: var(--space-4); }
}

@media (pointer: coarse), (max-width: 767px) {
  .sf .sf-cart-quantity .wm-quantity-stepper { grid-template-columns: repeat(3, 44px); }
  .sf .sf-cart-quantity .wm-quantity-stepper-button,
  .sf .sf-cart-quantity .wm-quantity-stepper-value { width: 44px; height: 44px; }
  .sf .sf-cart-controls .sf-cart-remove { min-height: 44px; }
}

/* Empty feedback needs two concise sections, not a rating chart without data. */
.sf .sf-product-feedback {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-7);
  border-top: 1px solid var(--border-divider);
  margin-top: var(--space-6);
  padding-top: var(--space-5);
}
.sf .sf-product-feedback > .sf-section { min-width: 0; margin: 0; padding: 0; border: 0; }
.sf .sf-product-feedback .sf-section-title { font-size: var(--wm-type-section); margin-bottom: var(--space-2); }
.sf .sf-product-feedback .sf-reviews-grid { grid-template-columns: 1fr; gap: var(--space-3); }
.sf .sf-product-feedback .sf-reviews-empty { padding: 0; border: 0; background: transparent; }
.sf .sf-product-feedback .sf-reviews-empty p { font-size: var(--wm-type-body); }
.sf .sf-product-feedback.has-reviews { grid-template-columns: 1fr; }
.sf .sf-product-feedback.has-reviews .sf-reviews-grid { grid-template-columns: minmax(220px, 0.8fr) 2fr; }
@media (max-width: 760px) {
  .sf .sf-product-feedback,
  .sf .sf-product-feedback.has-reviews .sf-reviews-grid { grid-template-columns: 1fr; }
}
