/*
 * @shoppi/storefront-sdk — scoped stylesheet.
 *
 * All selectors start with .shoppi- to isolate from theme styles.
 * Uses CSS custom properties so merchants can override via the
 * block's primary_color setting.
 *
 * Philosophy: provide minimal structural CSS for Shoppi-specific
 * chrome (tabs, carousels, overlay, states). Product cards inherit
 * from the merchant's theme via theme-card cloning — we do NOT
 * style individual products.
 */

:root {
  /*
   * Default accent is a deep neutral (near-black) so the SDK doesn't stamp
   * Shoppi's own brand colour onto every merchant's store out of the box.
   * Merchants override --shoppi-primary via the block's primary_color
   * theme setting to match their own palette.
   */
  --shoppi-primary: #111111;
  --shoppi-primary-hover: #000000;
  --shoppi-text: #1a1a1a;
  --shoppi-text-muted: #6b6b6b;
  --shoppi-border: #e1e1e1;
  --shoppi-border-strong: #dfe1e5;
  --shoppi-bg: #ffffff;
  --shoppi-bg-muted: #f5f5f5;
  --shoppi-radius: 8px;
  --shoppi-radius-lg: 16px;
  --shoppi-radius-pill: 999px;
  --shoppi-gap: 16px;
  --shoppi-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.04);
  --shoppi-shadow-md: 0 4px 14px rgba(32, 33, 36, 0.12);
  --shoppi-shadow-lg: 0 20px 40px -8px rgba(0, 0, 0, 0.14), 0 8px 16px -4px rgba(0, 0, 0, 0.08);
  --shoppi-icon-hover: rgba(60, 64, 67, 0.08);
  --shoppi-placeholder: #9aa0a6;
  --shoppi-recording: #ea4335;
  --shoppi-ease: cubic-bezier(0.4, 0, 0.2, 1);
}

/* ── States (loading / error / empty) ──────────────────────────────────── */
.shoppi-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 48px 16px;
  text-align: center;
  color: var(--shoppi-text-muted);
  min-height: 200px;
}
.shoppi-state__icon {
  font-size: 32px;
  margin-bottom: 12px;
  opacity: 0.6;
}
.shoppi-state__message {
  font-size: 14px;
  max-width: 320px;
}
.shoppi-state--loading .shoppi-state__icon {
  animation: shoppi-spin 0.8s linear infinite;
}
@keyframes shoppi-spin {
  to { transform: rotate(360deg); }
}

/* ── Default product card (Layer 0 fallback only) ──────────────────────── */
.shoppi-card {
  display: flex;
  flex-direction: column;
  text-decoration: none;
  color: inherit;
  background: var(--shoppi-bg);
  border-radius: var(--shoppi-radius);
  overflow: hidden;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.shoppi-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}
.shoppi-card__image {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  background: var(--shoppi-bg-muted);
}
.shoppi-card__info { padding: 12px; }
.shoppi-card__brand { font-size: 11px; text-transform: uppercase; color: var(--shoppi-text-muted); margin-bottom: 4px; }
.shoppi-card__name { font-size: 14px; font-weight: 500; color: var(--shoppi-text); margin: 0 0 6px; line-height: 1.3; }
.shoppi-card__price { font-size: 14px; font-weight: 600; color: var(--shoppi-text); }
.shoppi-card__compare { text-decoration: line-through; color: var(--shoppi-text-muted); font-weight: 400; margin-left: 6px; }

/* ── Group layouts (tabs / rows / grid / carousel) ─────────────────────── */
.shoppi-groups { display: block; }
.shoppi-groups__title { font-size: 20px; font-weight: 600; margin: 0 0 16px; color: var(--shoppi-text); }

/* Tabs */
.shoppi-tabs__list {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--shoppi-border);
  margin-bottom: 16px;
  overflow-x: auto;
  scrollbar-width: none;
}
.shoppi-tabs__list::-webkit-scrollbar { display: none; }
.shoppi-tabs__tab {
  flex: 0 0 auto;
  background: transparent;
  border: none;
  padding: 10px 16px;
  font-size: 14px;
  font-weight: 500;
  color: var(--shoppi-text-muted);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.shoppi-tabs__tab:hover { color: var(--shoppi-text); }
.shoppi-tabs__tab--active { color: var(--shoppi-primary); border-bottom-color: var(--shoppi-primary); }
.shoppi-tabs__tab:focus-visible { outline: 2px solid var(--shoppi-primary); outline-offset: 2px; border-radius: 4px; }
.shoppi-tabs__panel { display: none; }
.shoppi-tabs__panel--active { display: block; }

/* Rows (horizontal scroll per group) */
.shoppi-rows__group { margin-bottom: 32px; }
.shoppi-rows__group:last-child { margin-bottom: 0; }
.shoppi-rows__label { font-size: 14px; font-weight: 600; margin: 0 0 12px; color: var(--shoppi-text); }

/* Product grid — used by tabs / grid / rows layouts to lay out cards. */
.shoppi-grid,
.shoppi-product-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: var(--shoppi-gap);
}
/* Horizontal-scroll carousel — applied to both rows AND tabs panels so
   each group reads as a carousel instead of a wrapping grid.

   Flex (not grid) — earlier `grid-auto-flow: column` leaked intrinsic
   content width up the parent chain and triggered a body-wide horizontal
   scroll. Flex `nowrap` + `overflow-x: auto` is the canonical pattern.

   Kept deliberately simple: no scroll-snap, no `overflow-y: hidden`, no
   `width: 100%` (block flex containers default to filling parent and
   adding `width: 100%` on top can confuse scroll calculations on some
   browsers). */
.shoppi-rows__group > .shoppi-product-grid,
.shoppi-tabs__panel > .shoppi-product-grid {
  display: flex;
  flex-wrap: nowrap;
  gap: var(--shoppi-gap);
  /* CRITICAL: pin the grid's width to 100% of its parent. Without this,
     a flex-nowrap container grows to fit its content (intrinsic size) →
     no overflow → no scroll. With `width: 100%; max-width: 100%` the
     grid stays at parent width and oversized children trigger
     `overflow-x: auto` properly. */
  width: 100%;
  max-width: 100%;
  min-width: 0;
  overflow-x: auto;
  padding-bottom: 12px;
  scrollbar-width: thin;
  scrollbar-color: rgba(0, 0, 0, 0.25) transparent;
  /* Smooth momentum scroll on iOS / mac trackpad. */
  -webkit-overflow-scrolling: touch;
}
/* Slot wrapper — created by render-groups.ts around EVERY card
   (default, theme-card clone, or section-rendered native). Because the
   slot is our own class, theme CSS (even inside `@layer`) never
   targets it. Sizing is applied here, not on the card itself. */
.shoppi-rows__group > .shoppi-product-grid > .shoppi-product-grid__slot,
.shoppi-tabs__panel > .shoppi-product-grid > .shoppi-product-grid__slot {
  /* Tile width is driven by a CSS variable so the section block can set
     it per-section (Tile size setting in theme editor) and a merchant
     can override globally via theme custom CSS:
       :root { --shoppi-tile-width: 320px; }
     Default is a fixed 260px — guaranteed to overflow a typical desktop
     recommendation row (7 × 260 + gaps > 1800px) so the carousel has
     something to scroll. A `vw`-based clamp was seductive but broke
     inside Shopify's theme-preview iframe where `vw` referred to a
     narrower viewport than the grid's own width, causing flex-basis to
     resolve to exactly `gridWidth / childCount` → zero overflow. */
  flex: 0 0 var(--shoppi-tile-width, 260px);
  min-width: 0;
  box-sizing: border-box;
}
/* Narrow viewports: shrink the default tile so two fit side-by-side on
   mobile. Merchant overrides via `--shoppi-tile-width` still win. */
@media (max-width: 640px) {
  .shoppi-rows__group > .shoppi-product-grid > .shoppi-product-grid__slot,
  .shoppi-tabs__panel > .shoppi-product-grid > .shoppi-product-grid__slot {
    flex: 0 0 var(--shoppi-tile-width, 180px);
  }
}
/* Inner card fills the slot — `!important` here is defensive against
   themes that set `width: auto` / `max-width: Xpx` on their product-card
   element. The slot has already enforced the outer dimension, so making
   the inner card 100% just lets it fill that space. */
.shoppi-product-grid__slot > * {
  width: 100% !important;
  max-width: 100% !important;
}

/* Always-visible 8px scrollbar on Webkit (Chrome / Safari) — macOS hides
   overlay scrollbars until you actively scroll, which makes the carousel
   feel broken to first-time visitors. Force a slim, theme-friendly
   scrollbar so the affordance is always there. */
.shoppi-rows__group > .shoppi-product-grid::-webkit-scrollbar,
.shoppi-tabs__panel > .shoppi-product-grid::-webkit-scrollbar {
  height: 8px;
  -webkit-appearance: none;
}
.shoppi-rows__group > .shoppi-product-grid::-webkit-scrollbar-track,
.shoppi-tabs__panel > .shoppi-product-grid::-webkit-scrollbar-track {
  background: transparent;
}
.shoppi-rows__group > .shoppi-product-grid::-webkit-scrollbar-thumb,
.shoppi-tabs__panel > .shoppi-product-grid::-webkit-scrollbar-thumb {
  background: rgba(0, 0, 0, 0.25);
  border-radius: 999px;
}
.shoppi-rows__group > .shoppi-product-grid::-webkit-scrollbar-thumb:hover,
.shoppi-tabs__panel > .shoppi-product-grid::-webkit-scrollbar-thumb:hover {
  background: rgba(0, 0, 0, 0.4);
}

/* Containment — only the absolute minimum to keep the carousel's content
   from pushing parents wider. Block parents with `min-width: 0` allow
   the flex container's overflow to clip-and-scroll instead of growing
   the parent. We do NOT add `overflow-x` rules on parents here — those
   were swallowing the carousel's scroll events on Mac trackpads. */
.shoppi-rows,
.shoppi-rows__group,
.shoppi-tabs,
.shoppi-tabs__panels,
.shoppi-tabs__panel,
.shoppi-similar,
.shoppi-outfits,
[data-shoppi-mount="similar"],
[data-shoppi-mount="outfits"] {
  min-width: 0;
  /* Cap every layer at the viewport so an ancestor with its own wider
     layout (common inside Shopify's theme-editor preview iframe, which
     can render content boxes wider than the iframe's own viewport) can
     never stretch our grid beyond what the shopper actually sees. If
     the grid is capped at viewport width, our fixed-size slots always
     overflow → the grid's own `overflow-x: auto` kicks in → full
     trackpad scroll reaches every card. */
  max-width: 100vw;
}
/* Section-level safety net — clip page-wide horizontal overflow at the
   Shopify section that contains our mount. `overflow-x: clip` (not
   `hidden`) doesn't establish a scroll container, so the inner carousel
   keeps its own scroll. `max-width: 100vw` here is the load-bearing
   constraint: when the section's own ancestor is wider than viewport
   (Horizon's theme-editor iframe does this), this caps us at viewport
   so slots overflow the grid instead of extending past the clip line
   where nothing is reachable. */
.shopify-section:has([data-shoppi-mount="similar"]),
.shopify-section:has([data-shoppi-mount="outfits"]) {
  overflow-x: clip;
  max-width: 100vw;
}
/* Hidden panels (via [hidden] attribute + inactive class) must win. */
.shoppi-tabs__panel[hidden] { display: none !important; }

/* Carousel */
.shoppi-carousel { position: relative; }
.shoppi-carousel__track {
  display: flex;
  gap: var(--shoppi-gap);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
  padding-bottom: 8px;
}
.shoppi-carousel__track::-webkit-scrollbar { display: none; }
.shoppi-carousel__slide {
  flex: 0 0 100%;
  min-width: 0;
  scroll-snap-align: start;
}
.shoppi-carousel__label {
  font-size: 14px;
  font-weight: 600;
  margin: 0 0 12px;
  color: var(--shoppi-text);
}
.shoppi-carousel__prev,
.shoppi-carousel__next {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  background: var(--shoppi-bg);
  border: 1px solid var(--shoppi-border);
  border-radius: 50%;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  font-size: 18px;
  z-index: 2;
  transition: background 0.15s ease;
}
.shoppi-carousel__prev { left: -8px; }
.shoppi-carousel__next { right: -8px; }
.shoppi-carousel__prev:hover,
.shoppi-carousel__next:hover { background: var(--shoppi-bg-muted); }
.shoppi-carousel__prev:disabled,
.shoppi-carousel__next:disabled { opacity: 0.4; cursor: not-allowed; }
.shoppi-carousel__prev:focus-visible,
.shoppi-carousel__next:focus-visible { outline: 2px solid var(--shoppi-primary); outline-offset: 2px; }

/* Add-All button (outfits) */
.shoppi-add-all {
  background: var(--shoppi-primary);
  color: #fff;
  border: none;
  padding: 12px 20px;
  border-radius: var(--shoppi-radius);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  margin-top: 12px;
  transition: background 0.15s ease;
}
.shoppi-add-all:hover { background: var(--shoppi-primary-hover); }
.shoppi-add-all:disabled { opacity: 0.6; cursor: not-allowed; }
.shoppi-add-all:focus-visible { outline: 2px solid var(--shoppi-text); outline-offset: 2px; }

/* ── Search bar + overlay ──────────────────────────────────────────────── */
.shoppi-search-bar { position: relative; width: 100%; }

/* Google-style pill bar: layered shadow on hover / focus, border softens
   to transparent so the shadow reads as depth. Height 52px + 16px font is
   iOS zoom-safe and gives the mic/camera icons a 40px hit target with
   breathing room.

   The `--shoppi-field-*` custom properties are set at runtime by
   core/theme-search.ts when it detects the theme's native search field, so
   the pill matches the storefront (pill vs square, fill, border, type). When
   nothing is detected the props are unset and these fall back to the
   original fixed Shoppi pill — byte-identical to before. */
.shoppi-search-bar__form {
  display: flex;
  align-items: center;
  gap: 0;
  height: 52px;
  border: var(--shoppi-field-border, 1px solid var(--shoppi-border-strong));
  border-radius: var(--shoppi-field-radius, var(--shoppi-radius-pill));
  padding: 0 8px 0 18px;
  background: var(--shoppi-field-bg, var(--shoppi-bg));
  box-shadow: var(--shoppi-shadow-sm);
  transition: border-color 0.18s var(--shoppi-ease), box-shadow 0.18s var(--shoppi-ease);
}
.shoppi-search-bar__form:hover,
.shoppi-search-bar__form:focus-within {
  border-color: transparent;
  box-shadow: var(--shoppi-shadow-md);
}
.shoppi-search-bar__input {
  flex: 1;
  /* The pill's visible box (fill, border, radius) is owned entirely by the
     parent form. The input must stay a naked text field, so lock its paint
     properties with !important — theme `input[type="search"]` rules have
     higher specificity than our single class and otherwise leak through,
     painting a mismatched inner rectangle / border inside our pill. */
  border: none !important;
  background: transparent !important;
  box-shadow: none !important;
  outline: none;
  padding: 0;
  /* font-size stays 16px (iOS won't zoom the page on focus at >=16px) even
     when the theme field uses a smaller size — only the family is matched. */
  font-size: 16px;
  font-family: var(--shoppi-field-font, inherit);
  line-height: 1;
  color: var(--shoppi-field-color, #202124) !important;
  min-width: 0;
}
.shoppi-search-bar__input::placeholder { color: var(--shoppi-placeholder); }

/* Circular 40x40 icon buttons — subtle grey hover, primary colour on
   hover. Specific selector beats theme `button { ... }` resets without
   resorting to !important. */
.shoppi-search-bar__form > .shoppi-search-bar__mic,
.shoppi-search-bar__form > .shoppi-search-bar__camera {
  background: transparent;
  border: none;
  cursor: pointer;
  width: 40px;
  height: 40px;
  padding: 0;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--shoppi-text-muted);
  flex-shrink: 0;
  position: relative;
  transition: color 0.15s var(--shoppi-ease), background 0.15s var(--shoppi-ease);
}
.shoppi-search-bar__form > .shoppi-search-bar__mic:hover,
.shoppi-search-bar__form > .shoppi-search-bar__camera:hover {
  background: var(--shoppi-icon-hover);
  color: var(--shoppi-primary);
}
.shoppi-search-bar__mic:focus-visible,
.shoppi-search-bar__camera:focus-visible { outline: 2px solid var(--shoppi-primary); outline-offset: 2px; }

/* Recording state: red + background tint + pulse (reduced-motion safe). */
.shoppi-search-bar__mic--recording,
.shoppi-search-bar__form > .shoppi-search-bar__mic--recording:hover {
  color: var(--shoppi-recording);
  background: rgba(234, 67, 53, 0.08);
  animation: shoppi-pulse 1.2s ease-in-out infinite;
}
@keyframes shoppi-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.75; transform: scale(0.95); }
}
@media (prefers-reduced-motion: reduce) {
  .shoppi-search-bar__mic--recording { animation: none; }
}

/* Tooltip via ::after — content is the button's aria-label. Dark pill,
   fades in on hover. Pointer-events disabled so it never steals clicks. */
.shoppi-search-bar__mic::after,
.shoppi-search-bar__camera::after {
  content: attr(aria-label);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  background: #202124;
  color: #fff;
  font-size: 12px;
  line-height: 1;
  white-space: nowrap;
  padding: 6px 10px;
  border-radius: 4px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s var(--shoppi-ease);
  z-index: 10;
}
.shoppi-search-bar__mic:hover::after,
.shoppi-search-bar__camera:hover::after,
.shoppi-search-bar__mic:focus-visible::after,
.shoppi-search-bar__camera:focus-visible::after { opacity: 1; }
.shoppi-search-bar__mic--recording::after { content: "Stop recording"; }

/* 1px vertical divider between mic and camera. */
.shoppi-search-bar__sep {
  width: 1px;
  height: 20px;
  background: var(--shoppi-border-strong);
  margin: 0 2px;
  flex-shrink: 0;
}

/* Algolia-style ⌘K hint chip — inline mode only. Hidden on focus so it
   doesn't overlap the shopper's input. */
.shoppi-search-bar__shortcut {
  display: inline-flex;
  align-items: center;
  padding: 3px 8px;
  margin-right: 4px;
  font-size: 11px;
  font-weight: 600;
  color: var(--shoppi-text-muted);
  background: var(--shoppi-bg-muted);
  border: 1px solid var(--shoppi-border);
  border-radius: 5px;
  line-height: 1;
  letter-spacing: 0.02em;
  flex-shrink: 0;
  transition: opacity 0.15s var(--shoppi-ease);
}
.shoppi-search-bar__form:focus-within .shoppi-search-bar__shortcut { opacity: 0; }

/* Dropdown — layered shadow, rounded corners, slim scrollbar. Keeps the
   existing defensive grid + child-width resets so theme-cloned cards don't
   each claim the full dropdown width. */
.shoppi-search-bar__dropdown {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  right: 0;
  background: var(--shoppi-bg);
  border: 1px solid var(--shoppi-border);
  border-radius: var(--shoppi-radius-lg);
  box-shadow: var(--shoppi-shadow-lg);
  max-height: 420px;
  overflow-y: auto;
  z-index: 50;
  padding: 12px;
  scrollbar-width: thin;
  scrollbar-color: var(--shoppi-border) transparent;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
}
.shoppi-search-bar__dropdown::-webkit-scrollbar { width: 6px; }
.shoppi-search-bar__dropdown::-webkit-scrollbar-thumb { background: var(--shoppi-border); border-radius: 4px; }
@media (min-width: 640px) {
  .shoppi-search-bar__dropdown { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
  .shoppi-search-bar__dropdown { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
.shoppi-search-bar__dropdown > * {
  width: auto !important;
  min-width: 0 !important;
  max-width: 100% !important;
  margin: 0 !important;
}
.shoppi-search-bar__dropdown > * > * {
  max-width: 100% !important;
  width: auto !important;
}
.shoppi-search-bar__dropdown[hidden] { display: none; }

/* Icon-trigger mode — circular 40x40 button that opens the modal. */
.shoppi-search-bar--icon { width: auto; }
.shoppi-search-bar__trigger {
  background: transparent;
  border: none;
  cursor: pointer;
  color: inherit;
  width: 40px;
  height: 40px;
  padding: 0;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color 0.15s var(--shoppi-ease), background 0.15s var(--shoppi-ease);
}
.shoppi-search-bar__trigger:hover { background: var(--shoppi-icon-hover); color: var(--shoppi-primary); }
.shoppi-search-bar__trigger:focus-visible { outline: 2px solid var(--shoppi-primary); outline-offset: 2px; }

/* Modal — backdrop blur + entrance animation. Backdrop-filter degrades
   gracefully in browsers that don't support it. */
.shoppi-search-modal {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  z-index: 9999;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 10vh 16px 16px;
  animation: shoppi-fade-in 0.12s var(--shoppi-ease);
}
@keyframes shoppi-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.shoppi-search-modal__panel {
  position: relative;
  /* No card — the panel is invisible, only the white pill floats over
     the blurred backdrop. Padding gives the close button and dropdown
     room without drawing a visible container. */
  background: transparent;
  border: none;
  box-shadow: none;
  width: min(680px, 92vw);
  padding: 36px 0 0;
  /* Flex column capped to the viewport — the modal pads 10vh on top and
     16px at the bottom, so the panel can be at most that tall. Without the
     cap a long results list (and its scrollbar) overflows past the bottom
     of the page and the theme-editor preview iframe. The dropdown below
     takes the leftover height and scrolls within it. */
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 10vh - 16px);
  animation: shoppi-rise 0.18s var(--shoppi-ease) both;
}
@keyframes shoppi-rise {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .shoppi-search-modal,
  .shoppi-search-modal__panel { animation: none; }
}
/* The pill floats over the dark frosted panel — heavier layered shadow
   gives it depth, and the border drops to transparent so the shadow
   reads as the only edge. */
.shoppi-search-modal__panel .shoppi-search-bar__form {
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.18),
    0 12px 32px rgba(0, 0, 0, 0.32);
  border-color: transparent;
  /* Hold the 52px pill height — the panel is a flex column, so without
     this the form would squish when the dropdown is tall. */
  flex-shrink: 0;
}
.shoppi-search-modal__panel .shoppi-search-bar__form:hover,
.shoppi-search-modal__panel .shoppi-search-bar__form:focus-within {
  border-color: transparent;
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.22),
    0 16px 40px rgba(0, 0, 0, 0.40);
}
.shoppi-search-modal__panel .shoppi-search-bar__dropdown {
  position: static;
  margin-top: 12px;
  background: transparent;
  box-shadow: none;
  border: none;
  border-radius: 0;
  padding: 0;
  /* Take the height left over in the (viewport-capped) panel and scroll
     inside it — replaces a fixed 60vh that, stacked under the 10vh modal
     offset + close button + pill, could push the scrollbar off-screen.
     `flex: 0 1 auto` keeps a short list at its natural height (no giant
     empty card); `min-height: 0` lets it shrink so `overflow-y` engages. */
  flex: 0 1 auto;
  min-height: 0;
}
/* Wrap the results in a white card only when product cards are actually
   rendered. Loading/empty/error states stay transparent over the blurred
   backdrop — only the populated results surface needs the panel. */
.shoppi-search-modal__panel .shoppi-search-bar__dropdown:has([role="option"]) {
  background: var(--shoppi-bg);
  border-radius: var(--shoppi-radius-lg);
  padding: 12px;
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.18),
    0 12px 32px rgba(0, 0, 0, 0.28);
}
/* Loading / empty / error states sit over the dark frosted backdrop with no
   white card behind them (only populated results get the card). The default
   muted-grey state text is nearly invisible there — switch to light-on-dark
   inside the modal; the 50% black scrim guarantees the contrast holds. */
.shoppi-search-modal__panel .shoppi-state {
  color: rgba(255, 255, 255, 0.92);
}

.shoppi-search-modal__close {
  position: absolute;
  top: 8px;
  right: 8px;
  /* Sits on the dark frosted panel — strong contrast off-white so it's
     clearly clickable; subtle white-tint hover for affordance. */
  background: transparent;
  border: none;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  color: rgba(255, 255, 255, 0.92);
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  z-index: 2;
  transition: color 0.15s var(--shoppi-ease), background 0.15s var(--shoppi-ease);
}
.shoppi-search-modal__close:hover { background: rgba(255, 255, 255, 0.16); color: #fff; }
.shoppi-search-modal__close:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

/* ── Search Results Page ───────────────────────────────────────────────── */
.shoppi-srp { display: block; }
.shoppi-srp__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 16px;
}
.shoppi-results-stats { font-size: 14px; color: var(--shoppi-text-muted); }
.shoppi-did-you-mean { font-size: 14px; color: var(--shoppi-text); }
.shoppi-did-you-mean__link {
  color: var(--shoppi-primary);
  text-decoration: underline;
  font-weight: 500;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
}
.shoppi-srp__sort-select {
  border: 1px solid var(--shoppi-border);
  background: var(--shoppi-bg);
  padding: 8px 12px;
  border-radius: var(--shoppi-radius);
  font-size: 14px;
  cursor: pointer;
}
.shoppi-srp__layout {
  display: grid;
  grid-template-columns: 260px 1fr;
  gap: 32px;
  /* Horizontal gutters — the layout appends directly to the section root,
     which has no theme padding. Use the theme's own page-margin variable
     (Horizon exposes --page-margin; Dawn exposes --page-width) with a
     sensible fallback so the sidebar doesn't touch the viewport edge. */
  padding-left: var(--page-margin, clamp(16px, 5vw, 48px));
  padding-right: var(--page-margin, clamp(16px, 5vw, 48px));
  max-width: var(--page-width, 1440px);
  margin-left: auto;
  margin-right: auto;
  /* Breathing room between the native heading/search-input block above and
     our layout. Without it, the sidebar + grid butt right up against the
     result-count paragraph. */
  margin-top: 24px;
  box-sizing: border-box;
}
@media (max-width: 768px) {
  .shoppi-srp__layout { grid-template-columns: 1fr; }
  .shoppi-srp__facets { display: none; }
}
.shoppi-srp__facets { position: sticky; top: 16px; align-self: start; max-height: calc(100vh - 32px); overflow-y: auto; }
.shoppi-srp__facets-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.shoppi-srp__facets-heading { font-size: 14px; font-weight: 600; margin: 0; text-transform: uppercase; color: var(--shoppi-text-muted); letter-spacing: 0.5px; }
.shoppi-srp__facets-clear { background: transparent; border: 0; padding: 0; font-size: 12px; color: var(--shoppi-primary); cursor: pointer; text-transform: none; letter-spacing: 0; }
.shoppi-srp__facets-clear:hover { text-decoration: underline; }
.shoppi-srp__header { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 16px; }
.shoppi-srp__header-title { font-size: 20px; font-weight: 600; margin: 0; color: var(--shoppi-text); line-height: 1.3; }
.shoppi-srp__header-sep { color: var(--shoppi-text-muted); font-weight: 400; }
.shoppi-srp__header-query { color: var(--shoppi-primary); font-weight: 600; }
.shoppi-srp__sort { display: inline-flex; align-items: center; gap: 8px; }
.shoppi-srp__sort-label { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: var(--shoppi-text-muted); }
.shoppi-srp__sort-select {
  font: inherit;
  font-size: 13px;
  color: var(--shoppi-text);
  background: var(--shoppi-bg);
  border: 1px solid var(--shoppi-border-strong);
  border-radius: var(--shoppi-radius);
  padding: 6px 28px 6px 10px;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path fill='none' stroke='%236b6b6b' stroke-width='1.5' d='M1 1l4 4 4-4'/></svg>");
  background-repeat: no-repeat;
  background-position: right 10px center;
}
.shoppi-srp__sort-select:focus-visible { outline: 2px solid var(--shoppi-primary); outline-offset: 2px; }
.shoppi-srp__facet-group { border-bottom: 1px solid var(--shoppi-border); padding: 12px 0; }
.shoppi-srp__facet-heading { font-size: 14px; font-weight: 600; margin: 0 0 8px; color: var(--shoppi-text); }
.shoppi-srp__facet-body { display: flex; flex-direction: column; gap: 6px; }
.shoppi-srp__facet-option { display: flex; align-items: center; gap: 8px; font-size: 14px; color: var(--shoppi-text); cursor: pointer; }
.shoppi-srp__facet-option input { margin: 0; }

/* Boolean-facet toggle (CSS-only pill switch). The underlying input is
   visually hidden but still gets focus/keyboard events so screen readers
   announce it as a switch. The `.track` div is what the user sees. */
.shoppi-srp__facet-toggle { display: flex; align-items: center; gap: 10px; font-size: 14px; color: var(--shoppi-text); cursor: pointer; user-select: none; }
.shoppi-srp__facet-toggle-input { position: absolute; opacity: 0; pointer-events: none; width: 0; height: 0; }
.shoppi-srp__facet-toggle-track {
  position: relative;
  width: 34px; height: 20px;
  border-radius: 999px;
  background: var(--shoppi-border);
  transition: background 0.15s ease;
  flex-shrink: 0;
}
.shoppi-srp__facet-toggle-knob {
  position: absolute;
  top: 2px; left: 2px;
  width: 16px; height: 16px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 2px rgba(0,0,0,0.2);
  transition: transform 0.15s ease;
}
.shoppi-srp__facet-toggle-input:checked + .shoppi-srp__facet-toggle-track {
  background: var(--shoppi-primary);
}
.shoppi-srp__facet-toggle-input:checked + .shoppi-srp__facet-toggle-track .shoppi-srp__facet-toggle-knob {
  transform: translateX(14px);
}
.shoppi-srp__facet-toggle-input:focus-visible + .shoppi-srp__facet-toggle-track {
  outline: 2px solid var(--shoppi-primary);
  outline-offset: 2px;
}
.shoppi-srp__facet-toggle-label { color: var(--shoppi-text-muted); font-size: 13px; }

.shoppi-srp__facet-slider { width: 100%; }
.shoppi-srp__facet-range-label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  font-size: 13px;
  color: var(--shoppi-text);
  margin-bottom: 12px;
  font-weight: 500;
}
.shoppi-srp__facet-range-label-value {
  background: var(--shoppi-bg-muted);
  border: 1px solid var(--shoppi-border);
  border-radius: 6px;
  padding: 3px 8px;
  min-width: 56px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.shoppi-srp__facet-range-label-dash { color: var(--shoppi-text-muted); }

/* Dual-handle range slider — a visible `.rail` div sits behind two
   transparent `<input type="range">` overlays. The inputs' native
   tracks are hidden so only their thumbs show, and the thumbs draw
   on the same rail. An inner `.fill` bar shows the currently-selected
   sub-range. Without this pattern the two native tracks would stack
   at slightly different vertical offsets, producing the "one thumb
   above / one thumb below the line" defect. */
.shoppi-srp__facet-range-track {
  position: relative;
  height: 28px;
  margin: 0 8px;
}
.shoppi-srp__facet-range-rail {
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 4px;
  transform: translateY(-50%);
  background: var(--shoppi-border);
  border-radius: 2px;
}
.shoppi-srp__facet-range-fill {
  position: absolute;
  top: 0;
  bottom: 0;
  background: var(--shoppi-primary);
  border-radius: 2px;
}
.shoppi-srp__facet-range-input {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  width: 100%;
  height: 28px;
  background: transparent;
  pointer-events: none;
  -webkit-appearance: none;
  appearance: none;
  outline: none;
  margin: 0;
}
/* Transparent native tracks — our `.rail` is the only visible track. */
.shoppi-srp__facet-range-input::-webkit-slider-runnable-track {
  height: 4px;
  background: transparent;
}
.shoppi-srp__facet-range-input::-moz-range-track {
  height: 4px;
  background: transparent;
}
.shoppi-srp__facet-range-input::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #fff;
  border: 2px solid var(--shoppi-primary);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
  cursor: pointer;
  pointer-events: auto;
  /* Vertically center the thumb on the 28px track (14px - 9px ≈ 5px). */
  margin-top: -7px;
  position: relative;
  z-index: 2;
}
.shoppi-srp__facet-range-input::-moz-range-thumb {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #fff;
  border: 2px solid var(--shoppi-primary);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
  cursor: pointer;
  pointer-events: auto;
  position: relative;
  z-index: 2;
}
.shoppi-srp__facet-range-input:focus-visible::-webkit-slider-thumb {
  outline: 2px solid var(--shoppi-primary);
  outline-offset: 2px;
}
.shoppi-srp__active-filters { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
.shoppi-srp__chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: var(--shoppi-bg-muted);
  border: 1px solid var(--shoppi-border);
  border-radius: 999px;
  padding: 4px 10px;
  font-size: 13px;
  cursor: pointer;
  color: var(--shoppi-text);
}
.shoppi-srp__chip:hover { background: #ececec; }

/* Defensive grid scoping — themes (Horizon, Dawn) ship `.card-wrapper`,
   `.product-card-wrapper`, etc. with `width: 100%` and section
   containers that constrain the SRP host to a narrow `page-width`.
   Force the SRP host to break out and claim full width, then use
   auto-fit with a 220px minimum so tracks never collapse below
   readable width (no character-per-line text wrap).

   Applied via BOTH `[data-shoppi-mount="search-results"]` (for any future
   dedicated SRP block) and `.shoppi-srp__layout` (used by the
   AI Search head embed when it injects into the theme's /search section).
*/
/* Section-block variant (registry-mounted) — break out of Shopify's
   <shopify-app-block> width constraint. The AI-search injection appends
   directly into the theme's search section root and doesn't need this. */
[data-shoppi-mount="search-results"] {
  width: 100% !important;
  max-width: none !important;
  min-width: 0 !important;
  flex: 1 1 100% !important;
  box-sizing: border-box !important;
}
.shopify-app-block:has([data-shoppi-mount="search-results"]) {
  width: 100% !important;
  max-width: none !important;
  min-width: 0 !important;
  flex: 1 1 100% !important;
}
[data-shoppi-mount="search-results"] .shoppi-srp__grid,
.shoppi-srp__layout .shoppi-srp__grid {
  display: grid !important;
  /* auto-fill (not auto-fit): keeps unused tracks in place, so a single
     result stays at its minimum 220px width instead of stretching to the
     full row. 1fr cap lets multi-product grids flex up to the available
     space. */
  grid-template-columns: repeat(auto-fill, minmax(min(220px, 100%), 1fr));
  gap: var(--shoppi-gap);
  width: 100%;
  /* Justify items to the start so a single card in a wide row left-aligns
     cleanly under the filters column rather than centring alone. */
  justify-content: start;
}
[data-shoppi-mount="search-results"] .shoppi-srp__grid > *,
.shoppi-srp__layout .shoppi-srp__grid > * {
  width: auto !important;
  min-width: 0 !important;
  max-width: 100% !important;
  margin: 0 !important;
}
/* The cloned card may have an inner wrapper (.card, .card__inner, etc.)
   with its own width cap — flatten it so the grid track is the only
   width source. */
[data-shoppi-mount="search-results"] .shoppi-srp__grid > * > *,
.shoppi-srp__layout .shoppi-srp__grid > * > * {
  max-width: 100% !important;
  width: auto !important;
}
.shoppi-srp__grid--list {
  grid-template-columns: 1fr !important;
}
.shoppi-srp__grid--list > * {
  display: flex;
  gap: 16px;
  align-items: stretch;
}

/* Top-bar facets — render groups as a horizontal flex row. */
.shoppi-srp__layout--top .shoppi-srp__layout {
  grid-template-columns: 1fr;
}
.shoppi-srp__facets--top {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 16px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--shoppi-border);
  position: static;
  max-height: none;
  overflow: visible;
}
.shoppi-srp__facets--top .shoppi-srp__facet-group {
  border: 1px solid var(--shoppi-border);
  border-radius: 999px;
  padding: 6px 12px;
  background: var(--shoppi-bg);
}

/* Drawer facets — fixed slide-in panel toggled by a button in the header. */
.shoppi-srp__drawer-toggle {
  border: 1px solid var(--shoppi-border);
  background: var(--shoppi-bg);
  padding: 8px 14px;
  border-radius: var(--shoppi-radius);
  font-size: 14px;
  cursor: pointer;
}
.shoppi-srp__layout--drawer .shoppi-srp__layout {
  grid-template-columns: 1fr;
}
.shoppi-srp__facets--drawer {
  position: fixed;
  top: 0;
  right: 0;
  height: 100vh;
  width: min(360px, 90vw);
  transform: translateX(100%);
  transition: transform 0.2s ease;
  z-index: 60;
  background: var(--shoppi-bg);
  padding: 16px;
  overflow-y: auto;
  box-shadow: -8px 0 24px rgba(0, 0, 0, 0.1);
  max-height: none;
}
.shoppi-srp__facets--drawer[aria-expanded="true"] {
  transform: none;
}

/* Off layout — collapse the 2-col grid; nothing else needed. */
.shoppi-srp__layout--off .shoppi-srp__layout {
  grid-template-columns: 1fr;
}
/* ── Infinite scroll (sentinel + load-more indicator) ──────────────────────
   The sentinel is a visually-inert spacer the IntersectionObserver watches;
   the indicator announces an in-flight page fetch and doubles as a polite
   live region for screen readers. Pure scroll-to-load — no "Load more"
   button — matching the wagon playground and Horizon's default search. */
.shoppi-srp__sentinel {
  width: 100%;
  height: 1px;
}
.shoppi-srp__loading-more {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 20px 0;
  font-size: 14px;
  color: var(--shoppi-text-muted);
}
/* `display: flex` above beats the `hidden` attribute's UA `display: none`,
   so restore it explicitly when hidden. */
.shoppi-srp__loading-more[hidden] { display: none; }
.shoppi-srp__loading-more::before {
  content: '';
  width: 16px;
  height: 16px;
  border: 2px solid var(--shoppi-border);
  border-top-color: var(--shoppi-primary);
  border-radius: 50%;
  animation: shoppi-spin 0.8s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
  .shoppi-srp__loading-more::before { animation: none; }
}

/* ── Highlight (matched terms in product names) ────────────────────────── */
mark.shoppi-highlight,
.shoppi-card mark,
.shoppi-srp mark {
  background: rgba(17, 17, 17, 0.1);
  color: inherit;
  padding: 0 2px;
  border-radius: 2px;
}

