/* Shopiza – custom styles */

/* Remove browser default focus outline globally — we use custom box-shadow instead */
*:focus { outline: none !important; }
*:focus-visible { outline: none !important; }

/* ─── Shop Storefront Styles ─── */

/* Hero / Header area */
.shop-hero {
  padding: 0.75rem 0 1rem;
}

/* Phones sit the trail closer under the fixed header. The clearance below the
   header is already --header-gap (12px), and this wrapper stacked another 12px
   on top of it, so the breadcrumbs started 24px down — a wide empty band on a
   small screen. Trimming the wrapper rather than the token keeps --header-gap
   the single answer for header clearance, so pages with no hero are unmoved. */
@media (max-width: 991.98px) {
  .shop-hero { padding-top: 2px; }
}

.shop-hero-title {
  font-size: 1.75rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  margin-bottom: 0.25rem;
}

.shop-hero-sub {
  color: var(--muted);
  font-size: 0.95rem;
}

/* Cart button with count badge */
.shop-cart-btn {
  position: relative;
  padding-right: 2.5rem !important;
}

.shop-cart-count {
  position: absolute;
  right: 0.5rem;
  top: 50%;
  transform: translateY(-50%);
  background: var(--primary-solid, var(--primary));
  color: var(--primary-fg, var(--bg, #f8f9fb));
  font-size: 0.6875rem;
  font-weight: 700;
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}

/* Sidebar */
.shop-sidebar-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius, 0.625rem);
  padding: 1rem;
}

/* The filters card ships with .mt-3, which is the GAP below the categories
   card above it. Search has no categories card, so that margin became a 16px
   drop and the filters box no longer lined up with the view toggle across the
   row. A leading card has nothing to be spaced from, so it loses the margin.
   !important because .mt-3 is a Bootstrap utility.
   Scoped to .shop-sidebar-wrap so the phone offcanvas copy, where the card is
   also first but the spacing is the panel's own, keeps its margin. */
.shop-sidebar-wrap > .shop-sidebar-card:first-child { margin-top: 0 !important; }

.shop-sidebar-title {
  font-weight: 600;
  font-size: 0.8125rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  margin-bottom: 0.75rem;
}

.shop-filter-options { }

/* --- Dual range slider --- */
.filter-range-slider { padding: 0 2px; }
.filter-range-track {
  position: relative;
  height: 28px;
  display: flex;
  align-items: center;
}
.filter-range-track::before {
  content: '';
  position: absolute;
  left: 0; right: 0;
  height: 4px;
  background: var(--bs-border-color);
  border-radius: 2px;
}
.filter-range-fill {
  position: absolute;
  height: 4px;
  background: var(--primary-solid, var(--primary, #6366f1));
  border-radius: 2px;
  pointer-events: none;
  box-shadow: 0 0 0 1px rgba(0,0,0,0.08);
}
.filter-range-thumb {
  -webkit-appearance: none;
  appearance: none;
  position: absolute;
  width: 100%;
  height: 28px;
  background: none;
  pointer-events: none;
  margin: 0;
  padding: 0;
}
.filter-range-thumb::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--primary-solid, var(--primary, #6366f1));
  border: 2px solid var(--bg, #fff);
  box-shadow: 0 0 0 1.5px rgba(0,0,0,0.15), 0 1px 4px rgba(0,0,0,.2);
  cursor: pointer;
  pointer-events: auto;
  position: relative;
  z-index: 1;
}
.filter-range-thumb::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--primary-solid, var(--primary, #6366f1));
  border: 2px solid var(--bg, #fff);
  box-shadow: 0 0 0 1.5px rgba(0,0,0,0.15), 0 1px 4px rgba(0,0,0,.2);
  cursor: pointer;
  pointer-events: auto;
}
.filter-range-thumb-max, .filter-price-thumb-max { z-index: 2; }
[data-theme="dark"] .filter-range-thumb::-webkit-slider-thumb { border-color: #222; }
[data-theme="dark"] .filter-range-thumb::-moz-range-thumb { border-color: #222; }

.shop-filter-group + .shop-filter-group {
  border-top: 1px solid var(--border);
  padding-top: 0.75rem;
}

/* ─── Product Cards ─── */
.product-card {
  background: var(--card);
  /* The 1px edge is an INSET SHADOW, not a border. A real border sits inside
     the overflow clip, so the photo stopped 1px short on every side and a
     full-bleed image read as narrower than its card. An inset shadow paints
     with the background, i.e. UNDER the children, so the image now runs to
     the card's true edge and covers the line along its own three sides while
     the line still shows around the body below. */
  border: 0;
  box-shadow: inset 0 0 0 1px var(--border);
  border-radius: 8px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  height: 100%;
  transition: box-shadow 0.2s, transform 0.2s;
}

.product-card:hover {
  /* Keep the inset edge; a bare box-shadow here would wipe it. */
  box-shadow: inset 0 0 0 1px var(--border), 0 8px 24px rgba(0,0,0,.08);
  transform: translateY(-2px);
}

[data-theme="dark"] .product-card:hover {
  box-shadow: inset 0 0 0 1px var(--border), 0 8px 24px rgba(0,0,0,.3);
}

.product-card-img {
  display: block;
  position: relative;
  overflow: hidden;
  aspect-ratio: 1;
  /* No backdrop by default. The white letterbox surface is added by the
     `has-letterbox` class (set in navigation.js) ONLY when the photo doesn't
     fill the frame or the owner set image padding. When the photo fills the
     frame exactly — the common case — a white surface behind it has no job to
     do and actively hurts: at the card's rounded top corners it bleeds
     through the clip's antialiasing as a white wedge, obvious in dark mode
     over a dark photo. */
  /* Round the top corners to match the card. The card already clips to its
     own 10px radius, but the image and its white letterbox backdrop were
     SQUARE, so at rest their opaque corner filled the region the card's curve
     carves away and bled through the clip's antialiasing as a white wedge —
     visible only in dark mode, and only where the photo itself is dark. While
     the hover zoom runs the image is composited and clipped cleanly, which is
     exactly why it looked right mid-animation and wrong at both ends.
     Rounding it here means nothing square sits under that curve: the photo
     is clipped by this element's rounded overflow, so the photo forms the
     corner itself. */
  border-radius: 8px 8px 0 0;
  /* `padding` driven by the shop's catalog_options.image_padding setting —
     the storefront layout sets `--card-img-padding: Npx` on a parent and
     this rule reads it. Defaults to 0 so unconfigured shops are unchanged. */
  padding: var(--card-img-padding, 0);
  box-sizing: border-box;
}
.product-card-img.has-letterbox { background: var(--card, #ffffff); }
/* Hairline INSIDE the photo. Over a colourful or dark image it disappears;
   over a white-background product shot it draws the edge the card border used
   to provide. Sits above the photo but below any badge (no z-index). */
.product-card-img::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.07);
  pointer-events: none;
}
.product-card-img.is-portrait { aspect-ratio: 3 / 4; }
.product-card-img.is-landscape { aspect-ratio: 4 / 3; }
/* Dark mode: keep the image's letterbox area white. Most product photos
   are shot on a white seamless, so a dark backdrop creates a hard contrast
   line at the photo's edges when `object-fit: contain` letterboxes the
   image to fit the configured aspect ratio. */
[data-theme="dark"] .product-card-img.has-letterbox {
  background: #ffffff;
}

.product-card-img picture {
  display: block;
  width: 100%;
  height: 100%;
}

.product-card-img img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  transition: transform 0.35s ease;
  /* The card image sits on a WHITE container (object-fit: contain letterboxes
     on white, and dark mode forces #ffffff), and card heights are fractional
     — 309.328px on a stock catalog. When the hover zoom runs, the browser
     rasterizes the image on its own layer and rounds that fractional edge
     differently, exposing a 1px strip of the white container under the photo.
     Promoting the layer permanently (translateZ) is NOT the fix: it makes the
     rounding permanent, so the seam stops flashing and simply stays.
     Instead give the photo a hair of overscale at rest. The scale is centred,
     so each edge gets half of it: 1% of a ~309px card is ~1.5px of bleed past
     the bottom, comfortably more than a whole pixel of rounding, and
     overflow:hidden clips it. Both states stay plain 2D scales, so nothing
     changes about how the element is composited. Cost is 0.5% cropped off each
     edge of the photo, which is not perceptible at card size. */
  transform: scale(1.01);
}

.product-card:hover .product-card-img img.product-card-img-main {
  transform: scale(1.05);
}

/* Hover image swap */
.product-card-img-hover {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  opacity: 0;
  transition: opacity 0.35s ease;
  z-index: 1;
}
.product-card:hover .product-card-img-hover {
  opacity: 1;
}

.product-card-placeholder {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  font-size: 2rem;
  opacity: 0.3;
}

.product-badge {
  position: absolute;
  top: 0.5rem;
  left: 0.5rem;
  font-size: 0.6875rem;
  font-weight: 600;
  padding: 0.2rem 0.5rem;
  border-radius: 0.25rem;
  z-index: 2;
}

.product-badge-oos {
  /* Brighter red-orange than the old flat red: hue moves from 0 to about 14
     degrees and the fill lifts, which also buys the black label a little more
     contrast (6.38:1, up from 5.58:1). Slightly more opaque so it holds its
     colour over a busy photo. */
  background: rgba(251, 82, 39, .92);
  color: #000;
}

.product-card-body {
  padding: 0.875rem;
  display: flex;
  flex-direction: column;
  flex: 1;
  gap: 0.25rem;
  container-type: inline-size;
}

.product-card-title {
  font-weight: 600;
  font-size: 0.9rem;
  color: var(--fg);
  text-decoration: none;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  line-height: 1.3;
}

.product-card-title:hover { color: var(--primary-text, var(--primary)); }

.product-card-price {
  font-weight: 700;
  font-size: 1.0625rem;
  color: var(--primary-text, var(--primary));
}

/* Featured badge */
/* Featured is a badge on the IMAGE now, pinned bottom-left. .product-badge
   supplies the shared box; these override the top-left anchor and the colour.
   Sold-out stays top-left, so the two never overlap on the same card. */
.product-badge-featured {
  top: auto;
  bottom: 0.5rem;
  left: 0.5rem;
  /* One ramp and one label colour for both themes: the brighter yellow-gold
     with a black label, which is the pairing that actually reads. White over
     gold was tried in light mode and did not work even with a dark halo.
     Flat by design: no border, no shadows. The light band is a plateau from
     38% to 62% so the sheen spans most of the badge rather than glinting at
     a single point. Black measures 8.87:1 to 15.13:1 across the stops. */
  background: linear-gradient(135deg,
    #e29a1e 0%,
    #f0b431 18%,
    #ffd75e 38%,
    #ffd75e 62%,
    #f0b431 82%,
    #e29a1e 100%);
  color: #000;
}

/* Card SKU */
.product-card-sku {
  font-size: 0.75rem;
  line-height: 1.2;
}

/* Card short description — matches the product title's size and color
   so it reads as a continuation of the title rather than secondary
   metadata. Slightly relaxed line-height for multi-line readability,
   plus a generous bottom gap so it doesn't crowd the SKU below. */
.product-card-short-desc {
  /* Matches .product-card-attrs — the two sit next to each other on the card
     and read as one block of supporting detail under the title. */
  font-size: 0.75rem;
  line-height: 1.35;
  /* Deliberately no text-wrap: pretty here. It suppresses hyphenation, badly
     on WebKit (zero hyphens in prose on iOS at any card width) and at wider
     columns in Chrome too. Card columns are narrow enough that hyphens beat
     last-line balancing, so pretty stays off for this element. It was worth
     measuring: a probe that forces a break with an over-wide word still
     hyphenates under pretty and wrongly reads as working. Count line breaks
     that land inside a word instead. */
  /* Hyphenation is opt-in per language below. Card columns are narrow, so a
     long compound (German/Nordic/Hungarian especially) otherwise forces an
     ugly ragged line or overflows. */
  /* Prefixed form for older WebKit. Current iOS reports the standard property
     as supported and hyphenates from it alone, so this is belt and braces
     rather than the thing that makes iOS work. */
  -webkit-hyphens: manual;
  hyphens: manual;
  /* Never strand a fragment: only hyphenate words of 6+ chars, keeping at
     least 3 characters on each side of the break. WebKit has no
     hyphenate-limit-chars; its own two properties cover the before/after
     halves of the same intent. */
  -webkit-hyphenate-limit-before: 3;
  -webkit-hyphenate-limit-after: 3;
  hyphenate-limit-chars: 6 3 3;
  color: var(--fg);
  margin-top: 2px;
  margin-bottom: 10px;
}
/* Pull bulleted/numbered lists flush-left so the marker + text line up
   with the other card text. The browser default `padding-left: 40px` on
   <ul>/<ol> would push the entire list far to the right; we cut it down
   to just enough for the marker glyph (1.1rem). Margins are zeroed so
   the wrapper's own `margin-bottom: 10px` is the only gap between the
   list and the SKU below — matches the plain-text spacing. */
/* Auto-hyphenation, enabled only for languages where it is typographically
   standard AND browsers ship a hyphenation dictionary. Matched on the
   element's own `lang` (set from the language the text actually resolved to,
   see product-cards.ejs) rather than the page language, so an untranslated
   product that fell back to the shop default is hyphenated by ITS rules, not
   the visitor's. Languages left out (el, bg, uk, tr) simply keep manual
   hyphenation — dictionary coverage there is inconsistent across browsers and
   a missing dictionary is silently a no-op anyway. */
.product-card-short-desc:lang(en), .product-card-short-desc:lang(de),
.product-card-short-desc:lang(nl), .product-card-short-desc:lang(da),
.product-card-short-desc:lang(sv), .product-card-short-desc:lang(no),
.product-card-short-desc:lang(fi), .product-card-short-desc:lang(hu),
.product-card-short-desc:lang(cs), .product-card-short-desc:lang(sk),
.product-card-short-desc:lang(pl), .product-card-short-desc:lang(sl),
.product-card-short-desc:lang(hr), .product-card-short-desc:lang(ro),
.product-card-short-desc:lang(it), .product-card-short-desc:lang(es),
.product-card-short-desc:lang(pt), .product-card-short-desc:lang(fr),
.product-card-short-desc:lang(lt), .product-card-short-desc:lang(lv),
.product-card-short-desc:lang(et) {
  -webkit-hyphens: auto;
  hyphens: auto;
}

.product-card-short-desc ul,
.product-card-short-desc ol {
  padding-left: 1.1rem;
  margin: 0;
}

/* ── Collapsible filter groups ──
   Visitors browsing a shop with many attributes want to fold away the ones
   they don't care about. Uses the same rotating chevron as the category tree
   directly above it in this sidebar (and as the admin's collapsible
   sections), rather than a +/- idiom the product uses nowhere else.
   Collapsed state is remembered per shop in localStorage — see navigation.js. */
.shop-filter-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  padding: 2px 0;
  background: none;
  border: 0;
  color: inherit;
  font-weight: 600;
  font-size: 0.8125rem;
  line-height: 1.35;
  text-align: left;
  cursor: pointer;
}
.shop-filter-head:hover { color: var(--primary-text, var(--primary)); }
.shop-filter-chev {
  flex: 0 0 auto;
  opacity: 0.55;
  transition: transform 0.18s ease;
}
.shop-filter-group.is-collapsed .shop-filter-chev { transform: rotate(-90deg); }
.shop-filter-group.is-collapsed .shop-filter-body { display: none; }
.shop-filter-body { margin-top: 4px; }

/* Whole-card collapse (Categories / Filters). Same chevron and rotation as the
   groups inside, so the rail has one collapse idiom top to bottom. */
.shop-sidebar-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  flex: 1 1 auto;
  min-width: 0;
  padding: 0;
  background: none;
  border: 0;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
/* In the Filters card the head sits in a flex row beside "Clear all", which
   stretches it. In the Categories card it is a direct child of the card, where
   a button shrink-wraps its content — so the chevron ended up next to the
   title instead of at the right edge. Only that case needs the full width. */
.shop-sidebar-card > .shop-sidebar-head { width: 100%; }
/* The collapse markup moved the title into a button with mb-0, which dropped
   the breathing room the plain <h6> used to carry. Restore it on the body that
   follows the head. Scoped to a head that is a DIRECT child of the card (the
   Categories case) — in the Filters card the head sits inside a flex row and
   the first group already brings its own top margin. */
.shop-sidebar-card > .shop-sidebar-head + .shop-sidebar-body { margin-top: 0.75rem; }
.shop-sidebar-head:hover .shop-sidebar-title { color: var(--primary-text, var(--primary)); }
.shop-sidebar-card.is-collapsed .shop-filter-chev { transform: rotate(-90deg); }
.shop-sidebar-card.is-collapsed .shop-sidebar-body { display: none; }

/* Long option lists show the first 12 with a "Show more". Hidden rows are
   display:none rather than height-clipped so they cost no layout space and
   stay out of the tab order while collapsed. */
.shop-filter-options .filter-option-overflow { display: none; }
.shop-filter-options.is-expanded .filter-option-overflow { display: block; }
.shop-filter-more {
  display: inline-block;
  margin-top: 4px;
  padding: 0;
  background: none;
  border: 0;
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.35;
  color: var(--primary-text, var(--primary));
  cursor: pointer;
}
.shop-filter-more:hover { text-decoration: underline; }

/* Card attributes */
.product-card-attrs {
  font-size: 0.75rem;
  line-height: 1.35;
  color: var(--muted, #6c757d);
  margin-bottom: 0.125rem;
}
.product-card-attr-name {
  font-weight: 600;
  color: var(--fg);
}

/* Card action row (qty + add-to-cart side by side) */
.product-card-action-row {
  display: flex;
  align-items: stretch;
  gap: 0.375rem;
  margin-top: 0.5rem;
}
.product-card-action-row .qty-control-sm {
  flex-shrink: 0;
}
.product-add-btn-icon {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.375rem;
  font-size: 0.8125rem;
  padding: 0 0.5rem;
  white-space: nowrap;
  overflow: hidden;
}
.product-card-action-row .qty-control-sm .qty-input {
  width: 2.5rem;
}

/* Add-to-cart label: auto-hide when card is too narrow for text */
.card-atc-label { margin-left: 0.25rem; }
@media (max-width: 767.98px), (min-width: 992px) and (max-width: 1399.98px) {
  .card-atc-label { display: none; }
}
@container (max-width: 260px) {
  .card-atc-label { display: none; }
}
/* One product per row (the phone default, and .layout-single once chosen): the
   card is the full width of the screen, so the button has room for the label
   that a two-up GRID tile does not. The 768px rule above is sized for that
   tile and cannot tell the layouts apart. */
@media (max-width: 767.98px) {
  .product-grid.layout-single .card-atc-label,
  .product-grid:not(.layout-grid):not(.layout-list) .card-atc-label {
    display: inline;
  }
}
/* Restated so the container guard still wins on a genuinely narrow card, which
   the rule above would otherwise outrank — but at 240px rather than 260px.
   260 is sized for a two-up tile, where the button is the whole body width.
   Here the stepper takes 98px + 6px gap first, so the button is body - 104,
   and the longest of the 25 labels ("In winkelwagen") needs 140px with its
   icon and padding. That works out at a 244px body, so 240 is the honest
   threshold and a 320px phone keeps its label. */
@container (max-width: 240px) {
  .product-grid.layout-single .card-atc-label,
  .product-grid:not(.layout-grid):not(.layout-list) .card-atc-label {
    display: none;
  }
}

/* Card variant selectors (always visible) */
.card-variant-selectors {
  margin-bottom: 0.25rem;
}
.card-variant-selectors .form-select-sm {
  font-size: 0.75rem;
  padding: 0.2rem 1.75rem 0.2rem 0.5rem;
  height: auto;
}
.card-variant-selectors .mb-1:last-child { margin-bottom: 0 !important; }

/* Disabled add-to-cart until variant selected */
.card-atc-has-variant:disabled {
  opacity: 0.45;
  background-color: var(--primary) !important;
  border-color: var(--primary) !important;
  outline: none !important;
  box-shadow: none !important;
}

/* Flash animation for variant selects */
@keyframes variantFlash {
  0%, 100% { box-shadow: none; }
  50% { box-shadow: 0 0 0 2px var(--primary-text, var(--primary, #6f42c1)); }
}
.card-variant-select.is-flashing {
  animation: variantFlash 0.35s ease 3;
}

.product-add-btn {
  margin-top: 0.5rem;
  font-size: 0.8125rem;
  padding: 0.375rem 0.75rem;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.375rem;
}

/* ─── Product Detail ─── */
.product-detail-img {
  border-radius: var(--radius, 0.625rem);
  overflow: hidden;
  background: var(--hover, #f3f4f6);
  border: 1px solid var(--border);
}

.product-detail-img img {
  width: 100%;
  height: auto;
  display: block;
}

.product-detail-placeholder {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  opacity: 0.3;
}

.product-detail-info {
  display: flex;
  flex-direction: column;
}

.product-detail-cat {
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--primary-text, var(--primary));
  text-decoration: none;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 0.25rem;
}

.product-detail-cat:hover { text-decoration: underline; }

.product-detail-title {
  font-size: 1.75rem;
  font-weight: 700;
  margin-bottom: 0.75rem;
  letter-spacing: -0.01em;
}

.product-detail-price-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 1rem;
}

.product-detail-price {
  font-size: 1.5rem;
  font-weight: 800;
  color: var(--primary-text, var(--primary));
}

.product-detail-desc {
  color: var(--muted);
  font-size: 0.9375rem;
  line-height: 1.6;
  margin-bottom: 1.5rem;
}

/* Tighter list indent inside product description blocks (right-column
   short description AND the full-width long description below the
   gallery). Default browser padding-left of 40px makes the list look
   detached from the surrounding text; 1.1rem fits the marker tightly. */
.product-detail-desc ul,
.product-detail-desc ol,
.product-detail-full-desc ul,
.product-detail-full-desc ol {
  padding-left: 1.1rem;
  margin-left: 0;
}

.product-detail-form { margin-top: auto; }

/* ─── Quantity Controls ─── */
.qty-control {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  overflow: hidden;
  background: var(--card);
}

.qty-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  border: none;
  background: transparent;
  color: var(--fg);
  cursor: pointer;
  font-size: 0.75rem;
  transition: background 0.15s;
}

.qty-btn:hover { background: var(--hover, #f3f4f6); }

.qty-input {
  width: 2.5rem;
  text-align: center;
  border: none;
  background: transparent;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--fg);
  -moz-appearance: textfield;
}

.qty-input::-webkit-inner-spin-button,
.qty-input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }

.qty-control-sm .qty-btn { width: 1.75rem; height: 1.75rem; font-size: 0.625rem; }
.qty-control-sm .qty-input { width: 2.2rem; font-size: 0.8125rem; }

/* Unit name shown inside the quantity stepper, e.g. "6 karton". */
.qty-unit {
  font-size: 0.75rem;
  color: var(--muted);
  padding: 0 0.45rem 0 0.1rem;
  white-space: nowrap;
  user-select: none;
}

/* ─── Cart Items ─── */
.cart-item {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1rem 1.25rem;
  border-bottom: 1px solid var(--border);
}

.cart-item:last-child { border-bottom: none; }

.cart-item-img {
  width: 72px;
  height: 72px;
  border-radius: 0.5rem;
  overflow: hidden;
  flex-shrink: 0;
  background: var(--hover, #f3f4f6);
}

.cart-item-img img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.cart-item-placeholder {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  opacity: 0.3;
}

.cart-item-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.125rem; }

.cart-item-name {
  font-weight: 600;
  font-size: 0.9375rem;
}

/* Secondary cart-item lines (SKU, variation + unit price) — 13px, evenly
   spaced from the name via the flex `gap` above. */
.cart-item-sub {
  color: var(--muted);
  font-size: 0.8125rem;
  line-height: 1.35;
}

.cart-item-unit-price {
  color: var(--muted);
  font-size: 0.8125rem;
}

.cart-item-actions {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-shrink: 0;
}

.cart-item-total {
  font-weight: 700;
  font-size: 0.9375rem;
  min-width: 5rem;
  text-align: right;
}

.cart-summary-card {
  position: sticky;
  /* Add the announcement-bar offset so a sticky bar doesn't cover the top of
     the summary; in scroll-away mode the variable resets to 0 once the bar
     slides off, so the summary tracks the header naturally. */
  top: calc(80px + var(--announcement-bar-h, 0px));
  transition: top 0.22s ease;
}

/* Checkout summary: dual-direction sticky (same behaviour as the category
   sidebar) so a tall summary reveals its totals as you scroll down instead of
   pushing them below the fold. navigation.js drives `top` dynamically on
   ≥992px screens; this rule is the pre-JS fallback and the mobile behaviour. */
.checkout-summary-card {
  position: sticky;
  top: calc(var(--navbar-height, 64px) + var(--header-gap, 12px) + var(--announcement-bar-h, 0px));
  transition: top 0.22s ease;
}

/* Blocked-screen card inside the demo admin. Reuses .promos-empty's dashed
   frame so it reads as "nothing here", not as an error page. */
.demo-blocked__icon {
  font-size: 1.6rem;
  color: var(--pz-text-muted, #6c757d);
  margin-bottom: 0.85rem;
  display: block;
}
.demo-blocked__actions {
  display: flex;
  gap: 0.5rem;
  justify-content: center;
  flex-wrap: wrap;
}

/* ─── Read-only demo admin: banner + toast ─────────────────────────
   Rendered only for a demo session (middleware/demoAdminGuard.js). Sits
   outside .admin-main so soft-nav swaps never remove it. */
.demo-admin-banner {
  /* Horizontal margins come from the shared rule further down that this banner
     borrows from .owner-verify-banner, so the two line up exactly. */
  border: 1px solid var(--border, #e5e7eb);
  border-left: 4px solid var(--primary, #2563eb);
  background: var(--card, #fff);
  color: var(--fg, #111827);
  border-radius: 10px;
  padding: 0.85rem 1rem;
}
.demo-admin-banner .small {
  color: var(--fg-muted, #6c757d);
}
.demo-admin-banner__actions .btn {
  white-space: nowrap;
}
.demo-admin-toast {
  position: fixed;
  left: 50%;
  bottom: 1.5rem;
  transform: translateX(-50%) translateY(0.5rem);
  /* Above everything, deliberately. The toast fires precisely when someone
     clicks Save or Delete, and those live INSIDE modals (.promos-modal is
     z-index 2000 — the toast used to tie with it and lose on DOM order, so the
     one message explaining why nothing happened was hidden behind the dialog
     that prompted it). 10000 clears the highest value otherwise in use (9999). */
  z-index: 10000;
  background: var(--fg, #111827);
  color: var(--bg, #fff);
  border-radius: 999px;
  padding: 0.6rem 1.15rem;
  font-size: 0.9rem;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.22);
  opacity: 0;
  transition: opacity 0.2s ease, transform 0.2s ease;
  pointer-events: none;
}
.demo-admin-toast.is-visible {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

/* ─── Demo shop: stand-in card block ───────────────────────────────
   Only rendered on a shop with is_demo set (utils/demoMode.js). It mirrors the
   real Stripe card step's shape so the demo reads as the product, while the
   inputs are readonly and nameless — they collect nothing and post nothing. */
.demo-card__note {
  font-size: 0.85rem;
  line-height: 1.45;
  color: var(--fg-muted, #6c757d);
  background: var(--bg, rgba(0, 0, 0, 0.03));
  border-radius: 8px;
  padding: 0.6rem 0.75rem;
}
.demo-card .form-label {
  color: var(--fg-muted, #6c757d);
}
/* Readonly by design: keep the resting look of a real field (a greyed-out
   disabled style would read as "broken" rather than "sample data"), but drop
   the affordances that invite typing. */
.demo-card .form-control[readonly] {
  background-color: var(--card, #fff);
  color: var(--fg, #212529);
  cursor: default;
  user-select: none;
}
.demo-card__number {
  position: relative;
}
.demo-card__number .form-control {
  padding-right: 2.5rem;
}
.demo-card__icon {
  position: absolute;
  right: 0.85rem;
  top: 50%;
  transform: translateY(-50%);
  color: var(--fg-muted, #6c757d);
  pointer-events: none;
}

/* ─── Checkout method chooser (Guest / Apple Pay / Google Pay) ─── */
.checkout-methods {
  display: flex;
  gap: 0.5rem;
  margin-bottom: 1rem;
}
/* Express Checkout (wallet) button sits beside the guest button, sharing the row equally and
   matching its height (flex stretch) + radius. The Google/Apple button supplies its own fill,
   so no border/padding here — just sizing. Stacks under the guest button on narrow screens. */
.checkout-method--wallet {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: center;
}
.checkout-method--wallet [data-express-checkout-element] { width: 100%; }
@media (max-width: 575.98px) {
  .checkout-methods { flex-direction: column; }
}
.checkout-method {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.95rem 0.75rem;
  border: 1.5px solid var(--border);
  border-radius: 0.625rem;
  background: var(--card);
}
.checkout-method--guest {
  flex: 1 1 0;
  gap: calc(0.5rem - 3px);
}
.checkout-method--icon {
  flex: 1 1 0;
  padding-top: 0.4rem;
  padding-bottom: 0.4rem;
}
.checkout-method__icon { font-size: 1.4rem; line-height: 1; color: var(--muted); }
.checkout-method--guest .checkout-method__icon { font-size: 1rem; }
/* Wallet glyphs are wide wordmarks that sit a touch high in their box, so
   enlarge them and nudge down a couple of px to centre them optically. */
.checkout-method--icon .checkout-method__icon { font-size: 2.3rem; transform: translateY(2px); }
.checkout-method__label { font-size: 1rem; font-weight: 500; color: var(--fg); }
.checkout-method--active {
  border-color: var(--primary);
  background: var(--primary-subtle, rgba(159, 80, 223, 0.08));
}
.checkout-method--active .checkout-method__icon { color: var(--primary); }
.checkout-login-divider {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  margin: 1rem 0;
  font-size: 0.85rem;
  color: var(--muted);
}
.checkout-login-divider::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--border);
}
.checkout-login-divider a { white-space: nowrap; }
.checkout-method--disabled {
  /* Rendered at full strength (looks like a normal button) but inert until
     the wallet integrations ship. */
  pointer-events: none;
}

@media (max-width: 767px) {
  .cart-item { flex-wrap: wrap; gap: 0.75rem; }
  .cart-item-actions { width: 100%; justify-content: space-between; }
  .cart-item-total { min-width: auto; }
}

/* ─── Checkout Steps ─── */
.checkout-steps {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0;
  margin-bottom: 2rem;
}

.checkout-step {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.8125rem;
  color: var(--muted);
  font-weight: 500;
  flex: 0 0 auto;
}

.checkout-step.active { color: var(--primary-text, var(--primary)); font-weight: 600; }
.checkout-step.done { color: var(--fg); }

.checkout-step-num {
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.75rem;
  font-weight: 700;
  border: 2px solid var(--border);
  background: var(--card);
}

.checkout-step.active .checkout-step-num {
  background: var(--primary-solid, var(--primary));
  border-color: var(--primary-solid, var(--primary));
  color: var(--primary-fg, var(--bg, #f8f9fb));
}

.checkout-step.done .checkout-step-num {
  background: var(--fg);
  border-color: var(--fg);
  color: var(--card);
}

.checkout-step-line {
  flex: 1 1 auto;
  min-width: 1.5rem;
  height: 2px;
  background: var(--border);
  margin: 0 0.5rem;
}

.checkout-step.done + .checkout-step-line,
.checkout-step.active + .checkout-step-line {
  background: var(--primary);
}

/* ─── Distraction-free checkout header ─── */
/* On checkout the header shows only the brand + a "Secure checkout" badge;
   the progress steps live above the form, and the other action buttons are
   dropped server-side (rendered only in the non-checkout branch). */
.checkout-secure {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--muted);
  white-space: nowrap;
}
/* Secure-checkout + Data-protection badges in the checkout top bar — icons in
   the shop's brand colour, 20px between the two badges + a 10px gap to the
   right edge. */
.checkout-trust { gap: 20px; margin-right: 10px; }
.checkout-secure i { color: var(--primary-text, var(--primary)); }

/* Keep the storefront header exactly as tall on checkout as on every other shop
   page. On checkout the action buttons (menu / search / cart / account) are
   dropped server-side, so the tallest thing left in the bar is the brand logo.
   With the default 28px logo that made the checkout bar ~3px shorter than the
   rest of the shop, and hiding the trust badges on mobile (below) would shorten
   it further. 31px is the rendered height of the `btn btn-sm` controls that set
   the height everywhere else, so pinning the row to it makes the two identical.
   Applied to BOTH modes: where the logo is taller than 31px the min-height never
   binds and nothing changes. */
body > header .navbar > .container { min-height: 31px; }

@media (max-width: 767.98px) {
  /* Hide the "Secure checkout" / "Data protection" badges on phones — they
     crowd the brand at this width. The header keeps its height via the
     min-height above, not via this content. */
  .checkout-secure { display: none; }
}

/* "Add billing details" disclosure toggle — styled as a plain link. */
.checkout-billing__toggle {
  cursor: pointer;
  color: var(--primary-text, var(--primary));
  font-weight: 500;
  width: fit-content;
  list-style: none;
}
.checkout-billing__toggle::-webkit-details-marker { display: none; }
.checkout-billing__toggle::marker { content: ""; }

@media (min-width: 992px) {
  /* Order-summary column ~20px narrower; the form column absorbs the width. */
  .checkout-cols > .col-lg-5 { flex: 0 0 auto; width: calc(41.666667% - 20px); }
  .checkout-cols > .col-lg-7 { flex: 0 0 auto; width: calc(58.333333% + 20px); }
}

/* ─── Category tree (inherits from old styles) ─── */
:root {
  --category-border-color: rgba(0, 0, 0, 0.05);
  --category-active-color: var(--primary-text, var(--primary, #9f50df));
}

[data-theme="dark"] {
  --category-border-color: rgba(255, 255, 255, 0.08);
  --category-active-color: var(--primary-text, var(--primary, #9f50df));
}

.category-tree { font-size: 0.875rem; }

.category-tree-item { padding: 0.125rem 0; }

.category-tree-item + .category-tree-item {
  border-top: 1px solid var(--category-border-color);
}

.category-tree-item .category-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  color: inherit;
  padding: 0.25rem 0.5rem;
}

.category-tree-item .category-toggle:hover,
.category-tree-item .category-toggle:focus {
  text-decoration: none;
  color: var(--category-active-color);
}

.category-tree-item .category-toggle .chevron-icon {
  transition: transform 0.2s ease;
}

.category-tree-item .category-toggle[aria-expanded="true"] .chevron-icon {
  transform: rotate(180deg);
}

.category-tree-item .category-dot {
  /* Occupies the same 16px box as the chevron button it stands in for (a 14px
     glyph plus the button's 1px transparent border each side), with the dot
     centred inside it. It used to be a bare 6px box, so a leaf category's
     label sat 10px left of its siblings' and the dot itself 5px left of their
     chevrons. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
}
.category-tree-item .category-dot::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--category-border-color);
}

.category-tree-item a {
  color: inherit;
  display: inline-flex;
  align-items: center;
  padding: 0.25rem 0.5rem;
  /* Colour only: hovering a category used to also paint a rounded grey pill
     behind the label, which read as a button in a list that is not one. The
     colour change alone carries the affordance. */
  transition: color 0.15s;
}

.category-tree-item a:hover,
.category-tree-item a:focus {
  text-decoration: none;
  color: var(--category-active-color);
}

.category-tree-item a.active {
  color: var(--category-active-color);
  font-weight: 600;
}

.category-tree .collapse { transition: height 0.2s ease; }

/* ─── Shop loader ─── */
.shop-loader {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.8);
  z-index: 9999;
}

[data-theme="dark"] .shop-loader {
  /* Was the old blue page colour; now the neutral one. */
  background: rgba(22, 22, 22, 0.8);
}

/* ─── Toast notification ─── */
.shop-toast {
  position: fixed;
  bottom: 1.5rem;
  right: 1.5rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  padding: 0.75rem 1.25rem;
  box-shadow: 0 4px 12px rgba(0,0,0,.12);
  font-size: 0.875rem;
  font-weight: 500;
  z-index: 9999;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  animation: slideUp 0.3s ease, fadeOut 0.3s ease 2s forwards;
}

.shop-toast i { color: var(--primary-text, var(--primary)); }
.shop-toast.shop-toast-error i { color: #dc3545; }
.shop-toast.shop-toast-error { border-color: #dc3545; }

@keyframes slideUp {
  from { transform: translateY(1rem); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}

@keyframes fadeOut {
  to { opacity: 0; transform: translateY(-0.5rem); pointer-events: none; }
}

/* ─── Layout Toggle ─── */
/* One merged segmented control, not three separate buttons. */
.shop-layout-toggle {
  display: inline-flex;
  gap: 0;
}

.layout-btn {
  background: var(--card);
  /* Same edge as the .btn-sm controls beside it (see --btn-outline-border). */
  border: 1px solid var(--btn-outline-border, var(--border));
  color: var(--muted);
  /* Matched to the .btn-sm controls it sits beside (the filter button, and the
     header's icon buttons): a 31px box — 8px vertical padding + a 21px line
     box + 2px border. It used to be a 2rem square on --radius (0.625rem), so
     it read a size and a corner larger than everything around it. */
  width: 31px;
  height: 31px;
  /* Square by default; only the two OUTER edges of the group get rounded,
     below. */
  border-radius: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 0.15s ease;
  font-size: 0.8125rem;
  /* So the highlighted segment's border can sit above its neighbour's. */
  position: relative;
}
.layout-btn svg { width: 13px; height: 13px; display: block; }

/* Neighbours share a single 1px edge instead of stacking two. */
.layout-btn + .layout-btn { margin-left: -1px; }
/* Without this the shared edge is drawn by whichever segment comes later,
   so an active or hovered segment would lose its left border. */
.layout-btn:hover,
.layout-btn:focus-visible,
.layout-btn.active { z-index: 1; }

/* Outer corners. :first-child is no good here: the one-per-row segment is
   d-lg-none, and display:none still matches it structurally, so on desktop
   the rounding would land on an invisible button and the grid segment would
   come out square. Target the segments by name per breakpoint instead. */
.shop-layout-toggle > .layout-btn:last-child {
  border-top-right-radius: 0.5rem;
  border-bottom-right-radius: 0.5rem;
}
@media (max-width: 991.98px) {
  .shop-layout-toggle > .layout-btn[data-layout="single"] {
    border-top-left-radius: 0.5rem;
    border-bottom-left-radius: 0.5rem;
  }
}
@media (min-width: 992px) {
  .shop-layout-toggle > .layout-btn[data-layout="grid"] {
    border-top-left-radius: 0.5rem;
    border-bottom-left-radius: 0.5rem;
  }
}

.layout-btn:hover { color: var(--fg); border-color: var(--primary); }
.layout-btn.active { background: var(--primary); color: var(--primary-fg); border-color: var(--primary); }

/* Column count on phones. The markup is `col-6 col-lg-4`, i.e. two-up below
   lg. Phones now default to ONE product per row instead, and .layout-grid is
   how the shopper opts back into two. Doing it in CSS rather than in JS means
   the first paint is already one-up, so the grid never flashes two-up before
   the toggle initialises. Desktop is untouched: col-lg-4 still wins there, and
   the one-per-row button is d-lg-none. */
@media (max-width: 991.98px) {
  .product-grid:not(.layout-grid):not(.layout-list) > .product-grid-item {
    flex: 0 0 100%;
    max-width: 100%;
    width: 100%;
  }
  .product-grid.layout-grid > .product-grid-item {
    flex: 0 0 50%;
    max-width: 50%;
    width: 50%;
  }
}

/* List layout */
.product-grid.layout-list {
  flex-direction: column !important;
}

.product-grid.layout-list .product-grid-item {
  width: 100% !important;
  max-width: 100% !important;
  flex: 0 0 100% !important;
}

.product-grid.layout-list .product-card {
  flex-direction: row;
  align-items: stretch;
}

/* ── List view reads as a table ───────────────────────────────────────────
   Rows sit flush against each other and share a single hairline, so the
   collection reads as one block rather than 48 separate cards.

   The card's edge is normally an INSET SHADOW, which cannot be dropped on one
   side — two flush rows would show a 2px seam. A real border can, and the
   objection that forced the shadow (a full-bleed photo stopping 1px short)
   does not apply here: the list photo is inset 10px from the edge anyway. */
.product-grid.layout-list { --bs-gutter-y: 0; }

.product-grid.layout-list .product-card {
  box-shadow: none;
  border: 1px solid var(--border);
  border-radius: 0;
}
/* Each row after the first leans on its neighbour's bottom edge. */
.product-grid.layout-list > .product-grid-item + .product-grid-item .product-card {
  border-top: 0;
}
/* Only the outer corners of the whole block are rounded. */
.product-grid.layout-list > .product-grid-item:first-child .product-card {
  border-top-left-radius: 8px;
  border-top-right-radius: 8px;
}
.product-grid.layout-list > .product-grid-item:last-child .product-card {
  border-bottom-left-radius: 8px;
  border-bottom-right-radius: 8px;
}
/* The lift and drop-shadow belonged to a detached card; in a table they would
   tear a row out of the block and cast a shadow on its neighbours. A row tint
   is the table equivalent. Placed after the dark-mode hover rule so it wins on
   equal specificity. */
.product-grid.layout-list .product-card:hover,
[data-theme="dark"] .product-grid.layout-list .product-card:hover {
  transform: none;
  box-shadow: none;
  background: var(--hover, rgba(0, 0, 0, .03));
}

.product-grid.layout-list .product-card-img {
  width: 150px;
  min-width: 150px;
  /* Inset from the card edge and rounded on all four corners, so the photo
     reads as a tile inside the row rather than as a panel welded to its left
     edge. */
  margin: 10px;
  border-radius: 8px;
  /* Pin it to a square instead of letting align-items:stretch pull it to the
     row height. A stretched box puts its bottom edge well below the picture,
     which is the other way the badges ended up off-image. Top-aligned here:
     on a phone the text stack is much taller than the thumbnail, and centring
     leaves it floating mid-row instead of starting level with the name. */
  align-self: flex-start;
  aspect-ratio: 1;
}

.product-grid.layout-list .product-card-img img {
  border-radius: 8px;
  /* Fill the square. With contain the photo letterboxes inside its box, so a
     badge pinned to the box's bottom edge sits on empty backdrop rather than
     on the picture. A list thumbnail is small and square, so cropping to fill
     is the right trade here. */
  object-fit: cover;
}

/* The hairline that separates a white-background photo from the card is a
   full inset ring. In grid view its side edges land on the card edge and
   vanish; in a list row the right edge reads as a divider between the photo
   and the text. The photo is inset and rounded here, so it needs no ring. */
.product-grid.layout-list .product-card-img::after { display: none; }

/* Attribute tags in list view. This is the LOOK only — which breakpoints
   actually show them is decided separately, because desktop shows them always
   while phones show them only when there is vertical room (see below). Same
   tag shape as .price-vat-badge, so a card has one vocabulary for small
   qualifiers.

   Only the value is shown: in a row this narrow the "Marke:" half is mostly
   noise and the values alone still read. Yes/no attributes read as features
   instead of data — a "no" earns no tag at all, a "yes" becomes the thing
   itself ("✓ Bio", not "Bio: ✓ Ja"). Its label comes from the element's own
   attribute because the name span serves grid view, where it keeps its colon. */
.product-grid.layout-list .product-card-attrs {
  flex-wrap: wrap;
  gap: 4px;
}
.product-grid.layout-list .product-card-attr {
  padding: 1px 6px;
  border-radius: 6px;
  background: var(--hover, rgba(0, 0, 0, .05));
  border: 1px solid var(--btn-outline-border, var(--border));
  color: var(--muted);
  font-size: 0.6875rem;
  line-height: 1.5;
  white-space: nowrap;
}
.product-grid.layout-list .product-card-attr-name { display: none; }

/* The bottom-band controls are pinned to one height so a variant row and a
   normal row cannot differ. Left to content they land apart: add-to-cart takes
   its height from the quantity stepper beside it, while Select options sizes
   from its own padding. The stepper follows, since the action row stretches
   its items.

   The height depends on which add-to-cart markup the shop's quantity-field
   setting produced, so it is a token rather than a literal:

     quantity ON  -> .product-card-action-row, whose button is pinned to the
                     32px stepper next to it
     quantity OFF -> a bare .product-add-btn with no stepper to follow, which
                     sizes from its own padding to 35px

   Without this, turning the quantity field off left Select options 3px shorter
   than the add-to-cart button beside it. Every control reads the same token,
   so retuning either number moves all of them together. */
.product-grid.layout-list { --list-action-h: 32px; }
.product-grid.layout-list .product-card:has(.product-add-btn) { --list-action-h: 35px; }

.product-grid.layout-list .product-add-btn-icon,
.product-grid.layout-list .product-add-btn,
.product-grid.layout-list .product-card--has-variants .card-options-btn {
  min-height: var(--list-action-h);
}

/* The stepper is pinned too, and this is the part that actually matters.
   .qty-input carries a width and a font-size but no height, so its box is
   whatever the engine's default number input is — taller on WebKit than on
   Blink. The action row stretches its items, so on iPhone that pushed
   add-to-cart above 32px while Select options, sized by its own rule, stayed
   at 32 and read as the short one. Desktop never showed it because Blink
   happened to land on the same number. */
.product-grid.layout-list .qty-control {
  height: 32px;
}
.product-grid.layout-list .qty-control .qty-input {
  height: 100%;
  min-height: 0;
  padding-top: 0;
  padding-bottom: 0;
}
.product-grid.layout-list .product-card-attr[data-bool="no"] { display: none; }
.product-grid.layout-list .product-card-attr[data-bool="yes"] .product-card-attr-value { display: none; }
.product-grid.layout-list .product-card-attr[data-bool="yes"]::before {
  content: "✓ " attr(data-attr-name);
}

.product-grid.layout-list .product-card-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* The image already carries its own margin, so this side's padding doubled
     the gap to the text (10 + 14). Drop it and the text sits the same 10px
     off the photo as the photo sits off the card. */
  padding-left: 0;
}

.product-grid.layout-list .product-card-placeholder {
  border-radius: 8px;
}

/* List rows are short and wide: the short description and the attribute lines
   are the two blocks that force them tall, and they are the least scannable
   part of a row. Name, SKU and price carry the row. Both still render in grid
   view, and the owner's show_short_description / show_attributes settings
   still decide whether they exist at all. */
.product-grid.layout-list .product-card-short-desc,
.product-grid.layout-list .product-card-attrs {
  display: none;
}

@media (max-width: 575px) {
  .product-grid.layout-list .product-card-img {
    width: 92px;
    min-width: 92px;
    margin: 8px;
  }
}

/* ─── Product Pagination ─── */
.product-pagination { margin-top: 1.5rem; }
.product-pagination .pagination { gap: 0.25rem; }
.product-pagination .page-link {
  border-radius: 0.375rem !important;
  padding: 0.4rem 0.75rem !important;
  font-size: 0.875rem !important;
  min-width: 2.25rem;
  text-align: center;
  color: var(--primary) !important;
  background: transparent !important;
  border-color: var(--primary) !important;
}
.product-pagination .page-link:hover,
.product-pagination .page-link:focus {
  color: #fff !important;
  background: var(--primary) !important;
  border-color: var(--primary) !important;
}
.product-pagination .page-item.active .page-link {
  color: #fff !important;
  background: var(--primary) !important;
  border-color: var(--primary) !important;
}
.product-pagination .page-item.disabled .page-link {
  color: var(--muted) !important;
  background: transparent !important;
  border-color: var(--border) !important;
}
#loadMoreBtn { min-width: 200px; }

/* ─── Sticky category sidebar ─── */
@media (min-width: 992px) {
  .shop-sidebar-wrap {
    position: sticky;
    /* Anchor below the fixed header — uses the same `--navbar-height` as
       `main`'s margin-top, plus a 10px buffer so the sidebar sits slightly
       lower than the product grid for visual breathing room. */
    top: calc(var(--navbar-height, 64px) + var(--header-gap, 12px) + var(--announcement-bar-h, 0px));
    transition: top 0.22s ease;
    /* JS overrides top dynamically for tall sidebars */
  }

  /* The sticky sidebar used to anchor 20px below the header while the rest of
     the page cleared it exactly, so the row was padded 20px to keep both
     columns level. Both now anchor on --header-gap, so that compensation is
     obsolete: keeping it just pushed the whole catalog an extra 20px down. */
  .shop-catalog-row:first-child { padding-top: 0; }
}

:root {
  --bg: #f8f9fb;
  --fg: #111827;
  --muted: #6b7280;
  --primary: #9f50df;
  --primary-fg: #ffffff;
  --primary-text: #9f50df;
  /* Link colour fallback for a shop that hasn't picked an accent yet.
     layout.ejs only emits the --bs-link-* block when primary_color is a valid
     hex, so those shops kept Bootstrap's blue for every link while their
     buttons were already this purple -- most visible on the seeded "Browse our
     catalog" CTA on a new shop's home page. Values are #9f50df and the same
     80%-darkened hover layout.ejs computes, so an accent-less shop matches a
     shop whose owner picked this exact colour.

     All four, including the hover TRIPLET: Bootstrap 5.3 hovers links by
     reassigning --bs-link-color-rgb from --bs-link-hover-color-rgb, so a plain
     --bs-link-hover-color is never read and links turn blue on hover. Same
     trap documented in layout.ejs.

     app.css is linked before layout.ejs's <style>, and both target :root, so a
     real accent still wins on later-wins. */
  --bs-link-color: rgb(159,80,223);
  --bs-link-color-rgb: 159,80,223;
  --bs-link-hover-color: rgb(127,64,178);
  --bs-link-hover-color-rgb: 127,64,178;
  --switch-knob: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e");
  /* Checkbox tick + radio dot fallbacks (white mark on the default purple).
     Sit alongside --switch-knob so no-shop pages (signup/login) that don't get
     the per-shop theme <style> in layout.ejs still render the mark. The shop
     theme block overrides these with brand-contrast-adapted versions. */
  --check-mark: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");
  --radio-dot: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e");
  --primary-solid: #9f50df;
  --primary-2: #8155de;
  --primary-subtle: rgba(159,80,223,.08);
  --card: #ffffff;
  --border: #e5e7eb;
  --navbar-bg: rgba(255, 255, 255, 0.85);
  /* The SAME colour at full opacity. The header's "solid background" option
     must only remove the transparency, not change the hue -- reaching for
     --bg there tinted the bar a different colour instead. Keep these two in
     step: same rgb, alpha 1. */
  --navbar-bg-solid: rgb(255, 255, 255);
  --navbar-border: #e5e7eb;
  --sidebar-bg: #ffffff;
  --hover: #f3f4f6;
  --radius: 0.625rem;
  --shadow-sm: 0 1px 2px rgba(0,0,0,.04);
  --shadow: 0 1px 3px rgba(0,0,0,.06), 0 1px 2px rgba(0,0,0,.04);
}

[data-theme="dark"] {
  --bg: var(--az-bg);
  --fg: var(--az-text);
  --muted: var(--az-text-sub);
  /* Same purple as light mode. This used to be #b07ce8 — the brand purple
     pre-lightened by 15% — which washed the colour out and dropped the
     white label on it to 3.52:1, under the 4.5:1 AA floor. A shop that
     hasn't picked an accent yet lands here, so it was the first thing a new
     owner saw in dark mode. --primary-fg (:root) is #fff → 4.48:1, the same
     as light mode. Accents dark enough to sink into the page background are
     lifted server-side in layout.ejs, not here. */
  --primary: #9f50df;
  --primary-2: #ab65e3;
  --primary-subtle: rgba(159,80,223,.1);
  /* Same no-shop fallbacks as light :root (see there). White mark reads on the
     default purple in dark mode too. */
  --primary-solid: #9f50df;
  --switch-knob: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e");
  --check-mark: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");
  --radio-dot: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e");
  --card: var(--az-card);
  --border: var(--az-card-border);
  --navbar-bg: rgba(28, 28, 28, 0.85);
  /* Same colour, alpha 1 -- see the light-mode note above. */
  --navbar-bg-solid: rgb(28, 28, 28);
  --navbar-border: var(--az-topbar-border);
  --search-border: var(--az-card-border);
  --sidebar-bg: var(--az-sb-bg);
  --hover: var(--az-table-hover);
  --shadow-sm: 0 1px 2px rgba(0,0,0,.2);
  --shadow: 0 1px 3px rgba(0,0,0,.3), 0 1px 2px rgba(0,0,0,.2);
}

html, body {
  min-height: 100vh;
  margin: 0;
  padding: 0;
  color: var(--fg);
  background: var(--bg) !important;
  font-family: Inter, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji";
}

/* ─── Global card styling ─── */
.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
}

.card-header {
  background: var(--card);
  border-bottom: 1px solid var(--border);
  padding: 0.875rem 1.25rem;
}

.card-body { padding: 1.25rem; }

/* ─── Header ─── */
/* Storefront top header. Scoped to body > header so it can't hijack any
   stray <header> element used inside a page (e.g. an admin section title). */
body > header {
  position: fixed;
  /* Pushed below the announcement bar via a CSS variable set from JS once the
     bar height is measured. We subtract 1px so the header overlaps the bar's
     bottom edge — without it, sub-pixel rendering can produce a 1px gap that
     lets product images bleed through during scroll. max() clamps to 0 so the
     header doesn't drift above the viewport when the bar isn't present. */
  top: max(0px, calc(var(--announcement-bar-h, 0px) - 1px));
  left: 0; right: 0;
  z-index: 1030;
  background: var(--navbar-bg);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--navbar-border);
  transition: top 0.22s ease;
}
/* On touch devices, backdrop-filter breaks position:fixed during momentum
   scroll, URL-bar transitions, and keyboard events. Use solid background. */
@media (hover: none) and (pointer: coarse) {
  body > header {
    background: var(--bg);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
}

.navbar {
  background: transparent !important;
  padding: 0.625rem 0;
}

/* Breathing room between the fixed header and the content under it. Set to the
   .container gutter so the space below the bar reads the same as the space
   beside a product card. Kept SEPARATE from --navbar-height on purpose:
   --navbar-height is now measured at runtime (public/js/header-height.js) and
   means "how tall the bar actually is", nothing more. Baking the gap into that
   number is what made it drift every time the bar's height changed. */
:root { --header-gap: 12px; }

/* Clearing the fixed header uses PADDING, not margin. main's top margin
   collapsed straight out through body (body has no border, padding or BFC),
   so it moved the page instead of pushing the content down, and the first row
   of content ended up sitting behind the header. That is the recurring
   "wrong gap under the header" bug. Padding cannot collapse, so the offset
   always lands where it is supposed to.
   --navbar-height is the measured bar height (public/js/header-height.js);
   --header-gap is the breathing room below it. */
main {
  padding-top: calc(var(--navbar-height, 64px) + var(--announcement-bar-h, 0px) + var(--header-gap, 12px)) !important;
  margin-top: 0 !important;
  transition: padding-top 0.22s ease;
}

body main.container {
  padding-top: calc(var(--navbar-height, 64px) + var(--announcement-bar-h, 0px) + var(--header-gap, 12px)) !important;
  margin-top: 0 !important;
}

body main.container.my-4 {
  padding-top: calc(var(--navbar-height, 64px) + var(--announcement-bar-h, 0px) + var(--header-gap, 12px)) !important;
  margin-top: 0 !important;
  margin-bottom: 1.5rem !important;
}

/* Block-driven Home page: bottom of the fixed header sits flush
   against the first block's top edge. On mobile that's fine — the
   navbar is taller proportionally so there's already visual breathing
   room. On desktop the slim navbar makes the hero feel cramped, so
   add a small top padding at md+ to give the hero room to breathe.
   Scoped to .shop-home-blocks so it doesn't bleed into catalog /
   product / contact / collection / page views (each of those uses
   data-ajax-fragment too, but only home_blocks gets this class). */
@media (min-width: 768px) {
  .shop-home-blocks { padding-top: 0.65rem; }
}

/* ───────── Storefront announcement bar ─────────
   Pinned above the fixed header via a higher z-index. Two behaviours:
   --sticky stays put, --scrolls slides up after the visitor scrolls past it. */
.shop-announcement-bar {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 1040;
  padding: 9px 0;
  font-size: 14px;
  line-height: 1.45;
  letter-spacing: 0.01em;
  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.08);
  transition: transform 0.28s cubic-bezier(.4, 0, .2, 1);
  will-change: transform;
}
/* .container constrains the banner content to the page's main
   content width, matching the navbar / breadcrumbs / page content
   below. The close button is positioned relative to this container
   so it sits on the right edge of the content column, not the
   viewport. position:relative on the container is what makes that
   anchoring possible. */
.shop-announcement-bar__container {
  position: relative;
  padding-right: 44px;  /* reserve room for the close button */
  min-width: 0;
}
.shop-announcement-bar:not(.shop-announcement-bar--dismissible) .shop-announcement-bar__container {
  padding-right: 0;
}
.shop-announcement-bar__inner {
  min-width: 0;
}
/* Rich text inherits the bar's colour. Tighten paragraph margins
   so a single <p> doesn't add extra vertical space. */
.shop-announcement-bar__inner p { margin: 0; }
.shop-announcement-bar__inner h1,
.shop-announcement-bar__inner h2,
.shop-announcement-bar__inner h3,
.shop-announcement-bar__inner h4 { font-size: inherit; line-height: inherit; margin: 0; font-weight: 700; }
.shop-announcement-bar__inner a { color: inherit; }
.shop-announcement-bar__link {
  color: inherit;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: rgba(255, 255, 255, 0.5);
  transition: text-decoration-color 0.15s ease;
}
.shop-announcement-bar__link:hover {
  color: inherit;
  text-decoration-color: currentColor;
}
.shop-announcement-bar__close {
  position: absolute;
  right: 6px; top: 50%;
  transform: translateY(-50%);
  width: 32px; height: 32px;
  border: 0; padding: 0;
  background: transparent;
  color: inherit;
  font-size: 22px;
  line-height: 1;
  opacity: 0.65;
  cursor: pointer;
  border-radius: 6px;
  transition: opacity 0.15s ease, background 0.15s ease;
}
.shop-announcement-bar__close:hover {
  opacity: 1;
  background: rgba(255, 255, 255, 0.12);
}
.shop-announcement-bar--scrolls.is-scrolled-away {
  transform: translateY(-100%);
}
.shop-announcement-bar.is-dismissed {
  transform: translateY(-100%);
  pointer-events: none;
}
/* Admin shell never has a real announcement bar — force the offset to 0 so a
   stale --announcement-bar-h (e.g. from a prior storefront visit, or a misfire
   anywhere upstream) can't push the admin layout around. */
body:has(.admin-shell[data-admin-shell]) {
  --announcement-bar-h: 0px;
}

/* ─── Admin shell layout (legacy — overridden by .az-* block at bottom) ─── */

/* ─── Admin nav ─── */
.admin-nav { display: flex; flex-direction: column; gap: 0.25rem; }

.nav-section { padding: 0 0.75rem; margin-bottom: 0.5rem; }

.nav-section-label {
  font-size: 0.6875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted);
  padding: 0.5rem 0.75rem 0.25rem;
}

.nav-link-item {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.5rem 0.75rem;
  border-radius: 0.5rem;
  color: var(--fg);
  text-decoration: none;
  font-size: 0.875rem;
  font-weight: 450;
  transition: background 0.15s, color 0.15s;
}

.nav-link-item i {
  width: 1.125rem;
  text-align: center;
  font-size: 0.8125rem;
  color: var(--muted);
  transition: color 0.15s;
}

.nav-link-item:hover {
  background: var(--hover);
  color: var(--fg);
  text-decoration: none;
}

.nav-link-item.active {
  background: var(--primary-subtle);
  color: var(--primary-text, var(--primary));
  font-weight: 550;
}

.nav-link-item.active i { color: var(--primary-text, var(--primary)); }

/* ─── Stat cards ─── */
.stat-card .card-body { padding: 1rem 1.25rem; }

.stat-icon {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 0.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.9rem;
  flex-shrink: 0;
}

.bg-primary-subtle { background: var(--primary-subtle, rgba(159,80,223,.1)); color: var(--primary); }
.bg-success-subtle { background: rgba(16,185,129,.1); color: #10b981; }
.bg-warning-subtle { background: rgba(245,158,11,.1); color: #f59e0b; }
.bg-info-subtle    { background: rgba(59,130,246,.1); color: #3b82f6; }

[data-theme="dark"] .bg-primary-subtle { background: rgba(176,124,232,.12); }
[data-theme="dark"] .bg-success-subtle { background: rgba(16,185,129,.12); }
[data-theme="dark"] .bg-warning-subtle { background: rgba(245,158,11,.12); }
[data-theme="dark"] .bg-info-subtle    { background: rgba(59,130,246,.12); }

.stat-value { font-size: 1.25rem; font-weight: 700; line-height: 1.2; }
.stat-label { font-size: 0.75rem; color: var(--muted); margin-top: 0.125rem; }

/* ─── Badge subtle variants ─── */
.bg-success-subtle.text-success, .badge.bg-success-subtle { background: rgba(16,185,129,.12) !important; color: #10b981 !important; }
.bg-danger-subtle.text-danger, .badge.bg-danger-subtle  { background: rgba(239,68,68,.12) !important; color: #ef4444 !important; }
.bg-warning-subtle.text-warning, .badge.bg-warning-subtle { background: rgba(245,158,11,.12) !important; color: #f59e0b !important; }

/* ─── Tables ─── */
.table { color: var(--fg); }
.table th {
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  border-bottom-width: 1px;
  border-color: var(--border);
  padding: 0.75rem;
}
.table td { padding: 0.75rem; border-color: var(--border); vertical-align: middle; }
.table-hover > tbody > tr { transition: background 0.15s; }
.table-hover > tbody > tr:hover > td { background: var(--hover) !important; }

[data-theme="dark"] .table td,
[data-theme="dark"] .table th {
  background: var(--card) !important;
  color: var(--fg) !important;
  border-color: var(--border) !important;
}

[data-theme="dark"] .table thead th {
  background: var(--card) !important;
  color: var(--muted) !important;
}

[data-theme="dark"] .table-hover > tbody > tr:hover > td {
  background: var(--hover) !important;
}

[data-theme="dark"] .table-light th {
  background: var(--card) !important;
  color: var(--muted) !important;
}

/* ─── Buttons ─── */
.btn { border-radius: 0.5rem; font-weight: 500; font-size: 0.875rem; }

.btn-primary {
  background: linear-gradient(135deg, var(--primary), var(--primary-2));
  border: 1px solid var(--primary);
  color: var(--primary-fg);
  box-shadow: inset 0 0 0 1px rgba(0,0,0,0.06);
}
.btn-primary:hover { background: linear-gradient(135deg, var(--primary-2), var(--primary-2)) !important; border-color: var(--primary-2) !important; color: var(--primary-fg, #fff) !important; }
.btn-primary:disabled, .btn-primary.disabled {
  background: linear-gradient(135deg, var(--primary), var(--primary-2));
  border-color: var(--primary);
  color: var(--primary-fg);
  opacity: .65;
}
.btn-primary:focus, .btn-primary:active, .btn-primary:focus-visible {
  border-color: var(--primary-2) !important;
  background: linear-gradient(135deg, var(--primary), var(--primary-2)) !important;
  /* Focus halo uses the shop's brand color (from --primary-shadow set in
     layout.ejs theme tokens) — no more universal purple glow on every
     button click. Falls back to the legacy purple if the page didn't
     emit the theme tokens for some reason. */
  box-shadow: 0 0 0 0.2rem var(--primary-shadow, rgba(159,80,223,.25)) !important;
  outline: none !important;
}

.btn-outline-primary {
  color: var(--primary-text, var(--primary)) !important;
  border-color: var(--primary) !important;
}
.btn-outline-primary:hover {
  color: var(--primary-fg, #fff) !important;
  border-color: var(--primary) !important;
  background: var(--primary) !important;
}

/* Storefront controls take their resting border from the dashboard's header
   buttons (.az-topbar-btn: 1px #dedddb in light). Bootstrap's outline-secondary
   resolves currentColor to #6c757d, far heavier than anything in the dashboard.
   Driven by a variable rather than a new rule so the existing hover / active /
   dark-mode rules keep winning on specificity exactly as before, and so admin
   pages (variable unset) keep Bootstrap's stronger outline. */
.btn-outline-secondary { border-color: var(--btn-outline-border, currentColor) !important; }
body.is-shop-front { --btn-outline-border: #dedddb; }
/* Dark keeps the value shipped earlier: --border, i.e. the dashboard's card
   border. Must be theme-scoped or the light hex would leak into dark. */
[data-theme="dark"] body.is-shop-front { --btn-outline-border: var(--az-control-border); }
/* Filter inputs are controls too, so they take the control edge rather than
   the panel edge. Scoped to the storefront: admin content fields keep theirs,
   which is what the dashboard uses below its topbar. */
[data-theme="dark"] body.is-shop-front .form-control,
[data-theme="dark"] body.is-shop-front .form-select,
[data-theme="dark"] body.is-shop-front textarea.form-control {
  border-color: var(--btn-outline-border, var(--border)) !important;
}
/* Category separators (and the leaf dot, which shares the token) matched the
   panel edge as a translucent white. Same control edge, so the filter column
   reads at one weight. Storefront only — the admin categories page uses this
   same tree. */
[data-theme="dark"] body.is-shop-front { --category-border-color: var(--az-control-border); }
/* Filled states (hover / active / open dropdown): the outline uses
   currentColor, which is WHITE once the button fills and its text goes white
   — a stray white ring. Match the border to the grey fill instead (the fill
   already signals the state). Dark mode keeps its own translucent border. */
.btn-outline-secondary:hover,
.btn-outline-secondary:active,
.btn-outline-secondary.active,
.btn-outline-secondary.show,
.btn-check:checked + .btn-outline-secondary {
  border-color: #6c757d !important;
}
/* Borders come from the dashboard palette, not a translucent white. The old
   rgba(229,231,235,.28) was the pre-neutral blue-grey at 28% over the page,
   landing around #505153 — a bright ring next to the dashboard's own #2a2a2a
   card borders. Resting now matches --border exactly; hover/active step up to
   the dashboard's input border so the state is still readable. */
[data-theme="dark"] .btn-outline-secondary:active,
[data-theme="dark"] .btn-outline-secondary.active,
[data-theme="dark"] .btn-outline-secondary.show,
[data-theme="dark"] .btn-check:checked + .btn-outline-secondary {
  border-color: var(--btn-outline-border, var(--az-topbar-input-border, #333)) !important;
}
[data-theme="dark"] .btn-outline-secondary {
  color: var(--fg) !important;
  /* Was pinned to --border, i.e. the dashboard's CARD edge, which left shop
     buttons a step fainter than the dashboard's own buttons. Those use the
     control edge. Admin leaves the token unset and keeps the card edge. */
  border-color: var(--btn-outline-border, var(--border)) !important;
}
[data-theme="dark"] .btn-outline-secondary:hover {
  background: var(--hover) !important;
  border-color: var(--btn-outline-border, var(--az-topbar-input-border, #333)) !important;
}

/* ─── Forms ─── */
.form-control, .form-select {
  border-radius: 0.5rem;
  border-color: var(--border);
  font-size: 0.875rem;
  padding: 0.5rem 0.75rem;
}

.form-check-input[type="checkbox"]:checked {
  background-color: var(--primary-solid, var(--primary)) !important;
  border-color: var(--primary-solid, var(--primary)) !important;
  background-image: var(--check-mark) !important;
}
.form-check-input[type="radio"]:checked {
  background-color: var(--primary-solid, var(--primary)) !important;
  border-color: var(--primary-solid, var(--primary)) !important;
  background-image: var(--radio-dot) !important;
}
/* Remove Bootstrap's hardcoded blue focus knob on unchecked switches */
.form-switch .form-check-input:focus:not(:checked) {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280%2c0%2c0%2c0.25%29'/%3e%3c/svg%3e") !important;
}
/* Track uses the solid accent; the knob SVG already adapts to accent brightness */
.form-switch .form-check-input:checked {
  background-color: var(--primary-solid, var(--primary)) !important;
  background-image: var(--switch-knob) !important;
  border-color: var(--primary-solid, var(--primary)) !important;
  box-shadow: 0 0 0 1px rgba(0,0,0,0.1) !important;
}

.form-control:focus, .form-select:focus,
.form-control:focus-visible, .form-select:focus-visible,
.form-check-input:focus, .form-check-input:focus-visible {
  border-color: var(--primary) !important;
  box-shadow: none !important;
  outline: 0 !important;
}

.form-label { font-weight: 500; font-size: 0.875rem; margin-bottom: 0.375rem; }

[data-theme="dark"] .form-control {
  background-color: var(--card) !important;
  border-color: var(--border) !important;
  color: var(--fg) !important;
}
[data-theme="dark"] .form-select {
  background-color: var(--card) !important;
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ccc' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e") !important;
  border-color: var(--border) !important;
  color: var(--fg) !important;
}
[data-theme="dark"] textarea.form-control {
  background: var(--card) !important;
  border-color: var(--border) !important;
  color: var(--fg) !important;
}

[data-theme="dark"] .input-group-text {
  background-color: var(--hover) !important;
  border-color: var(--border) !important;
  color: var(--muted) !important;
}
[data-theme="dark"] .shop-search-panel .input-group-text {
  background-color: var(--card) !important;
  border-color: var(--btn-outline-border, var(--search-border, #6c757d)) !important;
}
[data-theme="dark"] .form-control:focus,
[data-theme="dark"] .form-select:focus,
[data-theme="dark"] textarea.form-control:focus {
  border-color: var(--primary) !important;
  box-shadow: none !important;
}

[data-theme="dark"] .form-label { color: var(--fg) !important; }
[data-theme="dark"] .form-text { color: var(--muted) !important; }

/* ─── Alerts ─── */
[data-theme="dark"] .alert { background: var(--card) !important; border-color: var(--border) !important; color: var(--fg) !important; }
[data-theme="dark"] .alert-success { background: rgba(34,197,94,.1) !important; border-color: rgba(34,197,94,.25) !important; color: #10b981 !important; }
[data-theme="dark"] .alert-danger  { background: rgba(239,68,68,.1) !important; border-color: rgba(239,68,68,.25) !important; color: #f87171 !important; }
[data-theme="dark"] .alert-warning { background: rgba(245,158,11,.1) !important; border-color: rgba(245,158,11,.25) !important; color: #fbbf24 !important; }
[data-theme="dark"] .alert-info    { background: rgba(59,130,246,.1) !important; border-color: rgba(59,130,246,.25) !important; color: #60a5fa !important; }

/* ─── text-primary utility: always use the page-readable version ─── */
.text-primary { color: var(--primary-text, var(--primary)) !important; }

/* ─── Badges ─── */
/* The sale % bubble. It was inline-styled identically in five places, so the
   look lives here instead and they stay in step. No ring around it: the solid
   accent fill already separates it from the card. */
.badge.badge-primary {
  background: var(--primary, #0d6efd);
  color: var(--primary-fg, #fff) !important;
  font-size: 11px;
  padding: 3px 5px;
  vertical-align: middle;
}
/* The product page shows it beside a much larger price, so it scales up. */
.product-detail-price-row .badge.badge-primary {
  font-size: 12px;
  padding: 4px 6px;
  align-self: center;
}
[data-theme="dark"] .badge { background: var(--border) !important; color: var(--fg) !important; }
/* …but the cart-count bubble is an accent, not a neutral status pill. The ID
   selector outranks the generic `.badge` rule above so it keeps the accent in
   dark mode (raw-ish fill + intelligent number), matching light mode. */
[data-theme="dark"] #headerCartBadge { background: var(--primary-solid, var(--primary)) !important; color: var(--primary-fg, var(--bg, #161616)) !important; }
[data-theme="dark"] .badge.bg-primary { background: var(--primary) !important; color: var(--primary-fg) !important; }
[data-theme="dark"] .badge.badge-primary { background: var(--primary) !important; color: var(--primary-fg, #fff) !important; }
[data-theme="dark"] .badge.bg-success { background: #10b981 !important; color: #fff !important; }
[data-theme="dark"] .badge.bg-danger  { background: #f87171 !important; color: #fff !important; }
[data-theme="dark"] .badge.bg-warning { background: #fbbf24 !important; color: #000 !important; }

/* ─── Dark-mode card/misc ─── */
[data-theme="dark"] .card { background: var(--card) !important; border-color: var(--border) !important; color: var(--fg) !important; }
[data-theme="dark"] .card-header { background: var(--card) !important; border-bottom-color: var(--border) !important; }
[data-theme="dark"] .card-body { background: var(--card) !important; color: var(--fg) !important; }
[data-theme="dark"] .text-muted { color: var(--muted) !important; }
[data-theme="dark"] .bg-light, [data-theme="dark"] .bg-white { background: var(--card) !important; }
[data-theme="dark"] .border { border-color: var(--border) !important; }
[data-theme="dark"] .border-top { border-top-color: var(--border) !important; }
[data-theme="dark"] .border-bottom { border-bottom-color: var(--border) !important; }

/* ─── Modals ─── */
[data-theme="dark"] .modal-content { background: var(--card) !important; border-color: var(--border) !important; color: var(--fg) !important; }
[data-theme="dark"] .modal-header { background: var(--card) !important; border-bottom-color: var(--border) !important; }
[data-theme="dark"] .modal-footer { background: var(--card) !important; border-top-color: var(--border) !important; }
[data-theme="dark"] .btn-close { filter: invert(1) grayscale(100%) brightness(200%); }

/* The unit-delete confirmation modal lives inside #modal-custom-units
   which is appended to <body>, outside .admin-shell — so the global
   --card/--border tokens resolve to the storefront palette. Pull the
   admin --az-* tokens directly so the colors match the product-delete
   confirmation modal in both light and dark mode. */
#deleteUnitModal .modal-content,
#teamDeleteConfirmModal .modal-content { background: var(--az-card) !important; border-color: var(--az-card-border) !important; color: var(--az-text) !important; }
#deleteUnitModal .modal-header,
#teamDeleteConfirmModal .modal-header { background: var(--az-card) !important; border-bottom-color: var(--az-card-border) !important; }
#deleteUnitModal .modal-footer,
#teamDeleteConfirmModal .modal-footer { background: var(--az-card) !important; border-top-color: var(--az-card-border) !important; }

/* ─── Dropdowns ─── */
.dropdown-menu { background: var(--card) !important; border-color: var(--border) !important; border-radius: var(--radius); box-shadow: var(--shadow); }
.dropdown-item { color: var(--fg) !important; font-size: 0.875rem; }
.dropdown-item:hover, .dropdown-item:focus { background: var(--primary-text, var(--primary)) !important; color: var(--bg, #f8f9fb) !important; box-shadow: inset 0 0 0 1px rgba(0,0,0,0.06) !important; }
.dropdown-item.active, .dropdown-item:active { background: var(--primary, #6366f1) !important; color: var(--primary-fg, #fff) !important; box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08) !important; }
.dropdown-divider { border-color: var(--border) !important; }

/* ─── Clickable table rows ─── */
.table tbody tr[onclick], .table tbody tr[style*="cursor"] { cursor: pointer; transition: background 0.15s; }

/* ─── AJAX content ─── */
#main-content { background: transparent !important; min-height: 100vh; }
main.container { background: transparent !important; }

/* ─── Rich Text Editor ─── */
.rte-quill-root .rte-quill-toolbar { border: 0 !important; padding: 0 !important; background: transparent !important; }
.rte-quill-root .ql-container.ql-snow { border-color: var(--border) !important; border-radius: 0.5rem !important; background: var(--card) !important; color: var(--fg) !important; }
.rte-quill-root .ql-editor { min-height: 140px; color: var(--fg) !important; }
.rte-quill-root .ql-editor a { color: var(--primary) !important; }
.rte-quill-root textarea.rte-code-view { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; min-height: 140px; }
.rte-quill-root .ql-toolbar select.ql-header,
.rte-quill-root .ql-toolbar select.ql-color { background: var(--card) !important; color: var(--fg) !important; border-color: var(--border) !important; }
.rte-quill-root .ql-toolbar.ql-snow .ql-picker-label,
.rte-quill-root .ql-toolbar.ql-snow .ql-picker-item { color: var(--fg) !important; }
.rte-quill-root .ql-toolbar.ql-snow .ql-picker-label { border-color: var(--border) !important; background: var(--card) !important; }
.rte-quill-root .ql-toolbar.ql-snow .ql-picker-options { border-color: var(--border) !important; background: var(--card) !important; }

/* ─── Custom rich text editor (replaces Quill) ─── */
.rte-root { display: flex; flex-direction: column; gap: 6px; }
.rte-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.rte-toolbar .rte-heading { width: auto; min-width: 95px; }
.rte-editor.form-control {
  min-height: 92px;
  height: auto;
  /* No `white-space: pre-wrap` here: the editor stores HTML, and load /
     paste paths already convert literal "\n" to <br> (richtext.js).
     pre-wrap would additionally render the indentation newlines that
     legalService templates use for readability (e.g. between <br> and
     the next field line in the imprint) as visible blank lines, so
     editor preview didn't match the storefront render. Default
     `normal` collapses that whitespace, matching the final render. */
  word-wrap: break-word;
  cursor: text;
  /* User-resizable: drag the bottom-right corner to grow the field.
     resize:vertical requires overflow to not be `visible` — `auto`
     keeps scrollbars only when content actually overflows. */
  resize: vertical;
  overflow: auto;
}
/* Detailed description rich-text editor (product form): 6 lines tall by
   default and user-resizable via the bottom-right drag handle. The
   resize handle requires `overflow` to be other than `visible`, so we
   use `auto` — content scrolls internally when it exceeds the height
   (or the user can drag the handle taller). */
.product-section[data-section-key="detailed_description"] .rte-editor.form-control {
  height: 156px;
  min-height: 156px;
  resize: vertical;
  overflow: auto;
}
.rte-editor a { color: var(--primary); }
.rte-editor h1, .rte-editor h2, .rte-editor h3 { margin: 0.4em 0; line-height: 1.25; }
.rte-editor h1 { font-size: 1.5rem; font-weight: 600; }
.rte-editor h2 { font-size: 1.25rem; font-weight: 600; }
.rte-editor h3 { font-size: 1.1rem; font-weight: 600; }
.rte-editor ul, .rte-editor ol { padding-left: 1.5rem; margin: 0.4em 0; }
.rte-editor p { margin: 0.4em 0; }
.rte-editor p:first-child, .rte-editor h1:first-child, .rte-editor h2:first-child, .rte-editor h3:first-child { margin-top: 0; }
.rte-editor p:last-child, .rte-editor ul:last-child, .rte-editor ol:last-child { margin-bottom: 0; }
.rte-source.form-control {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 0.875rem;
  min-height: 92px;
}

/* Tables inserted by the rich-text editor. Same classes apply in the
   editor preview (admin) and on the storefront (shop block render),
   since both load this stylesheet.
   `table-layout: fixed` is required so the percentage widths on
   <col> elements actually shape the columns — without it the browser
   computes widths from content and ignores <col style="width:33%">. */
.rte-table { border-collapse: collapse; width: 100%; margin: 0.5em 0; table-layout: fixed; }
.rte-table td, .rte-table th { padding: 6px 10px; vertical-align: top; word-wrap: break-word; }
.rte-table--bordered, .rte-table--bordered td, .rte-table--bordered th { border: 1px solid var(--border, #dee2e6); }
.rte-table--zebra tbody tr:nth-child(even) { background: var(--bg-soft, rgba(0,0,0,0.04)); }
.rte-table--bg tbody tr,
.rte-table--bg thead tr { background: var(--rte-table-bg, transparent); }
[data-theme="dark"] .rte-table--zebra tbody tr:nth-child(even) { background: rgba(255,255,255,0.05); }

/* Editor-only scaffolding for tables — a faint dashed outline around
   every cell so the owner can SEE the column boundaries (and find the
   right edge to drag for resizing) even when the visible border
   option is off. `outline` doesn't participate in box-sizing, so it
   neither shifts layout nor competes with the real border when
   .rte-table--bordered is set. None of this leaks to the storefront
   because .rte-editor only exists in the admin contenteditable mount. */
.rte-editor .rte-table td,
.rte-editor .rte-table th {
  outline: 1px dashed rgba(108, 117, 125, 0.35);
  outline-offset: -1px;
}
[data-theme="dark"] .rte-editor .rte-table td,
[data-theme="dark"] .rte-editor .rte-table th {
  outline-color: rgba(255, 255, 255, 0.22);
}

/* ─── Landing page ─── */
.shadow-glow { box-shadow: 0 10px 30px var(--primary-glow, rgba(159,80,223,.35)); }

/* Landing page: prevent horizontal scroll from full-bleed effects.
   Clip only at html level (viewport edge) so box-shadows on centered
   elements (.landing-btn-primary glow, .landing-cta) can flow past the
   container without being cut off at its edges. */
html { overflow-x: clip; }
body.is-landing-page { overflow-x: visible; max-width: none; }
body.is-landing-page main { position: relative; z-index: 1; overflow-x: visible; }
/* On mobile, re-clip at body level so absolutely-positioned hero cards
   (and any stray wide content) never create a horizontal scrollbar. */
@media (max-width: 768px) {
  body.is-landing-page,
  body.is-landing-page main { overflow-x: clip; }
}

/* Full-bleed: breaks out of .container to span viewport edge-to-edge */
.landing-full-bleed {
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  position: relative;
}

.landing-section { position: relative; }

/* ── Ambient page background: single tilted multi-color gradient + dot pattern ── */
.landing-bg {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
  color: var(--primary);
}
.landing-gradient {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(135deg,
      rgba(124,58,237,0.14) 0%,
      rgba(159,80,223,0.12) 22%,
      rgba(210,90,190,0.11) 48%,
      rgba(240,130,130,0.10) 72%,
      rgba(250,190,120,0.11) 100%
    );
  /* NO background-attachment: fixed here. The element already fills the
     position:fixed .landing-bg, so attachment added nothing visually — but
     ANY background-attachment:fixed forces Chrome onto main-thread scrolling
     for the whole page (choppy scroll). Light mode was choppy while dark was
     smooth precisely because the dark override below uses the background
     shorthand, which resets attachment to scroll. (Diagnosed 2026-07-21.) */
}
[data-theme="dark"] .landing-gradient {
  background:
    linear-gradient(135deg,
      rgba(124,58,237,0.20) 0%,
      rgba(159,80,223,0.16) 28%,
      rgba(210,90,190,0.13) 55%,
      rgba(240,130,130,0.11) 80%,
      rgba(250,190,120,0.10) 100%
    );
}
.landing-grid-pattern {
  position: absolute;
  inset: 0;
  color: #4a2a7a;
  opacity: 0.14;
  mix-blend-mode: multiply;
}
[data-theme="dark"] .landing-grid-pattern {
  color: #d9c6f5;
  opacity: 0.12;
  mix-blend-mode: screen;
}

/* ── Eyebrow ── */
.landing-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  padding: .4rem .9rem;
  border-radius: 999px;
  background: var(--primary-subtle);
  color: var(--primary);
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  border: 1px solid rgba(159,80,223,.2);
  backdrop-filter: blur(6px);
  /* Don't let the copy's flex layout stretch the pill to full width */
  align-self: flex-start;
}
.landing-eyebrow-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--primary);
  box-shadow: 0 0 0 4px rgba(159,80,223,.2);
  animation: landingDotPulse 2.4s ease-in-out infinite;
}
@keyframes landingDotPulse {
  0%,100% { box-shadow: 0 0 0 0 rgba(159,80,223,.4); }
  50% { box-shadow: 0 0 0 6px rgba(159,80,223,0); }
}

/* ── Hero ── */
.landing-hero {
  padding: 4rem 0 5rem;
}

.landing-hero-grid {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 1.1fr .9fr;
  gap: 3rem;
  align-items: center;
}
/* Desktop: visually center the copy against the demo. The box keeps its
   constant min-height (2-line rotator cushion, absorbed upward without
   moving the subtitle/buttons); this bottom padding splits the reserve
   space evenly above and below the content instead of all-above. */
@media (min-width: 992px) {
  .landing-hero-copy { padding-bottom: 52px; }
}
.landing-hero-copy {
  max-width: 720px;
  /* Bottom-anchor the content so when the rotator grows to 2 lines the
     eyebrow + static title shift upward, and the subtitle + buttons stay
     put. min-height reserves enough room for the tallest (2-line) case
     in any supported language — extra cushion since the subtitle can
     wrap on wider h1 lines. */
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  min-height: 412px;
}

.landing-hero-title {
  font-size: clamp(2.125rem, 4.7vw, 3.5rem);
  line-height: 1.08;
  letter-spacing: -0.025em;
  margin-top: 1rem;
}
.landing-hero-title-static {
  background: linear-gradient(135deg, var(--fg) 0%, var(--primary) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

/* Rotating text (JS-driven) — same size as static line, inherits line-height */
.landing-hero-title-static,
.landing-hero-title-rotator-line { display: block; }
.landing-hero-title-rotator-line {
  font-size: 1em;
  line-height: inherit;
  min-height: 1.1em;
}
.landing-rotator {
  display: inline-block;
  background: linear-gradient(120deg, var(--primary) 0%, #e066b5 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  transition: opacity .26s ease, transform .26s ease;
  will-change: opacity, transform;
  /* Tight line-height + inline-block + background-clip:text can clip
     descenders (g, y, j). Give the box a few px of bottom room without
     changing the surrounding layout. */
  padding-bottom: 0.14em;
  margin-bottom: -0.14em;
}
.landing-rotator.landing-rotator--out {
  opacity: 0;
  transform: translateY(-14px);
}

.landing-hero-subtitle {
  color: var(--muted);
  /* Same size as the section leads (.lp2-lead) */
  font-size: 15.5px;
  line-height: 1.5;
  max-width: 540px;
}

/* Hero primary CTA: animated gradient + pulsing glow */
.landing-btn-primary {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: none;
  border-radius: 12px;
  color: #fff;
  font-weight: 700;
  overflow: hidden;
  isolation: isolate;
  background: linear-gradient(120deg, #7c3aed 0%, #9f50df 25%, #e066b5 50%, #9f50df 75%, #7c3aed 100%);
  /* 200% size + one full tile of travel per cycle = seamless loop
     (the gradient's ends match, so the wrap point is invisible). */
  background-size: 200% 100%;
  /* Sliding gradient + breathing glow (smooth in-out, no ring pulse) */
  animation: landingBtnGradient 9s linear infinite, landingBtnGlow 3.6s ease-in-out infinite;
  box-shadow: 0 0 24px rgba(159,80,223,.35);
  transition: transform .2s ease, box-shadow .3s ease;
}
.landing-btn-primary-inner {
  display: inline-flex;
  align-items: center;
  padding: .75rem 1.75rem;
  position: relative;
  z-index: 1;
}
.landing-btn-primary::after {
  content: '';
  position: absolute;
  inset: 0;
  /* Subtle sweep: narrow, faint band on a slower cadence */
  background: linear-gradient(115deg, transparent 38%, rgba(255,255,255,.18) 50%, transparent 62%);
  transform: translateX(-130%);
  animation: landingBtnSheen 5.5s ease-in-out infinite;
  pointer-events: none;
  z-index: 0;
}
.landing-btn-primary:hover,
.landing-btn-primary:focus {
  color: #fff;
  transform: translateY(-2px);
  /* Drop the glow animation on hover so the stronger static glow can win
     (a running box-shadow animation would override the hover shadow). */
  animation: landingBtnGradient 9s linear infinite;
  box-shadow: 0 0 44px rgba(159,80,223,.6), 0 0 0 6px rgba(159,80,223,.18);
}
.landing-btn-primary:active { transform: translateY(0); }
/* Nudging arrow: a gentle double-tap toward the end of each cycle;
   on hover it holds a small offset instead. */
.landing-btn-primary .fa-arrow-right { animation: landingBtnArrow 4.5s ease-in-out infinite; }
.landing-btn-primary:hover .fa-arrow-right,
.landing-btn-primary:focus .fa-arrow-right {
  animation: none;
  transform: translateX(3px);
  transition: transform .2s ease;
}
@keyframes landingBtnArrow {
  0%, 78%, 100% { transform: translateX(0); }
  84% { transform: translateX(4px); }
  90% { transform: translateX(0); }
  95% { transform: translateX(2px); }
}
/* Position 200% -> 0% slides the image RIGHT, so the color bands flow
   left-to-right (matching the sheen direction). */
@keyframes landingBtnGradient {
  0% { background-position: 200% 50%; }
  100% { background-position: 0% 50%; }
}
/* Zero offset = the neon breath radiates evenly around the button. */
@keyframes landingBtnGlow {
  0%,100% { box-shadow: 0 0 24px rgba(159,80,223,.35); }
  50%     { box-shadow: 0 0 42px 4px rgba(159,80,223,.55); }
}
@keyframes landingBtnSheen {
  0%, 62%  { transform: translateX(-130%); }
  78%      { transform: translateX(130%); }
  100%     { transform: translateX(130%); }
}

/* Hero secondary CTA: minimal see-through gray/white */
.landing-btn-ghost {
  padding: .75rem 1.75rem;
  border-radius: 12px;
  font-weight: 600;
  color: var(--fg);
  background: rgba(255,255,255,.55);
  border: 1px solid rgba(16,24,40,.1);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  transition: background .2s ease, border-color .2s ease, transform .2s ease;
}
.landing-btn-ghost:hover,
.landing-btn-ghost:focus {
  background: rgba(255,255,255,.8);
  border-color: rgba(159,80,223,.35);
  color: var(--fg);
  transform: translateY(-2px);
}
[data-theme="dark"] .landing-btn-ghost {
  background: rgba(255,255,255,.06);
  border-color: rgba(255,255,255,.14);
  color: var(--fg);
}
[data-theme="dark"] .landing-btn-ghost:hover,
[data-theme="dark"] .landing-btn-ghost:focus {
  background: rgba(255,255,255,.12);
  border-color: rgba(159,80,223,.45);
}

/* Hero visual — big logo behind 3 stacked cards */
.landing-hero-media {
  position: relative;
  min-height: 460px;
}
.landing-hero-visual {
  position: relative;
  width: 100%;
  height: 100%;
  min-height: 460px;
}
.landing-hero-logo {
  position: absolute;
  top: 50%;
  left: 62%;
  width: 500px;
  max-width: 120%;
  transform: translate(-50%, -50%);
  opacity: .13;
  z-index: 0;
  filter: drop-shadow(0 20px 40px rgba(159,80,223,.3));
  animation: landingLogoFloat 9s ease-in-out infinite;
}
[data-theme="dark"] .landing-hero-logo { opacity: .18; }
@keyframes landingLogoFloat {
  0%,100% { transform: translate(-50%, -50%) rotate(0deg); }
  50% { transform: translate(-50%, calc(-50% - 12px)) rotate(-2deg); }
}

.landing-hero-card {
  position: absolute;
  width: 210px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 18px;
  overflow: hidden;
  box-shadow: 0 25px 60px rgba(16,24,40,.14), 0 6px 16px rgba(16,24,40,.08);
  transition: transform .3s ease;
  z-index: 2;
}
.landing-hero-card--1 {
  top: 10px; right: 90px; z-index: 4;
  transform: rotate(-5deg);
  animation: landingCardFloat1 7s ease-in-out infinite;
}
.landing-hero-card--2 {
  top: 140px; right: -10px; z-index: 3;
  transform: rotate(5deg);
  animation: landingCardFloat2 8s ease-in-out infinite;
}
.landing-hero-card--3 {
  top: 270px; right: 150px; z-index: 2;
  transform: rotate(-2deg);
  animation: landingCardFloat3 9s ease-in-out infinite;
}
@keyframes landingCardFloat1 {
  0%,100% { transform: rotate(-5deg) translateY(0); }
  50% { transform: rotate(-5deg) translateY(-10px); }
}
@keyframes landingCardFloat2 {
  0%,100% { transform: rotate(5deg) translateY(0); }
  50% { transform: rotate(5deg) translateY(10px); }
}
@keyframes landingCardFloat3 {
  0%,100% { transform: rotate(-2deg) translateY(0); }
  50% { transform: rotate(-2deg) translateY(-8px); }
}
.landing-hero-card-chip {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 2;
  padding: .25rem .6rem;
  border-radius: 999px;
  background: rgba(255,255,255,.95);
  color: #b8860b;
  font-size: .7rem;
  font-weight: 700;
  box-shadow: 0 2px 6px rgba(0,0,0,.12);
}
[data-theme="dark"] .landing-hero-card-chip { background: rgba(20,26,42,.95); color: #daa520; }
.landing-hero-card-img {
  height: 140px;
  background-color: var(--hover);
  background-size: cover;
  background-position: center;
}
.landing-hero-card-body { padding: .75rem .9rem; }
.landing-hero-card-title { font-size: .85rem; font-weight: 600; color: var(--fg); margin-bottom: .2rem; }
.landing-hero-card-price { font-size: .9rem; color: var(--primary); }
.landing-hero-card-pill { font-size: .75rem; color: var(--muted); }
.landing-hero-card-pill strong { color: var(--primary); margin-left: .25rem; }

/* ── Hero product tour — faithful Shopiza recreation (DOM screencast) ──
   A fake cursor drives a near-exact replica of the real Shopiza Admin
   (Settings: upload logo + pick brand colour → Products table → Create
   Product modal) then a Storefront window slides up (catalog with sale
   badges & variant dropdowns → add to cart → dark mode → product page with
   SKU/EAN/variants/reviews) → back to Admin → Analytics, and loops.
   Fixed 900×700 design, scaled to fit the hero's right column. No libraries.
   --tour-accent = the shop's brand colour; it live-recolours everything, as
   the real --primary does. Scoped to .tour-* + views/partials/_landing_tour.ejs */
/* iOS-safe scale-to-fit: .tour-fit is a NORMAL (untransformed) element sized to
   the visible area (column width × scaled height) and clips its child; the fixed
   900px .tour-stage is the TRANSFORMED child. Clipping a transformed child with a
   plain parent renders reliably on iOS (clipping a transformed element by its
   parent did not). Because .tour-fit is only column-wide, the stage can never
   widen the page. JS sets .tour-fit height + .tour-stage scale. */
.tour-holder { position: relative; width: 100%; max-width: 100%; }
/* No drop shadow (owner removed all demo glows/shadows 2026-07-21) — a
   hairline border keeps the frame edge readable on light bands. */
.tour-fit { position: relative; width: 100%; height: 0; overflow: hidden; border-radius: 10px; border: 1px solid rgba(16,24,40,.1); }
.tour-stage { position: absolute; top: 0; left: 0; width: 900px; height: 700px; transform-origin: top left; will-change: transform; --tour-accent: #9f50df; font-family: Inter, system-ui, sans-serif; }
.tour-stage a { text-decoration: none; } /* NOTE: don't set color here — it would beat .tour-child's own colour (specificity) and hide the submenu links */
/* Neutralise the ≤576 responsive constraints on the hero media/visual
   (max-width:300 + auto side-margins on the grid item) — iOS Safari mis-sizes a
   grid item with max-width + auto margins to ~0 width, which is why the demo
   vanished in portrait (that rule) but worked in landscape (it doesn't apply)
   and in desktop Chrome (which sizes it correctly). Make the tour column
   full-width and origin-anchored in every breakpoint. */
/* Display-only screencast: same as the PDP/checkout demos — pointer input
   falls through to the page so users can't scroll the tour's inner catalog. */
.landing-hero-media--tour { pointer-events: none; }
.landing-hero-media--tour, .landing-hero-visual--tour {
  min-height: 0 !important; max-width: none !important; margin-left: 0 !important; margin-right: 0 !important;
}

/* Browser-window shell (shared) */
.tour-win { position: absolute; inset: 0; overflow: hidden; background: #fff; }
.tour-chrome { height: 40px; display: flex; align-items: center; gap: 7px; padding: 0 15px; background: #eceef2; border-bottom: 1px solid rgba(16,24,40,.08); }
.tour-dot { width: 11px; height: 11px; border-radius: 50%; flex: 0 0 auto; }
.tour-dot--r { background: #ff5f57; } .tour-dot--y { background: #febc2e; } .tour-dot--g { background: #28c840; }
.tour-omni { flex: 1; margin-left: 12px; max-width: 460px; height: 24px; border-radius: 999px; background: #fff; border: 1px solid rgba(16,24,40,.1); display: flex; align-items: center; gap: 7px; padding: 0 12px; font-size: 12px; color: #6b7280; }
.tour-omni > i { font-size: 10px; color: #16a34a; }
.tour-view { position: absolute; top: 40px; left: 0; right: 0; bottom: 0; }

/* ============ ADMIN WINDOW ============ */
.tour-win--admin .tour-view { display: flex; }
.tour-side { width: 186px; flex: 0 0 186px; background: #141414; border-right: 1px solid #222; padding: 10px 10px 8px; display: flex; flex-direction: column; }
.tour-brand { display: flex; align-items: center; gap: 9px; padding: 6px 6px 12px; }
.tour-brand-badge { width: 30px; height: 30px; border-radius: 8px; background: var(--tour-accent); display: grid; place-items: center; color: #fff; font-weight: 700; font-size: 15px; flex: 0 0 auto; transition: background .45s; }
.tour-brand-name { font-size: 13.5px; font-weight: 600; color: #f0efed; flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tour-brand-cx { color: #6f6f6f; font-size: 10px; }
.tour-nav { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 9px; font-size: 13px; font-weight: 500; color: #d9d8d5; position: relative; }
.tour-nav svg { width: 16px; height: 16px; flex: 0 0 auto; color: #d9d8d5; }
.tour-nav .tour-caret2 { margin-left: auto; width: 11px; height: 11px; transition: transform .25s; color: #8a8a8a; }
.tour-nav.is-active { background: color-mix(in srgb, var(--tour-accent) 15%, transparent); color: #fff; }
.tour-nav.is-active svg { color: var(--tour-accent); }
.tour-nav.is-active::after { content: ""; position: absolute; right: 0; top: 50%; transform: translateY(-50%); width: 3px; height: 18px; border-radius: 2px 0 0 2px; background: var(--tour-accent); }
.tour-group.is-open > .tour-nav .tour-caret2 { transform: rotate(0deg); }
.tour-group > .tour-nav .tour-caret2 { transform: rotate(-90deg); }
.tour-children { max-height: 0; overflow: hidden; transition: max-height .3s ease; padding-left: 8px; position: relative; }
.tour-group.is-open > .tour-children { max-height: 320px; }
.tour-children::before { content: ""; position: absolute; left: 15px; top: 4px; bottom: 6px; width: 1px; background: #2c2c2c; }
.tour-child { display: block; padding: 6px 10px 6px 22px; border-radius: 8px; font-size: 12.5px; color: #b7b6b3; position: relative; }
.tour-child.is-active { background: color-mix(in srgb, var(--tour-accent) 11%, transparent); color: #fff; }
.tour-child.is-active::after { content: ""; position: absolute; right: 0; top: 50%; transform: translateY(-50%); width: 3px; height: 15px; border-radius: 2px 0 0 2px; background: var(--tour-accent); }
.tour-side-foot { margin-top: auto; padding: 8px 6px 8px; border-top: 1px solid #222; display: flex; align-items: center; gap: 9px; }
.tour-side-foot .tour-brand-badge { background: var(--tour-accent); font-size: 11px; }
.tour-side-foot .tour-brand-badge img { width: 18px; height: 18px; object-fit: contain; }
.tour-side-foot-name { font-size: 12.5px; font-weight: 600; color: #f0efed; line-height: 1.2; }
.tour-side-foot-plan { font-size: 10.5px; color: #8a8a8a; }

.tour-main { flex: 1; display: flex; flex-direction: column; overflow: hidden; min-width: 0; }
.tour-topbar { height: 50px; flex: 0 0 50px; display: flex; align-items: center; gap: 14px; padding: 0 18px; background: #fff; border-bottom: 1px solid #e8eaee; }
.tour-tsearch { width: 240px; height: 34px; border: 1px solid #e4e7ec; border-radius: 9px; display: flex; align-items: center; gap: 8px; padding: 0 12px; color: #9aa3b2; font-size: 12.5px; background: #f7f8fa; }
.tour-tsearch svg { width: 15px; height: 15px; flex: 0 0 auto; }
.tour-tsearch .kbd { margin-left: auto; font-size: 10px; color: #b3b9c2; border: 1px solid #e4e7ec; border-radius: 4px; padding: 1px 5px; background: #fff; }
.tour-tright { margin-left: auto; display: flex; align-items: center; gap: 17px; }
.tour-tbtn { display: inline-flex; align-items: center; gap: 6px; color: #4b5563; font-size: 13px; position: relative; }
.tour-tbtn svg { width: 18px; height: 18px; }
.tour-tbell .dot { position: absolute; top: -3px; right: -3px; width: 7px; height: 7px; border-radius: 50%; background: #ef4444; border: 1.5px solid #fff; }
.tour-tprofile { width: 30px; height: 30px; border-radius: 50%; background: color-mix(in srgb, var(--tour-accent) 16%, transparent); color: var(--tour-accent); display: grid; place-items: center; font-weight: 700; font-size: 13px; transition: background .4s, color .4s; }
.tour-main-body { flex: 1; position: relative; overflow: hidden; background: #f6f7f9; }
.tour-row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.tour-panel { position: absolute; inset: 0; padding: 20px 22px; opacity: 0; transform: translateY(8px); pointer-events: none; transition: opacity .35s ease, transform .35s ease; overflow: hidden; }
.tour-win--admin[data-view="settings"]  .tour-panel--settings,
.tour-win--admin[data-view="products"]  .tour-panel--products,
.tour-win--admin[data-view="analytics"] .tour-panel--analytics { opacity: 1; transform: none; }
.tour-ph { margin-bottom: 14px; }
.tour-ph-title { font-size: 18px; font-weight: 800; color: #111827; }
.tour-ph-sub { font-size: 12px; color: #6b7280; margin-top: 2px; }
.tour-label { font-size: 11px; font-weight: 700; color: #6b7280; margin: 0 0 6px; }

/* Settings tabs */
.tour-tabs { display: flex; gap: 20px; border-bottom: 1px solid #e5e7eb; margin-bottom: 16px; }
.tour-tab { display: flex; align-items: center; gap: 6px; padding: 0 2px 10px; font-size: 13px; font-weight: 600; color: #6b7280; border-bottom: 2px solid transparent; margin-bottom: -1px; }
.tour-tab svg { width: 15px; height: 15px; }
.tour-tab.is-active { color: var(--tour-accent); border-bottom-color: var(--tour-accent); }
.tour-section-title { font-size: 13px; font-weight: 800; color: #111827; display: flex; align-items: center; gap: 8px; margin-bottom: 14px; }
.tour-section-title i { color: #9aa3b2; }
.tour-cols2 { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; }
.tour-field { margin-bottom: 12px; }
.tour-input { border: 1px solid #d7dce3; border-radius: 8px; padding: 9px 11px; font-size: 12.5px; color: #111827; min-height: 36px; background: #fff; display: flex; align-items: center; transition: border-color .2s, box-shadow .2s; }
.tour-input.is-focus { border-color: var(--tour-accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--tour-accent) 18%, transparent); }
.tour-input.tour-input--muted { color: #9aa3b2; }
.tour-caret { display: inline-block; width: 1.5px; height: 15px; background: var(--tour-accent); opacity: 0; margin-left: 1px; vertical-align: middle; }
.is-focus > .tour-caret, .tour-input.is-focus .tour-caret { animation: tourBlink 1s steps(1) infinite; }
@keyframes tourBlink { 0%,50% { opacity: 1 } 50.01%,100% { opacity: 0 } }

/* Native-style colour picker */
.tour-color-row { display: flex; align-items: center; gap: 12px; position: relative; }
.tour-color-sw { width: 48px; height: 38px; border-radius: 8px; border: 1px solid #d7dce3; background: var(--tour-accent); flex: 0 0 auto; transition: background .3s; box-shadow: inset 0 0 0 2px #fff; }
.tour-color-help { font-size: 11px; color: #9aa3b2; line-height: 1.4; }
.tour-cpop { position: absolute; top: 46px; left: 0; width: 176px; background: #fff; border: 1px solid #e2e5ea; border-radius: 12px; box-shadow: 0 20px 44px rgba(16,24,40,.25); padding: 10px; opacity: 0; transform: translateY(-6px) scale(.97); transform-origin: top left; pointer-events: none; transition: opacity .2s, transform .2s; z-index: 6; }
.tour-cpop.is-open { opacity: 1; transform: none; }
.tour-cpop-sv { height: 96px; border-radius: 8px; position: relative; background:
    linear-gradient(to top, #000, transparent),
    linear-gradient(to right, #fff, hsl(var(--tour-hue,270), 90%, 50%)); }
.tour-cpop-dot { position: absolute; width: 12px; height: 12px; border-radius: 50%; border: 2px solid #fff; box-shadow: 0 0 0 1px rgba(0,0,0,.35); transform: translate(-50%,-50%); transition: left .5s ease, top .5s ease; }
.tour-cpop-hue { height: 12px; border-radius: 999px; margin-top: 9px; position: relative; background: linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00); }
.tour-cpop-thumb { position: absolute; top: 50%; width: 16px; height: 16px; border-radius: 50%; background: #fff; border: 1px solid rgba(0,0,0,.2); box-shadow: 0 1px 3px rgba(0,0,0,.3); transform: translate(-50%,-50%); transition: left .6s ease; }
.tour-cpop-hex { margin-top: 9px; font-size: 11px; font-weight: 700; color: #374151; letter-spacing: .04em; text-align: center; }

/* Logo upload */
.tour-logo-box { display: flex; align-items: center; gap: 12px; }
.tour-drop { width: 76px; height: 76px; border: 2px dashed #cbd2dd; border-radius: 12px; background: #fff; display: grid; place-items: center; text-align: center; color: #9aa3b2; font-size: 10px; flex: 0 0 auto; overflow: hidden; transition: border-color .25s, background .25s; }
.tour-drop-ph svg { margin-bottom: 3px; }
.tour-drop.is-drop { border-color: var(--tour-accent); background: color-mix(in srgb, var(--tour-accent) 7%, transparent); }
/* After "upload": show the actual Northlight logo lockup, right there. */
.tour-drop.has-logo { width: auto; min-width: 156px; height: 62px; padding: 0 16px; border-style: solid; border-color: #e3e6ec; }
.tour-drop.has-logo .tour-drop-ph { display: none; }
.tour-drop .tour-logo-preview { display: none; align-items: center; gap: 9px; }
.tour-drop.has-logo .tour-logo-preview { display: flex; }
.tour-logo-preview .tour-logomark { width: 30px; height: 30px; flex: 0 0 auto; }
.tour-logo-word { font-size: 17px; font-weight: 800; letter-spacing: -.01em; color: #1f2430; }
.tour-logo-word span { font-weight: 400; opacity: .7; }
.tour-upload-btn { display: inline-flex; align-items: center; gap: 7px; padding: 8px 12px; border-radius: 8px; background: #fff; border: 1px solid #d7dce3; font-size: 12px; font-weight: 600; color: #374151; }

/* Products table */
.tour-limit-card { background: #fff; border: 1px solid #e8eaee; border-radius: 10px; padding: 11px 14px; margin-bottom: 14px; font-size: 12px; color: #374151; display: flex; align-items: center; gap: 8px; }
.tour-limit-card b { color: #111827; }
.tour-toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.tour-toolbar .tour-th-title { font-size: 15px; font-weight: 700; color: #111827; margin-right: auto; }
.tour-select { border: 1px solid #d7dce3; border-radius: 8px; padding: 6px 26px 6px 10px; font-size: 12px; color: #374151; background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5L6 8l3.5-3.5' stroke='%236b7280' stroke-width='1.4' fill='none' stroke-linecap='round'/%3E%3C/svg%3E") no-repeat right 9px center; }
.tour-btn { display: inline-flex; align-items: center; gap: 7px; padding: 8px 13px; border-radius: 8px; background: var(--tour-accent); color: #fff; font-size: 12.5px; font-weight: 600; transition: background .4s, transform .1s; }
.tour-btn.is-press { transform: scale(.95); }
.tour-btn-ghost { padding: 8px 13px; border-radius: 8px; font-size: 12.5px; font-weight: 600; color: #6b7280; background: #eef0f3; }
.tour-table { width: 100%; background: #fff; border: 1px solid #e8eaee; border-radius: 10px; border-collapse: separate; border-spacing: 0; overflow: hidden; font-size: 12px; }
.tour-table th { text-align: left; padding: 9px 12px; font-size: 10.5px; font-weight: 700; color: #6b7280; text-transform: uppercase; letter-spacing: .03em; border-bottom: 1px solid #eceef2; background: #fafbfc; }
.tour-table td { padding: 8px 12px; border-bottom: 1px solid #f1f2f5; color: #374151; vertical-align: middle; }
.tour-table tr:last-child td { border-bottom: 0; }
.tour-prodcell { display: flex; align-items: center; gap: 10px; }
.tour-prodcell-img { width: 40px; height: 40px; border-radius: 5px; background-size: cover; background-position: center; background-color: #eceef2; flex: 0 0 auto; }
.tour-prodcell-name { font-weight: 600; color: #111827; }
.tour-prodcell-sub { font-size: 10.5px; color: #9aa3b2; }
.tour-badge { display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 999px; font-size: 10.5px; font-weight: 600; }
.tour-badge--cat { background: #eceef2; color: #4b5563; }
.tour-badge--stock { background: #dcfce7; color: #16a34a; }
.tour-badge--active { background: #dcfce7; color: #16a34a; }
.tour-badge--accent { background: var(--tour-accent); color: #fff; transition: background .4s; }
.tour-price { font-weight: 600; color: #111827; }
.tour-acts { color: #9aa3b2; display: flex; gap: 10px; }
.tour-row--new td { background: color-mix(in srgb, var(--tour-accent) 7%, #fff); }
/* Hidden entirely (display:none) so it reserves NO gap until it's created. */
.tour-row--new { display: none; }
.tour-row--new.is-in { display: table-row; }
.tour-row--new.is-in td { animation: tourRowIn .45s ease; }
@keyframes tourRowIn { from { opacity: 0; } to { opacity: 1; } }

/* Create-product modal */
.tour-modal-wrap { position: absolute; inset: 0; background: rgba(16,24,40,.4); display: grid; place-items: center; opacity: 0; pointer-events: none; transition: opacity .3s; z-index: 10; }
.tour-modal-wrap.is-open { opacity: 1; }
.tour-modal { width: 560px; max-height: 620px; background: #fff; border-radius: 16px; box-shadow: 0 40px 90px rgba(16,24,40,.45); overflow: hidden; display: flex; flex-direction: column; transform: translateY(16px) scale(.96); transition: transform .34s cubic-bezier(.34,1.4,.64,1); }
.tour-modal-wrap.is-open .tour-modal { transform: none; }
.tour-modal-h { display: flex; align-items: center; gap: 10px; padding: 14px 18px; border-bottom: 1px solid #eef0f3; }
.tour-modal-swatch { width: 30px; height: 30px; border-radius: 8px; background: var(--tour-accent); display: grid; place-items: center; color: #fff; font-size: 13px; transition: background .4s; }
.tour-modal-h-t { font-size: 15px; font-weight: 800; color: #111827; }
.tour-modal-x { margin-left: auto; color: #9aa3b2; font-size: 15px; }
.tour-modal-b { padding: 16px 18px; overflow: hidden; flex: 1 1 auto; min-height: 0; }
.tour-modal-b-inner { transition: transform .5s ease; }
.tour-sec { border: 1px solid #eceef2; border-radius: 10px; margin-bottom: 12px; overflow: hidden; }
.tour-sec-h { display: flex; align-items: center; padding: 10px 13px; font-size: 12.5px; font-weight: 700; color: #111827; background: #fafbfc; }
.tour-sec-h .fa-chevron-down { margin-left: auto; color: #b3b9c2; font-size: 10px; }
.tour-sec-b { padding: 13px; }
.tour-check { display: flex; align-items: center; gap: 8px; font-size: 12px; color: #374151; }
.tour-check-box { width: 16px; height: 16px; border-radius: 4px; border: 1.5px solid #cbd2dd; display: grid; place-items: center; color: #fff; font-size: 9px; }
.tour-check-box.on { background: var(--tour-accent); border-color: var(--tour-accent); transition: background .3s; }
.tour-langstrip { display: inline-flex; gap: 2px; margin-left: auto; }
.tour-lang { font-size: 9.5px; font-weight: 700; padding: 2px 5px; border-radius: 4px; color: #9aa3b2; }
.tour-lang.on { background: color-mix(in srgb, var(--tour-accent) 14%, transparent); color: var(--tour-accent); }
.tour-fhead { display: flex; align-items: center; margin-bottom: 6px; }
.tour-fhead .tour-label { margin: 0; }
.tour-inp { border: 1px solid #d7dce3; border-radius: 8px; padding: 8px 10px; font-size: 12px; color: #111827; min-height: 34px; display: flex; align-items: center; transition: border-color .2s, box-shadow .2s; }
.tour-inp.is-focus { border-color: var(--tour-accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--tour-accent) 16%, transparent); }
.tour-inp--area { min-height: 40px; align-items: flex-start; }
.tour-inp--pre { gap: 2px; } .tour-inp--pre .cur { color: #6b7280; }
.tour-row3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px; }
.tour-gallery { display: flex; gap: 9px; align-items: center; }
/* Hidden (display:none) until uploaded — so the "+" starts first and shifts
   right as each real image appears; no reserved empty gaps. */
.tour-gimg { width: 60px; height: 60px; border-radius: 8px; background-size: cover; background-position: center; background-color: #eceef2; display: none; transition: transform .4s cubic-bezier(.34,1.4,.64,1); }
.tour-gimg.is-in { display: block; }
.tour-gimg.is-drag { z-index: 5; box-shadow: 0 12px 26px rgba(16,24,40,.3); }
.tour-gadd { width: 60px; height: 60px; border: 2px dashed #cbd2dd; border-radius: 8px; display: grid; place-items: center; color: #9aa3b2; font-size: 12px; text-align: center; line-height: 1.2; order: 9; }
.tour-varopt { border: 1px solid #eceef2; border-radius: 8px; padding: 10px; }
.tour-chips { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px; }
.tour-vchip { padding: 4px 9px; border-radius: 6px; background: #f1f2f5; font-size: 11px; color: #374151; }
.tour-modal-f { padding: 13px 18px; border-top: 1px solid #eef0f3; display: flex; align-items: center; gap: 8px; }
.tour-modal-f .tour-btn-ghost { margin-left: auto; }

/* Analytics */
.tour-period { display: inline-flex; border: 1px solid #d7dce3; border-radius: 8px; overflow: hidden; font-size: 11px; }
.tour-period span { padding: 5px 11px; color: #6b7280; border-right: 1px solid #e5e7eb; }
.tour-period span:last-child { border-right: 0; }
.tour-period span.on { background: var(--tour-accent); color: #fff; transition: background .4s; }
.tour-kpis { display: grid; grid-template-columns: repeat(4, 1fr); gap: 11px; margin-bottom: 14px; }
.tour-kpi { background: #fff; border: 1px solid #e8eaee; border-radius: 10px; padding: 12px 13px; }
.tour-kpi-l { font-size: 10.5px; color: #6b7280; }
.tour-kpi-v { font-size: 20px; font-weight: 800; color: #111827; margin-top: 4px; }
.tour-kpi-d { font-size: 10.5px; color: #16a34a; font-weight: 700; margin-top: 2px; }
.tour-kpi-d.down { color: #dc2626; }
.tour-charts { display: grid; grid-template-columns: 1.5fr 1fr; gap: 12px; }
.tour-chart { background: #fff; border: 1px solid #e8eaee; border-radius: 10px; padding: 13px 15px; }
.tour-chart-t { font-size: 12px; font-weight: 700; color: #374151; margin-bottom: 12px; }
.tour-bars { display: flex; align-items: flex-end; gap: 8px; height: 118px; }
.tour-bar { flex: 1; background: var(--tour-accent); border-radius: 4px 4px 0 0; height: 4px; align-self: flex-end; opacity: .9; transition: height .8s cubic-bezier(.3,0,.2,1), background .4s; }
.tour-win--admin[data-view="analytics"] .tour-bar { height: var(--h); }
.tour-line { width: 100%; height: 118px; display: block; }
.tour-line path.area { fill: color-mix(in srgb, var(--tour-accent) 12%, transparent); stroke: none; opacity: 0; transition: opacity .5s .5s; }
.tour-line path.stroke { fill: none; stroke: var(--tour-accent); stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 420; stroke-dashoffset: 420; transition: stroke-dashoffset 1.2s ease, stroke .4s; }
.tour-win--admin[data-view="analytics"] .tour-line path.stroke { stroke-dashoffset: 0; }
.tour-win--admin[data-view="analytics"] .tour-line path.area { opacity: 1; }
.tour-atabs { display: flex; gap: 18px; border-bottom: 1px solid #e5e7eb; margin-bottom: 14px; }
.tour-atab { padding: 0 1px 9px; font-size: 12px; font-weight: 600; color: #6b7280; border-bottom: 2px solid transparent; margin-bottom: -1px; white-space: nowrap; }
.tour-atab.is-active { color: var(--tour-accent); border-bottom-color: var(--tour-accent); }
.tour-awidgets { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 12px; }
.tour-awidget { background: #fff; border: 1px solid #e8eaee; border-radius: 10px; padding: 13px 15px; }
.tour-awidget-t { font-size: 12px; font-weight: 700; color: #374151; margin-bottom: 11px; }
.tour-mvp-row { display: flex; align-items: center; gap: 9px; margin-bottom: 9px; }
.tour-mvp-img { width: 30px; height: 30px; border-radius: 5px; background-size: cover; background-position: center; flex: 0 0 auto; }
.tour-mvp-name { font-size: 11.5px; color: #374151; flex: 1; }
.tour-mvp-val { font-size: 11.5px; font-weight: 700; color: #111827; }
.tour-funnel-row { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; font-size: 11px; }
.tour-funnel-row .lbl { width: 108px; color: #6b7280; }
.tour-funnel-bar { flex: 1; height: 14px; border-radius: 4px; background: #eef0f3; overflow: hidden; }
.tour-funnel-bar span { display: block; height: 100%; background: var(--tour-accent); border-radius: 4px; width: 0; transition: width .8s cubic-bezier(.3,0,.2,1), background .4s; }
.tour-win--admin[data-view="analytics"] .tour-funnel-bar span { width: var(--w); }
.tour-funnel-row .val { width: 34px; text-align: right; font-weight: 700; color: #111827; }

/* ============ STOREFRONT WINDOW ============ */
/* The storefront window POPS IN over the admin (never sits below it — no
   peeking under the dashboard window onto the page below). */
.tour-win--store { opacity: 0; visibility: hidden; transform: scale(.985); transition: opacity .4s ease, transform .4s ease, visibility .4s; z-index: 20; }
.tour-stage.show-store .tour-win--store { opacity: 1; visibility: visible; transform: none; }
.tour-store { --st-bg: #fff; --st-fg: #111827; --st-card: #fff; --st-border: #ececf0; --st-muted: #6b7280; --st-head: #fff; --st-letterbox: #f6f7f9; position: absolute; inset: 0; background: var(--st-bg); transition: background .45s; display: flex; flex-direction: column; }
.tour-store.is-dark { --st-bg: #0e1116; --st-fg: #e7eaf0; --st-card: #161b22; --st-border: #262d38; --st-muted: #9aa3b8; --st-head: #12161c; --st-letterbox: #0b0e12; }
.tour-shead { height: 52px; flex: 0 0 52px; display: flex; align-items: center; gap: 20px; padding: 0 24px; background: var(--st-head); border-bottom: 1px solid var(--st-border); transition: background .45s, border-color .45s; }
.tour-logo { display: flex; align-items: center; gap: 9px; }
.tour-logomark { width: 26px; height: 26px; flex: 0 0 auto; }
.tour-logomark .pk { fill: var(--st-fg); transition: fill .45s; }
.tour-logomark .sun { fill: var(--tour-accent); transition: fill .4s; }
.tour-wordmark { font-size: 16px; font-weight: 800; letter-spacing: -.01em; color: var(--st-fg); transition: color .45s; }
.tour-wordmark span { font-weight: 400; opacity: .75; }
.tour-snav { display: flex; gap: 18px; font-size: 13px; font-weight: 500; color: var(--st-muted); transition: color .45s; }
.tour-sright { margin-left: auto; display: flex; align-items: center; gap: 18px; }
.tour-sicon { color: var(--st-fg); font-size: 14px; position: relative; transition: color .45s; display: inline-flex; }
.tour-sicon svg { width: 16px; height: 16px; }
.tour-lc { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--st-fg); transition: color .45s; }
.tour-cartbadge { position: absolute; top: -8px; right: -10px; min-width: 16px; height: 16px; padding: 0 4px; border-radius: 999px; background: var(--tour-accent); color: #fff; font-size: 9.5px; font-weight: 800; display: grid; place-items: center; transform: scale(0); transition: transform .3s cubic-bezier(.34,1.56,.64,1), background .4s; }
.tour-cartbadge.is-on { transform: scale(1); }
.tour-cartbadge.is-bump { animation: tourBump .4s ease; }
@keyframes tourBump { 30% { transform: scale(1.4); } }
.tour-sbody { flex: 1; position: relative; overflow: hidden; }
.tour-strack { display: flex; width: 200%; height: 100%; transition: transform .6s cubic-bezier(.5,.05,.2,1); }
.tour-store.show-product .tour-strack { transform: translateX(-50%); }
.tour-spage { width: 50%; flex: 0 0 50%; height: 100%; overflow: hidden; position: relative; }
.tour-sscroll { padding: 0 0 4px; }

/* Catalog */
.tour-cathero { margin: 18px 0; }
.tour-cathero h4 { font-size: 23px; font-weight: 800; color: var(--st-fg); transition: color .45s; }
.tour-cathero p { font-size: 12.5px; color: var(--st-muted); line-height: 1.6; margin-top: 7px; max-width: 92%; transition: color .45s; }
.tour-crumb { font-size: 11px; color: var(--st-muted); margin-bottom: 8px; display: flex; align-items: center; gap: 5px; transition: color .45s; }
.tour-crumb span { opacity: .55; }
/* Storefront catalog left sidebar (category tree + filters) */
/* Catalog page is ONE native-scroll container (.tour-catpage carries data-scroll).
   The category hero scrolls away with the content; the sidebar is position:sticky
   so it scrolls up to the header bar then stays pinned while the grid keeps
   scrolling. (Native scroll — NOT a transform-scroll — is what lets sticky work.) */
.tour-catpage { height: 100%; overflow-y: auto; overflow-x: hidden; padding: 0 24px 24px; box-sizing: border-box; position: relative; scrollbar-width: none; -ms-overflow-style: none; }
.tour-catpage::-webkit-scrollbar { width: 0; height: 0; display: none; }
.tour-catwrap { display: flex; gap: 22px; align-items: flex-start; }
.tour-scat-side { width: 166px; flex: 0 0 166px; position: sticky; top: 12px; align-self: flex-start; }
.tour-catcol { flex: 1; min-width: 0; }
.tour-scat-h { font-size: 11px; font-weight: 700; color: var(--st-fg); text-transform: uppercase; letter-spacing: .04em; margin-bottom: 10px; transition: color .45s; }
.tour-scat-cat { font-size: 12.5px; color: var(--st-muted); padding: 5px 0; transition: color .45s; }
.tour-scat-cat.is-active { color: var(--tour-accent); font-weight: 700; }
.tour-scat-f { display: flex; align-items: center; gap: 9px; font-size: 12px; color: var(--st-muted); padding: 5px 0; transition: color .45s; }
.tour-scat-box { width: 15px; height: 15px; border-radius: 4px; border: 1.5px solid var(--st-border); display: grid; place-items: center; color: #fff; font-size: 8px; flex: 0 0 auto; transition: border-color .45s, background .3s; }
.tour-scat-box.on { background: var(--tour-accent); border-color: var(--tour-accent); }
.tour-scat-range { height: 4px; border-radius: 999px; background: var(--st-border); position: relative; margin: 10px 0 8px; transition: background .45s; }
.tour-scat-range > span { position: absolute; left: 12%; right: 18%; top: 0; bottom: 0; background: var(--tour-accent); border-radius: 999px; transition: background .4s; }
.tour-scat-range::before, .tour-scat-range::after { content: ""; position: absolute; top: 50%; width: 12px; height: 12px; border-radius: 50%; background: var(--st-card); border: 2px solid var(--tour-accent); transform: translate(-50%,-50%); transition: background .45s, border-color .4s; }
.tour-scat-range::before { left: 12%; }
.tour-scat-range::after { left: 82%; }
.tour-scat-rangelbl { font-size: 11px; color: var(--st-muted); transition: color .45s; }
.tour-cattoolbar { display: flex; align-items: center; margin-bottom: 14px; color: var(--st-muted); font-size: 12px; transition: color .45s; }
.tour-cattoolbar .cnt { margin-right: auto; }
.tour-cattoolbar .sort { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--st-border); border-radius: 7px; padding: 5px 10px; transition: border-color .45s; }
.tour-sgrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
.tour-sprod { background: var(--st-card); border: 1px solid var(--st-border); border-radius: 12px; overflow: hidden; display: flex; flex-direction: column; transition: background .45s, border-color .45s, transform .22s, box-shadow .22s; }
.tour-sprod.is-hi { transform: translateY(-5px); box-shadow: 0 18px 36px rgba(16,24,40,.18); }
.tour-sprod-img { aspect-ratio: 1/1; background-size: cover; background-position: center; position: relative; background-color: var(--st-letterbox); transition: background-color .45s; }
.tour-oos { position: absolute; top: 8px; left: 8px; background: rgba(239,68,68,.92); color: #fff; font-size: 10px; font-weight: 700; padding: 2px 7px; border-radius: 6px; }
.tour-sprod-b { padding: 10px 11px 12px; display: flex; flex-direction: column; gap: 4px; flex: 1; }
.tour-featured { display: inline-flex; align-items: center; gap: 4px; font-size: 10px; font-weight: 700; color: #b8860b; }
.tour-sprod-t { font-size: 12.5px; font-weight: 600; color: var(--st-fg); line-height: 1.3; transition: color .45s; }
.tour-sprod-price { font-size: 13px; font-weight: 700; color: var(--tour-accent); display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 2px; }
.tour-off { background: var(--tour-accent); color: #fff; font-size: 9.5px; font-weight: 700; padding: 1px 5px; border-radius: 5px; transition: background .4s; }
.tour-was { color: var(--st-muted); text-decoration: line-through; font-weight: 500; font-size: 11px; transition: color .45s; }
.tour-vselect { margin-top: 6px; border: 1px solid var(--st-border); border-radius: 7px; padding: 5px 24px 5px 9px; font-size: 11px; color: var(--st-muted); background: var(--st-card) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5L6 8l3.5-3.5' stroke='%239aa3b8' stroke-width='1.4' fill='none' stroke-linecap='round'/%3E%3C/svg%3E") no-repeat right 8px center; transition: border-color .45s, background-color .45s; }
/* margin-top:auto pins every Add-to-cart to the card bottom, so they align on
   one line even when a variant dropdown makes some cards taller. */
.tour-satc { margin-top: auto; display: flex; align-items: center; justify-content: center; gap: 7px; padding: 8px 0; border-radius: 8px; background: var(--tour-accent); color: #fff; font-size: 11.5px; font-weight: 700; transition: background .4s, transform .1s; }
.tour-vselect { margin-bottom: 2px; }
.tour-satc.is-press { transform: scale(.94); }
.tour-satc.is-disabled { opacity: .42; }
.tour-satc svg { width: 13px; height: 13px; }
/* Variation card: a fake (animatable) size dropdown. Opens UPWARD so it stays
   inside the card (which is overflow:hidden for its rounded image corners). */
.tour-vwrap { position: relative; margin: 6px 0 2px; }
.tour-vdrop { display: flex; align-items: center; justify-content: space-between; gap: 6px; border: 1px solid var(--st-border); border-radius: 7px; padding: 5px 9px; font-size: 11px; color: var(--st-muted); background: var(--st-card); cursor: pointer; transition: border-color .3s, color .3s, background-color .45s; }
.tour-vdrop.has-value { color: var(--st-fg); border-color: var(--tour-accent); font-weight: 600; }
.tour-vdrop i { font-size: 8px; opacity: .65; }
.tour-vpop { position: absolute; bottom: calc(100% + 4px); left: 0; right: 0; background: var(--st-card); border: 1px solid var(--st-border); border-radius: 8px; box-shadow: 0 12px 26px rgba(16,24,40,.16); padding: 4px; z-index: 6; opacity: 0; transform: translateY(5px); pointer-events: none; transition: opacity .18s, transform .18s; }
.tour-vpop.open { opacity: 1; transform: none; pointer-events: auto; }
.tour-vopt { font-size: 11px; color: var(--st-fg); padding: 5px 7px; border-radius: 5px; transition: background .15s, color .15s; }
.tour-vopt.hl { background: var(--tour-accent); color: #fff; }

/* Product detail page */
.tour-pdp { padding: 18px 24px; display: grid; grid-template-columns: 1fr 1fr; gap: 22px; }
.tour-pdp-gallery { display: flex; flex-direction: column; gap: 10px; }
.tour-pdp-main { aspect-ratio: 1/1; border-radius: 14px; background-size: cover; background-position: center; background-color: var(--st-letterbox); transition: background-color .45s; }
.tour-pdp-thumbs { display: flex; gap: 9px; }
.tour-pdp-thumb { width: 52px; height: 52px; border-radius: 8px; background-size: cover; background-position: center; border: 2px solid transparent; }
.tour-pdp-thumb.on { border-color: var(--tour-accent); transition: border-color .3s; }
.tour-pdp-info { display: flex; flex-direction: column; }
.tour-pdp-cat { font-size: 11px; font-weight: 600; color: var(--tour-accent); margin-bottom: 4px; }
.tour-pdp-t { font-size: 21px; font-weight: 800; color: var(--st-fg); line-height: 1.15; transition: color .45s; }
.tour-pdp-priceline { display: flex; align-items: center; gap: 9px; margin: 10px 0 8px; flex-wrap: wrap; }
.tour-pdp-price { font-size: 22px; font-weight: 800; color: var(--tour-accent); }
.tour-instock { background: color-mix(in srgb, #16a34a 16%, transparent); color: #16a34a; font-size: 10.5px; font-weight: 700; padding: 3px 9px; border-radius: 999px; }
.tour-pdp-ids { font-size: 11px; color: var(--st-muted); margin-bottom: 10px; transition: color .45s; }
.tour-pdp-desc { font-size: 12px; color: var(--st-muted); line-height: 1.6; margin-bottom: 14px; transition: color .45s; }
.tour-pdp-varlabel { font-size: 11.5px; font-weight: 700; color: var(--st-fg); margin-bottom: 7px; transition: color .45s; }
.tour-varchips { display: flex; gap: 8px; margin-bottom: 16px; }
.tour-varchip { padding: 6px 13px; border-radius: 2rem; border: 1.5px solid var(--st-border); font-size: 11.5px; font-weight: 600; color: var(--st-fg); transition: all .2s; }
.tour-varchip.on { background: var(--tour-accent); border-color: var(--tour-accent); color: #fff; }
.tour-pdp-buy { display: flex; gap: 10px; align-items: stretch; }
.tour-qty { display: flex; align-items: center; border: 1px solid var(--st-border); border-radius: 9px; overflow: hidden; }
.tour-qty span { width: 30px; text-align: center; color: var(--st-muted); font-size: 13px; }
.tour-qty b { width: 34px; text-align: center; font-size: 13px; color: var(--st-fg); border-left: 1px solid var(--st-border); border-right: 1px solid var(--st-border); padding: 8px 0; }
.tour-pdp-atc { flex: 1; display: flex; align-items: center; justify-content: center; gap: 8px; border-radius: 9px; background: var(--tour-accent); color: #fff; font-size: 13px; font-weight: 700; transition: background .4s, transform .1s; }
.tour-pdp-atc.is-press { transform: scale(.97); }
.tour-pdp-atc svg { width: 15px; height: 15px; }
/* Reviews */
.tour-reviews { grid-column: 1 / -1; border-top: 1px solid var(--st-border); margin-top: 4px; padding-top: 16px; transition: border-color .45s; }
.tour-reviews-h { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
.tour-reviews-h .t { font-size: 15px; font-weight: 800; color: var(--st-fg); transition: color .45s; }
.tour-stars { display: inline-flex; gap: 1px; color: #f59e0b; font-size: 12px; }
.tour-reviews-h .meta { font-size: 12px; color: var(--st-muted); transition: color .45s; }
.tour-review { border-top: 1px solid var(--st-border); padding: 12px 0; transition: border-color .45s; }
.tour-review-top { display: flex; align-items: center; gap: 10px; margin-bottom: 5px; }
.tour-verified { margin-left: auto; display: inline-flex; align-items: center; gap: 4px; background: color-mix(in srgb, #16a34a 15%, transparent); color: #16a34a; font-size: 9.5px; font-weight: 700; padding: 2px 7px; border-radius: 999px; }
.tour-review-title { font-size: 12px; font-weight: 700; color: var(--st-fg); transition: color .45s; }
.tour-review-body { font-size: 11.5px; color: var(--st-muted); line-height: 1.55; margin: 3px 0; transition: color .45s; }
.tour-review-author { font-size: 11px; color: var(--st-muted); transition: color .45s; }

/* Add-to-cart toast (bottom-right of the storefront, like the real shop) */
.tour-toast { position: absolute; right: 18px; bottom: 18px; z-index: 30; display: flex; align-items: center; gap: 9px; padding: 11px 15px; border-radius: 10px; background: var(--st-card); box-shadow: 0 14px 34px rgba(16,24,40,.22); border: 1px solid var(--st-border); color: var(--st-fg); font-size: 12.5px; font-weight: 600; opacity: 0; transform: translateY(12px); pointer-events: none; transition: opacity .3s, transform .3s cubic-bezier(.34,1.5,.64,1), background .45s, color .45s, border-color .45s; }
.tour-toast.is-show { opacity: 1; transform: none; }
.tour-toast-check { width: 20px; height: 20px; border-radius: 50%; background: #16a34a; color: #fff; display: grid; place-items: center; font-size: 10px; flex: 0 0 auto; }

/* ============ FAKE CURSOR ============ */
.tour-cursor { position: absolute; top: 0; left: 0; width: 24px; height: 24px; z-index: 50; pointer-events: none; transform: translate(430px, 320px); transition: transform .8s cubic-bezier(.5,.05,.2,1); filter: drop-shadow(0 3px 5px rgba(0,0,0,.42)); will-change: transform; }
.tour-cursor > svg { display: block; width: 100%; height: 100%; transition: transform .12s ease; }
.tour-cursor.is-click > svg { transform: scale(.8); }
.tour-cursor::after { content: ""; position: absolute; top: 1px; left: 1px; width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--tour-accent); opacity: 0; transform: scale(.3); }
.tour-cursor.is-click::after { animation: tourRipple .55s ease-out; }
@keyframes tourRipple { 0% { opacity: .7; transform: scale(.3) } 100% { opacity: 0; transform: scale(2.3) } }

@media (max-width: 991.98px) { .landing-hero-visual--tour { max-width: none; } }
/* The hero tour plays even under reduced-motion (see _landing_tour.ejs), so the
   animated cursor stays visible — do not hide it here. */

/* ── Product-pages demo (Spotlight 2) — reuses the .tour-* chrome/storefront;
   .pdt-* adds the Catalog-Display settings scene (live sample card), instant
   search, list view and the PDP selling micro-animations. Same scale-to-fit
   scaffolding; independent data-pdt-* script. See _landing_pdp_demo.ejs. ── */
.pdt-spotlight-wrap { position: relative; }
/* Display-only screencast: ignore all pointer input so users can't wheel/touch-
   scroll the demo's inner catalog (the event falls through and scrolls the PAGE
   instead). The choreography scrolls programmatically via scrollTop — unaffected. */
.pdt-spotlight-wrap { pointer-events: none; }
/* On loop restart the whole demo is reset inside a `pdt-resetting` frame, which
   freezes every transition/animation so nothing (e.g. the toggle switches) is
   seen animating back to its start state. */
.pdt-resetting * { transition: none !important; animation: none !important; }
/* Admin · Catalog Display */
.pdt-catdesc { font-size: 12px; color: #6b7280; margin: -2px 0 14px; }
.pdt-catdisp { display: grid; grid-template-columns: 1fr 244px; gap: 26px; align-items: start; }
.pdt-catopts { display: flex; flex-direction: column; gap: 12px; }
.pdt-opt .tour-label { margin-bottom: 5px; }
/* extra breathing room around the image-padding slider, so it reads as a break
   before the run of Show-* switches the cursor toggles on */
.pdt-opt--gap { margin: 8px 0; }
.pdt-select { display: flex; align-items: center; justify-content: space-between; border: 1px solid #e3e5ea; border-radius: 8px; padding: 7px 11px; font-size: 12px; color: #374151; background: #fff; max-width: 260px; }
.pdt-select i { font-size: 9px; color: #9aa3b8; }
.pdt-toggle-row { display: flex; align-items: center; gap: 10px; }
.pdt-switch { width: 34px; height: 19px; border-radius: 999px; background: #d3d7df; position: relative; flex: 0 0 auto; transition: background .25s; }
.pdt-switch > i { position: absolute; top: 2px; left: 2px; width: 15px; height: 15px; border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(0,0,0,.2); transition: left .22s; }
.pdt-switch.on { background: var(--tour-accent); }
.pdt-switch.on > i { left: 17px; }
.pdt-tgl-label { font-size: 12.5px; color: #374151; }
.pdt-aspect { display: inline-flex; gap: 6px; }
.pdt-aspect span { font-size: 11px; padding: 4px 10px; border-radius: 7px; border: 1px solid #e3e5ea; color: #6b7280; }
.pdt-aspect span.is-on { border-color: var(--tour-accent); color: var(--tour-accent); background: color-mix(in srgb, var(--tour-accent) 10%, #fff); font-weight: 600; }
.pdt-slider { position: relative; height: 4px; border-radius: 999px; background: #e3e5ea; max-width: 210px; margin-top: 9px; }
.pdt-slider-fill { position: absolute; left: 0; top: 0; bottom: 0; background: var(--tour-accent); border-radius: 999px; }
.pdt-slider-thumb { position: absolute; top: 50%; width: 13px; height: 13px; border-radius: 50%; background: #fff; border: 2px solid var(--tour-accent); transform: translate(-50%,-50%); }
.pdt-catpreview { }
.pdt-scard { border: 1px solid #ececf0; border-radius: 12px; overflow: hidden; background: #fff; box-shadow: 0 8px 24px rgba(16,24,40,.08); }
.pdt-scard-img { aspect-ratio: 4/3; background: #eef0f4; }
.pdt-scard-img svg { width: 100%; height: 100%; display: block; }
.pdt-scard-b { padding: 11px 12px 13px; display: flex; flex-direction: column; gap: 4px; }
.pdt-scard-feat { align-self: flex-start; display: none; align-items: center; gap: 4px; font-size: 10px; font-weight: 700; color: #b8860b; }
.pdt-scard-feat.is-in { display: inline-flex; animation: pdtIn .35s ease; }
.pdt-scard-t { font-size: 13px; font-weight: 600; color: #111827; }
.pdt-scard-desc, .pdt-scard-sku, .pdt-scard-attrs, .pdt-scard-qty { display: none; }
.pdt-scard-desc.is-in { display: block; font-size: 11.5px; color: #6b7280; line-height: 1.5; animation: pdtIn .35s ease; }
.pdt-scard-sku.is-in { display: block; font-size: 10.5px; color: #9aa3b8; animation: pdtIn .35s ease; }
.pdt-scard-attrs.is-in { display: flex; flex-direction: column; gap: 1px; font-size: 11px; color: #4b5563; animation: pdtIn .35s ease; }
.pdt-scard-attrs b { font-weight: 600; color: #374151; }
.pdt-scard-price { font-size: 15px; font-weight: 800; color: var(--tour-accent); margin-top: 2px; }
.pdt-scard-row { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
.pdt-scard-qty.is-in { display: inline-flex; align-items: center; border: 1px solid #e3e5ea; border-radius: 7px; overflow: hidden; animation: pdtIn .35s ease; }
.pdt-scard-qty span { padding: 4px 8px; font-size: 12px; color: #6b7280; }
.pdt-scard-qty b { padding: 4px 9px; font-size: 12px; font-weight: 600; border-left: 1px solid #eef0f4; border-right: 1px solid #eef0f4; }
.pdt-scard-atc { flex: 1; display: flex; align-items: center; justify-content: center; gap: 6px; padding: 8px; border-radius: 8px; background: var(--tour-accent); color: #fff; font-size: 11px; font-weight: 700; }
.pdt-scard-atc i { font-size: 11px; }
.pdt-preview-tag { text-align: center; font-size: 10.5px; color: #9aa3b8; margin-top: 9px; }
.pdt-preview-tag i { margin-right: 4px; }
@keyframes pdtIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }

/* Storefront · instant search — full-width field below the header; the live
   results FLOAT over the catalog (absolute), so there is no reserved gap. */
.pdt-searchpanel { position: relative; max-height: 0; overflow: hidden; background: var(--st-head); transition: max-height .35s ease; z-index: 25; }
.pdt-searchpanel.open { max-height: 62px; overflow: visible; border-bottom: 1px solid var(--st-border); }
.pdt-searchbar { display: flex; align-items: center; gap: 10px; padding: 13px 24px; }
.pdt-search-mag { color: var(--st-muted); font-size: 13px; }
.pdt-searchph { font-size: 13px; color: var(--st-muted); }
.pdt-searchtext { font-size: 13px; color: var(--st-fg); }
.pdt-searchx { margin-left: auto; color: var(--st-muted); font-size: 13px; }
.pdt-caret2 { width: 1.5px; height: 15px; background: var(--tour-accent); display: inline-block; margin-left: -7px; animation: pdtCaret 1s steps(1) infinite; }
@keyframes pdtCaret { 50% { opacity: 0; } }
.pdt-sdrop { position: absolute; left: 24px; right: 24px; top: 100%; margin-top: 7px; border: 1px solid var(--st-border); border-radius: 10px; background: var(--st-card); box-shadow: 0 16px 40px rgba(16,24,40,.2); overflow: hidden; opacity: 0; transform: translateY(-6px); pointer-events: none; transition: opacity .2s, transform .2s; z-index: 40; }
.pdt-sdrop.open { opacity: 1; transform: none; pointer-events: auto; }
.pdt-sdrop-all { display: flex; align-items: center; gap: 8px; padding: 10px 14px; font-size: 12px; font-weight: 700; color: var(--tour-accent); border-bottom: 1px solid var(--st-border); }
.pdt-sdrop-cnt { margin-left: auto; font-size: 11px; color: var(--st-muted); font-weight: 500; }
.pdt-sdrop-item { display: flex; align-items: center; gap: 10px; padding: 9px 14px; }
.pdt-sdrop-item + .pdt-sdrop-item { border-top: 1px solid var(--st-border); }
.pdt-sdrop-thumb { width: 36px; height: 36px; border-radius: 6px; background-size: cover; background-position: center; flex: 0 0 auto; background-color: var(--st-letterbox); }
.pdt-sdrop-name { font-size: 12.5px; color: var(--st-fg); flex: 1; }
.pdt-sdrop-price { font-size: 12.5px; font-weight: 700; color: var(--tour-accent); }

/* Storefront · category tree + filters */
.pdt-cat-parent { display: flex; align-items: center; gap: 6px; padding: 4px 0; }
.pdt-cat-tgl { width: 16px; height: 16px; display: inline-flex; align-items: center; justify-content: center; color: var(--st-muted); }
.pdt-cat-tgl svg { width: 12px; height: 12px; transition: transform .22s; }
.pdt-cat-tgl.open svg { transform: rotate(180deg); }
.pdt-cat-name { font-size: 12.5px; color: var(--st-muted); }
.pdt-cat-name.is-active { color: var(--tour-accent); font-weight: 700; }
.pdt-cat-children { max-height: 0; overflow: hidden; transition: max-height .35s ease; padding-left: 22px; }
.pdt-cat-children.open { max-height: 130px; }
.pdt-cat-child { font-size: 12px; color: var(--st-muted); padding: 4px 0; }
.pdt-cat-child.is-active { color: var(--tour-accent); font-weight: 600; }
.pdt-cat-leaf { display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--st-muted); padding: 4px 0; }
.pdt-cat-dot { width: 16px; display: inline-flex; justify-content: center; flex: 0 0 auto; }
.pdt-cat-dot::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: currentColor; opacity: .5; }
.pdt-check { width: 15px; height: 15px; border-radius: 4px; border: 1.5px solid var(--st-border); display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; transition: background .2s, border-color .2s; }
.pdt-check.on { background: var(--tour-accent); border-color: var(--tour-accent); }
.pdt-check.on::after { content: "\2713"; color: #fff; font-size: 9px; font-weight: 700; line-height: 1; }
.pdt-sw { width: 12px; height: 12px; border-radius: 3px; display: inline-block; flex: 0 0 auto; }
.pdt-range { position: relative; height: 4px; border-radius: 999px; background: var(--st-border); margin: 8px 0 6px; }
.pdt-range-fill { position: absolute; left: 12%; right: 0; top: 0; bottom: 0; background: var(--tour-accent); border-radius: 999px; transition: right .4s; }
.pdt-range-thumb { position: absolute; top: 50%; width: 12px; height: 12px; border-radius: 50%; background: var(--st-card); border: 2px solid var(--tour-accent); transform: translate(-50%,-50%); transition: left .4s; }
.pdt-range-thumb--a { left: 12%; }
.pdt-range-thumb--b { left: 100%; }

/* Storefront · toolbar (grid/list toggle) + list view */
.pdt-toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; color: var(--st-muted); font-size: 12px; }
.pdt-viewtoggle { display: inline-flex; border: 1px solid var(--st-border); border-radius: 8px; overflow: hidden; }
.pdt-vbtn { width: 28px; height: 26px; display: inline-flex; align-items: center; justify-content: center; font-size: 11px; color: var(--st-muted); }
.pdt-vbtn.is-active { background: var(--tour-accent); color: #fff; }
.pdt-vbtn + .pdt-vbtn { border-left: 1px solid var(--st-border); }
.pdt-count { margin-right: auto; }
.pdt-sort { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--st-border); border-radius: 7px; padding: 5px 10px; }
.tour-sgrid { transition: opacity .3s; }
.tour-sgrid.is-loading { opacity: .25; }
/* rich catalog card (grid mode): short description + SKU + attributes + qty +
   add-to-cart, mirroring what the Catalog-Display toggles switched on. */
.pdt-card-main { display: flex; flex-direction: column; gap: 3px; }
.pdt-card-desc { font-size: 11px; color: var(--st-muted); line-height: 1.5; }
.pdt-card-sku { font-size: 10px; color: var(--st-muted); opacity: .85; letter-spacing: .02em; }
.pdt-card-attrs { display: flex; flex-direction: column; gap: 1px; font-size: 10.5px; color: var(--st-muted); margin-top: 1px; }
.pdt-card-attrs b { color: var(--st-fg); font-weight: 600; }
.tour-sprod-price { margin-top: 3px; }
.pdt-card-buy { display: flex; align-items: center; gap: 8px; margin-top: auto; padding-top: 9px; }
.pdt-card-qty { display: inline-flex; align-items: center; border: 1px solid var(--st-border); border-radius: 7px; overflow: hidden; flex: 0 0 auto; }
.pdt-card-qty span { padding: 4px 8px; font-size: 12px; color: var(--st-muted); }
.pdt-card-qty b { padding: 4px 9px; font-size: 12px; font-weight: 600; border-left: 1px solid var(--st-border); border-right: 1px solid var(--st-border); }
.pdt-card-buy .tour-satc { flex: 1; margin-top: 0; }
/* list view — small square image + vertically-centered text (name + price) +
   add-to-cart on the right; tighter than grid. */
.tour-sgrid.is-list { grid-template-columns: 1fr; gap: 8px; }
.tour-sgrid.is-list .tour-sprod { flex-direction: row; align-items: center; }
.tour-sgrid.is-list .tour-sprod-img { width: 60px; min-width: 60px; height: 60px; aspect-ratio: auto; align-self: center; margin: 8px 0 8px 8px; border-radius: 8px; }
.tour-sgrid.is-list .tour-sprod-b { flex: 1; flex-direction: row; align-items: center; gap: 12px; padding: 8px 14px; }
.tour-sgrid.is-list .pdt-card-main { flex: 1; gap: 2px; justify-content: center; }
.tour-sgrid.is-list .pdt-card-desc,
.tour-sgrid.is-list .pdt-card-sku,
.tour-sgrid.is-list .pdt-card-attrs,
.tour-sgrid.is-list .pdt-card-qty { display: none; }
.tour-sgrid.is-list .pdt-card-buy { margin: 0; padding: 0; flex: 0 0 auto; }
.tour-sgrid.is-list .pdt-card-buy .tour-satc { flex: 0 0 auto; width: auto; padding: 8px 18px; }

/* Storefront · PDP selling micro-animations */
.tour-pdp[data-pdt-pdpscroll] { display: block; padding: 0; height: 100%; overflow-y: auto; overflow-x: hidden; scrollbar-width: none; }
.tour-pdp[data-pdt-pdpscroll]::-webkit-scrollbar { width: 0; height: 0; display: none; }
.pdt-pdp-inner { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; padding: 18px 24px 34px; }
.pdt-pdp-titlerow { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* badge / discount / sale price appear one-by-one via `show` (display, so no
   reserved placeholder gap) + a zoom-from-bigger-to-normal `pdtZoomBig`. */
.pdt-pdp-badge { display: none; align-items: center; gap: 5px; font-size: 10.5px; font-weight: 700; color: #fff; background: linear-gradient(135deg, #f5b301, #e8890b); padding: 4px 10px; border-radius: 999px; white-space: nowrap; }
.pdt-pdp-badge.show { display: inline-flex; }
.pdt-pdp-badge.intro { animation: pdtZoomBig .6s cubic-bezier(.34,1.56,.64,1); }
.pdt-pdp-off { display: none; background: var(--tour-accent); color: #fff; font-size: 11px; font-weight: 700; padding: 2px 7px; border-radius: 5px; }
.pdt-pdp-off.show { display: inline-block; }
.pdt-pdp-off.pop { animation: pdtZoomBig .5s cubic-bezier(.34,1.56,.64,1); }
.pdt-pdp-orig { font-size: 22px; font-weight: 800; color: var(--tour-accent); position: relative; transition: font-size .4s ease, color .4s ease; }
.pdt-pdp-orig::after { content: ""; position: absolute; left: 0; top: 52%; height: 2px; width: 0; background: currentColor; transition: width .45s ease; }
.pdt-pdp-orig.struck { font-size: 16px; color: var(--st-muted); }
.pdt-pdp-orig.struck::after { width: 100%; }
.pdt-pdp-sale { display: none; font-size: 22px; font-weight: 800; color: var(--tour-accent); }
.pdt-pdp-sale.show { display: inline-block; }
.pdt-pdp-sale.pop { animation: pdtZoomBig .55s cubic-bezier(.34,1.7,.5,1); }
@keyframes pdtZoomBig { 0% { transform: scale(1.9); opacity: 0; } 60% { opacity: 1; } 100% { transform: scale(1); opacity: 1; } }
.pdt-rate-card { margin-top: 20px; border-top: 1px solid var(--st-border); padding-top: 14px; }
.pdt-rate-h { font-size: 14px; font-weight: 700; color: var(--st-fg); }
.pdt-rate-sub { font-size: 11.5px; color: var(--st-muted); margin: 3px 0 10px; }
.pdt-rate-sub i { margin-right: 4px; }
.pdt-rate { display: inline-flex; gap: 8px; font-size: 24px; color: #d1d5db; }
.pdt-rate i { transition: color .15s; }
.pdt-rate i.on { color: #f59e0b; }
.pdt-rate i.pop { animation: pdtStarPop .4s ease; }
@keyframes pdtStarPop { 0% { transform: scale(1); } 50% { transform: scale(1.42); } 100% { transform: scale(1); } }
.pdt-rate i[data-star5] { position: relative; }
.pdt-rate i[data-star5].burst::after { content: ""; position: absolute; left: 50%; top: 50%; width: 100%; height: 100%; border-radius: 50%; border: 2px solid #f59e0b; transform: translate(-50%,-50%) scale(.4); opacity: .75; animation: pdtStarGhost .6s ease forwards; pointer-events: none; }
@keyframes pdtStarGhost { to { opacity: 0; transform: translate(-50%,-50%) scale(2.7); } }

/* ── Checkout demo (Spotlight 3) — cart → coupon → checkout → order placed.
   Reuses the .tour-* browser chrome + storefront shell; .cko-* is the flow.
   Independent data-cko-* script. See _landing_checkout_demo.ejs. ── */
.cko-spotlight-wrap { position: relative; }
/* Display-only screencast: same as the PDP demo — pointer input falls through
   to the page so users can't scroll inside the demo's panels. */
.cko-spotlight-wrap { pointer-events: none; }
.cko-resetting * { transition: none !important; animation: none !important; }
/* three pages on one track */
.cko-track { display: flex; width: 300%; height: 100%; transition: transform .6s cubic-bezier(.5,.05,.2,1); }
.tour-store[data-step="checkout"] .cko-track { transform: translateX(-33.3333%); }
.tour-store[data-step="done"]     .cko-track { transform: translateX(-66.6667%); }
.cko-page { width: 33.3333%; flex: 0 0 33.3333%; height: 100%; overflow: hidden; }
.cko-scroll { height: 100%; overflow-y: auto; overflow-x: hidden; padding: 16px 22px 26px; box-sizing: border-box; position: relative; scrollbar-width: none; }
.cko-scroll::-webkit-scrollbar { width: 0; height: 0; display: none; }
/* distraction-free header on checkout/done, like .shop-checkout-mode */
.cko-trust { display: none; margin-left: auto; gap: 16px; font-size: 11.5px; color: var(--st-muted); white-space: nowrap; }
.cko-trust i { color: var(--tour-accent); margin-right: 5px; }
.tour-store[data-step="checkout"] .cko-trust, .tour-store[data-step="done"] .cko-trust { display: inline-flex; }
.tour-store[data-step="checkout"] .tour-snav, .tour-store[data-step="done"] .tour-snav,
.tour-store[data-step="checkout"] .tour-sright, .tour-store[data-step="done"] .tour-sright { display: none; }
/* shared shell */
.cko-head { margin-bottom: 14px; }
.cko-head h4 { font-size: 21px; font-weight: 800; color: var(--st-fg); }
.cko-head-sub { font-size: 12px; color: var(--st-muted); margin-top: 3px; }
/* stretch (not flex-start) so the summary column is as tall as the form —
   otherwise the sticky box releases as soon as the form scrolls past it. */
.cko-cols { display: flex; gap: 20px; align-items: stretch; }
.cko-main { flex: 1; min-width: 0; }
.cko-side { width: 248px; flex: 0 0 248px; }
.cko-sum--sticky { position: sticky; top: 0; }
/* cart line items */
.cko-items { border: 1px solid var(--st-border); border-radius: 12px; background: var(--st-card); overflow: hidden; }
.cko-item { display: flex; align-items: center; gap: 12px; padding: 12px 14px; border-bottom: 1px solid var(--st-border); }
.cko-item:last-child { border-bottom: none; }
.cko-item-img { width: 56px; height: 56px; border-radius: 8px; background-size: cover; background-position: center; flex: 0 0 auto; background-color: var(--st-letterbox); }
.cko-item-b { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.cko-item-b b { font-size: 12.5px; font-weight: 600; color: var(--st-fg); }
.cko-item-b em { font-size: 10.5px; color: var(--st-muted); font-style: normal; }
.cko-qty { display: inline-flex; align-items: center; border: 1px solid var(--st-border); border-radius: 8px; overflow: hidden; flex: 0 0 auto; }
.cko-qty-b { width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center; font-size: 13px; color: var(--st-muted); }
.cko-qty b { min-width: 26px; text-align: center; font-size: 12px; font-weight: 700; color: var(--st-fg); border-left: 1px solid var(--st-border); border-right: 1px solid var(--st-border); padding: 3px 0; }
.cko-item-t { font-size: 13px; font-weight: 700; color: var(--st-fg); min-width: 58px; text-align: right; display: inline-block; }
.cko-item-x { width: 26px; height: 26px; border-radius: 7px; border: 1px solid color-mix(in srgb, #ef4444 40%, transparent); color: #ef4444; display: inline-flex; align-items: center; justify-content: center; font-size: 10px; flex: 0 0 auto; }
/* summary box */
.cko-sum { border: 1px solid var(--st-border); border-radius: 12px; background: var(--st-card); padding: 14px; }
.cko-sum-h { font-size: 13px; font-weight: 700; color: var(--st-fg); margin-bottom: 10px; }
.cko-row { display: flex; align-items: center; justify-content: space-between; font-size: 12px; color: var(--st-muted); margin-bottom: 6px; }
.cko-row b { color: var(--st-fg); font-weight: 600; display: inline-block; }
.cko-row--muted, .cko-row--muted b { font-size: 11px; color: var(--st-muted); font-weight: 500; }
.cko-row--disc, .cko-row--ship { display: none; }
.cko-row--ship.is-in { display: flex; }
.cko-row--disc.is-in { display: flex; animation: ckoIn .35s ease; color: #16a34a; }
.cko-row--disc.is-in b { color: #16a34a; }
.cko-row--disc em { font-style: normal; opacity: .8; }
.cko-sum-total { display: flex; align-items: center; justify-content: space-between; border-top: 1px solid var(--st-border); margin-top: 9px; padding-top: 9px; font-size: 13px; font-weight: 700; color: var(--st-fg); }
.cko-sum-total b { font-size: 17px; font-weight: 800; color: var(--tour-accent); display: inline-block; }
.cko-shipnote { font-size: 10.5px; color: var(--st-muted); margin-top: 7px; }
.cko-shipnote i { margin-right: 4px; }
.tour-store[data-step="checkout"] .cko-shipnote, .tour-store[data-step="done"] .cko-shipnote { display: none; }
.is-bump { animation: ckoBump .5s ease; }
@keyframes ckoBump { 0% { transform: scale(1); } 35% { transform: scale(1.18); color: var(--tour-accent); } 100% { transform: scale(1); } }
@keyframes ckoIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
/* mini item list in the checkout/done summary */
.cko-mini { margin-bottom: 10px; }
.cko-mini-r { display: flex; align-items: center; gap: 8px; padding: 5px 0; border-bottom: 1px solid var(--st-border); }
.cko-mini-r:last-child { border-bottom: none; }
.cko-mini-img { width: 30px; height: 30px; border-radius: 6px; background-size: cover; background-position: center; flex: 0 0 auto; position: relative; background-color: var(--st-letterbox); }
.cko-mini-img i { position: absolute; top: -5px; right: -5px; min-width: 15px; height: 15px; border-radius: 999px; background: var(--tour-accent); color: #fff; font-size: 9px; font-style: normal; font-weight: 700; display: grid; place-items: center; padding: 0 3px; }
.cko-mini-n { flex: 1; font-size: 11px; color: var(--st-fg); }
.cko-mini-p { font-size: 11px; font-weight: 700; color: var(--st-fg); }
/* coupon */
.cko-coupon { margin-top: 10px; }
.cko-coupon-link { font-size: 11.5px; color: var(--tour-accent); font-weight: 600; }
.cko-coupon-link i { margin-right: 4px; }
.cko-coupon-box { display: none; gap: 6px; margin-top: 7px; }
.cko-coupon-box.is-in { display: flex; animation: ckoIn .3s ease; }
.cko-apply { border: 1px solid var(--tour-accent); color: var(--tour-accent); border-radius: 8px; padding: 6px 11px; font-size: 11.5px; font-weight: 700; white-space: nowrap; }
.cko-apply.is-press { transform: scale(.95); }
.cko-coupon-ok { display: none; align-items: center; justify-content: space-between; margin-top: 8px; font-size: 11.5px; color: #16a34a; font-weight: 600; }
.cko-coupon-ok.is-in { display: flex; animation: ckoIn .35s ease; }
.cko-coupon-ok i { margin-right: 4px; }
.cko-coupon-ok em { font-style: normal; font-weight: 500; color: var(--st-muted); text-decoration: underline; }
/* buttons */
.cko-btn { margin-top: 12px; display: flex; align-items: center; justify-content: center; gap: 8px; background: var(--tour-accent); color: #fff; border-radius: 9px; padding: 10px; font-size: 12.5px; font-weight: 700; transition: transform .1s; }
.cko-btn--place { margin-top: 18px; padding: 12px; font-size: 13.5px; }
.cko-btn.is-press { transform: scale(.97); }
/* checkout steps */
.cko-steps { display: flex; align-items: center; justify-content: center; gap: 0; margin-bottom: 16px; }
.cko-step { display: flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 600; color: var(--st-muted); }
.cko-step.is-active { color: var(--tour-accent); }
.cko-step.is-done { color: var(--st-fg); }
.cko-step-n { width: 20px; height: 20px; border-radius: 50%; border: 2px solid var(--st-border); background: var(--st-card); display: grid; place-items: center; font-size: 9.5px; font-style: normal; font-weight: 700; }
.cko-step.is-active .cko-step-n { background: var(--tour-accent); border-color: var(--tour-accent); color: #fff; }
.cko-step.is-done .cko-step-n { background: var(--st-fg); border-color: var(--st-fg); color: var(--st-card); }
.cko-step-line { flex: 1 1 auto; min-width: 22px; height: 2px; background: var(--st-border); margin: 0 8px; }
/* guest vs sign-in choice, two big side-by-side buttons */
.cko-methods { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 16px; }
.cko-method { display: flex; align-items: center; justify-content: center; gap: 8px; border: 1.5px solid var(--st-border); border-radius: 10px; padding: 13px 10px; font-size: 13px; font-weight: 700; color: var(--st-muted); background: var(--st-card); transition: border-color .2s, background .2s, color .2s; }
.cko-method i { font-size: 14px; }
.cko-method.is-on { border-color: var(--tour-accent); color: var(--tour-accent); background: color-mix(in srgb, var(--tour-accent) 8%, var(--st-card)); }
.cko-step.is-done + .cko-step-line, .cko-step.is-active + .cko-step-line { background: var(--tour-accent); }
/* form */
.cko-sec-h { font-size: 12.5px; font-weight: 700; color: var(--st-fg); margin: 16px 0 9px; }
.cko-sec-h:first-of-type { margin-top: 0; }
.cko-sec-h i { margin-right: 5px; color: var(--tour-accent); }
.cko-f2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.cko-field { margin-bottom: 10px; }
.cko-field label { display: block; font-size: 10.5px; color: var(--st-muted); margin-bottom: 4px; }
.cko-input { border: 1px solid var(--st-border); border-radius: 8px; padding: 7px 10px; font-size: 11.5px; color: var(--st-fg); background: var(--st-card); min-height: 32px; box-sizing: border-box; display: flex; align-items: center; position: relative; transition: border-color .2s, box-shadow .2s; }
.cko-input.is-focus { border-color: var(--tour-accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--tour-accent) 16%, transparent); }
.cko-input--area { min-height: 46px; align-items: flex-start; padding-top: 8px; }
.cko-input--sel { justify-content: space-between; color: var(--st-fg); }
.cko-input--sel i { font-size: 9px; color: var(--st-muted); }
.cko-input--sm { flex: 1; padding: 6px 9px; min-height: 30px; }
.cko-caret { width: 1.5px; height: 13px; background: var(--tour-accent); display: inline-block; margin-left: 1px; opacity: 0; }
.cko-input.is-focus .cko-caret { opacity: 1; animation: ckoCaret 1s steps(1) infinite; }
@keyframes ckoCaret { 50% { opacity: 0; } }
.cko-ph { position: absolute; left: 9px; font-size: 11px; color: var(--st-muted); pointer-events: none; }
.cko-help { font-size: 10px; color: var(--st-muted); margin-top: 4px; }
/* option rows (shipping + payment) */
.cko-opt { display: flex; align-items: center; gap: 9px; border: 1px solid var(--st-border); border-radius: 9px; padding: 9px 11px; margin-bottom: 7px; background: var(--st-card); transition: border-color .2s, background .2s; }
.cko-opt.is-hi { border-color: color-mix(in srgb, var(--tour-accent) 55%, transparent); }
.cko-opt.is-on { border-color: var(--tour-accent); background: color-mix(in srgb, var(--tour-accent) 6%, var(--st-card)); }
.cko-radio { width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--st-border); flex: 0 0 auto; display: grid; place-items: center; }
.cko-opt.is-on .cko-radio { border-color: var(--tour-accent); }
.cko-opt.is-on .cko-radio::after { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--tour-accent); }
.cko-opt-b { flex: 1; display: flex; flex-direction: column; gap: 1px; }
.cko-opt-b b { font-size: 12px; font-weight: 600; color: var(--st-fg); }
.cko-opt-b b i { margin-right: 5px; color: var(--st-muted); }
.cko-opt-b em { font-size: 10.5px; color: var(--st-muted); font-style: normal; }
.cko-opt-p { font-size: 12px; font-weight: 700; color: var(--st-fg); }
.cko-mark { min-width: 34px; height: 20px; border-radius: 5px; display: inline-grid; place-items: center; font-size: 10px; font-weight: 800; color: var(--st-muted); border: 1px solid var(--st-border); padding: 0 6px; }
.cko-mark--apple { background: #111; color: #fff; border-color: #111; }
.cko-mark--gpay { background: #fff; color: #3c4043; border-color: #dadce0; }
.cko-mark--pp { background: #003087; color: #fff; border-color: #003087; }
.cko-mark--klarna { background: #ffb3c7; color: #17120f; border-color: #ffb3c7; }
/* stripe card panel */
.cko-cardbox { display: none; }
.cko-cardbox.is-in { display: block; animation: ckoIn .3s ease; }
.cko-card-row { display: flex; align-items: center; gap: 12px; border: 1px solid var(--st-border); border-radius: 9px; padding: 10px 12px; margin: -2px 0 9px; background: var(--st-letterbox); }
.cko-card-num { flex: 1; font-size: 12px; letter-spacing: .06em; color: var(--st-fg); font-weight: 600; }
.cko-card-meta { font-size: 11px; color: var(--st-muted); }
/* order placed */
.cko-cols--done { align-items: flex-start; }
.cko-done-head { text-align: center; padding: 6px 0 14px; }
.cko-check { display: inline-block; }
.cko-check svg { width: 68px; height: 68px; display: block; }
.cko-check .ring { fill: none; stroke: #16a34a; stroke-width: 3; stroke-dasharray: 145; stroke-dashoffset: 145; }
.cko-check .tick { fill: none; stroke: #16a34a; stroke-width: 4.5; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 44; stroke-dashoffset: 44; }
.cko-check.is-in { animation: ckoPop .5s cubic-bezier(.34,1.56,.64,1); }
.cko-check.is-in .ring { animation: ckoDraw .55s ease forwards; }
.cko-check.is-in .tick { animation: ckoDraw .4s .42s ease forwards; }
@keyframes ckoDraw { to { stroke-dashoffset: 0; } }
@keyframes ckoPop { 0% { transform: scale(.5); } 60% { transform: scale(1.08); } 100% { transform: scale(1); } }
.cko-done-t { font-size: 19px; font-weight: 800; color: var(--st-fg); margin-top: 8px; }
.cko-done-s { font-size: 12.5px; color: var(--st-muted); margin-top: 4px; }
.cko-done-n { font-size: 12px; color: var(--st-muted); margin-top: 7px; }
.cko-done-n b { color: var(--st-fg); }
.cko-maplbl { font-size: 11.5px; font-weight: 700; color: var(--st-fg); margin-bottom: 7px; }
.cko-maplbl i { color: var(--tour-accent); margin-right: 5px; }
/* stylized delivery map — self-contained SVG (no external tiles, CSP-safe);
   same footprint as the real map block (8px radius, panel height). */
.cko-map { position: relative; height: 196px; border-radius: 8px; overflow: hidden; border: 1px solid var(--st-border); }
.cko-map svg { width: 100%; height: 100%; display: block; }
.cko-pin { position: absolute; left: 48.5%; top: 47%; font-size: 26px; color: var(--tour-accent); opacity: 0; transform: translate(-50%,-190%) scale(.6); filter: drop-shadow(0 3px 5px rgba(16,24,40,.35)); }
.cko-pin.is-in { animation: ckoPin .6s cubic-bezier(.34,1.56,.64,1) forwards; }
@keyframes ckoPin { to { opacity: 1; transform: translate(-50%,-100%) scale(1); } }
.cko-pin::after { content: ""; position: absolute; left: 50%; bottom: -4px; width: 26px; height: 9px; border-radius: 50%; background: color-mix(in srgb, var(--tour-accent) 30%, transparent); transform: translateX(-50%); }
.cko-map-card { position: absolute; left: 10px; bottom: 10px; background: var(--st-card); border: 1px solid var(--st-border); border-radius: 8px; padding: 7px 10px; display: flex; flex-direction: column; gap: 1px; box-shadow: 0 6px 18px rgba(16,24,40,.14); }
.cko-map-card b { font-size: 11.5px; color: var(--st-fg); }
.cko-map-card em { font-size: 10.5px; color: var(--st-muted); font-style: normal; }

/* ── Features ── */
.landing-feature {
  padding: 1.75rem;
  background: rgba(255,255,255,.88);
  border: 1px solid var(--border);
  border-radius: 16px;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  transition: transform .2s ease, box-shadow .2s ease, border-color .2s ease;
}
[data-theme="dark"] .landing-feature { background: rgba(20,26,42,.78); }
.landing-feature:hover {
  transform: translateY(-4px);
  box-shadow: 0 14px 35px rgba(159,80,223,.08);
  border-color: rgba(159,80,223,.3);
}
.landing-feature-icon {
  width: 52px;
  height: 52px;
  border-radius: 14px;
  background: linear-gradient(135deg, var(--primary-subtle), rgba(230,110,180,.1));
  color: var(--primary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 1.3rem;
  margin-bottom: 1rem;
}
.landing-feature-title { font-weight: 700; font-size: 1.05rem; margin-bottom: .5rem; color: var(--fg); }
.landing-feature-text { color: var(--muted); font-size: .92rem; line-height: 1.55; margin-bottom: 0; }
/* Horizontal variant: bigger icon to the top-left of the copy (used by the
   speed + security highlight). */
.landing-feature--row { display: flex; align-items: flex-start; gap: 1.15rem; }
.landing-feature-icon--lg { width: 66px; height: 66px; border-radius: 18px; font-size: 1.9rem; flex-shrink: 0; margin-bottom: 0; }

/* ── Screenshot sections ── */
.landing-screenshot-frame {
  position: relative;
  border-radius: 20px;
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--card);
}
.landing-screenshot-img {
  display: block;
  width: 100%;
  height: auto;
}
/* Demo glows removed entirely on 2026-07-21 (owner request) — the demos sit
   flat on the page with only the .tour-fit drop shadow. */

/* ── Storefront showcase (Spotlight 1) — three cross-fading shop scenes, each
   with its own logo + accent colour, the third in dark mode. Same scale-to-fit,
   transform-on-child architecture as the hero tour. ── */
.showcase-holder { width: 100%; }
/* Match the hero demo's browser-window corner radius (.tour-fit is 10px), not
   the 20px used for the static screenshot frames. */
.landing-screenshot-frame--showcase { border-radius: 10px; }
.showcase-fit { position: relative; width: 100%; overflow: hidden; background: #eef1f6; }
.showcase-stage { position: absolute; top: 0; left: 0; width: 1120px; height: 720px; transform-origin: top left; }
.sc-scene {
  position: absolute; inset: 0; opacity: 0; transform: scale(1.02); z-index: 0;
  transition: opacity .55s ease, transform .55s ease;
  display: flex; flex-direction: column;
  background: var(--sc-bg); color: var(--sc-fg);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  /* light-theme tokens (dark scene overrides below) */
  --sc-bg:#f8f9fb; --sc-fg:#111827; --sc-muted:#6b7280; --sc-card:#ffffff;
  --sc-border:#e5e7eb; --sc-navbar:#ffffff; --sc-primary-fg:#ffffff;
  --sc-chrome-bg:#eceef2; --sc-chrome-border:rgba(16,24,40,.08); --sc-omni-bg:#ffffff; --sc-omni-border:rgba(16,24,40,.1);
}
.sc-scene[data-dark] {
  --sc-bg:#0b0f19; --sc-fg:#e5e7eb; --sc-muted:#9ca3af; --sc-card:#141a2a;
  --sc-border:#1f2937; --sc-navbar:#0e1424;
  --sc-chrome-bg:#20242e; --sc-chrome-border:rgba(255,255,255,.06); --sc-omni-bg:#2a2f3a; --sc-omni-border:rgba(255,255,255,.1);
}
/* Gentle cross-fade: the just-left scene stays solid underneath (is-prev) so
   the incoming scene fades (+ a subtle settle) in over a solid layer. */
.sc-scene.is-prev { opacity: 1; transform: none; z-index: 1; transition: none; }
.sc-scene.is-active { opacity: 1; transform: none; z-index: 2; }

.sc-chrome { flex: 0 0 40px; display: flex; align-items: center; gap: 7px; padding: 0 15px; background: var(--sc-chrome-bg); border-bottom: 1px solid var(--sc-chrome-border); }
.sc-dot { width: 11px; height: 11px; border-radius: 50%; flex: 0 0 auto; }
.sc-dot--r { background: #ff5f57; }
.sc-dot--y { background: #febc2e; }
.sc-dot--g { background: #28c840; }
.sc-omni { flex: 1; margin-left: 12px; max-width: 460px; height: 24px; border-radius: 999px; background: var(--sc-omni-bg); border: 1px solid var(--sc-omni-border); display: flex; align-items: center; gap: 7px; padding: 0 12px; font-size: 12px; color: var(--sc-muted); }
.sc-omni .fa-lock { font-size: 10px; color: #16a34a; }

.sc-win { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; background: var(--sc-bg); }
.sc-header { flex: 0 0 58px; display: flex; align-items: center; gap: 18px; padding: 0 22px; background: var(--sc-navbar); border-bottom: 1px solid var(--sc-border); }
.sc-brand { display: inline-flex; align-items: center; gap: 9px; text-decoration: none; }
.sc-logomark { width: 27px; height: 27px; flex: 0 0 27px; }
.sc-wordmark { font-size: 17px; font-weight: 800; color: var(--sc-fg); letter-spacing: -.01em; }
.sc-wordmark span { font-weight: 400; opacity: .7; }
.sc-brand-badge { width: 30px; height: 30px; border-radius: 8px; background: var(--sc-primary); color: var(--sc-primary-fg); display: inline-flex; align-items: center; justify-content: center; flex: 0 0 30px; font-size: 15px; font-weight: 700; }
.sc-brand-name { font-size: 17px; font-weight: 700; color: var(--sc-fg); letter-spacing: -.01em; }
.sc-nav { display: flex; align-items: center; gap: 18px; }
.sc-nav-link { font-size: 14px; color: var(--sc-fg); opacity: .78; text-decoration: none; }
.sc-nav-link.is-active { opacity: 1; color: var(--sc-primary); font-weight: 600; }
.sc-tools { margin-left: auto; display: flex; align-items: center; gap: 8px; }
.sc-tool { height: 30px; min-width: 30px; padding: 0 9px; display: inline-flex; align-items: center; justify-content: center; gap: 6px; font-size: 12.5px; color: var(--sc-fg); border: 1px solid var(--sc-border); border-radius: 8px; background: var(--sc-card); }
.sc-cart { position: relative; }
.sc-cart-badge { position: absolute; top: -6px; right: -6px; min-width: 16px; height: 16px; padding: 0 4px; font-size: 10px; font-weight: 700; line-height: 16px; text-align: center; border-radius: 8px; background: var(--sc-primary); color: var(--sc-primary-fg); }

.sc-body { flex: 1 1 auto; min-height: 0; overflow: hidden; }

/* home */
.sc-hero { height: 250px; margin: 18px 22px; border-radius: 14px; overflow: hidden; background-size: cover; background-position: center; position: relative; display: flex; align-items: center; }
.sc-hero::after { content: ""; position: absolute; inset: 0; background: linear-gradient(90deg, rgba(0,0,0,.55), rgba(0,0,0,.05)); }
.sc-hero-inner { position: relative; z-index: 1; padding: 0 40px; max-width: 62%; color: #fff; }
.sc-hero-eyebrow { font-size: 12px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; opacity: .9; }
.sc-hero-title { font-size: 38px; font-weight: 800; line-height: 1.05; margin: 8px 0 10px; letter-spacing: -.02em; }
.sc-hero-sub { font-size: 15px; opacity: .92; margin-bottom: 18px; max-width: 380px; }
.sc-hero-btn { border: none; border-radius: 10px; padding: 11px 20px; background: var(--sc-primary); color: var(--sc-primary-fg); font-size: 14px; font-weight: 600; display: inline-flex; align-items: center; gap: 9px; }
.sc-featured { padding: 4px 22px 0; }
.sc-sec-title { font-size: 17px; font-weight: 700; margin-bottom: 12px; color: var(--sc-fg); }

/* product grid + card */
.sc-grid { display: grid; gap: 16px; }
.sc-grid--3 { grid-template-columns: repeat(3, 1fr); }
.sc-card { background: var(--sc-card); border: 1px solid var(--sc-border); border-radius: 10px; overflow: hidden; display: flex; flex-direction: column; height: 100%; }
.sc-card-img { display: block; aspect-ratio: 4 / 3; background: var(--sc-bg); overflow: hidden; }
.sc-card-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sc-card-body { padding: 11px 12px 12px; display: flex; flex-direction: column; gap: 6px; flex: 1 1 auto; }
.sc-feat { font-size: 11px; font-weight: 700; color: #b8860b; display: inline-flex; align-items: center; gap: 4px; }
.sc-card-title { font-size: 14px; font-weight: 600; color: var(--sc-fg); text-decoration: none; }
.sc-card-price { font-size: 15px; font-weight: 700; color: var(--sc-primary); line-height: 1.3; }
.sc-badge { font-size: 10px; font-weight: 700; color: var(--sc-primary-fg); background: var(--sc-primary); border-radius: 5px; padding: 1px 5px; }
.sc-old { font-size: 12px; color: var(--sc-muted); text-decoration: line-through; font-weight: 500; }
.sc-atc { margin-top: 2px; border: none; border-radius: 8px; padding: 8px 12px; background: var(--sc-primary); color: var(--sc-primary-fg); font-size: 12.5px; font-weight: 600; display: inline-flex; align-items: center; justify-content: center; gap: 7px; }
.sc-atc .fa-shopping-bag { font-size: 11px; }
/* In a product card, the button sticks to the bottom so every card's
   add-to-cart lines up horizontally regardless of title/price height. */
.sc-card .sc-atc { margin-top: auto; }

/* catalog */
.sc-catalog { display: flex; gap: 18px; padding: 18px 22px; }
.sc-side { flex: 0 0 210px; display: flex; flex-direction: column; gap: 14px; }
.sc-side-card { background: var(--sc-card); border: 1px solid var(--sc-border); border-radius: 10px; padding: 14px; }
.sc-side-title { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--sc-muted); margin-bottom: 10px; }
.sc-side-title.mt-3 { margin-top: 14px; }
.sc-side-item { display: block; font-size: 13.5px; color: var(--sc-fg); text-decoration: none; padding: 5px 0; opacity: .85; }
.sc-side-item.is-active { color: var(--sc-primary); font-weight: 600; opacity: 1; }
.sc-range { position: relative; height: 4px; border-radius: 3px; background: var(--sc-border); margin: 4px 0 8px; }
.sc-range-fill { position: absolute; left: 8%; right: 26%; top: 0; bottom: 0; border-radius: 3px; background: var(--sc-primary); }
.sc-range-knob { position: absolute; top: 50%; width: 13px; height: 13px; border-radius: 50%; background: #fff; border: 2px solid var(--sc-primary); transform: translate(-50%, -50%); }
.sc-range-labels { display: flex; justify-content: space-between; font-size: 11px; color: var(--sc-muted); }
.sc-check { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--sc-fg); padding: 4px 0; }
.sc-box { width: 15px; height: 15px; border-radius: 4px; border: 1.5px solid var(--sc-border); flex: 0 0 15px; }
.sc-box.is-on { background: var(--sc-primary); border-color: var(--sc-primary); }
.sc-main { flex: 1 1 auto; min-width: 0; }
.sc-sortbar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; }
.sc-layout { display: inline-flex; border: 1px solid var(--sc-border); border-radius: 8px; overflow: hidden; }
.sc-lbtn { width: 32px; height: 30px; display: inline-flex; align-items: center; justify-content: center; font-size: 12px; color: var(--sc-muted); background: var(--sc-card); }
.sc-lbtn.is-active { background: var(--sc-primary); color: var(--sc-primary-fg); }
.sc-sort { font-size: 13px; color: var(--sc-fg); border: 1px solid var(--sc-border); border-radius: 8px; padding: 6px 12px; background: var(--sc-card); display: inline-flex; align-items: center; gap: 7px; }
.sc-sort-cx { font-size: 10px; opacity: .6; }

/* product page */
.sc-pdp { padding: 16px 22px; }
.sc-crumb { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--sc-primary); margin-bottom: 14px; }
.sc-crumb a { color: var(--sc-primary); text-decoration: none; }
.sc-crumb .fa-home { font-size: 12px; }
.sc-sep { color: var(--sc-muted); opacity: .6; }
.sc-crumb-cur { color: var(--sc-muted); }
.sc-pdp-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 28px; }
.sc-gallery-main { aspect-ratio: 4 / 3; border-radius: 12px; background-size: cover; background-position: center; background-color: var(--sc-card); }
.sc-thumbs { display: flex; gap: 8px; margin-top: 10px; }
.sc-thumb { width: 56px; height: 56px; border-radius: 8px; background-size: cover; background-position: center; border: 2px solid transparent; }
.sc-thumb.is-on { border-color: var(--sc-primary); }
.sc-pdp-info { padding-top: 4px; }
.sc-pdp-cat { font-size: 12px; font-weight: 700; letter-spacing: .07em; color: var(--sc-primary); }
.sc-pdp-title { font-size: 30px; font-weight: 800; margin: 4px 0 12px; letter-spacing: -.02em; }
.sc-price-row { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; }
.sc-stock { font-size: 11px; font-weight: 600; color: #22c55e; background: rgba(34,197,94,.16); border-radius: 6px; padding: 2px 8px; }
.sc-price-big { font-size: 26px; font-weight: 800; color: var(--sc-primary); margin-bottom: 10px; }
.sc-sku { font-size: 12px; color: var(--sc-muted); margin-bottom: 12px; }
.sc-pdp-desc { font-size: 13.5px; color: var(--sc-muted); line-height: 1.55; margin-bottom: 16px; }
.sc-opt-label { font-size: 13px; font-weight: 600; margin-bottom: 8px; }
.sc-chips { display: flex; gap: 8px; margin-bottom: 18px; }
.sc-chip { min-width: 40px; text-align: center; font-size: 13px; padding: 7px 12px; border: 1px solid var(--sc-border); border-radius: 8px; color: var(--sc-fg); background: var(--sc-card); }
.sc-chip.is-on { border-color: var(--sc-primary); color: var(--sc-primary); font-weight: 600; }
.sc-buy { display: flex; align-items: center; gap: 12px; }
.sc-qty { display: inline-flex; align-items: center; border: 1px solid var(--sc-border); border-radius: 8px; overflow: hidden; }
.sc-qbtn { width: 34px; height: 40px; display: inline-flex; align-items: center; justify-content: center; font-size: 16px; color: var(--sc-muted); }
.sc-qval { width: 34px; text-align: center; font-size: 14px; font-weight: 600; }
.sc-atc--lg { padding: 12px 24px; font-size: 14px; flex: 1 1 auto; }

.landing-bullets { list-style: none; padding: 0; margin: 0; }
.landing-bullets li {
  display: flex;
  align-items: flex-start;
  gap: .75rem;
  padding: .5rem 0;
  color: var(--fg);
}
.landing-bullets li i {
  color: var(--primary);
  margin-top: .2rem;
  font-size: 1rem;
}

/* ── Foundation / partner logo strip (real grayscale logos) ── */
.landing-partners { padding: 2.6rem 0 3rem; }
.landing-partners-row {
  display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center;
  gap: 2rem 2.4rem;
}
.landing-partner-logo {
  /* Logos are pre-trimmed to their ink bounds, so equal height = equal
     optical size. Wordmarks (Hetzner/Resend) get a smaller height below. */
  height: 46px; width: auto; max-width: 190px; flex: 0 0 auto;
  filter: grayscale(1); opacity: .55;
  transition: opacity .2s ease, filter .2s ease, transform .2s ease;
}
/* Wordmarks (Hetzner/Resend): smaller, and nudged down to sit on the same
   visual baseline as the icon-lockup logos. */
.landing-partner-logo--wordmark { height: 30px; position: relative; top: 10px; }
/* Optional larger variant (currently unused). */
.landing-partner-logo--lg { height: 52px; }
.landing-partner-logo:hover { filter: grayscale(0); opacity: 1; transform: translateY(-2px); }
/* Dark mode: render every logo as a clean white silhouette (colored or dark
   wordmarks stay visible), brightening on hover instead of revealing color. */
[data-theme="dark"] .landing-partner-logo { filter: grayscale(1) brightness(0) invert(1); opacity: .62; }
[data-theme="dark"] .landing-partner-logo:hover { filter: grayscale(1) brightness(0) invert(1); opacity: 1; transform: translateY(-2px); }
/* On small screens fall back to centered wrapping so logos don't stretch apart */
@media (max-width: 575.98px) {
  .landing-partners-row { justify-content: center; gap: 1.5rem 2rem; }
  .landing-partner-logo { height: 38px; }
  .landing-partner-logo--lg { height: 42px; }
  .landing-partner-logo--wordmark { height: 25px; top: 8px; }
}

/* ── Centered section heading (storefront / dashboard intros) ── */
.landing-section-head { max-width: 760px; margin: 0 auto 3rem; text-align: center; }
.landing-section-head h2 { font-weight: 800; letter-spacing: -.02em; margin: .9rem 0 .6rem; }
.landing-section-head p { color: var(--muted); font-size: 1.05rem; line-height: 1.6; margin: 0; }

/* ── Spotlight rows (image + copy, alternating) ── */
.landing-spotlight + .landing-spotlight { margin-top: 3.5rem; }

/* ── Screenshot placeholder figure — owner swaps the <img src> ── */
.landing-shot { margin: 0; }
.landing-shot-frame {
  position: relative; border-radius: 18px; overflow: hidden;
  border: 1px solid var(--border); background: var(--card);
  box-shadow: 0 24px 60px -30px rgba(40,16,80,.5), 0 8px 24px rgba(159,80,223,.08);
}
.landing-shot-frame img { display: block; width: 100%; height: auto; }
.landing-shot figcaption {
  display: flex; align-items: center; justify-content: center; gap: .4rem;
  font-size: .8rem; color: var(--muted); margin-top: .7rem;
}
.landing-shot figcaption i { color: var(--primary); opacity: .7; }

/* ── Light/dark showcase sub-label ── */
.landing-theme-label {
  text-align: center; font-weight: 700; font-size: 1.05rem;
  color: var(--muted); margin: 0 0 1.25rem; letter-spacing: -.01em;
}

/* ── Coming soon ── */
.landing-soon-card {
  position: relative;
  padding: 1.75rem;
  background: rgba(255,255,255,.88);
  border: 1px dashed var(--border);
  border-radius: 16px;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  transition: transform .2s ease, border-color .2s ease;
}
[data-theme="dark"] .landing-soon-card { background: rgba(20,26,42,.78); }
.landing-soon-card:hover {
  transform: translateY(-3px);
  border-color: rgba(159,80,223,.4);
}
.landing-soon-badge {
  position: absolute;
  top: 12px;
  right: 12px;
  padding: .2rem .55rem;
  border-radius: 999px;
  background: linear-gradient(120deg, #f59e0b, #f97316);
  color: #fff;
  font-size: .7rem;
  font-weight: 700;
  letter-spacing: .02em;
}
.landing-soon-badge i { font-size: .65rem; margin-right: .25rem; }
.landing-soon-icon {
  width: 52px;
  height: 52px;
  border-radius: 14px;
  background: linear-gradient(135deg, rgba(245,158,11,.15), rgba(249,115,22,.1));
  color: #f59e0b;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 1.3rem;
  margin-bottom: 1rem;
}
.landing-soon-title { font-weight: 700; font-size: 1.05rem; margin-bottom: .5rem; color: var(--fg); }
.landing-soon-text { color: var(--muted); font-size: .92rem; line-height: 1.55; margin-bottom: 0; }

/* ── Demo strip ── */
.landing-demo {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2rem;
  padding: 2rem 2.5rem;
  background: linear-gradient(135deg, var(--primary-subtle) 0%, var(--card) 100%);
  border: 1px solid var(--border);
  border-radius: 16px;
  flex-wrap: wrap;
}

/* The two demo entry points (storefront + read-only admin) stay together as a
   unit when the band wraps, instead of one button drifting onto its own row. */
.landing-demo__actions {
  display: flex;
  gap: 0.75rem;
  flex-wrap: wrap;
}

/* ── Pricing table (mobile scrollable) ── */
.pricing-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border-radius: 14px;
  border: 1px solid var(--border);
  background: rgba(255,255,255,.88);
  box-shadow: var(--shadow-sm);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
[data-theme="dark"] .pricing-scroll { background: rgba(20,26,42,.78); }
.pricing-scroll .pricing-table {
  min-width: 720px;
  width: 100%;
  border-collapse: collapse;
  background: transparent;
}
.pricing-scroll .pricing-table th,
.pricing-scroll .pricing-table td {
  padding: 1rem;
  border-bottom: 1px solid var(--border);
  color: var(--fg);
  background: transparent;
  vertical-align: middle;
}
.pricing-scroll .pricing-table tr:last-child td { border-bottom: none; }
.pricing-scroll .pricing-table thead th { color: var(--muted); font-weight: 600; vertical-align: bottom; }

/* Plan header: name + price stack */
.pricing-plan-name {
  font-size: .95rem;
  font-weight: 700;
  color: var(--fg);
  letter-spacing: .01em;
  text-transform: uppercase;
  margin-bottom: .35rem;
}
.pricing-plan-name--popular {
  position: relative;
  color: var(--primary);
}
.pricing-plan-popular {
  display: inline-block;
  margin-left: .4rem;
  padding: .1rem .5rem;
  font-size: .62rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: #fff;
  background: linear-gradient(120deg, #7c3aed, #e066b5);
  border-radius: 999px;
  vertical-align: middle;
  box-shadow: 0 3px 10px rgba(159,80,223,.35);
}
.pricing-plan-price {
  display: inline-flex;
  align-items: baseline;
  gap: .05rem;
  background: linear-gradient(120deg, var(--primary) 0%, #e066b5 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  font-weight: 800;
}
.pricing-plan-currency { font-size: 1rem; margin-right: .1rem; }
.pricing-plan-amount { font-size: 1.9rem; line-height: 1; letter-spacing: -.02em; }
.pricing-plan-period {
  font-size: .72rem;
  font-weight: 600;
  color: var(--muted);
  -webkit-text-fill-color: var(--muted);
  margin-left: .15rem;
  letter-spacing: .02em;
}

/* Pricing CTAs */
.pricing-btn-free,
.pricing-btn-gradient,
.pricing-btn-outline { white-space: nowrap; }
.pricing-btn-free {
  background: linear-gradient(120deg, #16a34a, #22c55e);
  color: #fff !important;
  font-weight: 700;
  border: none;
  padding: .55rem 1rem;
  border-radius: 10px;
  box-shadow: 0 6px 16px rgba(34,197,94,.35);
  transition: transform .2s ease, box-shadow .2s ease, filter .2s ease;
}
.pricing-btn-free:hover,
.pricing-btn-free:focus {
  transform: translateY(-2px);
  box-shadow: 0 10px 22px rgba(34,197,94,.45);
  color: #fff !important;
  filter: brightness(1.05);
}
.pricing-btn-gradient {
  background: linear-gradient(120deg, #7c3aed 0%, #9f50df 50%, #e066b5 100%);
  background-size: 180% 100%;
  background-position: 0% 50%;
  color: #fff !important;
  font-weight: 700;
  border: none;
  padding: .55rem 1rem;
  border-radius: 10px;
  box-shadow: 0 6px 16px rgba(159,80,223,.35);
  transition: background-position .5s ease, transform .2s ease, box-shadow .2s ease;
}
.pricing-btn-gradient:hover,
.pricing-btn-gradient:focus {
  background-position: 100% 50%;
  transform: translateY(-2px);
  box-shadow: 0 12px 26px rgba(159,80,223,.5);
  color: #fff !important;
}
.pricing-btn-outline {
  background: transparent;
  color: var(--fg) !important;
  font-weight: 600;
  border: 1px solid var(--border);
  padding: .55rem 1rem;
  border-radius: 10px;
  transition: border-color .2s ease, color .2s ease, background .2s ease;
}
.pricing-btn-outline:hover,
.pricing-btn-outline:focus {
  border-color: var(--primary);
  color: var(--primary) !important;
  background: var(--primary-subtle);
}

/* ── Monthly / yearly toggle ── */
.pricing-toggle-wrap {
  display: flex;
  justify-content: center;
  margin: 0 0 1.5rem;
}
.pricing-toggle {
  display: inline-flex;
  padding: 4px;
  background: var(--bs-tertiary-bg, #f4f4f5);
  border-radius: 999px;
  border: 1px solid var(--border, #e4e4e7);
  gap: 2px;
}
[data-theme="dark"] .pricing-toggle {
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(255, 255, 255, 0.1);
}
.pricing-toggle-btn {
  appearance: none;
  background: transparent;
  border: 0;
  padding: .5rem 1.1rem;
  font-size: .875rem;
  font-weight: 600;
  color: var(--muted, #71717a);
  border-radius: 999px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  transition: background-color .15s ease, color .15s ease;
  white-space: nowrap;
}
.pricing-toggle-btn:hover { color: var(--fg, #18181b); }
.pricing-toggle-btn.is-active {
  background: var(--card, #ffffff);
  color: var(--fg, #18181b);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
}
[data-theme="dark"] .pricing-toggle-btn.is-active {
  background: rgba(255, 255, 255, 0.1);
  color: #fff;
}
.pricing-toggle-badge {
  display: inline-block;
  padding: 2px 8px;
  font-size: .65rem;
  font-weight: 700;
  letter-spacing: .03em;
  background: linear-gradient(120deg, #16a34a, #22c55e);
  color: #fff;
  border-radius: 999px;
  line-height: 1.3;
}

/* ── Price variants (monthly vs yearly) ── */
.pricing-price-variant { display: none; }
[data-pricing-mode="monthly"] .pricing-price-variant--monthly { display: inline; }
[data-pricing-mode="yearly"] .pricing-price-variant--yearly { display: inline; }
.pricing-plan-billed-note {
  display: block;
  margin-top: .2rem;
  /* Readable, not decorative: this line carries the amount actually charged
     while the headline above shows a per-month figure that cannot be paid
     monthly. Same reasoning as .lp2-pt-note on the landing table. */
  font-size: .78rem;
  font-weight: 600;
  color: var(--fg, #18181b);
  letter-spacing: 0;
  text-transform: none;
}
.pricing-plan-custom {
  font-size: 1.4rem;
  font-weight: 700;
  letter-spacing: -.01em;
}

/* ── Shop-owner mode: current plan + "Switch" buttons ── */
.pricing-btn-current {
  background: var(--bs-success, #16a34a);
  color: #fff !important;
  border: none;
  padding: .55rem 1rem;
  border-radius: 10px;
  font-weight: 700;
  white-space: nowrap;
  opacity: 1 !important;
  cursor: default;
}
.pricing-btn-current:disabled { opacity: 1 !important; }
.pricing-plan-current-badge {
  display: inline-block;
  margin-left: .35rem;
  padding: 2px 8px;
  font-size: .65rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  background: var(--bs-success, #16a34a);
  color: #fff;
  border-radius: 999px;
  vertical-align: middle;
}
.pricing-scroll .pricing-table th.pricing-th--current {
  background: rgba(22, 163, 74, 0.06);
}
[data-theme="dark"] .pricing-scroll .pricing-table th.pricing-th--current {
  background: rgba(34, 197, 94, 0.1);
}

/* ── Superadmin mode: editable limit inputs ── */
.pricing-scroll .pricing-table .pricing-input {
  max-width: 110px;
  margin: 0 auto;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* ── Superadmin mode: pricing editor FX diff pills ── */
.pricing-diff-pill {
  min-width: 64px;
  justify-content: center;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}
.pricing-diff-pill--neutral { color: var(--muted, #71717a); }
.pricing-diff-pill--up      { color: #b91c1c; background: rgba(220, 38, 38, 0.08); }
.pricing-diff-pill--down    { color: #15803d; background: rgba(22, 163, 74, 0.08); }
[data-theme="dark"] .pricing-diff-pill--up   { color: #fca5a5; background: rgba(220, 38, 38, 0.16); }
[data-theme="dark"] .pricing-diff-pill--down { color: #86efac; background: rgba(22, 163, 74, 0.18); }

/* ── Final CTA ── */
.landing-cta {
  position: relative;
  overflow: hidden;
  text-align: center;
  padding: 4.5rem 2rem;
  background:
    radial-gradient(ellipse at 20% 20%, #c17ce8 0%, transparent 50%),
    radial-gradient(ellipse at 80% 80%, #e066b5 0%, transparent 50%),
    linear-gradient(135deg, #7c3aed 0%, #9f50df 55%, #b85cb8 100%);
  border-radius: 24px;
  color: #fff;
  box-shadow: 0 30px 80px rgba(159,80,223,.35);
}
.landing-cta-pattern {
  position: absolute;
  inset: 0;
  background-image:
    radial-gradient(circle at 2px 2px, rgba(255,255,255,.15) 1.5px, transparent 2px);
  background-size: 24px 24px;
  pointer-events: none;
  opacity: .5;
}
.landing-cta-sheen {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(115deg, transparent 0%, rgba(255,255,255,0.18) 45%, rgba(255,255,255,0) 60%);
  pointer-events: none;
  mix-blend-mode: overlay;
}
.landing-cta-inner {
  position: relative;
  z-index: 1;
  max-width: 680px;
  margin: 0 auto;
}
.landing-cta h2, .landing-cta p, .landing-cta .lead { color: #fff; }
.landing-cta-badge {
  display: inline-flex;
  align-items: center;
  padding: .4rem 1rem;
  margin-bottom: 1rem;
  border-radius: 999px;
  background: rgba(255,255,255,.18);
  color: #fff;
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  border: 1px solid rgba(255,255,255,.3);
  backdrop-filter: blur(6px);
}
.landing-cta-btn {
  background: #fff;
  color: #7c3aed !important;
  border: none;
  font-weight: 700;
  padding: .9rem 2rem;
  border-radius: 12px;
  box-shadow: 0 10px 30px rgba(0,0,0,.15);
  transition: transform .2s ease, box-shadow .2s ease;
}
.landing-cta-btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 15px 40px rgba(0,0,0,.2);
  color: #6d28d9 !important;
}
.landing-cta-fineprint {
  margin: 1.25rem 0 0;
  font-size: .85rem;
  color: rgba(255,255,255,.85) !important;
  letter-spacing: .01em;
}

/* ── Suggest-a-feature card ── */
.landing-suggest-card {
  background: rgba(255,255,255,.88);
  border: 1px dashed rgba(245,158,11,.45);
}
[data-theme="dark"] .landing-suggest-card { background: rgba(20,26,42,.78); }
.landing-suggest-icon {
  background: linear-gradient(135deg, rgba(245,158,11,.25), rgba(249,115,22,.15)) !important;
  color: #f59e0b !important;
  animation: landingSuggestBlink 3.2s ease-in-out infinite;
}
@keyframes landingSuggestBlink {
  0%,100% { box-shadow: 0 0 0 0 rgba(245,158,11,0); }
  50%     { box-shadow: 0 0 0 6px rgba(245,158,11,.18); }
}
.landing-suggest-form textarea {
  resize: vertical;
  min-height: 72px;
}
.landing-suggest-submit {
  background: linear-gradient(120deg, #f59e0b, #f97316);
  color: #fff !important;
  border: none;
  font-weight: 700;
  padding: .45rem 1rem;
  border-radius: 10px;
  box-shadow: 0 6px 16px rgba(249,115,22,.3);
  transition: transform .2s ease, box-shadow .2s ease, filter .2s ease;
}
.landing-suggest-submit:hover,
.landing-suggest-submit:focus {
  transform: translateY(-2px);
  box-shadow: 0 10px 22px rgba(249,115,22,.4);
  filter: brightness(1.05);
  color: #fff !important;
}
.landing-suggest-submit:disabled {
  opacity: .65;
  cursor: not-allowed;
  transform: none;
}

/* ── Responsive ── */
@media (max-width: 992px) {
  .landing-hero-grid { grid-template-columns: 1fr; gap: 2rem; }
  .landing-hero-media { min-height: 420px; margin-top: 1rem; }
  .landing-hero-visual { max-width: 420px; margin: 0 auto; }
  .landing-hero-logo { left: 50%; width: 420px; }
  /* More headroom on mobile/tablet: larger min-height gives the content
     room to shift upward when the rotator becomes 2 lines. */
  .landing-hero-copy { min-height: 440px; }
  /* Tighter section rhythm on tablets */
  body.is-landing-page .landing-section.py-5 { padding-top: 2.25rem !important; padding-bottom: 2.25rem !important; }
}
@media (max-width: 768px) {
  body.is-landing-page .landing-section.py-5 { padding-top: 1.75rem !important; padding-bottom: 1.75rem !important; }
  .landing-hero { padding: 1rem 0 2rem; }
}
@media (max-width: 576px) {
  /* Hero sits close to the fixed header */
  .landing-hero { padding: .5rem 0 1.5rem; }
  .landing-hero-copy { text-align: center; margin: 0 auto; }
  .landing-hero-copy .d-flex { justify-content: center; }
  .landing-hero-subtitle { margin-left: auto; margin-right: auto; }
  /* Mobile: copy is centered, so center the eyebrow pill too */
  .landing-eyebrow { align-self: center; }
  .landing-hero-title { text-align: center; }
  .landing-hero-title-static,
  .landing-hero-title-rotator-line { text-align: center; }
  .landing-rotator { display: inline-block; }

  /* Compact, centered card stack with overlap + visible "S" logo */
  .landing-hero-media, .landing-hero-visual { min-height: 430px; max-width: 300px; margin-left: auto; margin-right: auto; }
  .landing-hero-card { width: 160px; }
  .landing-hero-card--1 { right: auto; left: 0; top: 0; }
  .landing-hero-card--2 { right: 0; left: auto; top: 80px; }
  .landing-hero-card--3 { right: auto; left: 70px; top: 180px; }
  /* Logo large enough to peek out between cards, nudged slightly right */
  .landing-hero-logo { left: 52%; width: 380px; opacity: .22; }
  [data-theme="dark"] .landing-hero-logo { opacity: .28; }

  /* Smaller paddings on small screens */
  body.is-landing-page .landing-section.py-5 { padding-top: 1.25rem !important; padding-bottom: 1.25rem !important; }
  .landing-demo { padding: 1.5rem; flex-direction: column; align-items: flex-start; }
  /* Narrow screens: full-width stacked buttons rather than two cramped ones. */
  .landing-demo__actions { width: 100%; }
  .landing-demo__actions .btn { flex: 1 1 100%; }
  .landing-cta { padding: 2.5rem 1.25rem; border-radius: 20px; }
}

/* ─── Auth pages (login + signup) ─── */
/* Same tilted gradient + dot pattern as the landing page, painted directly
   onto the body so the whole page (below header, above footer) is covered. */
body.is-auth-page {
  background:
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='32' height='32'><circle cx='1.5' cy='1.5' r='1.5' fill='%234a2a7a' fill-opacity='0.14'/></svg>"),
    linear-gradient(135deg,
      rgba(124,58,237,0.14) 0%,
      rgba(159,80,223,0.12) 22%,
      rgba(210,90,190,0.11) 48%,
      rgba(240,130,130,0.10) 72%,
      rgba(250,190,120,0.11) 100%
    ),
    var(--bg) !important;
  background-attachment: fixed, fixed, fixed;
  background-size: 32px 32px, auto, auto;
}
[data-theme="dark"] body.is-auth-page {
  background:
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='32' height='32'><circle cx='1.5' cy='1.5' r='1.5' fill='%23d9c6f5' fill-opacity='0.12'/></svg>"),
    linear-gradient(135deg,
      rgba(124,58,237,0.20) 0%,
      rgba(159,80,223,0.16) 28%,
      rgba(210,90,190,0.13) 55%,
      rgba(240,130,130,0.11) 80%,
      rgba(250,190,120,0.10) 100%
    ),
    var(--bg) !important;
  background-attachment: fixed, fixed, fixed;
  background-size: 32px 32px, auto, auto;
}

/* Lock the page to viewport height with no scroll, and zero the
   main margin so body's fixed gradient isn't pushed down 64px by
   main's margin-top collapsing into body. Footer hidden too. */
body.is-auth-page {
  height: 100vh;
  overflow: hidden;
}
body.is-auth-page footer { display: none !important; }
body.is-auth-page main,
body.is-auth-page main.container,
body.is-auth-page main.container.my-4 {
  margin-top: 0 !important;
  margin-bottom: 0 !important;
}
body.is-auth-page .auth-center {
  position: fixed;
  inset: 0;
  overflow-y: auto;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 64px 1rem 2rem;
}
body.is-auth-page .auth-inner {
  width: 100%;
  max-width: 520px;
  /* margin:auto centers vertically when there's room; when the card is taller
     than the viewport, the auto margins collapse so the top stays reachable and
     the container scrolls (align-items:center would clip the top instead). */
  margin: auto;
}
/* Mobile: when the card fills the viewport it pins to the top, so give it the
   same ~1rem gap below the fixed header bar as it has on the sides. */
@media (max-width: 767.98px) {
  body.is-auth-page .auth-center { padding-top: calc(var(--navbar-height, 64px) + 1rem); }
}

/* Shop customer auth pages (storefront layout — preserves header/footer).
   Used by shop_account_{login,register,forgot,reset}.ejs and the subscribe/
   unsubscribe status views. Narrow centered column with padding from the
   sticky shop header bar above. */
body:not(.is-auth-page) .auth-center {
  max-width: 480px;
  margin: 0 auto;
  padding: 4rem 1rem 3rem;
}
body:not(.is-auth-page) .auth-inner {
  width: 100%;
}

/* Auth cards default to the signup width (it has the most fields). On phones the
   viewport (minus .auth-center padding) already caps it below this, so the
   width only matters on desktop. */
body.is-auth-page .auth-inner:has(.auth-card) { max-width: 590px; }
/* Login and forgot-password have far fewer fields, so give their cards a
   narrower desktop width. The #id args make these overrides win on specificity
   regardless of order. */
body.is-auth-page .auth-inner:has(#loginForm),
body.is-auth-page .auth-inner:has(#forgotForm),
body.is-auth-page .auth-inner:has(#resetForm) { max-width: 390px; }
/* ─── Signup: form card (whole content in a solid rounded box) ─── */
.auth-inner .auth-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 2rem;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.12);
}
@media (max-width: 575.98px) {
  .auth-inner .auth-card { padding: 1.5rem; }
}
/* Terms/privacy acceptance line: a touch smaller than Bootstrap's .small. */
.auth-card .form-check-label { font-size: 0.8rem; }
/* Match the ".shopiza.io" suffix to the input's 14px text size. */
.auth-inner .auth-card .input-group-text { font-size: 0.875rem; }
/* Give the submit button the same 46px height as the Continue-with-Google button. */
.auth-inner .auth-card button[type="submit"] {
  height: 46px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* Nudge the Shop URL (i) help-tip 2px left. The transform MUST go on
   the button, not the .help-tip wrapper — a transform on the wrapper makes it the
   containing block for the fixed-positioned bubble, which mis-placed the tooltip
   off-screen (hiding it + spawning scrollbars). The button is a sibling of the
   bubble, so transforming it is safe and the JS positions the bubble off its rect. */
.auth-inner .auth-card .help-tip__btn { transform: translate(-2px, 0); }
/* Purple (brand) on hover, instead of the admin --pz-brand blue fallback. */
.auth-inner .auth-card .help-tip__btn:hover,
.auth-inner .auth-card .help-tip.is-open .help-tip__btn { color: var(--primary); }
/* Field borders a touch more visible on the card (they read faint on the solid
   background). ~20% of the foreground mixed in, so it holds in light + dark. */
.auth-inner .auth-card .form-control,
.auth-inner .auth-card .form-select,
.auth-inner .auth-card .input-group-text,
.auth-inner .auth-card .form-check-input {
  border-color: #c3c9d1;
  border-color: color-mix(in srgb, var(--border), var(--fg) 20%);
}
/* Dark mode sets the .form-control border with !important (see ~line 1734), so
   match it with !important + higher specificity to win. */
[data-theme="dark"] .auth-inner .auth-card .form-control,
[data-theme="dark"] .auth-inner .auth-card .form-select,
[data-theme="dark"] .auth-inner .auth-card .input-group-text,
[data-theme="dark"] .auth-inner .auth-card .form-check-input {
  border-color: #454e5e !important;
  border-color: color-mix(in srgb, var(--border), var(--fg) 20%) !important;
}

/* ─── Signup: password reveal toggle + strength meter ─── */
.signup-pw-wrap { position: relative; }
.signup-pw-wrap .form-control { padding-right: 2.75rem; }
.signup-pw-toggle {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  width: 2.75rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: transparent;
  color: var(--muted, #6c757d);
  cursor: pointer;
  padding: 0;
}
.signup-pw-toggle:hover { color: var(--fg); }
.signup-pw-meter { display: flex; gap: 6px; margin-top: .5rem; }
.signup-pw-meter span {
  flex: 1;
  height: 6px;
  border-radius: 3px;
  background: var(--border, #dee2e6);
  transition: background .15s ease;
}
.signup-pw-meter[data-level="1"] span:nth-child(-n+1) { background: #e5484d; }
.signup-pw-meter[data-level="2"] span:nth-child(-n+2) { background: #e08c00; }
.signup-pw-meter[data-level="3"] span:nth-child(-n+3) { background: #2f6feb; }
.signup-pw-meter[data-level="4"] span:nth-child(-n+4) { background: #2fa84f; }
.signup-pw-label { min-height: 1.1em; font-weight: 600; margin-top: .35rem; }
.signup-pw-label[data-level="1"] { color: #e5484d; }
.signup-pw-label[data-level="2"] { color: #e08c00; }
.signup-pw-label[data-level="3"] { color: #2f6feb; }
.signup-pw-label[data-level="4"] { color: #2fa84f; }

/* Auth inline links (terms, privacy, sign in): neutral not blue, underlined,
   brand accent on hover. */
.auth-inner a.auth-link {
  color: var(--fg);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
  font-weight: 600;
  transition: color .12s ease;
}
.auth-inner a.auth-link:hover { color: var(--pz-brand, var(--primary-text, #7c3aed)); }

/* Login "Forgot your password?" — sits beside the password label. Reads as
   plain text (normal colour, a touch smaller, no underline); brand accent on
   hover. */
.auth-inner .login-forgot-link {
  color: var(--fg);
  font-size: 0.8rem;
  text-decoration: none;
  transition: color .12s ease;
}
.auth-inner .login-forgot-link:hover { color: var(--pz-brand, var(--primary-text, #7c3aed)); }

/* "Continue with Google" button. The multi-colour G works on either theme, so
   we invert the container for contrast: DARK button on light mode, WHITE button
   on dark mode. */
.btn-google {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  height: 46px;
  background: #131314;
  color: #e3e3e3;
  border: 1px solid #5f6368;
  font-weight: 600;
}
.btn-google:hover, .btn-google:focus { background: #1f2021; color: #e3e3e3; border-color: #7a7d80; }
.btn-google svg { width: 18px; height: 18px; flex: 0 0 auto; }
[data-theme="dark"] .btn-google { background: #fff; color: #1f1f1f; border-color: #dadce0; }
[data-theme="dark"] .btn-google:hover { background: #f1f3f4; color: #1f1f1f; border-color: #d2d5da; }

/* "or with email" divider */
.auth-divider {
  display: flex;
  align-items: center;
  text-align: center;
  color: var(--muted, #8a8479);
  font-size: 13px;
}
.auth-divider::before, .auth-divider::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--border, #dee2e6);
}
.auth-divider span { padding: 0 12px; white-space: nowrap; }

/* Legal pages (/terms, /privacy, /dpa): readable long-form typography, plus a
   highlight for [TODO] placeholders that are still to be filled while in draft. */
.legal { line-height: 1.6; }
.legal h2 { margin-top: 2rem; margin-bottom: .5rem; }
.legal .legal-meta { color: var(--muted); font-size: .92rem; line-height: 1.5; }
.legal ul { padding-left: 1.25rem; }
.legal li { margin-bottom: .35rem; }
.legal-todo {
  background: #fff3cd;
  color: #664d03;
  padding: 0 .3rem;
  border-radius: 3px;
  font-weight: 600;
}
[data-theme="dark"] .legal-todo { background: #4d3f00; color: #ffe08a; }

/* ─── Image uploader + thumbnails ─── */
.shop-logo { max-height: 80px; max-width: 220px; object-fit: contain; border-radius: 8px; }
.category-thumb { width: 36px; height: 36px; object-fit: cover; border-radius: 4px; border: 1px solid var(--border); background: var(--card); }

/* ─── FilePond dark ─── */
[data-theme="dark"] .filepond--panel-root { background: var(--card) !important; border-color: var(--border) !important; }
[data-theme="dark"] .filepond--drop-label { color: var(--fg) !important; }
[data-theme="dark"] .filepond--item-panel { background: var(--card) !important; }

/* ─── Category link ─── */
.category-link { color: var(--fg) !important; transition: color 0.15s; }
.category-link:hover { color: var(--primary-text, var(--primary)) !important; }

.htmx-request .btn { opacity: .5; transition: opacity 300ms ease-in; }

/* ─── List group (legacy compat) ─── */
[data-theme="dark"] .list-group-item { background: var(--card) !important; border-color: var(--border) !important; color: var(--fg) !important; }

/* ─── Dark mode: admin sidebar ─── */
[data-theme="dark"] .admin-sidebar { background: var(--sidebar-bg); border-right-color: var(--border); }
[data-theme="dark"] .nav-link-item { color: var(--fg); }
[data-theme="dark"] .nav-link-item:hover { background: var(--hover); color: var(--fg); }
[data-theme="dark"] .nav-link-item.active { background: var(--primary-subtle); color: var(--primary); }
[data-theme="dark"] .nav-section-label { color: var(--muted); }

/* ─── Dark mode: qty controls ─── */
[data-theme="dark"] .qty-control { border-color: var(--btn-outline-border, var(--border)); background: var(--card); }
[data-theme="dark"] .qty-btn { color: var(--fg); }
[data-theme="dark"] .qty-btn:hover { background: var(--hover); }
[data-theme="dark"] .qty-input { color: var(--fg); }

/* ─── Dark mode: shop components ─── */
[data-theme="dark"] .product-card { background: var(--card); border-color: var(--border); }
[data-theme="dark"] .product-card-title { color: var(--fg); }
[data-theme="dark"] .product-card-placeholder { background: var(--hover); }
[data-theme="dark"] .product-detail-img { background: var(--hover); border-color: var(--border); }
[data-theme="dark"] .cart-item { border-color: var(--border); }
[data-theme="dark"] .cart-item-img { background: var(--hover); }
[data-theme="dark"] .shop-sidebar-card { background: var(--card); border-color: var(--border); }
[data-theme="dark"] .checkout-step-num { background: var(--card); border-color: var(--border); }
[data-theme="dark"] .checkout-step.active .checkout-step-num { background: var(--primary); border-color: var(--primary); }
[data-theme="dark"] .checkout-step.done .checkout-step-num { background: var(--fg); border-color: var(--fg); color: var(--card); }
[data-theme="dark"] .checkout-step-line { background: var(--border); }

/* ─── Dark mode: breadcrumbs ─── */
[data-theme="dark"] .breadcrumb-item a { color: var(--primary-text); }
[data-theme="dark"] .breadcrumb-item.active { color: var(--fg); }
/* Admin still uses Bootstrap's ::before divider; the storefront moved its own
   to ::after (see below), which carries its colour in both themes. */
[data-theme="dark"] .breadcrumb-item+.breadcrumb-item::before { color: var(--muted); }

/* ─── Storefront breadcrumbs ───
   The separator hangs off the END of the crumb before it, not the start of the
   one after. Bootstrap does the latter, so a trail that wraps begins its new
   line with a stray "/". As a flex item's own ::after it can never be carried
   down on its own. Scoped to the storefront so the admin keeps Bootstrap's. */
body.is-shop-front .breadcrumb-item + .breadcrumb-item::before { content: none; }
body.is-shop-front .breadcrumb-item + .breadcrumb-item { padding-left: 0; }
/* Flex on the ITEM, not just the list. The templates put the link on its own
   line, so the newline before </li> rendered as a real space sitting between
   the link and the ::after — the slash ended up with more room before it than
   after. A flex container drops whitespace-only runs, leaving the padding as
   the only spacing, equal on both sides. */
body.is-shop-front .breadcrumb-item {
  display: flex;
  align-items: baseline;
}
body.is-shop-front .breadcrumb-item:not(:last-child)::after {
  content: "/";
  padding: 0 0.5rem;
  color: var(--muted);
}

@media (max-width: 991.98px) {
  /* Matches .product-card-title, the same tier as the card headings it sits
     above. */
  body.is-shop-front .breadcrumb { font-size: 0.9rem; }
  /* Tighter around the separator, and the home glyph runs a touch smaller than
     the text: at this size its cap height reads heavier than the letters. */
  body.is-shop-front .breadcrumb-item:not(:last-child)::after { padding: 0 4px; }
  /* Nudged up a pixel: the glyph sits low against the crumb text at this size.
     A transform so the optical fix costs no layout. */
  body.is-shop-front .breadcrumb .fa-home {
    font-size: 0.9em;
    transform: translateY(-1px);
  }
}

/* ─── Dark mode: Bootstrap text utilities that don't auto-adapt ─── */
[data-theme="dark"] .text-body  { color: var(--fg)    !important; }
[data-theme="dark"] .text-secondary { color: var(--muted) !important; }
[data-theme="dark"] .text-dark  { color: var(--fg)    !important; }
[data-theme="dark"] h1, [data-theme="dark"] h2, [data-theme="dark"] h3,
[data-theme="dark"] h4, [data-theme="dark"] h5, [data-theme="dark"] h6 { color: var(--fg) !important; }
/* …but the landing demos embed a self-contained storefront that keeps its OWN
   light/dark theme (--st-fg) regardless of the page theme. The rule above is
   !important, so headings inside a demo store (cart/checkout titles, category
   titles) would otherwise render in the PAGE's foreground and go near-invisible
   on the demo's white panel. Re-assert the storefront's own colour. */
[data-theme="dark"] .tour-store h1, [data-theme="dark"] .tour-store h2,
[data-theme="dark"] .tour-store h3, [data-theme="dark"] .tour-store h4,
[data-theme="dark"] .tour-store h5, [data-theme="dark"] .tour-store h6 { color: var(--st-fg) !important; }

/* ─── Dark mode: placeholder text ─── */
[data-theme="dark"] ::placeholder { color: var(--muted) !important; opacity: 1; }

/* ─── Dark mode: variant chips & section ─── */
[data-theme="dark"] #variantSection { border-color: var(--border) !important; }
[data-theme="dark"] #variantSectionToggle { color: var(--fg) !important; }
[data-theme="dark"] .variant-option-group { border-color: var(--border) !important; }
[data-theme="dark"] .variant-value-chip { background: var(--hover) !important; border-color: var(--border) !important; }
[data-theme="dark"] .variant-value-chip input { color: var(--fg) !important; background: transparent !important; }
/* The admin-scoped `.form-control` rule at ~line 3054 sets every input
   inside the admin shell to a dark `#161616` in dark mode, overriding the
   `background: transparent` above. For value-name inputs we want the chip
   to be the visible surface — so cancel that paint with a higher-specificity
   selector that keeps the input transparent. */
[data-theme="dark"] .admin-shell[data-admin-shell] .variant-value-chip .form-control {
  background-color: transparent !important;
}

/* ─── Product detail variant chips ─── */
.variant-chip {
  display: inline-block;
  padding: 0.375rem 0.875rem;
  font-size: 0.875rem;
  line-height: 1.4;
  border: 1.5px solid var(--border);
  border-radius: 2rem;
  background: transparent;
  color: var(--fg);
  cursor: pointer;
  transition: border-color .15s, background .15s, color .15s;
  -webkit-tap-highlight-color: transparent;
}
.variant-chip:hover {
  border-color: var(--primary);
  color: var(--primary);
}
.variant-chip.active {
  border-color: var(--primary-solid, var(--primary));
  background: var(--primary-solid, var(--primary));
  color: var(--primary-fg, var(--bg, #fff));
  box-shadow: 0 0 0 1px rgba(0,0,0,0.1);
}
.variant-chip.is-flashing {
  animation: variantFlash 0.35s ease 3;
}
.variant-chip.chip-oos {
  opacity: 0.35;
  cursor: not-allowed;
  text-decoration: line-through;
  pointer-events: none;
}

/* ─── Attributes masonry layout ─── */
.attr-groups-masonry {
  column-count: 1;
  column-gap: 0.5rem;
}
@media (min-width: 768px) {
  .attr-groups-masonry { column-count: 2; }
}
.attr-groups-masonry .attr-group-section {
  break-inside: avoid;
  margin-bottom: 0.5rem;
}

/* ─── Attributes section & groups hover ─── */
#attributeSectionToggle:hover { background: rgba(0,0,0,.04) !important; }
.attr-group-section:hover { background: rgba(0,0,0,.03); }

/* ─── Dark mode: attributes section & groups ─── */
[data-theme="dark"] #attributeSection { border-color: var(--border) !important; }
[data-theme="dark"] #attributeSectionToggle { color: var(--fg) !important; }
[data-theme="dark"] #attributeSectionToggle:hover { background: rgba(255,255,255,.06) !important; }
[data-theme="dark"] .attr-group-section { border-color: var(--border) !important; }
[data-theme="dark"] .attr-group-toggle { color: var(--fg) !important; }
[data-theme="dark"] .attr-group-section:hover { background: rgba(255,255,255,.04); }

/* ─── Mobile shop header ─── */
@media (max-width: 991px) {
  /* Kept as the positioning context for the search panel. */
  .navbar .container { position: relative; flex-wrap: nowrap; }
  /* Tighter bar on phones. Bootstrap's .navbar-brand carries 5px top + 5px
     bottom padding, which bought nothing here: the brand is already the
     tallest thing in the row, so that 10px was pure header height. Dropping
     it plus trimming the navbar's own 10px to 8px takes the default bar from
     63px to 49px. Keep --navbar-height (layout.ejs) in step with this. */
  body > header .navbar { padding: 0.5rem 0; }
  body > header .navbar-brand { padding-top: 0; padding-bottom: 0; }
  /* The brand used to be absolutely centred here, which took it out of the
     flex flow — so nothing constrained its width and a large logo setting
     (20-72px tall, any aspect ratio) simply lay on top of the menu / search /
     cart buttons.
     Putting it back IN FLOW is what makes the owner's setting work: flexbox
     hands the brand exactly the space left between the controls, whatever
     they happen to be on this page, and the logo renders at the height the
     owner chose right up until it would actually reach a button. Capping it
     with a fixed max-width instead made the clamp bind at every setting, so
     the logo came out the same small size no matter what was configured. */
  .shop-menu-toggle { flex: 0 0 auto; }
  .navbar .container > .ms-auto { flex: 0 0 auto; }
  /* Search sits beside the menu button (markup order already puts it there),
     so it only needs to hold its size. The gutter next to the brand belongs
     on the brand itself, below, or the logo ends up 8px off centre: spacing
     the search button pushes the brand right, with nothing balancing it on
     the other side. */
  .navbar .container > .shop-search-toggle { flex: 0 0 auto; }
  /* Account takes the slot search used to hold, ahead of the cart. */
  .navbar .container > .ms-auto > .shop-account-btn { order: -1; }
  .shop-brand {
    flex: 1 1 auto;
    min-width: 0;
    justify-content: center;
    /* Equal gutters both sides, matching the 8px rhythm of the button gaps,
       so the logo reads as centred between the two clusters. */
    margin: 0 .5rem !important;
  }
  /* Only binds once the logo is wider than that gap. Width-clamping an <img>
     that carries an inline height would squash it, so let it scale down
     proportionally inside the box instead. */
  .shop-brand img {
    min-width: 0;
    max-width: 100%;
    object-fit: contain;
    object-position: center;
  }
  /* Same for the no-logo fallback, so a long shop name ellipsises rather than
     pushing the badge out under the buttons. */
  .shop-brand-fallback { max-width: 100%; min-width: 0; }
  .shop-brand-fallback__name { max-width: 100%; min-width: 0; }
}
/* Desktop is unchanged. The search button is a container-level sibling now, so
   order + an auto margin put it back at the head of the right-hand cluster
   (search, cart, account, ...) instead of leaving it beside the brand. The
   .5rem matches the group's own gap-2 so the spacing is seamless. */
@media (min-width: 992px) {
  .navbar .container > .shop-search-toggle { order: 10; margin-left: auto; margin-right: .5rem; }
  .navbar .container > .ms-auto { order: 11; margin-left: 0 !important; }
}

/* ─── Issue 3: Hide desktop category sidebar on mobile ─── */
@media (max-width: 991px) {
  [data-ajax-container="shop"] aside.col-lg-3 {
    display: none !important;
  }
}

/* ─── Prevent iOS zoom on input focus + double-tap zoom on buttons ─── */
html, body {
  touch-action: manipulation;
}
a, button, input, select, textarea, label, [role="button"] {
  touch-action: manipulation;
}
@media (max-width: 991px) {
  input, select, textarea { font-size: 16px !important; }
}

/* ─── Footer page links ─── */
.footer-page-link:hover { text-decoration: underline !important; }

/* Footer links sit one step below the surrounding footer text. The wrapping
   `.container small text-muted` already applies Bootstrap's .small (0.875em),
   so this is an ABSOLUTE size on purpose: a second relative step would compound
   against that wrapper the same way the per-link `.small` did before it was
   removed. The copyright line keeps the wrapper size. */
footer > .container.small a { font-size: 12px; }
/* Copyright line matches the links in size, bold so it still anchors the row. */
.footer-copy { font-size: 12px; font-weight: 700; }

/* ─── Legal page typography (tightened) ───
   Scoped to `.legal-content` which is added on partials/shop/page.ejs
   only for page_kind starting with `legal_`. The substantive legal
   templates pack a lot of h2 + p pairs, so the Bootstrap defaults
   (1rem top + 1rem bottom = ~32px between sections) felt loose.
   These rules halve that gap while keeping headings visually
   distinct. Only affects the legal page render — other rich_text
   blocks stay on Bootstrap defaults. */
.legal-content h1 { font-size: 1.65rem; margin: 0 0 0.7rem; line-height: 1.25; }
.legal-content h2 { font-size: 1.15rem; margin: 1.1rem 0 0.35rem; line-height: 1.3; font-weight: 600; }
.legal-content h3 { font-size: 1rem;    margin: 0.9rem 0 0.3rem;  line-height: 1.3; font-weight: 600; }
.legal-content p,
.legal-content ul,
.legal-content ol,
.legal-content table { margin: 0 0 0.55rem; }
.legal-content ul,
.legal-content ol { padding-left: 1.25rem; }
.legal-content li { margin: 0.15rem 0; }
.legal-content .block-rich-text { margin-bottom: 0 !important; }

/* Flash a red ring on the acknowledgement checkbox when the user
   clicks Generate in the legal-text modal without ticking it. Same
   pulse pattern as the storefront variant-chip flash (variantFlash
   above): 3 iterations of 0.35s box-shadow. */
@keyframes legal-ack-flash {
	0%, 100% { box-shadow: none;                                  border-color: #dee2e6; }
	50%      { box-shadow: 0 0 0 4px rgba(220, 53, 69, .55);      border-color: #dc3545; }
}
.legal-ack-flash {
	animation: legal-ack-flash 0.35s ease 3;
}
/* The Generate button looks faded until the user ticks the ack box,
   so the requirement is visually obvious without making the button
   unclickable (it stays clickable so the click can trigger the flash
   above). */
.btn.is-pending-ack {
	opacity: 0.55;
	cursor: not-allowed;
}

/* Admin block editor surface (views/admin/pages/_blocks_editor.ejs).
   The project uses [data-theme="dark"] not [data-bs-theme="dark"], so
   Bootstrap's --bs-tertiary-bg / --bs-border-color never flip in dark
   mode and the editor stays bright. Use the project's own theme vars. */
.block-editor-surface {
	background: var(--bg, #f8f9fb);
	border: 1px solid var(--border, #e5e7eb);
}

/* ─── Shop header page nav: scroll, never wrap ───────────────────────────
   With the category list in the menu the item count is the owner's, not ours,
   so overflow is normal rather than exceptional. Wrapping would silently
   change the header's height and, in the search-first layout, drop a second
   row onto the one the cart already occupies. So the track scrolls sideways,
   the tail fades to say there is more, and a chevron pages through it.
   `hidden` comes off in navigation.js only when the track really overflows. */
.shop-page-nav[data-nav-scroller] {
  position: relative;
  /* flex-basis 0, not auto. A wrapping flex container assigns items to LINES
     using their base size and only THEN shrinks them, so a nav whose basis is
     its content width pushes the cart onto a row of its own before any
     shrinking gets a chance to help. With basis 0 the nav never forces a wrap
     and simply takes whatever the row has left.

     1px rather than 0: measured, a zero basis leaves the item at zero width
     here -- it is placed on the line but grow never reaches it. One pixel is
     enough to be treated as a real item, and grow then hands it the rest.
     0px -> nav 0px, cart wrapped.  1px -> nav 856px, cart alongside. */
  flex: 1 1 1px;
  /* Both of these, and the pair on the track below, are what stop a long menu
     from pushing its neighbours around: without them the nav reports its full
     content width as its minimum, and in the search-first layout that shoved
     the cart onto a row of its own. */
  min-width: 0;
  gap: .25rem;
}
.shop-page-nav__track {
  flex: 1 1 auto;
  /* A flex item defaults to min-width:auto, i.e. never smaller than its
     content -- the scroll container above it is powerless until this is 0. */
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 1rem;
  overflow-x: auto;
  scroll-behavior: smooth;
  scrollbar-width: none;           /* the chevrons are the affordance */
  -ms-overflow-style: none;
}
.shop-page-nav__track::-webkit-scrollbar { display: none; }
.shop-page-nav__track > * { flex: 0 0 auto; }

/* The fade starts well before the edge, so an item dissolves as it approaches
   the chevron rather than being cut off sharply beside it. Each direction only
   fades on the side that actually has more to show. */
.shop-page-nav.can-scroll-right .shop-page-nav__track {
  mask-image: linear-gradient(to right, #000 calc(100% - 48px), transparent);
}
.shop-page-nav.can-scroll-left .shop-page-nav__track {
  mask-image: linear-gradient(to right, transparent, #000 48px);
}
.shop-page-nav.can-scroll-left.can-scroll-right .shop-page-nav__track {
  mask-image: linear-gradient(to right, transparent, #000 48px, #000 calc(100% - 48px), transparent);
}

/* Flex SIBLINGS of the track, not absolutely positioned over it. Overlaying
   them meant reserving space with track padding, and that padding is part of
   the scrollable content: adding it grew scrollWidth, so the end of the list
   was never actually the end and the right chevron never went away. As real
   items they take their own width and can never sit on top of a link. */
.shop-page-nav__arrow {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--fg);
  opacity: .75;
  font-size: .75rem;
  cursor: pointer;
  z-index: 1;
}
/* The two arrows sit differently on purpose.

   NEXT stays in normal flow and holds its space for as long as the menu
   overflows, only its visibility changing. That is what keeps the measurement
   stable: taking it out of layout hands the track ~26px more room, which
   creates 26px more to scroll, which brings it straight back.

   PREV is an overlay. In flow it reserved space at the LEFT as well, so a menu
   that overflows started indented while one that fits sat flush -- the same
   shop looked misaligned at different window widths. It can overlay safely
   because it only ever appears once the list has been scrolled, so whatever is
   under it is content already scrolled past, and the left fade covers it. */
.shop-page-nav__arrow { display: none; }
.shop-page-nav.has-overflow .shop-page-nav__arrow--next {
  display: flex;
  visibility: hidden;
  /* In from the very edge: hard against the cart or the button cluster it read
     as one of them rather than as the menu's own control. */
  margin-right: 10px;
}
.shop-page-nav.has-overflow.can-scroll-right .shop-page-nav__arrow--next { visibility: visible; }
.shop-page-nav.has-overflow.can-scroll-left .shop-page-nav__arrow--prev {
  display: flex;
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  z-index: 1;
}
.shop-page-nav__arrow:hover { opacity: 1; color: var(--primary-text, var(--primary)); }

/* ─── Shop header page nav links (inside navbar) ─── */
.shop-page-nav-link {
	font-size: .9rem;
	color: var(--fg) !important;
	text-decoration: none !important;
	opacity: .8;
	transition: color .15s, opacity .15s;
}
.shop-page-nav-link:hover,
.shop-page-nav-link.active {
	color: var(--primary-text, var(--primary)) !important;
	opacity: 1;
}

/* ─── CMS page content ─── */
.page-content { line-height: 1.7; }
.page-content h1, .page-content h2, .page-content h3, .page-content h4 { margin-top: 1.5rem; margin-bottom: 0.75rem; }
.page-content p { margin-bottom: 1rem; }
.page-content ul, .page-content ol { margin-bottom: 1rem; padding-left: 1.5rem; }
.page-content a { color: var(--primary-text, var(--primary)); text-decoration: underline; }
.page-content img { max-width: 100%; height: auto; border-radius: 0.375rem; }

/* ─── Offcanvas dark mode ─── */
[data-theme="dark"] .offcanvas {
  background: var(--card) !important;
  border-color: var(--border) !important;
  color: var(--fg) !important;
}
[data-theme="dark"] .offcanvas-header { border-bottom-color: var(--border) !important; }
[data-theme="dark"] .offcanvas .list-group-item { background: var(--card) !important; border-color: var(--border) !important; color: var(--fg) !important; }
[data-theme="dark"] .offcanvas .btn-link { color: var(--fg) !important; }

/* ─── Header Search ─── */
.shop-search-panel {
  background: transparent;
  border-top: 1px solid var(--navbar-border);
  padding: .65rem 0;
  position: relative;
  z-index: 1020;
}
.shop-search-panel .input-group-text {
  border-color: var(--btn-outline-border, var(--search-border, #6c757d));
  background-color: var(--card);
}
.shop-search-panel .form-control {
  border-color: var(--btn-outline-border, var(--search-border, #6c757d));
  background-color: var(--card) !important;
  color: var(--fg);
}
.shop-search-panel .btn-outline-secondary {
  background-color: var(--card);
}
.shop-search-panel .form-control:focus,
.shop-search-panel .form-control:focus-visible {
  box-shadow: none !important;
  outline: none !important;
  border-color: var(--btn-outline-border, var(--search-border, #6c757d)) !important;
}
.shop-search-panel .form-control:focus ~ .input-group-text,
.shop-search-panel .input-group:focus-within .input-group-text {
  border-color: var(--btn-outline-border, var(--search-border, #6c757d)) !important;
}
.shop-search-panel .btn:focus,
.shop-search-panel .btn:focus-visible {
  box-shadow: none !important;
  outline: none !important;
}
.shop-search-inner { position: relative; }
.shop-search-dropdown {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: .5rem;
  box-shadow: 0 8px 32px rgba(0,0,0,.14);
  max-height: 420px;
  overflow-y: auto;
  z-index: 1030;
}
.shop-search-show-all {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .7rem 1rem;
  color: var(--primary);
  font-weight: 600;
  font-size: .875rem;
  border-bottom: 1px solid var(--border);
  background: none;
  width: 100%;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  transition: background .12s;
  border-radius: .5rem .5rem 0 0;
}
.shop-search-show-all:hover,
.shop-search-show-all.active { background: var(--primary-subtle); color: var(--primary); }
.shop-search-item {
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: .55rem 1rem;
  text-decoration: none;
  color: var(--fg);
  border-bottom: 1px solid var(--border);
  transition: background .12s;
  cursor: pointer;
}
.shop-search-item:last-child { border-bottom: none; border-radius: 0 0 .5rem .5rem; }
.shop-search-item:hover,
.shop-search-item.active { background: var(--primary-subtle); color: var(--fg); }
.shop-search-item-thumb {
  width: 42px;
  height: 42px;
  object-fit: cover;
  border-radius: .375rem;
  flex-shrink: 0;
  background: var(--border);
}
.shop-search-item-thumb-placeholder {
  width: 42px;
  height: 42px;
  border-radius: .375rem;
  flex-shrink: 0;
  background: var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  font-size: .8rem;
}
.shop-search-item-name {
  flex: 1;
  font-size: .875rem;
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.shop-search-item-price {
  font-size: .8rem;
  color: var(--primary);
  font-weight: 600;
  white-space: nowrap;
  flex-shrink: 0;
}
.shop-search-spinner {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.25rem;
  color: var(--muted);
  font-size: .875rem;
  gap: .5rem;
}

/* --- Collection Carousel --- */
.collection-carousel {
  position: relative;
}
.collection-carousel-track {
  display: flex;
  gap: .75rem;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding-bottom: .25rem;
}
.collection-carousel-track::-webkit-scrollbar { display: none; }
.collection-carousel-item {
  flex: 0 0 180px;
  min-width: 0;
}
@media (min-width: 576px) {
  .collection-carousel-item { flex: 0 0 200px; }
}
@media (min-width: 992px) {
  .collection-carousel-item { flex: 0 0 220px; }
}
.collection-carousel-item .product-card { height: 100%; }
.collection-carousel-item .product-card-img img,
.collection-carousel-item .product-card-img picture img {
  aspect-ratio: 1;
  object-fit: cover;
}

/* Carousel arrows */
.carousel-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1px solid var(--bs-border-color);
  background: rgba(255,255,255,.92);
  color: var(--bs-body-color);
  display: flex;
  align-items: center;
  justify-content: center;
  padding-top: 4px;
  cursor: pointer;
  box-shadow: 0 2px 6px rgba(0,0,0,.1);
  transition: opacity .2s, background .15s;
  opacity: 0;
  pointer-events: none;
  font-size: .8rem;
}
.collection-carousel:hover .carousel-arrow.is-visible,
.collection-carousel .carousel-arrow.is-visible:focus-visible {
  opacity: 1;
  pointer-events: auto;
}
.carousel-arrow:hover { background: #fff; box-shadow: 0 2px 8px rgba(0,0,0,.18); }
.carousel-arrow-left { left: 6px; }
.carousel-arrow-right { right: 6px; }
@media (max-width: 575.98px) {
  .carousel-arrow { width: 30px; height: 30px; font-size: .7rem; }
}

/* Collection widget */
.collection-widget { border-bottom: 1px solid var(--bs-border-color); padding-bottom: 1rem; }
.collection-widget:last-child { border-bottom: none; }
[data-theme="dark"] .collection-widget { border-bottom-color: rgba(255,255,255,.08); }

/* Separator block line — uses the same divider colours as the
   collection-widget border above so dividers across the storefront
   feel consistent in both light + dark mode. */
.block-separator__line {
  margin: 0;
  border: 0;
  border-top: 1px solid var(--bs-border-color);
  opacity: 1;
}
[data-theme="dark"] .block-separator__line { border-top-color: rgba(255,255,255,.08); }
[data-theme="dark"] .carousel-arrow { background: rgba(30,30,30,.92); border-color: rgba(255,255,255,.12); color: rgba(255,255,255,.85); }
[data-theme="dark"] .carousel-arrow:hover { background: rgba(40,40,40,1); color: #fff; }

/* ── Superadmin "viewing shop" banner ──────────────────────── */
.superadmin-shop-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  padding: .5rem .75rem;
  background: #fff3cd;
  border-bottom: 1px solid #ffc107;
  color: #664d03;
  flex-shrink: 0;
}
.superadmin-shop-exit-btn {
  background: rgba(102,77,3,.1);
  border: 1px solid rgba(102,77,3,.25);
  color: #664d03;
  font-size: .75rem;
  padding: .2rem .5rem;
  line-height: 1.4;
}
.superadmin-shop-exit-btn:hover { background: rgba(102,77,3,.2); color: #664d03; }
[data-theme="dark"] .superadmin-shop-banner { background: rgba(255,193,7,.15); border-color: rgba(255,193,7,.4); color: #ffc107; }
[data-theme="dark"] .superadmin-shop-exit-btn { background: rgba(255,193,7,.1); border-color: rgba(255,193,7,.3); color: #ffc107; }
[data-theme="dark"] .superadmin-shop-exit-btn:hover { background: rgba(255,193,7,.2); color: #ffd60a; }

/* Shop row hover — reinforce the pointer cursor */
.shop-row:hover td { background: var(--bs-table-hover-bg); }

/* ── Debug bar ──────────────────────────────────────────────── */
.shopiza-debug-bar {
  background: #1e1e2e;
  color: #cdd6f4;
  font-size: .78rem;
  border-bottom: 2px solid #f38ba8;
}
.shopiza-debug-toggle {
  display: flex;
  align-items: center;
  width: 100%;
  padding: .35rem 1rem;
  background: transparent;
  border: none;
  color: #cdd6f4;
  cursor: pointer;
  font-size: .78rem;
  gap: .25rem;
}
.shopiza-debug-toggle:hover { background: rgba(255,255,255,.05); }
.shopiza-debug-badge { background: rgba(243,139,168,.2); color: #f38ba8; border-radius: .25rem; padding: .05rem .4rem; font-size: .7rem; }
.shopiza-debug-chevron { transition: transform .2s; font-size: .7rem; }
.shopiza-debug-bar.expanded .shopiza-debug-chevron { transform: rotate(180deg); }
.shopiza-debug-body { display: none; padding: .5rem 1rem .75rem; border-top: 1px solid rgba(255,255,255,.08); }
.shopiza-debug-bar.expanded .shopiza-debug-body { display: block; }
.shopiza-debug-msg { padding: .2rem 0; display: flex; align-items: baseline; gap: .25rem; flex-wrap: wrap; }
.shopiza-debug-msg--error { color: #f38ba8; }
.shopiza-debug-msg--warn  { color: #fab387; }
.shopiza-debug-msg--info  { color: #89dceb; }
.shopiza-debug-detail { background: rgba(255,255,255,.07); border-radius: .2rem; padding: .05rem .35rem; font-size: .7rem; }
.shopiza-debug-grid { display: grid; grid-template-columns: auto 1fr; gap: .15rem .75rem; }
.shopiza-debug-key { color: #a6e3a1; font-weight: 600; white-space: nowrap; }
.shopiza-debug-val { color: #cdd6f4; font-family: monospace; word-break: break-all; }

/* ── Bug report floating button ─────────────────────────────── */
.shopiza-bug-btn {
  position: fixed;
  bottom: 1.5rem;
  right: 1.5rem;
  height: 2.6rem;
  padding: 0 .9rem;
  border-radius: 2rem;
  background: var(--primary, #6c63ff);
  color: var(--primary-fg, #fff);
  border: none;
  box-shadow: 0 4px 14px rgba(0,0,0,.25);
  font-size: .95rem;
  display: flex;
  align-items: center;
  gap: .35rem;
  cursor: pointer;
  z-index: 1050;
  transition: transform .15s, box-shadow .15s;
}
#shopizaBugBtn[data-raised] {
  bottom: 5rem;
}
.shopiza-bug-btn:hover { transform: scale(1.05); box-shadow: 0 6px 18px rgba(0,0,0,.3); }
.shopiza-bug-btn-sep {
  display: inline-block;
  width: 1px;
  height: .9rem;
  background: rgba(255,255,255,.35);
  margin: 0 .1rem;
}
/* Powered-by floater (free plan). Scoped to the ID: .shopiza-bug-btn is shared
   with the bug-report button, which keeps its own size and position. */
#shopizaPoweredBtn {
  right: calc(1.5rem - 5px);
  bottom: calc(1.5rem - 5px);
  height: calc(2.6rem - 4px);
  /* width joins the transitions because the two frames differ in length and
     the box is animated between them; opacity is the footer fade. */
  transition: transform .15s, box-shadow .15s, width .45s ease, opacity .25s ease;
  white-space: nowrap;
  overflow: hidden;
}
/* Out of the way of the footer links rather than floating over them. */
#shopizaPoweredBtn.is-at-footer {
  opacity: 0;
  pointer-events: none;
}
/* Footer link row. Gaps live here rather than on a Bootstrap gap utility so
   the two axes can differ: a comfortable column gap between items, a much
   tighter row gap once the row wraps. One line-height for every child, so a
   wrapped block reads as evenly spaced lines rather than as two rows that
   happen to sit near each other. */
.footer-links {
  column-gap: 1rem;
  row-gap: 0.15rem;
  line-height: 1.9;
}
.footer-links > * {
  line-height: inherit;
}

.shopiza-powered-badge {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  line-height: 1.2;
  gap: 0;
}
/* Expressed against the original values so the 1px step stays legible. */
.shopiza-powered-line1 {
  font-size: calc(.6rem - 1px);
  font-weight: 400;
  opacity: .85;
  transition: opacity .2s;
}
.shopiza-powered-line2 {
  font-size: calc(.75rem - 1px);
  font-weight: 700;
  transition: opacity .2s;
}
.shopiza-powered-fade {
  opacity: 0 !important;
  transition: opacity .15s !important;
}

/* Bug report modal */
.shopiza-bug-modal {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,.5);
  z-index: 1055;
  display: flex !important;
  align-items: flex-end;
  justify-content: flex-end;
  padding: 1rem;
}
.shopiza-bug-modal.d-none { display: none !important; }
.shopiza-bug-modal-inner {
  background: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: .75rem;
  width: 100%;
  max-width: 420px;
  max-height: 90vh;
  overflow-y: auto;
  box-shadow: 0 20px 60px rgba(0,0,0,.3);
  animation: shopizaBugSlideUp .2s ease;
}
@keyframes shopizaBugSlideUp {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: translateY(0); }
}
.shopiza-bug-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: .85rem 1rem .75rem;
  border-bottom: 1px solid var(--bs-border-color);
}
.shopiza-bug-modal-close {
  background: none;
  border: none;
  font-size: 1.4rem;
  line-height: 1;
  color: var(--bs-secondary-color);
  cursor: pointer;
  padding: 0 .25rem;
}
.shopiza-bug-modal-close:hover { color: var(--bs-body-color); }
.shopiza-bug-modal-body { padding: 1rem; }
.shopiza-bug-debug {
  font-size: .7rem;
  background: var(--bs-secondary-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: .375rem;
  padding: .5rem .75rem;
  max-height: 160px;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-all;
  color: var(--bs-body-color);
}
.shopiza-bug-submit-btn {
  background: var(--primary, #6c63ff);
  border-color: var(--primary, #6c63ff);
  color: var(--primary-fg, #fff);
}
.shopiza-bug-submit-btn:hover {
  background: var(--primary-2, #5a52d5);
  border-color: var(--primary-2, #5a52d5);
  color: var(--primary-fg, #fff);
}

/* Bug report modal — dark mode */
[data-theme="dark"] .shopiza-bug-modal {
  background: rgba(0,0,0,.6);
}
[data-theme="dark"] .shopiza-bug-modal-inner {
  background: var(--card);
  border-color: var(--border);
  box-shadow: 0 20px 60px rgba(0,0,0,.6);
}
[data-theme="dark"] .shopiza-bug-modal-header {
  border-bottom-color: var(--border);
}
[data-theme="dark"] .shopiza-bug-modal-close {
  color: var(--muted);
}
[data-theme="dark"] .shopiza-bug-modal-close:hover {
  color: var(--fg);
}
[data-theme="dark"] .shopiza-bug-modal-inner .form-label,
[data-theme="dark"] .shopiza-bug-modal-inner .fw-semibold {
  color: var(--fg);
}
[data-theme="dark"] .shopiza-bug-modal-inner .form-select,
[data-theme="dark"] .shopiza-bug-modal-inner .form-control {
  background: var(--bg);
  border-color: var(--border);
  color: var(--fg);
}
[data-theme="dark"] .shopiza-bug-modal-inner .form-select:focus,
[data-theme="dark"] .shopiza-bug-modal-inner .form-control:focus {
  background: var(--bg);
  border-color: var(--primary);
  color: var(--fg);
}
[data-theme="dark"] .shopiza-bug-debug {
  background: var(--bg);
  border-color: var(--border);
  color: var(--muted);
}
[data-theme="dark"] .shopiza-bug-modal-inner .text-muted {
  color: var(--muted) !important;
}

/* Bug reports table message truncation */
.dev-report-msg {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  word-break: break-word;
}
.dev-report-msg.expanded {
  display: block;
  -webkit-line-clamp: unset;
}

/* ── Settings page — tabbed shell + section panes ── */
.settings-shell { position: relative; }
.settings-tabs {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--border, #e7e5e4);
  margin: 8px 0 22px;
  padding: 0;
}
.settings-tab {
  background: transparent;
  border: 0;
  padding: 10px 14px;
  font: 600 13px/1 Inter, system-ui, sans-serif;
  color: var(--az-text-sub, #78716c);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color 0.15s, border-color 0.15s;
}
.settings-tab:hover { color: var(--az-text, #1c1917); }
.settings-tab.is-active,
.settings-tab[aria-selected="true"] {
  color: var(--primary, #10b981);
  border-bottom-color: var(--primary, #10b981);
}
/* Tab panes — visibility driven by [data-active-tab] on the shell. */
.settings-pane { display: none; }
.settings-shell[data-active-tab="shop"] .settings-pane[data-tab-pane="shop"],
.settings-shell[data-active-tab="selling"] .settings-pane[data-tab-pane="selling"],
.settings-shell[data-active-tab="domains"] .settings-pane[data-tab-pane="domains"],
.settings-shell[data-active-tab="advanced"] .settings-pane[data-tab-pane="advanced"] { display: block; }
/* Save bar (inside welcome_form) — hide on the Advanced tab (it has its
   own per-section save buttons). Shop/Selling/Domains all share the same
   form so the bar is visible on all three. */
.settings-shell[data-active-tab="advanced"] .settings-save-bar { display: none; }

/* Floating Save bar — `position: fixed` so it sits flush at the viewport
   bottom (no gap from the admin-main padding). `left` matches the sidebar
   width so the bar starts where the main column starts. JS toggles
   `.is-resting` when the natural Save button (inside welcome_form) enters
   the viewport, sliding the floater away to avoid a duplicate. */
.settings-float-save {
  position: fixed;
  left: var(--az-sidebar-w, 240px);
  right: 0;
  bottom: 0;
  /* Stay BELOW `.admin-sidebar` (z-index 10) so the sidebar's closing
     animation — which transitions width back from expanded to collapsed
     while its z-index has already dropped from 50 to 10 — paints cleanly
     on top of the bar instead of being clipped by it. */
  z-index: 5;
  padding: 18px 22px;
  display: flex;
  justify-content: flex-start;
  /* Centre the button vertically so the gap above it matches the gap below. */
  align-items: center;
  background: var(--card, #fff);
  border-top: 1px solid var(--border, #e7e5e4);
  box-shadow: 0 -6px 16px rgba(0, 0, 0, 0.05);
  transition: opacity 0.22s ease, transform 0.22s ease, left 0.22s ease;
}
.admin-shell[data-admin-shell].is-collapsed .settings-float-save {
  left: var(--az-sidebar-w-collapsed, 64px);
}
[data-theme="dark"] .settings-float-save {
  background: var(--az-card, #222);
  border-top-color: var(--az-card-border, #2a2a2a);
  box-shadow: 0 -6px 16px rgba(0, 0, 0, 0.35);
}
/* Previously the float bar faded out when the natural in-form Save
   button entered the viewport. That looked weird on shorter tabs
   (Domains) where the natural bar was always visible — there was no
   floating bar at all. Now the floater is always visible on every
   settings tab that should have a Save action, and the natural in-form
   bar is hidden via the rule below. */
.settings-float-save.is-resting {
  /* Intentionally a no-op — keep the floater visible regardless. */
}
/* Hide the natural in-form Save bar everywhere — the floating bar at
   the viewport bottom is the single canonical Save action. The natural
   bar's submit button stays in the DOM though, because the floater's
   trigger button programmatically clicks it. */
.settings-shell .settings-save-bar { display: none; }
.settings-float-save .btn,
.settings-save-bar .btn {
  /* 34px matches the onboarding pill (2.1rem) and the sidebar upgrade CTA. */
  height: 34px;
  padding: 0 16px;
  font-size: 13px;
  font-weight: 500;
  border-radius: 8px;
  display: inline-flex;
  align-items: center;
}
.settings-float-save .btn .fa-save,
.settings-save-bar .btn .fa-save { font-size: 12px; }

/* Bottom padding on settings pages — must clear the fixed floating Save
   bar (~70px tall including its own padding) so the last item in the
   tab content (e.g. the "Connect a domain" button on the Domains tab)
   isn't hidden behind the bar when the viewport is short. Hidden bar
   on Advanced tab is harmless — the extra padding just leaves blank
   space below the last form there. */
.admin-shell[data-admin-shell] .admin-main:has(.settings-shell) {
  padding-bottom: 110px;
}

/* Same clearance on the pages edit screen — the .settings-float-save
   class is reused there (see views/admin/pages/edit.ejs) so the last
   field doesn't end up tucked behind the floating Save bar. */
.admin-shell[data-admin-shell] .admin-main:has([data-pages-float-save]) {
  padding-bottom: 110px;
}

/* Standalone product create page — third user of the same floating bar.
   Clearance so the last section isn't tucked behind it, and the natural
   in-form row hidden while its submit button stays in the DOM for the
   floater to click().

   88px, not the 110px the settings/pages screens use: the bar is 70px tall
   (18px padding + a 34px button + 18px), so this leaves an 18px gap at the
   bottom of the scroll, matching the bar's own internal padding. The larger
   value read as a hole under the last section. */
.admin-shell[data-admin-shell] .admin-main:has([data-product-float-save]) {
  padding-bottom: 88px;
}
[data-product-natural-save] { display: none !important; }
/* Visually de-emphasise the natural in-form Save row on the pages
   edit screen — the floating bar is the canonical action. Keep the
   button in the DOM so the floating-bar trigger can click() it and
   so screen-reader users can still tab to it. */
[data-pages-natural-save] {
  display: none !important;
}
/* Advanced tab has no floating save bar — restore the tighter padding so
   the last content section doesn't end with an awkward blank gap. */
.admin-shell[data-admin-shell] .admin-main:has(.settings-shell[data-active-tab="advanced"]) {
  padding-bottom: 18px;
}
/* The Advanced tab has independent forms with their own Save buttons. */
.settings-shell[data-active-tab="advanced"] .settings-float-save {
  display: none;
}
/* On mobile the sidebar is an off-canvas drawer, so the floating Save bar
   spans the full width (no sidebar offset). It stays visible — the natural
   Save button can be far down a long settings form on a phone. */
@media (max-width: 991px) {
  .settings-float-save { left: 0; padding: 12px 16px; }
  .admin-shell[data-admin-shell].is-collapsed .settings-float-save { left: 0; }
  /* Leave room on the right for the onboarding pill (which shrinks to icon +
     count on mobile so the two share the bottom row without overlapping). */
  body:has(.onboarding-widget) .settings-float-save { padding-right: 96px; }
}

/* ── Settings page sections — same collapsible look as `.product-section`
   in the product edit modal. The JS in settings.ejs wraps everything after
   `.settings-section-title` into `.settings-section-body` at runtime and
   wires a toggle on the title. ── */
.settings-shell .settings-section {
  border: 1px solid var(--border, #e7e5e4);
  border-radius: 8px;
  background: var(--card, #fff);
  /* !important overrides the Bootstrap `mb-4` class still present on the
     markup so the spacing stays consistent across all sections. */
  margin-bottom: 8px !important;
  padding: 0 !important;
  overflow: hidden;
}
.settings-shell .settings-section-title {
  display: flex;
  align-items: center;
  /* Tight gap between the chevron, the leading FontAwesome icon, and the
     title text so they read as a single label, not three columns. */
  gap: 8px;
  margin: 0;
  padding: 12px 16px;
  background: #fbfbfb;
  font-weight: 600;
  font-size: 15px;
  /* The legacy `.settings-section-title` rule earlier in this file styled
     the heading as uppercase/spaced — neutralize those here. */
  text-transform: none;
  letter-spacing: normal;
  color: var(--fg, inherit);
  cursor: pointer;
  user-select: none;
}
/* The leading FontAwesome icon inside the title carries `me-2` from the
   markup (0.5rem). Strip that since the flex `gap` already handles spacing. */
.settings-shell .settings-section-title > .fas,
.settings-shell .settings-section-title > .far,
.settings-shell .settings-section-title > .fab {
  margin-right: 0 !important;
}
[data-theme="dark"] .settings-shell .settings-section-title {
  background: #2a2a2a;
}

/* ── Checkout option editor (Settings → Selling) ─────────────────── */
.checkout-options-group__head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 12px;
  margin-bottom: 12px;
}
.checkout-options-list { display: flex; flex-direction: column; gap: 12px; }
.checkout-option-card {
  border: 1px solid var(--pz-border, #e5e7eb);
  border-radius: 10px;
  padding: 12px 14px;
  background: var(--pz-surface, #fff);
}
.checkout-option-card__head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 10px;
}
.checkout-option-card__icon {
  width: 30px; height: 30px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 8px;
  background: var(--pz-surface-muted, #f3f4f6);
  color: var(--pz-text-muted, #6b7280);
  font-size: 14px;
  flex-shrink: 0;
}
.checkout-option-card__head .az-lang-strip-wrap { flex: 1 1 auto; min-width: 0; }
.checkout-option-card__remove { flex-shrink: 0; }
[data-theme="dark"] .checkout-option-card {
  border-color: var(--pz-border, #3a3a3a);
  background: var(--pz-surface, #242424);
}
[data-theme="dark"] .checkout-option-card__icon { background: rgba(255, 255, 255, 0.06); }

/* Connect-a-provider scaffold (integrations placeholder) */
.checkout-connect {
  border: 1px dashed var(--pz-border, #d1d5db);
  border-radius: 10px;
  padding: 14px 16px;
  background: var(--pz-surface-muted, #fafafa);
}
.checkout-connect__head { display: flex; align-items: center; gap: 10px; }
.checkout-connect__providers { display: flex; flex-wrap: wrap; gap: 10px; }
.checkout-connect__provider {
  display: inline-flex; align-items: center;
  padding: 8px 14px;
  border: 1px solid var(--pz-border, #e5e7eb);
  border-radius: 8px;
  background: var(--pz-surface, #fff);
  color: var(--pz-text-muted, #9ca3af);
  font-size: 13px;
  cursor: not-allowed;
  opacity: 0.75;
}
[data-theme="dark"] .checkout-connect {
  border-color: rgba(255, 255, 255, 0.15);
  background: rgba(255, 255, 255, 0.03);
}
[data-theme="dark"] .checkout-connect__provider {
  border-color: rgba(255, 255, 255, 0.1);
  background: rgba(255, 255, 255, 0.04);
}
.settings-shell .settings-section-title .settings-section-chev {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  font-size: 12px;
  color: var(--fg-muted, #6c757d);
  transition: transform 0.18s ease;
  flex: 0 0 auto;
}
.settings-shell .settings-section.is-collapsed .settings-section-chev {
  transform: rotate(-90deg);
}
.settings-shell .settings-section-body {
  padding: 14px 16px 16px;
  border-top: 1px solid var(--border, #e7e5e4);
}
.settings-shell .settings-section.is-collapsed .settings-section-body {
  display: none;
}

/* Shop-logo preview inside the settings page — matches the storefront's
   navbar background (white seamless) so what you see here is the actual
   rendered height. The slider below adjusts the `height` of the image. */
.logo-preview {
  display: inline-block;
  background: #ffffff;
  border: 1px solid var(--border, #e7e5e4);
  border-radius: 6px;
  padding: 10px 14px;
  margin-bottom: 0;
  max-width: 100%;
  overflow: hidden;
}
.logo-preview__img {
  max-width: 100%;
  object-fit: contain;
}

/* Range slider knob — use the shop's accent. `--primary-text` is the page-
   background-readable variant computed in layout.ejs (white accent darkens
   to gray; very-dark accent lightens in dark mode), so the knob stays
   visible no matter what colour the shop owner picked. */
.form-range::-webkit-slider-thumb {
  background: var(--primary-solid, var(--primary, #6366f1));
}
.form-range::-moz-range-thumb {
  background: var(--primary-solid, var(--primary, #6366f1));
  border: 0;
}
.form-range:focus::-webkit-slider-thumb {
  box-shadow: 0 0 0 1px var(--card, #fff), 0 0 0 4px color-mix(in srgb, var(--primary-text, var(--primary, #6366f1)) 25%, transparent);
}
.form-range:focus::-moz-range-thumb {
  box-shadow: 0 0 0 1px var(--card, #fff), 0 0 0 4px color-mix(in srgb, var(--primary-text, var(--primary, #6366f1)) 25%, transparent);
}

/* Catalog display — live preview card on the right. The inner `.product-card`
   uses the storefront's existing styles directly, so the preview looks like
   what a visitor sees. Slight negative top margin pulls it up so it lines
   up with the top edge of the settings on the left. */
.catalog-preview-frame {
  max-width: 320px;
  margin: -16px auto 0;
}

/* Image-shape radio swatches — placeholder rectangles with the ratio
   text inside. The actual radio input still drives the value submitted; we
   just visually replace the text label with a sized swatch. */
.aspect-swatch {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--hover, #f3f4f6);
  border: 1px solid var(--border, #e7e5e4);
  border-radius: 4px;
  color: var(--fg-muted, #6c757d);
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  user-select: none;
}
.aspect-swatch--1x1 { width: 44px; height: 44px; }
.aspect-swatch--3x4 { width: 33px; height: 44px; }
.aspect-swatch--4x3 { width: 44px; height: 33px; }
[data-theme="dark"] .aspect-swatch {
  background: #2a2a2a;
  border-color: #3a3a3a;
}
/* Soft highlight on the selected radio's swatch. */
.form-check-input:checked + .form-check-label .aspect-swatch {
  border-color: var(--primary, #10b981);
  color: var(--primary, #10b981);
  background: color-mix(in srgb, var(--primary, #10b981) 12%, transparent);
}

/* Vertically centre the radio + swatch as a single row so the three
   `.form-check`s line up even though the swatches have different heights.
   Override Bootstrap's default block layout + absolutely-positioned input. */
.form-check:has(.aspect-swatch) {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding-left: 0;
  min-height: 44px;
  margin-bottom: 0;
}
.form-check:has(.aspect-swatch) > .form-check-input {
  float: none;
  margin: 0;
  flex: 0 0 auto;
}
.form-check:has(.aspect-swatch) > .form-check-label {
  display: inline-flex;
  align-items: center;
}
.catalog-preview-frame .product-card-img {
  cursor: default;
}
/* The shared `.card-atc-label` rule hides the "Add to cart" text at certain
   storefront-card breakpoints. The admin preview is a single fixed-width
   card, so always show the label here. */
.catalog-preview-frame .card-atc-label {
  display: inline !important;
}
.settings-section-title {
  font-size: .75rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted, #6c757d);
  margin-bottom: 1rem;
  padding-bottom: .5rem;
  border-bottom: 1px solid var(--border, #dee2e6);
}

/* ═══════════════════════════════════════════════════════════════════
   ADMIN SHELL — Design handoff (Shopiza Dashboard, 2026-04-20)
   Self-contained: sidebar + topbar + main, replaces old admin-shell.
   ═══════════════════════════════════════════════════════════════════ */

/* Admin shell typography is Inter (loaded site-wide via layout.ejs). No
   secondary font is imported — the design system standardises on Inter. */

:root {
  /* Sidebar is dark in both themes — matches the design brief */
  --az-sb-bg: #141414;
  --az-sb-border: #222;
  --az-sb-text: #f0efed;
  --az-sb-sub: #9a9a9a;
  --az-sb-muted: #7a7a7a;
  --az-sb-hover: rgba(255,255,255,0.05);
  --az-sb-active: rgba(255,255,255,0.06);
  --az-sb-line: #333333;

  --az-topbar: #ffffff;
  --az-topbar-border: #f0efed;
  --az-topbar-input: #fafaf9;
  --az-topbar-input-border: #f0efed;
  --az-topbar-input-text: #333;

  --az-card: #ffffff;
  --az-card-border: #f0efed;
  --az-text: #141414;
  --az-text-sub: #5f5f5f;
  --az-text-muted: #8a8a8a;
  --az-bg: #f5f4f1;
  --az-divider: #f0efed;
  --az-table-head: #fafaf9;
  --az-table-hover: #fdfcfa;
  --az-table-row: #f8f7f5;

  --az-radius-lg: 14px;
  --az-radius-md: 10px;
  --az-radius-sm: 8px;

  --az-sidebar-w: 240px;
  --az-sidebar-w-collapsed: 64px;
  --az-header-h: 60px;
}

[data-theme="dark"] {
  --az-sb-bg: #0f0f0f;
  --az-sb-border: #1c1c1c;
  --az-sb-text: #e8e7e4;
  --az-sb-sub: #9a9a9a;
  --az-sb-muted: #7a7a7a;
  --az-sb-hover: rgba(255,255,255,0.04);
  --az-sb-active: rgba(255,255,255,0.05);
  --az-sb-line: #2c2c2c;

  --az-topbar: #1c1c1c;
  --az-topbar-border: #252525;
  --az-topbar-input: #2a2a2a;
  --az-topbar-input-border: #333;
  --az-topbar-input-text: #ccc;

  --az-card: #222;
  --az-card-border: #2a2a2a;
  /* Controls carry a lighter edge than panels do: at #2a2a2a against #222 a
     button reads as a faint suggestion rather than something you can press.
     The storefront resolves its --btn-outline-border to this too, so both
     sides of the product get the same weight. */
  --az-control-border: #3a3a3a;
  --az-text: #e2e1de;
  --az-text-sub: #a6a6a6;
  --az-text-muted: #7e7e7e;
  --az-bg: #161616;
  --az-divider: #2a2a2a;
  --az-table-head: #1e1e1e;
  --az-table-hover: #272727;
  --az-table-row: #252525;
}

/* ── Shell (admin only) ──────────────────────────────── */
body:has(.admin-shell[data-admin-shell]) { background: var(--az-bg) !important; overflow: hidden; }
body:has(.admin-shell[data-admin-shell]) #main-content { height: 100vh; }

/* Within the admin shell, remap the legacy theme tokens (used by Bootstrap
   overrides on subpages: products, orders, settings, etc.) to the az-* tokens
   so every admin page shares identical colors in light and dark mode. */
.admin-shell[data-admin-shell] {
  --bg: var(--az-bg);
  --fg: var(--az-text);
  --muted: var(--az-text-sub);
  --card: var(--az-card);
  --border: var(--az-card-border);
  --hover: var(--az-table-hover);
  --sidebar-bg: var(--az-sb-bg);
  --navbar-bg: var(--az-topbar);
  --navbar-border: var(--az-topbar-border);
}
.admin-shell[data-admin-shell] .admin-main {
  color: var(--az-text);
}
.admin-shell[data-admin-shell] .admin-main .card {
  background: var(--az-card);
  border-color: var(--az-card-border);
  color: var(--az-text);
}
.admin-shell[data-admin-shell] .admin-main .card-header,
.admin-shell[data-admin-shell] .admin-main .card-footer {
  background: var(--az-card);
  border-color: var(--az-card-border);
  color: var(--az-text);
}
.admin-shell[data-admin-shell] .admin-main .table {
  --bs-table-bg: transparent;
  color: var(--az-text);
}
.admin-shell[data-admin-shell] .admin-main .table > :not(caption) > * > * {
  background-color: transparent;
  border-bottom-color: var(--az-card-border);
  color: var(--az-text);
}
.admin-shell[data-admin-shell] .admin-main .table thead th {
  background: var(--az-table-head);
  color: var(--az-text-sub);
  border-bottom-color: var(--az-card-border);
}
.admin-shell[data-admin-shell] .admin-main .table-hover > tbody > tr:hover > * {
  background-color: var(--az-table-hover);
  color: var(--az-text);
}
.admin-shell[data-admin-shell] .admin-main .form-control,
.admin-shell[data-admin-shell] .admin-main .form-select {
  background-color: var(--az-topbar-input);
  border-color: var(--az-topbar-input-border);
  color: var(--az-text);
}
.admin-shell[data-admin-shell] .admin-main .form-control:focus,
.admin-shell[data-admin-shell] .admin-main .form-select:focus {
  background-color: var(--az-topbar-input);
  color: var(--az-text);
  border-color: var(--primary);
}
.admin-shell[data-admin-shell] .admin-main .form-control::placeholder {
  color: var(--az-text-sub);
}
.admin-shell[data-admin-shell] .admin-main .input-group-text {
  background: var(--az-table-head);
  border-color: var(--az-topbar-input-border);
  color: var(--az-text-sub);
}
.admin-shell[data-admin-shell] .admin-main .list-group-item {
  background: var(--az-card);
  border-color: var(--az-card-border);
  color: var(--az-text);
}
.admin-shell[data-admin-shell] .admin-main .modal-content {
  background: var(--az-card);
  border-color: var(--az-card-border);
  color: var(--az-text);
}
.admin-shell[data-admin-shell] .admin-main .dropdown-menu {
  background: var(--az-card);
  border-color: var(--az-card-border);
  color: var(--az-text);
}
.admin-shell[data-admin-shell] .admin-main .dropdown-item {
  color: var(--az-text);
}
.admin-shell[data-admin-shell] .admin-main .dropdown-item:hover,
.admin-shell[data-admin-shell] .admin-main .dropdown-item:focus,
.admin-shell[data-admin-shell] .admin-main .dropdown-item.active,
.admin-shell[data-admin-shell] .admin-main .dropdown-item:active {
  background: var(--az-table-hover);
  color: var(--az-text);
  box-shadow: none;
}
.admin-shell[data-admin-shell] .admin-main .nav-tabs {
  border-bottom-color: var(--az-card-border);
}
.admin-shell[data-admin-shell] .admin-main .nav-tabs .nav-link {
  color: var(--az-text-sub);
  border-color: transparent;
}
.admin-shell[data-admin-shell] .admin-main .nav-tabs .nav-link:hover {
  color: var(--az-text);
  border-color: var(--az-card-border) var(--az-card-border) transparent;
}
.admin-shell[data-admin-shell] .admin-main .nav-tabs .nav-link.active {
  background: var(--az-card);
  color: var(--az-text);
  border-color: var(--az-card-border) var(--az-card-border) var(--az-card);
}
.admin-shell[data-admin-shell] .admin-main hr {
  border-color: var(--az-card-border);
  opacity: 1;
}
/* Secondary body text in the admin. `.text-muted` and `.form-text` are used
   interchangeably for the same kind of helper copy, so they must resolve to the
   SAME token: a section's description rendered as `.text-muted small` looked
   visibly dimmer than the field help below it rendered as `.form-text`. (There
   used to be a second, later `.text-muted` rule here pointing at the dimmer
   --az-text-muted, which silently won; it is gone.) */
.admin-shell[data-admin-shell] .admin-main .text-muted,
.admin-shell[data-admin-shell] .admin-main .form-text { color: var(--az-text-sub) !important; }
[data-theme="dark"] .admin-shell[data-admin-shell] .form-control,
[data-theme="dark"] .admin-shell[data-admin-shell] .form-select,
[data-theme="dark"] .admin-shell[data-admin-shell] textarea.form-control { background-color: #161616 !important; }
[data-theme="dark"] .admin-shell[data-admin-shell] .rte-quill-root .ql-container.ql-snow,
[data-theme="dark"] .admin-shell[data-admin-shell] .rte-quill-root .ql-toolbar.ql-snow .ql-picker-label,
[data-theme="dark"] .admin-shell[data-admin-shell] .rte-quill-root .ql-toolbar.ql-snow .ql-picker-options,
[data-theme="dark"] .admin-shell[data-admin-shell] .rte-quill-root .ql-toolbar select.ql-header,
[data-theme="dark"] .admin-shell[data-admin-shell] .rte-quill-root .ql-toolbar select.ql-color { background-color: #161616 !important; }
.admin-shell[data-admin-shell] .admin-main .bg-light { background: var(--az-table-head) !important; color: var(--az-text); }
.admin-shell[data-admin-shell] .admin-main .bg-white { background: var(--az-card) !important; color: var(--az-text); }
.admin-shell[data-admin-shell] .admin-main .border { border-color: var(--az-card-border) !important; }
.admin-shell[data-admin-shell] .admin-main .border-top,
.admin-shell[data-admin-shell] .admin-main .border-bottom,
.admin-shell[data-admin-shell] .admin-main .border-start,
.admin-shell[data-admin-shell] .admin-main .border-end { border-color: var(--az-card-border) !important; }

.admin-shell[data-admin-shell] {
  display: flex;
  height: 100vh;
  width: 100%;
  max-width: none;
  margin: 0;
  background: var(--az-bg);
  font-family: Inter, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  color: var(--az-text);
  overflow: hidden;
}

.admin-shell[data-admin-shell] .admin-sidebar {
  width: var(--az-sidebar-w);
  min-width: var(--az-sidebar-w);
  background: var(--az-sb-bg);
  border-right: 1px solid var(--az-sb-border);
  display: flex;
  flex-direction: column;
  height: 100%;
  position: relative;
  padding: 0;
  margin: 0;
  top: 0;
  overflow: hidden;
  z-index: 10;
  /* Slight overshoot on the width transition for the hover-expand "pop".
     `margin-right` is also transitioned so that the return-to-collapsed
     animation eases the sidebar back inside its slot smoothly. */
  transition:
    width 0.28s cubic-bezier(0.34, 1.4, 0.64, 1),
    min-width 0.28s cubic-bezier(0.34, 1.4, 0.64, 1),
    margin-right 0.28s cubic-bezier(0.34, 1.4, 0.64, 1),
    box-shadow 0.18s ease;
}

.admin-shell[data-admin-shell].is-collapsed:not(.is-hover-expanded) .admin-sidebar {
  width: var(--az-sidebar-w-collapsed);
  min-width: var(--az-sidebar-w-collapsed);
  cursor: default;
}

/* Hover-expand: the sidebar widens back to its full size on hover, but
   *overlays* the main content instead of pushing it. We pull this off by
   keeping `width` at the expanded size while shrinking the flex slot it
   occupies (via a negative `margin-right` worth the difference). The flex
   neighbour (`.admin-main-col`) therefore keeps starting at the collapsed
   offset, while the sidebar renders past its slot on top, lifted via
   `z-index`. Both `width` and `margin-right` transition smoothly, so the
   visual effect is a single pop-out animation with no content shift. */
.admin-shell[data-admin-shell].is-collapsed.is-hover-expanded .admin-sidebar {
  width: var(--az-sidebar-w);
  min-width: var(--az-sidebar-w);
  margin-right: calc(var(--az-sidebar-w-collapsed) - var(--az-sidebar-w));
  z-index: 50;
  cursor: default;
  box-shadow: 6px 0 24px rgba(0, 0, 0, 0.10);
  transition:
    width 0.28s cubic-bezier(0.34, 1.4, 0.64, 1),
    min-width 0.28s cubic-bezier(0.34, 1.4, 0.64, 1),
    margin-right 0.28s cubic-bezier(0.34, 1.4, 0.64, 1),
    box-shadow 0.18s ease;
}
[data-theme="dark"] .admin-shell[data-admin-shell].is-collapsed.is-hover-expanded .admin-sidebar {
  box-shadow: 6px 0 24px rgba(0, 0, 0, 0.45);
}

.admin-shell[data-admin-shell] .admin-main-col {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
  background: var(--az-bg);
}

.admin-shell[data-admin-shell] .admin-main {
  flex: 1;
  min-width: 0;
  overflow-y: auto;
  padding: 22px;
  background: var(--az-bg);
  color: var(--az-text);
}

/* ── Sidebar header (shop switcher + collapse) ─────────── */
.az-sidebar-header {
  position: relative;
  height: var(--az-header-h);
  min-height: var(--az-header-h);
  display: flex;
  align-items: center;
  padding: 0 12px 0 10px;
  border-bottom: 1px solid var(--az-sb-border);
  flex-shrink: 0;
  /* Lock width so the shop-switch/name doesn't resize during the sidebar
     expand/collapse animation. Reset when truly collapsed (rule below). */
  min-width: var(--az-sidebar-w, 240px);
  box-sizing: border-box;
}
.is-collapsed:not(.is-hover-expanded) .az-sidebar-header { min-width: 0; }
/* Left-anchored, not centred, so the badge doesn't re-centre (slide right) as
   the sidebar collapses. 13px header + the shop-switch's 4px left padding = 17px,
   which centres the 30px badge in the 64px collapsed rail. */
.is-collapsed:not(.is-hover-expanded) .az-sidebar-header { padding: 0 4px 0 14px; }

.az-shop-switch {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  background: transparent;
  border: 0;
  border-radius: 9px;
  padding: 6px 8px;
  cursor: pointer;
  color: var(--az-sb-text);
  font-family: inherit;
  text-align: left;
  transition: background 0.14s;
}
.az-shop-switch:hover { background: var(--az-sb-hover); }
.az-shop-switch[aria-expanded="true"] { background: var(--az-sb-active); }
.is-collapsed:not(.is-hover-expanded) .az-shop-switch { flex: 0 0 auto; padding: 6px 4px; cursor: ew-resize; pointer-events: none; }

.az-shop-badge {
  width: 30px; height: 30px;
  border-radius: 8px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 700;
  color: var(--primary-fg, #fff);
}
.az-shop-badge.sm { width: 26px; height: 26px; border-radius: 6px; font-size: 12px; }

.az-shop-name {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--az-sb-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1;
}
.is-collapsed:not(.is-hover-expanded) .az-shop-name,
.is-collapsed:not(.is-hover-expanded) .az-shop-switch > .az-chevron { display: none; }

.az-chevron {
  color: var(--az-sb-sub);
  flex-shrink: 0;
  transition: transform 0.2s;
}
.az-shop-switch[aria-expanded="true"] .az-chevron { transform: rotate(180deg); }

/* ── i18n field lang strip ── */
.az-lang-strip-wrap {
  display: flex;
  align-items: center;
  gap: 2px;
  max-width: 100%;
}
/* Hide the per-field language strip when the shop has only one locale
   enabled — with nothing to switch between, the strip is just visual
   noise above every input. :has(.az-lang-btn:only-child) matches when
   the strip has exactly one language button, which only happens when
   `normalizedSelectedLocales.length === 1` (or 0) in the server-side
   render. Works across every admin view that uses this component
   without touching individual EJS files. */
.az-lang-strip-wrap:has(.az-lang-strip > .az-lang-btn:only-child) {
  display: none;
}
.az-lang-strip {
  display: flex;
  overflow: hidden;
  flex-shrink: 1;
  min-width: 0;
}
.az-lang-btn {
  flex-shrink: 0;
  padding: 1px 5px;
  font-size: 11px;
  font-weight: 500;
  border: none;
  border-bottom: 2px solid transparent;
  background: transparent;
  color: #aaa;
  cursor: pointer;
  border-radius: 0;
  line-height: 1.6;
  white-space: nowrap;
  transition: color 0.1s, border-color 0.1s, background 0.1s;
}
.az-lang-btn:hover { color: #555; }
.az-lang-btn.is-active {
  color: var(--bs-body-color, #212529);
  border-bottom-color: var(--bs-body-color, #212529);
  font-weight: 700;
}
.az-lang-btn.is-empty { color: #dc3545; border-bottom-color: #dc3545; font-weight: 600; }
.az-lang-btn.is-empty.is-active { background: #dc3545; color: #fff; border-bottom-color: #dc3545; }
[data-theme="dark"] .az-lang-btn { color: #666; }
[data-theme="dark"] .az-lang-btn:hover { color: #bbb; }
[data-theme="dark"] .az-lang-btn.is-active { color: #e9ecef; border-bottom-color: #e9ecef; }
.az-lang-arrow {
  flex-shrink: 0;
  display: none;
  width: 18px;
  height: 22px;
  padding: 0;
  font-size: 14px;
  line-height: 1;
  background: #f8f9fa;
  border: 1px solid #dee2e6;
  border-radius: 3px;
  cursor: pointer;
  color: #888;
  align-items: center;
  justify-content: center;
}
.az-lang-strip--compact { max-width: 84px; }
.az-lang-arrow.is-visible { display: flex; }
.az-lang-arrow.has-empty { border-color: #dc3545; background: #fff5f5; color: #dc3545; }
[data-theme="dark"] .az-lang-arrow { background: #2a2a2a; border-color: #444; color: #888; }
[data-theme="dark"] .az-lang-arrow.has-empty { background: #3a1a1a; border-color: #dc3545; color: #dc3545; }

.az-collapse-btn {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  border-radius: 7px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  cursor: pointer;
  color: var(--az-sb-sub);
  transition: background 0.14s, color 0.14s;
}
.az-collapse-btn:hover { background: var(--az-sb-hover); color: var(--az-sb-text); }
.is-collapsed:not(.is-hover-expanded) .az-collapse-btn { display: none; }

.az-brand-row {
  display: flex;
  align-items: center;
  /* Bottom padding bumped from 16→22 to match the breathing room the
     sidebar header gives the shop-switch at the top. */
  padding: 0 10px 22px 0;
  transform: translateY(4px);
}
.az-brand-row .az-brand {
  flex: 1;
  padding-bottom: 0;
}
.is-collapsed:not(.is-hover-expanded) .az-brand-row { justify-content: center; padding-right: 0; }

.az-shop-dropdown {
  position: absolute;
  top: calc(100% + 2px);
  left: 8px;
  right: 8px;
  background: #1e1e1e;
  border: 1px solid #2a2a2a;
  border-radius: 12px;
  padding: 6px;
  z-index: 100;
  box-shadow: 0 8px 24px rgba(0,0,0,0.5);
}
.az-shop-dropdown[hidden] { display: none; }
.az-shop-dropdown-item {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 8px 10px;
  border-radius: 8px;
  cursor: default;
  background: transparent;
  color: #ddd;
  transition: background 0.12s;
}
.az-shop-dropdown-item.is-active { background: rgba(255,255,255,0.07); }
.az-shop-dropdown-name { font-size: 13px; font-weight: 500; flex: 1; color: #ddd; }
.az-shop-dropdown-dot { width: 6px; height: 6px; border-radius: 50%; }
.az-shop-dropdown-manage {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-radius: 8px;
  color: #aaa;
  font-size: 12.5px;
  text-decoration: none;
  border-top: 1px solid #2a2a2a;
  margin-top: 4px;
  padding-top: 10px;
  transition: color 0.12s, background 0.12s;
}
.az-shop-dropdown-manage:hover { color: #fff; background: rgba(255,255,255,0.04); text-decoration: none; }

/* Superadmin "viewing as shop" banner (shown inside sidebar when viewing a specific shop) */
.az-superadmin-banner {
  margin: 8px 10px 0;
  padding: 8px 10px;
  border-radius: 9px;
  background: rgba(255, 193, 7, 0.08);
  border: 1px solid rgba(255, 193, 7, 0.2);
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}
.az-superadmin-banner-text {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  color: #d4a107;
  font-size: 11px;
  font-weight: 600;
}
.az-superadmin-banner-text span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.az-superadmin-banner-text i { flex-shrink: 0; }
.az-superadmin-exit {
  background: transparent;
  border: 0;
  color: #d4a107;
  cursor: pointer;
  width: 20px; height: 20px;
  border-radius: 5px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 0.12s;
}
.az-superadmin-exit:hover { background: rgba(255, 193, 7, 0.15); }
.is-collapsed:not(.is-hover-expanded) .az-superadmin-banner { display: none; }

/* ── Sidebar nav ─────────────────────────────────────── */
.az-nav {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 12px 8px 1px;
  scrollbar-width: thin;
  scrollbar-color: rgba(255,255,255,0.22) transparent;
  /* Lock width during sidebar expand/collapse animation so nav-items + their
     labels don't reflow/resize as the sidebar width transitions. Sidebar's
     `overflow: hidden` clips the overflow during the animation. Reset to
     natural width when truly collapsed (see rule below) so icons can recenter. */
  min-width: var(--az-sidebar-w, 240px);
}
.is-collapsed:not(.is-hover-expanded) .az-nav { min-width: 0; }
.az-nav::-webkit-scrollbar { width: 6px; height: 6px; }
.az-nav::-webkit-scrollbar-track { background: transparent; }
.az-nav::-webkit-scrollbar-thumb {
  background: rgba(255,255,255,0.22);
  border-radius: 999px;
  transition: background 0.2s ease;
}
.az-nav::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.42); }
.az-nav:hover::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.32); }
.az-nav::-webkit-scrollbar-corner { background: transparent; }

.az-nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  /* Left padding (16px) puts the icon's centre on the SAME vertical line as the
     shop badge above and the collapsed-mode icons, so nothing shifts sideways
     when the sidebar hover-expands. Collapsed mode overrides this (centred). */
  padding: 9px 10px 9px 16px;
  border-radius: 9px;
  cursor: pointer;
  color: #d9d8d5;
  background: transparent;
  border: 0;
  width: 100%;
  font-family: inherit;
  font-size: 13.5px;
  font-weight: 500;
  line-height: 1;
  text-decoration: none;
  /* Only the hover colours fade — NOT layout. Transitioning `all` animated the
     padding as the sidebar hover-expands, so icons visibly slid a few px. */
  transition: background-color 0.13s, color 0.13s;
  position: relative;
  margin-bottom: 1px;
}
.az-nav-item:hover { background: var(--az-sb-hover); color: var(--az-sb-text); text-decoration: none; }
.az-nav-item.is-active {
  background: color-mix(in srgb, var(--primary, #b8d957) 14%, transparent);
  color: var(--az-sb-text);
}
.az-nav-item.is-active::after {
  content: '';
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 3px;
  height: 18px;
  border-radius: 2px 0 0 2px;
  background: var(--primary, #b8d957);
}
.az-nav-item.is-active .az-nav-icon { color: var(--primary, #b8d957); }
.is-collapsed:not(.is-hover-expanded) .az-nav-item.is-active::after { display: none; }

.az-nav-icon {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 17px;
  /* Fixed height matched to the label's line box, so a menu item is the same
     height with or without its label visible — keeps the icon from dropping a
     couple of pixels when the sidebar hover-expands. */
  height: 18px;
  color: inherit;
}

.az-nav-label {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: left;
  /* The nav item sets line-height:1, which clips descenders (g, y, p) when
     the label's overflow:hidden crops the line box. Give the text a roomier
     line box — pinned to 18px so it EXACTLY matches the icon's 18px height.
     Any mismatch (e.g. 1.35em = 18.2px) makes expanded rows a hair taller than
     collapsed ones; that fraction accumulates down the list until the lower
     items round to a 1px vertical jump on hover-expand. */
  line-height: 18px;
}
.is-collapsed:not(.is-hover-expanded) .az-nav-label,
.is-collapsed:not(.is-hover-expanded) .az-nav-caret { display: none; }
/* Collapsed mode keeps the item's expanded left-aligned padding (rather than
   re-centring) so the icon stays at the same x whether collapsed or expanded,
   and doesn't slide as the sidebar width animates. Only the label is hidden. */

.az-nav-caret {
  color: var(--az-sb-muted);
  display: inline-flex;
  align-items: center;
  transition: transform 0.2s;
  transform: rotate(-90deg);
}
.az-nav-group.is-open > .az-nav-parent .az-nav-caret { transform: none; }

.az-nav-parent { cursor: pointer; }

.az-nav-children {
  padding-left: 20px;
  position: relative;
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.22s ease, margin-bottom 0.22s ease;
}
.az-nav-group.is-open .az-nav-children { max-height: 400px; margin-bottom: 4px; }
.is-collapsed:not(.is-hover-expanded) .az-nav-children { display: none; }

.az-nav-tree-trunk {
  display: none;
}

.az-nav-child {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px 7px 30px;
  margin: 2px 0;
  color: #d9d8d5;
  text-decoration: none;
  font-size: 13px;
  font-weight: 400;
  transition: color 0.12s;
  isolation: isolate;
}
.az-nav-child::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: 18px;
  right: 0;
  border-radius: 8px;
  background: transparent;
  transition: background 0.12s;
  z-index: -1;
}
.az-nav-child:hover { color: var(--az-sb-text); text-decoration: none; }
.az-nav-child:hover::before { background: var(--az-sb-hover); }
.az-nav-child.is-active {
  color: var(--az-sb-text);
  font-weight: 500;
}
.az-nav-child.is-active::before {
  background: color-mix(in srgb, var(--primary, #b8d957) 10%, transparent);
}
.az-nav-child.is-active::after {
  content: '';
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 3px;
  height: 18px;
  border-radius: 2px 0 0 2px;
  background: var(--primary, #b8d957);
}
.is-collapsed:not(.is-hover-expanded) .az-nav-child.is-active::after { display: none; }
.az-nav-child-label { flex: 1; }

.az-nav-tree-branch {
  position: absolute;
  left: -2px;
  top: 50%;
  width: 16px;
  height: 1px;
  background: var(--az-sb-line);
}
/* Vertical line from branch upward to top of children container */
.az-nav-tree-branch::before {
  content: '';
  position: absolute;
  left: 0;
  width: 1px;
  bottom: 0;
  top: -400px;
  background: var(--az-sb-line);
}

.az-nav-divider {
  height: 1px;
  background: var(--az-sb-line);
  margin: 10px 4px;
}

/* ── Upgrade bubble ──────────────────────────────────── */
.az-upgrade {
  margin: 0 10px 12px;
  transform: translateY(-2px);
  background: #1e1e1e;
  border: 1px solid #2a2a2a;
  border-radius: 12px;
  padding: 14px;
  position: relative;
  flex-shrink: 0;
  /* Lock width so the banner doesn't reflow during the sidebar expand/
     collapse animation. Sidebar's `overflow: hidden` clips it during the
     transition. Released to natural width when truly collapsed below. */
  min-width: calc(var(--az-sidebar-w, 240px) - 20px);
  box-sizing: border-box;
}
.is-collapsed:not(.is-hover-expanded) .az-upgrade { display: none; }
.az-upgrade.is-dismissed { display: none; }

.az-upgrade-close {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 20px; height: 20px;
  border-radius: 6px;
  background: transparent;
  border: 0;
  color: #555;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  line-height: 1;
  transition: color 0.12s;
}
.az-upgrade-close:hover { color: #aaa; }

.az-upgrade-head {
  display: flex;
  align-items: center;
  gap: 9px;
  margin-bottom: 8px;
}
.az-upgrade-icon {
  width: 30px; height: 30px;
  border-radius: 8px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--primary-fg, #fff);
}
.az-upgrade-title {
  color: #f0efed;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: -0.1px;
}
.az-upgrade-body {
  font-size: 11.5px;
  color: #666;
  line-height: 1.55;
  margin: 0 0 12px;
}
.az-upgrade-cta {
  display: block;
  text-align: center;
  padding: 8px 0;
  color: var(--primary-fg, #ffffff);
  border: 0;
  border-radius: 8px;
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
  font-family: inherit;
  text-decoration: none;
  transition: opacity 0.14s;
}
.az-upgrade-cta:hover { opacity: 0.85; color: var(--primary-fg, #ffffff); text-decoration: none; }

/* ── Sidebar footer: divider + Shopiza brand ─────────── */
.az-sidebar-divider {
  height: 1px;
  background: var(--az-sb-border);
  margin: 0 12px 12px;
  transform: translateY(-2px);
}

.az-brand {
  /* Left padding bumped from 12→18 so the Shopiza badge sits at the same
     left offset as the shop-switch badge at the top of the sidebar
     (header padding 10px + shop-switch padding 8px = 18px). */
  padding: 0 18px 16px;
  display: flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  color: inherit;
}
.az-brand:hover { text-decoration: none; }
/* Padding-bottom stays 0 (matches the row's `.az-brand .az-brand-row`
   override) so the brand element keeps the same height — otherwise the
   row grows by 16px in collapsed mode and the anchored-bottom layout
   pushes the logo upward. */
/* Left-anchored at 17px (same as the header badge) rather than centred, so it
   doesn't slide right as the sidebar collapses. Bottom padding stays 0. */
.is-collapsed:not(.is-hover-expanded) .az-brand { padding: 0 0 0 18px; }

.az-brand-badge {
  width: 30px; height: 30px;
  border-radius: 8px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 800;
  color: var(--primary-fg, #fff);
}
.az-brand-badge--logo {
  overflow: hidden;
}
.az-brand-badge--logo img {
  width: 18px; height: 18px;
  object-fit: contain;
}
.az-brand-logo--light { display: block; }
.az-brand-logo--dark { display: none; }
[data-theme="dark"] .az-brand-logo--light { display: none; }
[data-theme="dark"] .az-brand-logo--dark { display: block; }
/* nowrap on the brand text so the plan label doesn't wrap onto two lines
   mid-animation while the sidebar's width transitions from collapsed to
   expanded — a multi-line wrap would temporarily push the logo upward. */
.az-brand-text { display: flex; flex-direction: column; line-height: 1.2; min-width: 0; }
.is-collapsed:not(.is-hover-expanded) .az-brand-text { display: none; }
.az-brand-name { color: var(--az-sb-text); font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.az-brand-version { color: var(--az-sb-muted); font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ── Topbar ──────────────────────────────────────────── */
.az-topbar {
  height: var(--az-header-h);
  min-height: var(--az-header-h);
  background: var(--az-topbar);
  border-bottom: 1px solid var(--az-topbar-border);
  display: flex;
  align-items: center;
  padding: 0 22px;
  gap: 10px;
  flex-shrink: 0;
  position: relative;
  z-index: 5;
}

.az-topbar-search {
  position: relative;
  flex: 1;
  max-width: 320px;
  display: flex;
  align-items: center;
}
.az-topbar-search svg {
  position: absolute;
  left: 11px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--az-text-muted);
  pointer-events: none;
}
.az-topbar-search input {
  width: 100%;
  /* Match the other topbar buttons (.az-topbar-btn): same 34px height,
     same border colour. box-sizing keeps the height exact with the border. */
  height: 34px;
  box-sizing: border-box;
  padding: 0 12px 0 33px;
  border: 1px solid #dedddb;
  border-radius: 9px;
  font-size: 13px;
  color: var(--az-topbar-input-text);
  background: var(--az-topbar-input);
  font-family: inherit;
  outline: none;
  transition: border-color 0.15s;
}
[data-theme="dark"] .az-topbar-search input { border-color: var(--az-control-border); }
.az-topbar-search input:focus { border-color: var(--primary, #b8d957); }
.az-topbar-search input::placeholder { color: var(--az-text-muted); }

/* Global search results dropdown */
.az-sr-box {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  background: var(--az-card);
  border: 1px solid var(--az-card-border);
  border-radius: var(--az-radius-md);
  box-shadow: 0 8px 24px rgba(0,0,0,.12);
  z-index: 9999;
  max-height: 420px;
  overflow-y: auto;
  padding: 6px;
}
.az-sr-group-label {
  padding: 6px 8px 4px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--az-text-muted);
}
.az-sr-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-radius: var(--az-radius-sm);
  color: var(--az-text);
  text-decoration: none;
  font-size: 13px;
  cursor: pointer;
  transition: background 0.1s;
}
.az-sr-item:hover,
.az-sr-item:focus {
  background: var(--az-table-hover);
  color: var(--az-text);
  text-decoration: none;
  outline: none;
}
.az-sr-icon {
  flex-shrink: 0;
  width: 20px;
  text-align: center;
  color: var(--az-text-muted);
  font-size: 12px;
}
.az-sr-name { flex: 1; font-weight: 500; }
.az-sr-sub {
  font-size: 12px;
  color: var(--az-text-sub);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 140px;
}
.az-sr-empty {
  padding: 20px 10px;
  text-align: center;
  font-size: 13px;
  color: var(--az-text-sub);
}

.az-topbar-spacer { flex: 1; }

.az-topbar-primary {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 8px 16px;
  background: var(--primary, #b8d957);
  color: var(--primary-fg, #ffffff) !important;
  border: 0;
  border-radius: var(--az-radius-md);
  font-size: 13px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  text-decoration: none;
  box-shadow: 0 2px 12px color-mix(in srgb, var(--primary, #b8d957) 38%, transparent);
  transition: opacity 0.14s;
}
.az-topbar-primary:hover { opacity: 0.85; color: var(--primary-fg, #ffffff) !important; text-decoration: none; }
.az-topbar-primary i { font-size: 11px; }

.az-topbar-btn {
  width: 34px;
  height: 34px;
  border-radius: 9px;
  border: 1px solid #dedddb;
  background: transparent;
  color: var(--az-text-sub);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: inherit;
  transition: all 0.13s;
  position: relative;
}
.az-topbar-btn:hover { background: var(--az-topbar-input); color: var(--az-text); }
[data-theme="dark"] .az-topbar-btn { border-color: var(--az-control-border); }
/* ── Add-to-cart feedback: flying product image + button checkmark ──
   Driven from navigation.js (playAddToCartFeedback). Deliberately NOT gated
   behind prefers-reduced-motion: every Shopiza animation plays everywhere. */
.atc-fly {
  position: fixed;
  z-index: 1080;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  border-radius: 10px;
  pointer-events: none;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.22);
  will-change: transform, opacity;
}
.shop-cart-btn.cart-bump { animation: atc-cart-bump 0.45s cubic-bezier(0.34, 1.56, 0.64, 1); }
@keyframes atc-cart-bump {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.22); }
  100% { transform: scale(1); }
}
/* The label is hidden rather than removed so the button keeps its exact width
   and the product grid does not reflow mid-animation. `color: transparent`
   is what hides the detail page's button, whose label is a bare text node
   with no element to target; .btn.atc-done outranks .btn-primary's colour. */
.btn.atc-done { position: relative; color: transparent; }
.btn.atc-done > * { visibility: hidden; }
.btn.atc-done .atc-check { visibility: visible; }
.atc-check {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 22px;
  height: 22px;
  fill: none;
  /* Explicit, because the rule above made currentColor transparent. */
  stroke: var(--primary-fg, #ffffff);
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.btn-sm .atc-check, .product-add-btn-icon .atc-check { width: 17px; height: 17px; }
.atc-check path {
  stroke-dasharray: 30;
  stroke-dashoffset: 30;
  animation: atc-draw 0.34s 0.05s cubic-bezier(0.65, 0, 0.35, 1) forwards;
}
@keyframes atc-draw { to { stroke-dashoffset: 0; } }

.az-topbar-btn-wide { width: auto; gap: 5px; padding: 0 10px; font-size: 12px; font-weight: 600; }
/* "Go to Shop" is an <a> wearing the .az-topbar-btn look, so it needs the
   underline and link colour suppressed in both states — the sibling controls
   are <button>s and never needed this. */
.az-topbar-shop,
.az-topbar-shop:hover { text-decoration: none; }
.az-topbar-shop:hover { color: var(--az-text); }
.az-topbar-shop svg { flex: 0 0 auto; }
.az-topbar-lang-code { color: var(--az-text); }
.az-topbar-bell-dot {
  position: absolute;
  top: 5px;
  right: 5px;
  width: 7px; height: 7px;
  background: #e05252;
  border-radius: 50%;
  border: 2px solid var(--az-topbar);
}

.az-topbar-menu { position: relative; }

.az-topbar-dropdown {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  background: var(--az-topbar);
  border: 1px solid var(--az-topbar-border);
  border-radius: 12px;
  padding: 6px;
  z-index: 200;
  box-shadow: 0 8px 24px rgba(0,0,0,0.12);
  min-width: 170px;
}
[data-theme="dark"] .az-topbar-dropdown { box-shadow: 0 8px 24px rgba(0,0,0,0.5); }
.az-topbar-dropdown-right { left: auto; right: 0; min-width: 190px; }
.az-topbar-dropdown[hidden] { display: none; }

.az-topbar-dropdown-item {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 8px 12px;
  border-radius: 8px;
  background: transparent;
  border: 0;
  cursor: pointer;
  color: var(--az-text);
  font-size: 13px;
  text-decoration: none;
  font-family: inherit;
  width: 100%;
  text-align: left;
  transition: background 0.12s;
}
.az-topbar-dropdown-item:hover { background: var(--az-topbar-input); text-decoration: none; color: var(--az-text); }
.az-topbar-dropdown-item.is-active { background: color-mix(in srgb, var(--primary, #b8d957) 10%, transparent); font-weight: 600; }
.az-topbar-dropdown-danger { color: #d9534f; }
.az-topbar-dropdown-danger:hover { color: #d9534f; background: rgba(217, 83, 79, 0.08); }
.az-topbar-dropdown-divider { height: 1px; background: var(--az-topbar-border); margin: 4px 0; }

.az-flag { font-size: 15px; line-height: 1; }
.az-lang-label { flex: 1; }
.az-lang-short { font-size: 11px; color: var(--az-text-muted); }

.az-topbar-divider {
  width: 1px;
  height: 22px;
  background: var(--az-topbar-border);
  /* Right side is pulled in (negative margin) to offset the user button's
     4px left padding, so the visible gap matches the left side. */
  margin: 0 -2px 0 2px;
}

.az-topbar-user { position: relative; }
.az-topbar-user-btn {
  display: flex;
  align-items: center;
  gap: 9px;
  background: transparent;
  border: 0;
  padding: 4px 6px 4px 4px;
  border-radius: 8px;
  cursor: pointer;
  font-family: inherit;
  transition: background 0.14s;
}
.az-topbar-user-btn:hover { background: var(--az-topbar-input); }
.az-topbar-user-avatar {
  width: 32px; height: 32px;
  border-radius: 50%;
  background: linear-gradient(135deg, #5b8ff9, #9b6fe8);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 700;
  color: #fff;
  flex-shrink: 0;
}
.az-topbar-user-info { display: flex; flex-direction: column; line-height: 1.2; text-align: left; }
.az-topbar-user-name { font-size: 12px; font-weight: 600; color: var(--az-text); }
.az-topbar-user-role { font-size: 11px; color: var(--az-text-muted); }
.az-topbar-user-btn svg { color: var(--az-text-muted); }

/* ── Admin main content: card/table/button restyling ── */
.admin-shell[data-admin-shell] .admin-main .card {
  background: var(--az-card);
  border: 1px solid var(--az-card-border);
  border-radius: var(--az-radius-lg);
  box-shadow: 0 1px 3px rgba(0,0,0,0.04);
  overflow: hidden;
}
.admin-shell[data-admin-shell] .admin-main .card-header {
  background: var(--az-card);
  border-bottom: 1px solid var(--az-divider);
  padding: 14px 22px;
}
.admin-shell[data-admin-shell] .admin-main .card-body { padding: 20px 22px; }
/* Cards whose body is a data table (products, collections, pages, …) drop the
   body padding so the table spans edge-to-edge — the cells carry their own
   spacing. The card clips the table to its rounded corners. */
.admin-shell[data-admin-shell] .admin-main .card-body:has(> .table-responsive),
.admin-shell[data-admin-shell] .admin-main .card-body:has(> table.table) { padding: 0; }
.admin-shell[data-admin-shell] .admin-main .card:has(> .card-body > .table-responsive),
.admin-shell[data-admin-shell] .admin-main .card:has(> .card-body > table.table) { overflow: hidden; }
/* Stat-table cards (analytics + dashboard) put a heading — and sometimes a
   trailing "view all" link — directly above a full-bleed table. Because the
   card-body padding is stripped for the edge-to-edge table, those siblings
   would otherwise sit flush to the edge. Re-inset them to the same 22px as the
   table cells so the heading and link line up with the columns. Cards whose
   body is ONLY a table (product / order / collection lists) have no such
   siblings, so they're unaffected. */
.admin-shell[data-admin-shell] .admin-main .card-body:has(> .table-responsive) > h6,
.admin-shell[data-admin-shell] .admin-main .card-body:has(> table.table) > h6 {
  padding: 18px 22px 12px;
  /* !important beats a Bootstrap `mb-3` utility some of these titles carry —
     without it those boxes get the padding-bottom PLUS a 16px margin. */
  margin-bottom: 0 !important;
}
.admin-shell[data-admin-shell] .admin-main .card-body:has(> .table-responsive) > a,
.admin-shell[data-admin-shell] .admin-main .card-body:has(> table.table) > a {
  display: inline-block;
  margin: 10px 22px 16px;
}

.admin-shell[data-admin-shell] .admin-main h1,
.admin-shell[data-admin-shell] .admin-main h2,
.admin-shell[data-admin-shell] .admin-main h3,
.admin-shell[data-admin-shell] .admin-main h4,
.admin-shell[data-admin-shell] .admin-main h5,
.admin-shell[data-admin-shell] .admin-main h6 {
  color: var(--az-text);
  letter-spacing: -0.3px;
}


.admin-shell[data-admin-shell] .admin-main .table { color: var(--az-text); margin-bottom: 0; }
.admin-shell[data-admin-shell] .admin-main .table thead tr { background: var(--az-table-head); }
.admin-shell[data-admin-shell] .admin-main .table th {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--az-text-muted);
  border-bottom: 1px solid var(--az-divider);
  padding: 10px 22px;
}
.admin-shell[data-admin-shell] .admin-main .table td {
  padding: 11px 22px;
  border-top: 1px solid var(--az-table-row);
  border-bottom: 0;
  color: var(--az-text);
  font-size: 13px;
}
.admin-shell[data-admin-shell] .admin-main .table-hover > tbody > tr:hover > td {
  background: var(--az-table-hover) !important;
}

/* Dashboard stat card */
.admin-shell[data-admin-shell] .admin-main .stat-card {
  background: var(--az-card);
  border: 1px solid var(--az-card-border);
  border-radius: var(--az-radius-lg);
  box-shadow: 0 1px 3px rgba(0,0,0,0.04);
}
.admin-shell[data-admin-shell] .admin-main .stat-card .card-body { padding: 20px 22px; }
.admin-shell[data-admin-shell] .admin-main .stat-value {
  font-size: 24px;
  font-weight: 700;
  letter-spacing: -0.5px;
  color: var(--az-text);
}
.admin-shell[data-admin-shell] .admin-main .stat-label {
  font-size: 12px;
  color: var(--az-text-sub);
  font-weight: 500;
}

/* Primary button — use accent */
.admin-shell[data-admin-shell] .admin-main .btn-primary {
  background: var(--primary, #b8d957);
  border-color: var(--primary, #b8d957);
  color: var(--primary-fg, #ffffff);
  font-weight: 600;
  box-shadow: 0 2px 12px color-mix(in srgb, var(--primary, #b8d957) 30%, transparent);
}
.admin-shell[data-admin-shell] .admin-main .btn-primary:hover,
.admin-shell[data-admin-shell] .admin-main .btn-primary:focus {
  background: var(--primary-2, var(--primary, #b8d957));
  border-color: var(--primary-2, var(--primary, #b8d957));
  color: var(--primary-fg, #ffffff);
  filter: brightness(0.95);
}
.admin-shell[data-admin-shell] .admin-main .btn-outline-primary {
  color: var(--primary, #b8d957) !important;
  border-color: var(--primary, #b8d957) !important;
}
.admin-shell[data-admin-shell] .admin-main .btn-outline-primary:hover {
  background: var(--primary, #b8d957) !important;
  color: #1a2200 !important;
  border-color: var(--primary, #b8d957) !important;
}

/* Form inputs: match topbar style */
.admin-shell[data-admin-shell] .admin-main .form-control,
.admin-shell[data-admin-shell] .admin-main .form-select {
  background-color: var(--az-topbar-input);
  border: 1px solid var(--az-topbar-input-border);
  color: var(--az-text);
  border-radius: var(--az-radius-md);
  font-size: 13px;
}
.admin-shell[data-admin-shell] .admin-main .form-control:focus,
.admin-shell[data-admin-shell] .admin-main .form-select:focus {
  border-color: var(--primary, #b8d957);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary, #b8d957) 18%, transparent);
}

/* Ensure a single, correctly-themed dropdown arrow on .form-select in admin
   (earlier background: shorthand was wiping Bootstrap's SVG arrow). */
.admin-shell[data-admin-shell] .admin-main .form-select {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23555' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
  background-repeat: no-repeat;
  background-position: right 0.75rem center;
  background-size: 16px 12px;
  padding-right: 2.25rem;
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
}
[data-theme="dark"] .admin-shell[data-admin-shell] .admin-main .form-select {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23bbb' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
}

/* Page heading spacing (design has 18px margin under heading block) */
.admin-shell[data-admin-shell] .admin-main > *:first-child { margin-top: 0; }

/* Responsive */
/* Hamburger + drawer backdrop are mobile-only elements; inert on desktop. */
.az-menu-toggle { display: none; }
.admin-sidebar-backdrop { display: none; }

@media (max-width: 991px) {
  /* Mobile: the sidebar becomes an off-canvas drawer (the topbar hamburger
     toggles .is-mobile-menu-open — see admin-shell.js). The shell keeps its
     desktop geometry (row flex, .admin-main scrolls internally); the fixed-
     position sidebar leaves the flow so the main column gets full width.
     The pre-drawer behavior stacked the entire nav on top of every page —
     unusable on phones. */
  .admin-shell[data-admin-shell] { height: 100dvh; }
  .admin-shell[data-admin-shell] .admin-sidebar {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    width: 280px;
    max-width: 85vw;
    height: 100dvh;
    z-index: 1100;
    transform: translateX(-100%);
    transition: transform 0.25s ease;
    overflow-y: auto;
  }
  .admin-shell[data-admin-shell].is-mobile-menu-open .admin-sidebar {
    transform: translateX(0);
    box-shadow: 0 0 44px rgba(0, 0, 0, 0.4);
  }
  /* A persisted desktop collapse must not shrink the drawer to icons. */
  .admin-shell[data-admin-shell].is-collapsed .admin-sidebar,
  .admin-shell[data-admin-shell].is-collapsed:not(.is-hover-expanded) .admin-sidebar {
    width: 280px;
    min-width: 0;
    cursor: default;
  }
  /* CRITICAL: when the drawer opens on a persisted-collapsed shell,
     admin-shell.js adds .is-hover-expanded (so labels show). The desktop
     hover-expand rule then forces z-index:50 + a negative margin, dropping
     the drawer BEHIND the backdrop (1099) — it looked faded and swallowed
     every tap. Re-assert the drawer's stacking here (this selector matches
     the desktop rule's specificity and wins by source order). */
  .admin-shell[data-admin-shell].is-collapsed.is-hover-expanded .admin-sidebar {
    z-index: 1100;
    margin-right: 0;
    box-shadow: 0 0 44px rgba(0, 0, 0, 0.4);
  }
  .admin-sidebar-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1099;
    background: rgba(0, 0, 0, 0.45);
  }
  .admin-shell[data-admin-shell].is-mobile-menu-open .admin-sidebar-backdrop { display: block; }
  .az-menu-toggle { display: inline-flex; }
  /* Hide the desktop collapse chevron inside the drawer — collapsing a
     280px drawer to an icon rail (which also hides the chevron itself)
     would strand the user in an icons-only drawer. */
  .admin-sidebar [data-az-collapse] { display: none; }
  .admin-shell[data-admin-shell] .admin-main { padding: 16px; }
  .az-topbar { padding: 0 12px; gap: 8px; }
  .az-topbar-search { max-width: none; }
  .az-topbar-user-info { display: none; }
  .az-topbar-primary span { display: none; }
  /* Icon-only below 992px: the topbar is tight here (search + language +
     theme + avatar), and this is where the hamburger appears. Reset the
     .az-topbar-btn-wide overrides so it returns to the 34px square. */
  .az-topbar-shop { width: 34px; padding: 0; gap: 0; }
  .az-topbar-shop span { display: none; }
}

/* ── Mobile portrait: compact topbar search + onboarding pill ── */
@media (max-width: 576px) {
  /* Date-range picker: the intermediate periods (This week / month / year)
     don't fit on a phone — show only All, Today and Custom. A selected
     intermediate period still shows so the active choice is never hidden. */
  .az-period-optional:not(.active) { display: none; }

  /* Product list: drop the description preview and tighten the cells so more
     columns fit before the row needs to scroll sideways. */
  .product-row-desc { display: none; }
  .admin-shell[data-admin-shell] .admin-main .table > :not(caption) > * > * { padding: 0.4rem 0.35rem; }
  /* ...but stat-table cards (dashboard/analytics: top products / sources /
     countries / pages / most-viewed) pair an h6 heading with the table in one
     padding-less card-body, so the tightening above left their rows nearly flush
     to the edge while the heading stayed inset at 22px. Re-inset the OUTER cells
     to 22px so the rows line up with the heading. Table-ONLY list cards
     (products / orders / collections) have no h6, so `:has(> h6)` skips them. */
  .admin-shell[data-admin-shell] .admin-main .card-body:has(> h6):has(> table.table) > table.table > :not(caption) > * > *:first-child,
  .admin-shell[data-admin-shell] .admin-main .card-body:has(> h6):has(> .table-responsive) .table > :not(caption) > * > *:first-child { padding-left: 22px; }
  .admin-shell[data-admin-shell] .admin-main .card-body:has(> h6):has(> table.table) > table.table > :not(caption) > * > *:last-child,
  .admin-shell[data-admin-shell] .admin-main .card-body:has(> h6):has(> .table-responsive) .table > :not(caption) > * > *:last-child { padding-right: 22px; }
  #productsTable td .me-3 { margin-right: 0.5rem !important; }
  #productsTable td img.me-3,
  #productsTable td .me-3.bg-light { width: 28px !important; height: 28px !important; }
  /* First column: a little breathing room on the left (the card body is now
     flush) and a wider name column. */
  #productsTable > thead > tr > th:first-child,
  #productsTable > tbody > tr > td:first-child {
    padding-left: 14px;
    min-width: 160px;
  }
  /* Product name: one step smaller + tighter line-height. */
  #productsTable > tbody > tr > td:first-child .fw-medium {
    font-size: 12px;
    line-height: 1.2;
  }
  /* The description preview is hidden on mobile, so the name is a single line
     — centre the image + name vertically (like every other cell) instead of
     the desktop top-alignment used when the description is present. */
  #productsTable > tbody > tr > td:first-child > a.d-flex {
    align-items: center !important;
  }

  /* The onboarding pill shares the bottom row with the settings Save bar —
     drop its text label (icon + count only) so the two fit without overlap. */
  body:has(.settings-float-save) .onboarding-pill__label { display: none; }

  /* Settings → currencies: one column on a phone (labels like "USD - US
     Dollar" are too long for two). Languages keep their two columns. */
  .currency-check-grid { columns: 1 !important; }

  /* Collapse the search to an icon button that matches the topbar buttons;
     admin-search.js expands it (focused) on tap and collapses on Escape /
     outside-tap. */
  .az-topbar-search:not(.is-expanded) {
    flex: 0 0 auto;
    width: 34px;
    max-width: 34px;
    height: 34px;
    justify-content: center;
    border: 1px solid #dedddb;
    border-radius: 9px;
    cursor: pointer;
  }
  .az-topbar-search:not(.is-expanded) input { display: none; }
  .az-topbar-search:not(.is-expanded) svg { position: static; left: auto; transform: none; }
  [data-theme="dark"] .az-topbar-search:not(.is-expanded) { border-color: var(--az-control-border); }
  .az-topbar-search.is-expanded {
    position: absolute;
    top: 0; bottom: 0; left: 8px; right: 8px;
    width: auto;
    max-width: none;
    z-index: 60;
    background: var(--az-topbar);
  }
}

/* Old admin styles: suppress in new shell */
.admin-shell[data-admin-shell] .admin-nav,
.admin-shell[data-admin-shell] .nav-section,
.admin-shell[data-admin-shell] .nav-section-label,
.admin-shell[data-admin-shell] .nav-link-item,
.admin-shell[data-admin-shell] .superadmin-shop-banner {
  /* not used in new markup, kept here for safety if any old view still emits them */
}

/* ═══ ADMIN DASHBOARD — page-level styles ═══════════════════════════ */
.admin-shell[data-admin-shell] .admin-main .az-page-head { margin-bottom: 18px; }
.admin-shell[data-admin-shell] .admin-main .az-page-title {
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.3px;
  color: var(--az-text);
  margin: 0;
}
.admin-shell[data-admin-shell] .admin-main .az-page-sub {
  font-size: 13px;
  color: var(--az-text-muted);
  margin: 3px 0 0;
}

.az-stat-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 14px;
  margin-bottom: 16px;
}
@media (max-width: 1100px) { .az-stat-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px)  { .az-stat-grid { grid-template-columns: 1fr; } }

.az-stat {
  background: var(--az-card);
  border: 1px solid var(--az-card-border);
  border-radius: var(--az-radius-lg);
  padding: 20px 22px;
  box-shadow: 0 1px 3px rgba(0,0,0,0.04);
}
.az-stat-label { font-size: 12px; color: var(--az-text-sub); font-weight: 500; margin-bottom: 8px; }
.az-stat-value { font-size: 24px; font-weight: 700; color: var(--az-text); letter-spacing: -0.5px; margin-bottom: 8px; line-height: 1.1; }
.az-stat-meta { display: flex; align-items: center; gap: 5px; font-size: 12px; }
.az-stat-meta-accent { color: var(--primary, #b8d957); font-weight: 600; }
.az-stat-meta-sub { color: var(--az-text-muted); }

.az-dash-row {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: 14px;
  margin-bottom: 16px;
}
@media (max-width: 991px) { .az-dash-row { grid-template-columns: 1fr; } }

.az-panel {
  background: var(--az-card);
  border: 1px solid var(--az-card-border);
  border-radius: var(--az-radius-lg);
  box-shadow: 0 1px 3px rgba(0,0,0,0.04);
  overflow: hidden;
}
.az-panel-head {
  padding: 18px 22px 14px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: 1px solid var(--az-divider);
  gap: 12px;
}
.az-panel-title { font-size: 13px; font-weight: 600; color: var(--az-text); }
.az-panel-sub  { font-size: 11px; color: var(--az-text-muted); margin-top: 2px; }
.az-panel-action {
  font-size: 12px;
  color: var(--az-text-sub);
  border: 1px solid var(--az-card-border);
  border-radius: 8px;
  padding: 5px 12px;
  background: var(--az-topbar-input);
  cursor: pointer;
  font-family: inherit;
  text-decoration: none;
  transition: all 0.13s;
}
.az-panel-action:hover { color: var(--az-text); border-color: var(--primary, #b8d957); text-decoration: none; }

.az-table-wrap { overflow-x: auto; }
.az-table {
  width: 100%;
  border-collapse: collapse;
  margin: 0;
}
.az-table thead tr { background: var(--az-table-head); }
.az-table th {
  padding: 10px 22px;
  text-align: left;
  font-size: 11px;
  color: var(--az-text-muted);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.az-table tbody tr {
  border-top: 1px solid var(--az-table-row);
  cursor: pointer;
  transition: background 0.13s;
}
.az-table tbody tr:hover { background: var(--az-table-hover); }
.az-table td { padding: 11px 22px; font-size: 13px; color: var(--az-text); vertical-align: middle; }
.az-table-id { color: var(--az-text-sub); font-weight: 500; }
.az-table-primary { font-weight: 500; color: var(--az-text); }
.az-table-secondary { color: var(--az-text-sub); font-size: 12px; margin-top: 2px; }
.az-table-amount { font-weight: 600; color: var(--az-text); }
.az-table-date { color: var(--az-text-muted); font-size: 12px; }
.az-table .text-end { text-align: right; }

.az-badge {
  display: inline-flex;
  align-items: center;
  font-size: 11px;
  font-weight: 600;
  padding: 3px 10px;
  border-radius: 20px;
  letter-spacing: 0.01em;
}
.az-badge-success { background: rgba(92,184,92,0.12); color: #3a8f3a; }
.az-badge-warning { background: rgba(240,160,50,0.12); color: #b07a10; }
.az-badge-danger  { background: rgba(224,82,82,0.12); color: #b83030; }
.az-badge-info    { background: rgba(90,150,249,0.12); color: #3a6fd4; }
[data-theme="dark"] .az-badge-success { background: rgba(92,184,92,0.16); color: #6bd36b; }
[data-theme="dark"] .az-badge-warning { background: rgba(240,160,50,0.16); color: #e9b660; }
[data-theme="dark"] .az-badge-danger  { background: rgba(224,82,82,0.18); color: #ef7a7a; }
[data-theme="dark"] .az-badge-info    { background: rgba(90,150,249,0.16); color: #7faaf8; }

.az-empty {
  padding: 48px 22px;
  text-align: center;
  color: var(--az-text-muted);
}
.az-empty i { font-size: 28px; opacity: 0.4; margin-bottom: 10px; display: block; }
.az-empty p { margin: 0; font-size: 13px; }

.az-action-list { padding: 14px 18px 18px; display: flex; flex-direction: column; gap: 6px; }
.az-action {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 10px;
  color: var(--az-text);
  font-size: 13px;
  font-weight: 500;
  text-decoration: none;
  border: 1px solid var(--az-card-border);
  background: var(--az-topbar-input);
  transition: all 0.13s;
}
.az-action:hover {
  background: color-mix(in srgb, var(--primary, #b8d957) 8%, var(--az-topbar-input));
  border-color: color-mix(in srgb, var(--primary, #b8d957) 40%, var(--az-card-border));
  color: var(--az-text);
  text-decoration: none;
}
.az-action-icon {
  width: 28px; height: 28px;
  border-radius: 7px;
  background: var(--az-card);
  color: var(--primary-text, var(--primary, #b8d957));
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  flex-shrink: 0;
}
.az-action-primary {
  background: var(--primary, #b8d957);
  color: var(--primary-fg, #ffffff);
  border-color: var(--primary, #b8d957);
}
.az-action-primary:hover {
  background: var(--primary, #b8d957);
  color: var(--primary-fg, #ffffff);
  opacity: 0.9;
}
.az-action-primary .az-action-icon { background: rgba(0,0,0,0.1); color: var(--primary-fg, #ffffff); }

.az-empty-state {
  padding: 56px 22px;
  text-align: center;
  color: var(--az-text-muted);
}
.az-empty-state i { font-size: 40px; opacity: 0.4; margin-bottom: 16px; display: block; }
.az-empty-state h5 { color: var(--az-text); margin: 0; font-weight: 600; }

/* ── Language / Currency Switcher (LC) ──────────────────────────────────── */
.lc-switcher {
  position: relative;
  display: inline-flex;
  align-items: center;
}
.lc-trigger {
  display: flex !important;
  align-items: center !important;
  gap: 6px !important;
  white-space: nowrap;
  user-select: none;
}
.lc-trigger.is-open {
  background-color: var(--primary-subtle, rgba(26,63,212,0.07)) !important;
  border-color: var(--primary, #1A3FD4) !important;
  color: var(--primary-text, var(--primary, #1A3FD4)) !important;
  box-shadow: none !important;
}
.lc-trigger-globe { flex-shrink: 0; opacity: 0.8; transform: translateY(1px); }
.lc-trigger.is-open .lc-trigger-globe { opacity: 1; }
.lc-trigger-label { display: inline-flex; align-items: center; }
.lc-trigger-code { font-weight: 600; letter-spacing: 0.03em; }
.lc-trigger-sep { }
.lc-trigger-chevron {
  width: 10px; height: 10px;
  flex-shrink: 0;
  transition: transform 0.18s;
  /* Full strength: it should read as the same colour as the label beside it,
     not a faded version of it. */
  opacity: 1;
}
.lc-trigger.is-open .lc-trigger-chevron { transform: rotate(180deg); opacity: 1; }

/* Panel */
.lc-panel {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  /* Size to content so the currency column fits its widest item ("HUF -
     Hungarian Forint") + the checkmark, instead of a fixed 50/50 split that
     truncated long labels. Capped at the viewport. */
  width: max-content;
  max-width: calc(100vw - 24px);
  background: var(--card, #fff);
  border: 1px solid var(--border, rgba(0,0,0,0.09));
  border-radius: 10px;
  box-shadow: 0 4px 6px -1px rgba(0,0,0,0.08), 0 10px 24px -4px rgba(0,0,0,0.10);
  overflow: hidden;
  z-index: 1050;
  display: flex;
  flex-direction: column;
}
.lc-panel[hidden] { display: none; }

/* Two columns */
.lc-cols { display: flex; height: 240px; }
.lc-col { flex: 0 0 auto; display: flex; flex-direction: column; min-width: 132px; max-width: 260px; }
.lc-col + .lc-col { border-left: 1px solid rgba(0,0,0,0.06); }

.lc-col-head {
  padding: 10px 14px 6px;
  font-size: 10px;
  font-weight: 600;
  color: var(--muted, #A8A49E);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  flex-shrink: 0;
}
.lc-col-search {
  padding: 8px 12px;
  border-bottom: 1px solid rgba(0,0,0,0.06);
  position: relative;
  flex-shrink: 0;
}
.lc-col-search-icon {
  position: absolute;
  left: 22px;
  top: 50%;
  transform: translateY(-50%);
  pointer-events: none;
  color: var(--muted, #A8A49E);
}
.lc-col-search input {
  width: 100%;
  padding: 5px 8px 5px 26px;
  background: var(--bg, #f8f9fb);
  border: 1px solid var(--border, rgba(0,0,0,0.09));
  border-radius: 5px;
  font-family: inherit;
  font-size: 13px;
  color: var(--fg, #18160F);
  outline: none;
  transition: border-color 0.1s;
}
.lc-col-search input:focus { border-color: var(--primary, #1A3FD4); }
.lc-col-search input::placeholder { color: var(--muted, #A8A49E); }

.lc-col-list {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
}
.lc-col-list::-webkit-scrollbar { width: 4px; }
.lc-col-list::-webkit-scrollbar-track { background: transparent; }
.lc-col-list::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.15); border-radius: 2px; }
.lc-col-list { scrollbar-width: thin; scrollbar-color: rgba(0,0,0,0.15) transparent; }

/* List item */
.lc-item {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 8px 14px;
  cursor: pointer;
  user-select: none;
  transition: background 0.08s;
  border-left: 2px solid transparent;
}
.lc-item:hover { background: rgba(0,0,0,0.04); }
.lc-item.is-selected {
  background: var(--primary-subtle, rgba(26,63,212,0.07));
  border-left-color: var(--primary, #1A3FD4);
}
.lc-item.is-hidden { display: none !important; }
.lc-item-flag { font-size: 16px; line-height: 1; flex-shrink: 0; }
.lc-item-sym {
  width: 26px; height: 26px;
  border-radius: 4px;
  background: var(--bg, #f8f9fb);
  border: 1px solid var(--border, rgba(0,0,0,0.09));
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 600;
  color: var(--muted, #6B6860);
  flex-shrink: 0;
}
.lc-item.is-selected .lc-item-sym {
  background: var(--primary, #1A3FD4);
  border-color: var(--primary, #1A3FD4);
  color: #fff;
}
.lc-item-body { flex: 1; min-width: 0; }
.lc-item-name {
  font-size: 13px;
  font-weight: 400;
  color: var(--fg, #18160F);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.2;
}
.lc-item.is-selected .lc-item-name {
  font-weight: 600;
  color: var(--primary-text, var(--primary, #1A3FD4));
}
.lc-item-sub {
  font-size: 11px;
  color: var(--muted, #A8A49E);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.2;
  margin-top: 1px;
}
.lc-item-code {
  font-size: 11px;
  color: var(--muted, #A8A49E);
  flex-shrink: 0;
}
.lc-item.is-selected .lc-item-code { color: var(--primary-text, var(--primary, #1A3FD4)); }
.lc-item-check {
  flex-shrink: 0;
  color: var(--primary, #1A3FD4);
  display: none;
}
.lc-item.is-selected .lc-item-check { display: block; }
.lc-no-results {
  padding: 20px 14px;
  font-size: 13px;
  color: var(--muted, #A8A49E);
  text-align: center;
}
.lc-no-results.is-hidden { display: none; }

/* Footer */
.lc-footer {
  border-top: 1px solid rgba(0,0,0,0.06);
  /* Right padding matches the 10px top/bottom so the Apply button sits an equal
     gap from the right edge as from the footer edges. Left stays 16px to line up
     with the column content above. */
  padding: 10px 10px 10px 16px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: var(--bg, #f8f9fb);
  flex-shrink: 0;
}
.lc-footer-text { font-size: 12px; color: var(--muted, #A8A49E); }
/* Mobile switcher (offcanvas): the selection is already visible in the two
   columns above, so drop the summary text and keep only the Apply button. */
#shopMobileMenu .lc-footer-text { display: none; }
#shopMobileMenu .lc-footer { justify-content: flex-end; }
.lc-apply {
  padding: 6px 18px;
  background: var(--primary, #1A3FD4);
  color: var(--primary-fg, #fff);
  border: none;
  border-radius: 6px;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: opacity 0.12s;
}
.lc-apply:hover { opacity: 0.88; }

/* Dark mode overrides */
[data-theme="dark"] .lc-trigger:hover { background: rgba(255,255,255,0.06) !important; }
[data-theme="dark"] .lc-item:hover { background: rgba(255,255,255,0.05); }
[data-theme="dark"] .lc-col + .lc-col { border-left-color: rgba(255,255,255,0.06); }
[data-theme="dark"] .lc-footer { border-top-color: rgba(255,255,255,0.06); }
[data-theme="dark"] .lc-col-search { border-bottom-color: rgba(255,255,255,0.06); }
[data-theme="dark"] .lc-col-list::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.2); }
[data-theme="dark"] .lc-col-list { scrollbar-color: rgba(255,255,255,0.2) transparent; }
[data-theme="dark"] .lc-panel { box-shadow: 0 4px 6px -1px rgba(0,0,0,0.3), 0 10px 24px -4px rgba(0,0,0,0.4); }

/* Mobile: stack columns vertically on narrow screens */
@media (max-width: 540px) {
  .lc-cols { flex-direction: column; height: auto; }
  .lc-col + .lc-col { border-left: none; border-top: 1px solid rgba(0,0,0,0.06); }
  .lc-col-list { max-height: 140px; }
  [data-theme="dark"] .lc-col + .lc-col { border-top-color: rgba(255,255,255,0.06); }
}

/* Admin topbar integration */
.az-topbar .lc-switcher { position: relative; }
.az-topbar .lc-trigger.is-open {
  background: var(--az-topbar-input, rgba(0,0,0,0.04)) !important;
  border-color: var(--primary, currentColor) !important;
}
.az-topbar .lc-panel {
  /* Only as wide as the longest language + its checkmark. The JS clamps its
     on-screen position so it never spills off the viewport (navigation.js
     openPanel). */
  width: max-content;
  min-width: 150px;
  max-width: min(240px, calc(100vw - 16px));
  z-index: 9999;
}
.az-topbar .lc-cols { height: 240px; }
/* Admin topbar is single-column (language only) and clamps its own panel width,
   so keep the original fill-the-panel behaviour there. */
.az-topbar .lc-col { flex: 1; min-width: 0; max-width: none; }
.az-topbar .lc-footer { display: none; }
/* Mobile: the single-column list was capped at 140px inside a forced 240px
   column (a ~100px empty gap). Let the column + list size themselves. */
@media (max-width: 540px) {
  .az-topbar .lc-cols { height: auto; }
  .az-topbar .lc-col-list { max-height: min(60vh, 340px); }
}

/* Phase 2 — storefront popup */
.sz-popup { position: fixed; inset: 0; z-index: 1080; display: flex; align-items: center; justify-content: center; padding: 16px; }
.sz-popup .sz-popup-overlay { position: absolute; inset: 0; background: rgba(0,0,0,.5); }
.sz-popup .sz-popup-card { position: relative; max-width: 460px; width: 100%; background: var(--bg, #fff); color: var(--fg, #1f2937); border: 1px solid var(--border, #e5e7eb); border-radius: 12px; padding: 28px 24px 24px; box-shadow: 0 20px 60px rgba(0,0,0,.25); animation: sz-popup-in .25s ease-out; overflow: hidden; }
[data-theme="dark"] .sz-popup .sz-popup-card { background: var(--card, #222); }
@keyframes sz-popup-in { from { opacity: 0; transform: translateY(12px) scale(.98); } to { opacity: 1; transform: translateY(0) scale(1); } }
.sz-popup .sz-popup-close { position: absolute; top: 8px; right: 12px; background: transparent; border: 0; font-size: 28px; line-height: 1; color: var(--fg-muted, #6b7280); cursor: pointer; padding: 4px 8px; z-index: 2; }
.sz-popup .sz-popup-content { position: relative; z-index: 1; }
.sz-popup .sz-popup-title { font-size: 20px; font-weight: 600; margin: 0 0 8px; line-height: 1.3; }
.sz-popup .sz-popup-body  { margin: 0 0 16px; line-height: 1.5; }
/* CTA button — own colours via CSS variables driven by inline style
   on the partial. Same pattern the hero block uses: .btn-primary's
   gradient shorthand + :hover !important would otherwise win over a
   plain inline background-color, leaving the brand gradient showing
   regardless of the owner's pick. Reading the variables here (with
   !important) gives the owner's colour priority across resting /
   hover / focus / active. When the variables are unset we fall back
   to the same gradient .btn-primary uses everywhere else. */
.sz-popup .sz-popup-cta.btn-primary,
.sz-popup .sz-popup-cta.btn-primary:hover,
.sz-popup .sz-popup-cta.btn-primary:focus,
.sz-popup .sz-popup-cta.btn-primary:active,
.sz-popup .sz-popup-cta.btn-primary:focus-visible {
  background: var(--popup-cta-bg, linear-gradient(135deg, var(--primary), var(--primary-2))) !important;
  border-color: var(--popup-cta-border, var(--primary)) !important;
  color: var(--popup-cta-fg, var(--primary-fg, #fff)) !important;
}

/* The button is wrapped in .sz-popup-cta-wrap which receives the
   margin-top / position / z-index. Matches the hero block's
   approach: putting these on a block-level wrapper avoids the
   "inline-block negative margin doesn't always pull up reliably
   inside flex columns" trap. The wrapper aligns horizontally via
   text-align inherited from .sz-popup-content (or flex-start /
   center / flex-end keyed off data-text-align). */
.sz-popup .sz-popup-cta-wrap { width: 100%; }
.sz-popup .sz-popup-cta   { display: inline-block; }
.sz-popup .sz-popup-cta--small  { padding: .25rem .75rem;  font-size: .75rem;  border-radius: .25rem; }
.sz-popup .sz-popup-cta--medium { padding: .375rem 1rem;   font-size: .875rem; border-radius: .375rem; }
.sz-popup .sz-popup-cta--large  { padding: .625rem 1.5rem; font-size: 1.0625rem; border-radius: .5rem; }

/* Stacked-sticker overlap: when body has a bg the wrapper pulls up
   over the body pill by 14px and sits on top via z-index 3. body
   pill is z-index 2; title pill has no explicit z-index (sits at
   1). When body has no bg the wrapper gets a normal 14px top
   margin so the button doesn't crash into the body text. */
.sz-popup .sz-popup-card[data-has-body-bg="1"] .sz-popup-cta-wrap {
  margin-top: -26px;
  position: relative;
  z-index: 3;
}
.sz-popup .sz-popup-card[data-has-body-bg="0"] .sz-popup-cta-wrap {
  margin-top: 14px;
}

/* Popup admin: always-visible colour pill. Wraps a native <input
   type="color"> so a click anywhere on the pill opens the picker.
   When data-has-value="0", the swatch shows a "no colour" red
   diagonal stripe on white; when "1", the picker's chosen colour
   renders. The × clear button always shows on the right. */
.popup-color-pill {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  cursor: pointer;
  user-select: none;
  margin-bottom: 0;
}
.popup-color-pill__swatch {
  display: inline-block;
  width: 28px;
  height: 24px;
  border: 1px solid var(--pz-border, #ced4da);
  border-radius: 4px;
  background-color: #fff;
  flex: 0 0 auto;
  pointer-events: none;  /* clicks go to the wrapping <label> → <input> */
}
.popup-color-pill[data-has-value="0"] .popup-color-pill__swatch {
  /* Diagonal "no colour" indicator goes from bottom-left to top-right.
     A gradient `to top left` axis (bottom-right → top-left) gives a
     line perpendicular to that, i.e. bottom-left → top-right.
     0.5px stops on either side of 50% produce a hairline that's ~1px
     thick on display — thinner than the previous 2px line. */
  background-image: linear-gradient(
    to top left,
    transparent calc(50% - 0.5px),
    #dc3545 calc(50% - 0.5px),
    #dc3545 calc(50% + 0.5px),
    transparent calc(50% + 0.5px)
  );
}
.popup-color-pill__input {
  position: absolute;
  left: 0; top: 0;
  width: 28px; height: 24px;
  opacity: 0;          /* swatch span is the visual; input handles clicks */
  border: 0; padding: 0;
  cursor: pointer;
}
.popup-color-pill__clear {
  appearance: none;
  background: transparent;
  border: 0;
  color: var(--pz-text-muted, #6c757d);
  font-size: 16px;
  line-height: 1;
  padding: 2px 4px;
  cursor: pointer;
  border-radius: 4px;
}
.popup-color-pill__clear:hover { background: rgba(220, 53, 69, 0.1); color: #dc3545; }
.popup-color-pill[data-has-value="0"] .popup-color-pill__clear { visibility: hidden; }

/* Card layout — CSS Grid puts the image and the content in the same
   1×1 cell so they overlay naturally. Image z-index 0 (default),
   content z-index 1. For aspect-auto the <img>'s natural height
   drives the cell's height; for fixed aspects the card sets
   aspect-ratio and the image fills via object-fit: cover. The
   card already has padding (top of file) — we strip it back when
   the layout switches to Grid so the image truly fills, then
   restore padding inside .sz-popup-content. */
.sz-popup .sz-popup-card {
  padding: 0;
  display: grid;
  grid-template-columns: 100%;
  grid-template-rows: auto;
}
.sz-popup .sz-popup-card__img {
  grid-row: 1; grid-column: 1;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* aspect-auto + image: let the <img> drive its natural height. We
   override the cover behaviour so the image renders at its own
   aspect ratio inside the card — no cropping. */
.sz-popup .sz-popup-card--aspect-auto .sz-popup-card__img {
  height: auto;
  object-fit: contain;
}
.sz-popup .sz-popup-card--aspect-square    { aspect-ratio: 1 / 1; }
.sz-popup .sz-popup-card--aspect-landscape { aspect-ratio: 16 / 9; }
.sz-popup .sz-popup-card--aspect-portrait  { aspect-ratio: 3 / 4; }

.sz-popup .sz-popup-card .sz-popup-content {
  grid-row: 1; grid-column: 1;
  z-index: 1;
  padding: 28px 24px 24px;
  display: flex;
  flex-direction: column;
}
.sz-popup .sz-popup-card[data-text-vpos="top"]    .sz-popup-content { justify-content: flex-start; }
.sz-popup .sz-popup-card[data-text-vpos="middle"] .sz-popup-content { justify-content: center; }
.sz-popup .sz-popup-card[data-text-vpos="bottom"] .sz-popup-content { justify-content: flex-end; }

/* Hero-style variant: title / body show white text over the image,
   with subtle text-shadow for legibility on busy backgrounds. The
   owner can pick explicit pill background colours via heading_bg /
   body_bg to make the contrast more deliberate. */
.sz-popup .sz-popup-card--has-image .sz-popup-close {
  color: #fff;
  text-shadow: 0 1px 2px rgba(0,0,0,0.6);
}
.sz-popup .sz-popup-card--has-image .sz-popup-title,
.sz-popup .sz-popup-card--has-image .sz-popup-body {
  color: #fff;
  text-shadow: 0 1px 2px rgba(0,0,0,0.4);
}

/* Colour pills — match the hero block's "stacked sticker label"
   look: square edges (no border-radius), heading pill in a normal
   posture, body pill slightly tilted left and overlapping the
   heading above via negative margin-top + z-index. The body's
   coloured background sits on top of the heading's where they
   overlap. */
.sz-popup .sz-popup-title__pill,
.sz-popup .sz-popup-body__pill {
  display: inline-block;
  max-width: 100%;
  position: relative;
  text-shadow: none;  /* the pill already provides contrast */
}
.sz-popup .sz-popup-title__pill {
  padding: 8px 16px;
}
.sz-popup .sz-popup-body__pill {
  padding: 16px 16px;
  transform: rotate(-1.2deg);
  transform-origin: left center;
  margin-top: -10px;
  z-index: 2;
}
/* When body is right-aligned, anchor the tilt origin on the right so
   the rotation pivots from the visible side instead of off-screen. */
.sz-popup .sz-popup-card[data-text-align="right"]  .sz-popup-body__pill { transform-origin: right center; }
.sz-popup .sz-popup-card[data-text-align="center"] .sz-popup-body__pill { transform-origin: center center; }
.sz-popup .sz-popup-title__pill p,
.sz-popup .sz-popup-body__pill p { margin: 0; }

html.sz-popup-open { overflow: hidden; }

/* Phase 9 — cookie consent banner */
.sz-consent { position: fixed; left: 0; right: 0; bottom: 0; z-index: 1090; padding: 12px; pointer-events: none; }
.sz-consent .sz-consent-card { pointer-events: auto; max-width: 720px; margin: 0 auto; background: var(--bg, #ffffff); color: var(--fg, #1f2937); border: 1px solid var(--border, #e5e7eb); border-radius: 10px; box-shadow: 0 12px 40px rgba(0,0,0,.18); padding: 14px 18px; display: flex; flex-direction: column; gap: 10px; }
@media (min-width: 720px) { .sz-consent .sz-consent-card { flex-direction: row; align-items: center; } .sz-consent .sz-consent-text { flex: 1; min-width: 0; } }
[data-theme="dark"] .sz-consent .sz-consent-card { background: var(--card, #222); }
.sz-consent .sz-consent-text strong { display: block; font-size: 14px; margin-bottom: 2px; }
.sz-consent .sz-consent-text p { font-size: 13px; line-height: 1.5; color: var(--fg-muted, #6b7280); margin: 0; }
.sz-consent .sz-consent-actions { display: flex; gap: 8px; flex-wrap: wrap; flex-shrink: 0; }
/* Equal-weight Accept/Reject/Customize — CNIL/EDPB compliance: same class,
   same min-width so no button is visually nudged toward selection. */
.sz-consent .sz-consent-btn { min-width: 110px; }
.sz-consent[hidden] { display: none !important; }

/* Phase 9b — granular categories panel */
/* In expanded mode the card stacks vertically so the title/body sits above
   the categories rather than being squished beside them. */
.sz-consent.is-expanded .sz-consent-card { flex-direction: column; align-items: stretch; }
.sz-consent.is-expanded .sz-consent-text { flex: initial; }
.sz-consent .sz-consent-categories { width: 100%; display: flex; flex-direction: column; gap: 14px; padding-top: 4px; }
.sz-consent .sz-consent-cat { display: flex; align-items: flex-start; gap: 12px; padding: 10px 12px; background: var(--card, #f9fafb); border: 1px solid var(--border, #e5e7eb); border-radius: 8px; }
[data-theme="dark"] .sz-consent .sz-consent-cat { background: rgba(255,255,255,.04); border-color: var(--border, #2a2a2a); }
.sz-consent .sz-consent-cat-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; cursor: pointer; }
.sz-consent .sz-consent-cat-text strong { font-size: 13px; }
.sz-consent .sz-consent-cat-desc { font-size: 12px; line-height: 1.45; color: var(--fg-muted, #6b7280); }
.sz-consent .sz-consent-cat-locked { font-size: 11px; color: var(--fg-muted, #6b7280); padding: 4px 8px; background: var(--bg, #ffffff); border: 1px solid var(--border, #e5e7eb); border-radius: 999px; flex-shrink: 0; align-self: center; }
[data-theme="dark"] .sz-consent .sz-consent-cat-locked { background: var(--card, #222); }
.sz-consent .sz-consent-toggle { position: relative; display: inline-block; width: 38px; height: 22px; flex-shrink: 0; align-self: center; cursor: pointer; }
.sz-consent .sz-consent-toggle input { opacity: 0; width: 0; height: 0; }
.sz-consent .sz-consent-toggle-slider { position: absolute; inset: 0; background-color: var(--border, #d1d5db); transition: .15s; border-radius: 22px; }
.sz-consent .sz-consent-toggle-slider::before { position: absolute; content: ""; height: 16px; width: 16px; left: 3px; top: 3px; background-color: #ffffff; transition: .15s; border-radius: 50%; box-shadow: 0 1px 2px rgba(0,0,0,.2); }
.sz-consent .sz-consent-toggle input:checked + .sz-consent-toggle-slider { background-color: var(--primary, #6366f1); }
.sz-consent .sz-consent-toggle input:checked + .sz-consent-toggle-slider::before { transform: translateX(16px); }
.sz-consent .sz-consent-toggle input:focus-visible + .sz-consent-toggle-slider { outline: 2px solid var(--primary, #6366f1); outline-offset: 2px; }
.sz-consent .sz-consent-actions-expanded { padding-top: 4px; border-top: 1px solid var(--border, #e5e7eb); margin-top: 4px; }

/* Landing — "Live now" badge variant (Phase 12 polish) */
.landing-soon-card .landing-live-badge { background: #dcfce7 !important; color: #15803d !important; border-color: #86efac !important; }
[data-theme="dark"] .landing-soon-card .landing-live-badge { background: rgba(34, 197, 94, .15) !important; color: #4ade80 !important; border-color: rgba(74, 222, 128, .35) !important; }

/* ─────────────────────────────────────────────────────────────────
   Admin → Marketing → Promos (V2 redesign — list + modal overlay)
   Sleek admin SaaS aesthetic: neutral stone grays, single emerald
   accent, traditional underlined tabs, list rows that open into a
   centered modal for create/edit. Tokens are inlined locally so the
   page reads cohesively even when admin theme variables vary.
   ───────────────────────────────────────────────────────────────── */

/* Local tokens kept under .promos-shell so the rest of admin is unaffected.
   The page sits flush against admin-main's own padding — no extra top
   indent — and stretches to the full content width. */
.promos-shell {
  --pz-bg: #fafaf9;
  --pz-surface: #ffffff;
  --pz-surface-muted: #f5f5f4;
  --pz-border: #e7e5e4;
  --pz-border-strong: #d6d3d1;
  --pz-text: #1c1917;
  --pz-text-muted: #78716c;
  --pz-text-subtle: #a8a29e;
  --pz-accent: #10b981;
  --pz-accent-deep: #059669;
  --pz-accent-soft: #ecfdf5;
  --pz-danger: #ef4444;
  /* Use the shop's brand colour from the global --primary var (set by the
     theme) for the primary button & save action; fall back to emerald. */
  --pz-brand: var(--primary, #10b981);
  --pz-brand-fg: var(--primary-fg, #ffffff);
  width: 100%;
  padding: 0;
  color: var(--pz-text);
}
[data-theme="dark"] .promos-shell {
  --pz-bg: #1a1a1a;
  --pz-surface: #222222;
  --pz-surface-muted: #2a2a2a;
  --pz-border: #333333;
  --pz-border-strong: #444444;
  --pz-text: #f3f4f6;
  --pz-text-muted: #94a3b8;
  --pz-text-subtle: #6b7280;
  --pz-accent: #10b981;
  --pz-accent-deep: #34d399;
  --pz-accent-soft: rgba(16, 185, 129, 0.12);
}

/* ── Page header (title + subtitle + primary action) ── */
.promos-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 18px;
  /* Narrow screens: the right-hand element (period picker, "+ New" button)
     drops to its own line below the title instead of squeezing/overflowing —
     matches the dashboard header's flex-wrap behaviour. */
  flex-wrap: wrap;
}
.promos-header__title {
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -0.3px;
  margin: 0;
  color: var(--pz-text);
}
.promos-header__subtitle {
  font-size: 13.5px;
  color: var(--pz-text-muted);
  margin: 4px 0 0;
}

/* ── Reusable inline help bubble (.help-tip) ──────────────────────
   A small (i) icon that reveals helper text on hover or click (click
   pins it). Markup: views/admin/partials/_help_tip.ejs; behaviour:
   the [data-help-tip] delegation in public/js/admin-shell.js. Designed
   to be dropped anywhere in the admin (security console today, owner
   dashboard later). */
.help-tip {
  position: relative;
  display: inline-flex;
  vertical-align: middle;
}
.help-tip__btn {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  margin: 0 0 0 5px;
  line-height: 1;
  font-size: 12px;
  color: var(--pz-text-subtle, #9ca3af);
  cursor: pointer;
  display: inline-flex;
  transition: color 0.15s ease;
}
.help-tip__btn:hover,
.help-tip.is-open .help-tip__btn { color: var(--pz-brand, #2563eb); }
.help-tip__bubble {
  /* Fixed + JS-positioned so the bubble escapes any overflow-clipping ancestor
     (e.g. a .card / scroll container). admin-shell.js sets top/left on show. */
  position: fixed;
  z-index: 1090;
  width: max-content;
  max-width: 264px;
  padding: 9px 11px;
  background: var(--pz-surface, #fff);
  color: var(--pz-text, #111827);
  border: 1px solid var(--pz-border, #e5e7eb);
  border-radius: 8px;
  box-shadow: 0 6px 22px rgba(0, 0, 0, 0.13);
  font-size: 12.5px;
  font-weight: 400;
  line-height: 1.45;
  text-align: left;
  white-space: normal;
  letter-spacing: normal;
  text-transform: none;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.12s ease;
}
.help-tip__bubble::after {
  content: "";
  position: absolute;
  top: 100%;
  left: var(--ht-arrow, 50%);
  transform: translateX(-50%);
  border: 6px solid transparent;
  border-top-color: var(--pz-surface, #fff);
  filter: drop-shadow(0 1px 0 var(--pz-border, #e5e7eb));
}
/* Flipped below the icon (no room above): arrow points up from the top edge. */
.help-tip--below .help-tip__bubble::after {
  top: auto;
  bottom: 100%;
  border-top-color: transparent;
  border-bottom-color: var(--pz-surface, #fff);
  filter: drop-shadow(0 -1px 0 var(--pz-border, #e5e7eb));
}
.help-tip:hover .help-tip__bubble,
.help-tip:focus-within .help-tip__bubble,
.help-tip.is-open .help-tip__bubble {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}

/* ── Header search box (used by the reviews moderation list) ── */
.promos-reviews-search { flex-shrink: 0; }
.promos-search {
  position: relative;
  display: inline-flex;
  align-items: center;
  width: 280px;
  max-width: 100%;
}
.promos-search__icon {
  position: absolute;
  left: 11px;
  font-size: 12px;
  color: var(--pz-text-subtle);
  pointer-events: none;
}
.promos-search__input.promos-input { padding-left: 30px; padding-right: 30px; }
.promos-search__clear {
  position: absolute;
  right: 9px;
  display: inline-flex;
  align-items: center;
  color: var(--pz-text-subtle);
  font-size: 12px;
  text-decoration: none;
}
.promos-search__clear:hover { color: var(--pz-text); }

/* ── Tabs (traditional underlined) ── */
.promos-tabs {
  display: flex;
  align-items: flex-end;
  gap: 0;
  border-bottom: 1px solid var(--pz-border);
  margin: 0 0 22px;
  padding: 0;
  list-style: none;
  /* When the tabs don't fit (narrow screens, many tabs) scroll THE TAB ROW
     horizontally instead of the whole page. Scrollbar hidden for a clean look;
     the row is still swipeable/scrollable. */
  flex-wrap: nowrap;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  -ms-overflow-style: none;
  -webkit-overflow-scrolling: touch;
}
.promos-tabs::-webkit-scrollbar { display: none; width: 0; height: 0; }
.promos-tab {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 10px 16px;
  flex: 0 0 auto;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 500;
  color: var(--pz-text-muted);
  cursor: pointer;
  text-decoration: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.promos-tab:hover { color: var(--pz-text); }
/* Two tab conventions live in the app: Bootstrap-driven marketing tabs toggle
   `.active`, while the settings / insights / analytics / security tabs (custom
   JS in admin-shell.js + navigation.js, and server-rendered links) use
   `.is-active`. Style both so neither set of pages loses its active accent. */
.promos-tab.active,
.promos-tab.is-active {
  color: var(--pz-text);
  /* Use the shop's brand colour for the active underline so the tab matches
     the "+ New" button and the bubble. */
  border-bottom-color: var(--pz-brand);
}
.promos-tab__icon {
  width: 14px; height: 14px;
  stroke: currentColor; stroke-width: 1.8; fill: none;
  stroke-linecap: round; stroke-linejoin: round;
}
.promos-tab__count {
  font-size: 11px;
  font-weight: 500;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--pz-surface-muted);
  color: var(--pz-text-muted);
  font-variant-numeric: tabular-nums;
  transition: background 0.15s ease, color 0.15s ease;
}
.promos-tab.active .promos-tab__count,
.promos-tab.is-active .promos-tab__count {
  background: var(--pz-brand);
  color: var(--pz-brand-fg);
}

/* ── Primary "New" button in the header ── */
.promos-btn {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  /* Fixed 34px so modal footer buttons (and the +New header buttons) match the
     settings Save button, the onboarding pill and the sidebar upgrade CTA. */
  height: 34px;
  padding: 0 14px;
  border-radius: 7px;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  text-decoration: none;
  font-family: inherit;
  border: 1px solid transparent;
  transition: opacity 0.15s ease, background 0.15s ease, border-color 0.15s ease;
}
.promos-btn--primary {
  background: var(--pz-brand);
  color: var(--pz-brand-fg);
  border-color: var(--pz-brand);
}
.promos-btn--primary:hover { opacity: 0.92; color: var(--pz-brand-fg); text-decoration: none; }
.promos-btn--secondary {
  background: var(--pz-surface);
  color: var(--pz-text);
  border-color: var(--pz-border);
}
.promos-btn--secondary:hover {
  border-color: var(--pz-border-strong);
  color: var(--pz-text);
  text-decoration: none;
}
.promos-btn--ghost {
  background: transparent;
  color: var(--pz-text-muted);
  border-color: transparent;
}
.promos-btn--danger {
  background: var(--pz-surface);
  color: var(--pz-danger);
  border-color: var(--pz-border);
}
.promos-btn--danger:hover { background: #fef2f2; border-color: #fecaca; color: var(--pz-danger); }
[data-theme="dark"] .promos-btn--danger:hover { background: rgba(239, 68, 68, 0.1); border-color: rgba(239, 68, 68, 0.3); }
.promos-btn__plus {
  width: 14px; height: 14px;
  stroke: currentColor; stroke-width: 2.2; fill: none;
  stroke-linecap: round;
}

/* ── Row card (banner / popup list item) ── */
.promos-list { display: flex; flex-direction: column; gap: 10px; }
.promos-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 16px;
  background: var(--pz-surface);
  border: 1px solid var(--pz-border);
  border-radius: 10px;
  cursor: pointer;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
  text-decoration: none;
  color: inherit;
}
.promos-row:hover {
  border-color: var(--pz-border-strong);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}
.promos-row--static { cursor: default; }
.promos-row--static:hover {
  border-color: var(--pz-border);
  box-shadow: none;
}
.promos-row__swatch {
  width: 40px; height: 40px;
  border-radius: 8px;
  display: grid;
  place-items: center;
  color: #fff;
  font-size: 14px;
  font-weight: 600;
  flex-shrink: 0;
  letter-spacing: 0.02em;
}
.promos-row__main { flex: 1; min-width: 0; }
.promos-row__title {
  font-size: 14px;
  font-weight: 600;
  color: var(--pz-text);
  margin-bottom: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.promos-row__meta {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 12px;
  color: var(--pz-text-muted);
  flex-wrap: wrap;
}
.promos-row__type {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 11.5px;
  color: var(--pz-text-subtle);
}
.promos-row__url {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
}
.promos-row__url-text {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 360px;
  display: inline-block;
}
.promos-row__locked {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--pz-text-subtle);
}
.promos-row__edit {
  appearance: none;
  padding: 6px 10px;
  font-size: 12px;
  font-weight: 500;
  color: var(--pz-text-muted);
  background: var(--pz-surface);
  border: 1px solid var(--pz-border);
  border-radius: 6px;
  cursor: pointer;
  font-family: inherit;
}
.promos-row__edit:hover {
  color: var(--pz-text);
  border-color: var(--pz-border-strong);
}

/* ── Status badge (LIVE / DRAFT / SCHEDULED / EXPIRED / PAUSED) ── */
.promos-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 9px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.3px;
  text-transform: uppercase;
  flex-shrink: 0;
}
.promos-status::before {
  content: '';
  width: 6px; height: 6px;
  border-radius: 50%;
  background: currentColor;
  display: inline-block;
}
.promos-status--live { background: var(--pz-accent-soft); color: var(--pz-accent-deep); }
.promos-status--draft, .promos-status--inactive { background: var(--pz-surface-muted); color: var(--pz-text-muted); }
.promos-status--scheduled { background: #dbeafe; color: #1e40af; }
.promos-status--expired { background: var(--pz-surface-muted); color: var(--pz-text-muted); }
.promos-status--paused { background: #fef3c7; color: #92400e; }
.promos-status--failed { background: #fee2e2; color: #b91c1c; }

/* Subscribers table inside the email page's Subscribers tab. Keep
   the heading row muted so it doesn't fight the promos-shell card
   styling, and let the email column be the visual anchor. */
.subscribers-table thead th {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--pz-text-subtle);
  border-bottom: 1px solid var(--pz-border);
}
.subscribers-table tbody td { vertical-align: middle; }

/* Small pill shown alongside a campaign's subject in the email
   list when the resolved subject is in a different language than
   the admin's current UI. Tells the owner at a glance that they
   haven't written a subject in their working language yet. */
.promos-row__lng-chip {
  display: inline-block;
  margin-right: 6px;
  padding: 1px 6px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .02em;
  color: var(--pz-text-subtle);
  background: var(--pz-surface-muted);
  border-radius: 4px;
  vertical-align: 1px;
}

/* Campaign subject row: emoji picker button glued to the left of
   the input. The picker's popover sits below the trigger, anchored
   to the row's first column. */
.campaign-subject-row {
  display: flex;
  gap: 8px;
  align-items: stretch;
  position: relative;
}
.campaign-emoji-picker { position: relative; }
.campaign-emoji-btn {
  display: flex; align-items: center; justify-content: center;
  width: 38px; height: 100%; min-height: 36px;
  padding: 0;
  background: var(--pz-surface);
  border: 1px solid var(--pz-border);
  border-radius: 6px;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  transition: border-color .12s ease, background .12s ease;
}
.campaign-emoji-btn:hover { border-color: var(--pz-border-strong); background: var(--pz-surface-muted); }
.campaign-emoji-btn[aria-expanded="true"] {
  border-color: var(--pz-accent-deep, #1a8917);
  background: var(--pz-accent-soft, #e7f5e6);
}
/* Faint by default so the picker doesn't shout for attention next
   to the subject field. The icon perks up to full colour on hover
   or while the popover is open, signalling it's interactive. */
.campaign-emoji-btn > span {
  opacity: .25;
  filter: grayscale(1);
  transition: opacity .12s ease, filter .12s ease;
}
.campaign-emoji-btn:hover > span,
.campaign-emoji-btn:focus-visible > span,
.campaign-emoji-btn[aria-expanded="true"] > span {
  opacity: 1;
  filter: none;
}
.campaign-emoji-popover {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 50;
  width: 280px;
  max-height: 260px;
  overflow-y: auto;
  padding: 10px;
  background: var(--pz-surface);
  border: 1px solid var(--pz-border);
  border-radius: 8px;
  box-shadow: 0 6px 20px rgba(0,0,0,0.08);
}
.campaign-emoji-popover[hidden] { display: none; }
.campaign-emoji-grid {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 2px;
}
.campaign-emoji-pick {
  display: flex; align-items: center; justify-content: center;
  width: 100%; aspect-ratio: 1;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 4px;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  transition: background .1s ease;
}
.campaign-emoji-pick:hover, .campaign-emoji-pick:focus-visible {
  background: var(--pz-surface-muted);
  outline: none;
}
.campaign-subject-row > .promos-input { flex: 1; min-width: 0; }

/* Campaign test-send panel (the collapsible section inside the
   edit campaign modal). Styled to look like a quiet sub-section
   inside the modal body — distinct enough that the owner sees it,
   subtle enough that it doesn't fight the main editor for
   attention. */
.campaign-test-panel { padding-top: 0; }
.campaign-test-panel details {
  border: 1px solid var(--pz-border);
  border-radius: 8px;
  background: var(--pz-surface-muted);
}
.campaign-test-panel__summary {
  display: flex;
  align-items: center;
  padding: 12px 14px;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  list-style: none;
  user-select: none;
}
.campaign-test-panel__summary::-webkit-details-marker { display: none; }
.campaign-test-panel details[open] .campaign-test-panel__chev {
  transform: rotate(180deg);
}
.campaign-test-panel__chev { transition: transform .15s ease; }
.campaign-test-panel__form {
  padding: 0 14px 14px;
  border-top: 1px solid var(--pz-border);
}
.campaign-test-panel__form > div:first-child { padding-top: 12px; }
.campaign-test-panel__locales {
  display: flex; flex-wrap: wrap; gap: 6px;
}
.campaign-test-panel__locale-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px;
  background: var(--pz-surface);
  border: 1px solid var(--pz-border);
  border-radius: 999px;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
}
.campaign-test-panel__locale-chip input { margin: 0; }
.campaign-test-panel__locale-chip:has(input:checked) {
  border-color: var(--pz-accent-deep, #1a8917);
  background: var(--pz-accent-soft, #e7f5e6);
  color: var(--pz-accent-deep, #1a8917);
}
[data-theme="dark"] .promos-status--failed { background: rgba(239, 68, 68, 0.18); color: #fca5a5; }
[data-theme="dark"] .promos-status--scheduled { background: rgba(59, 130, 246, 0.15); color: #93c5fd; }
[data-theme="dark"] .promos-status--paused { background: rgba(245, 158, 11, 0.15); color: #fbbf24; }

/* ── Footer hint ── */
.promos-footer-hint {
  margin-top: 24px;
  padding: 16px 20px;
  border-radius: 10px;
  border: 1px dashed var(--pz-border-strong);
  color: var(--pz-text-muted);
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 13px;
  gap: 12px;
}
.promos-footer-hint__count {
  font-size: 12px;
  color: var(--pz-text-subtle);
}

/* ── Empty state ── */
.promos-empty {
  text-align: center;
  padding: 56px 24px;
  border: 1px dashed var(--pz-border-strong);
  border-radius: 10px;
  color: var(--pz-text-muted);
}
.promos-empty__icon {
  width: 48px; height: 48px;
  border-radius: 50%;
  background: var(--pz-surface-muted);
  display: inline-grid;
  place-items: center;
  font-size: 1.2rem;
  margin-bottom: 14px;
  color: var(--pz-text-subtle);
}

/* ── Modal overlay (covers the admin main, scrim + centered card) ── */
.promos-modal { position: fixed; inset: 0; z-index: 2000; display: none; }
.promos-modal[data-open="true"] { display: grid; place-items: center; padding: 32px; }
/* Phone portrait: the 32px inset + a 92vh card overflowed both ends (header
   and footer clipped). Shrink the inset and cap the card to the DYNAMIC
   viewport height (100dvh accounts for the mobile browser chrome) so the
   pinned header + footer stay on screen and only the body scrolls. */
@media (max-width: 576px) {
  .promos-modal[data-open="true"] { padding: 8px; }
  /* Higher specificity than the base `.promos-modal__card { max-height: 92vh }`
     which is declared LATER in the file — otherwise it wins on source order. */
  .promos-modal[data-open="true"] .promos-modal__card { max-height: calc(100dvh - 16px); }
}
.promos-modal__scrim {
  position: absolute; inset: 0;
  background: rgba(28, 25, 23, 0.45);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  animation: promos-fade 0.18s ease;
}
/* Light-mode tokens are declared as the BASE on .promos-modal__card itself
   (not via :not([data-theme])) so the [data-theme="dark"] override below
   actually wins in dark mode. The modal is rendered as a sibling of the
   .promos-shell rather than a descendant, so we can't inherit shell tokens
   — we have to redeclare them here. */
.promos-modal__card {
  --pz-bg: #fafaf9;
  --pz-surface: #ffffff;
  --pz-surface-muted: #f5f5f4;
  --pz-border: #e7e5e4;
  --pz-border-strong: #d6d3d1;
  --pz-text: #1c1917;
  --pz-text-muted: #78716c;
  --pz-text-subtle: #a8a29e;
  --pz-accent: #10b981;
  --pz-accent-deep: #059669;
  --pz-accent-soft: #ecfdf5;
  --pz-danger: #ef4444;
  /* Mirror the shop's brand colour into the modal scope so the Save
     button matches the page-header "+ New banner" button. */
  --pz-brand: var(--primary, #10b981);
  --pz-brand-fg: var(--primary-fg, #ffffff);
  position: relative;
  width: 100%;
  max-width: 760px;
  max-height: 92vh;
  background: var(--pz-surface);
  color: var(--pz-text);
  border-radius: 14px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.18);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  animation: promos-pop 0.2s ease;
}
[data-theme="dark"] .promos-modal__card {
  --pz-bg: #1a1a1a;
  --pz-surface: #222222;
  --pz-surface-muted: #2a2a2a;
  --pz-border: #333333;
  --pz-border-strong: #444444;
  --pz-text: #f3f4f6;
  --pz-text-muted: #94a3b8;
  --pz-text-subtle: #6b7280;
  --pz-accent: #10b981;
  --pz-accent-deep: #34d399;
  --pz-accent-soft: rgba(16, 185, 129, 0.14);
  --pz-danger: #f87171;
  /* keep the brand vars — they already use var(--primary) which works in both modes */
  background: var(--pz-surface);
  color: var(--pz-text);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.6);
}
/* The scrim is darker in dark mode for the same visual depth. */
[data-theme="dark"] .promos-modal__scrim { background: rgba(0, 0, 0, 0.65); }

/* Shipping-zone country picker: sticky region headers + container border use the
   modal's own --pz-* tokens so they adapt to dark mode (the old bg-body-secondary
   / Bootstrap border did not). */
.zone-country-picker { border-color: var(--pz-border) !important; }
.zone-region-head {
  background: var(--pz-surface-muted);
  border-bottom: 1px solid var(--pz-border);
}
.zone-region-head .text-muted { color: var(--pz-text-muted) !important; }
.zone-region-head [data-zone-region-all] { color: var(--pz-brand); }

/* Add-shipping-option picker: field-like trigger whose menu opens UPWARD (the
   button sits low in the modal, so a downward menu fell below the fold). Items
   pad their icons inside the hover background. */
/* Trigger sizes to its content (was a fixed 320px); the menu matches its width. */
.zone-add-wrap { display: inline-block; }
.zone-type-menu {
  position: absolute; left: 0; right: 0; bottom: calc(100% + 4px);
  background: var(--pz-surface); z-index: 30; padding: 4px;
  max-height: 260px; overflow: auto; white-space: nowrap;
}
.zone-type-item {
  background: transparent; border: 0; border-radius: 6px;
  padding: 6px 10px; color: var(--pz-text); font-size: .875rem;
}
.zone-type-item:hover:not(.is-disabled) { background: var(--pz-surface-muted); cursor: pointer; }
.zone-type-item.is-disabled { color: var(--pz-text-muted); cursor: not-allowed; }
/* "+ Add shipping method description" link — no colour change on hover. */
[data-opt-add-description], [data-opt-add-description]:hover, [data-opt-add-description]:focus {
  color: var(--pz-brand); text-decoration: none;
}
/* Hover affordance on collapsed option rows (modal) + zone rows (settings tab):
   the whole row tints to the field background to signal it's clickable. */
.page-block-card:not(.is-expanded) .page-block-card__head:hover { background: var(--pz-bg); }
.page-block-card[data-zone-card] { cursor: pointer; }
.page-block-card[data-zone-card]:hover { background: var(--hover); }

/* Placeholder product cards in the design customizer's preview frame, standing
   in for a shop with no products yet (services/ghostProducts.js). Never
   rendered for a real visitor.

   Slightly faded and non-interactive: they exist to show the owner what the
   layout does with products in it, and anything that invites a click is a
   promise the preview cannot keep. Pointer-events off the whole card rather
   than per-link, so the title, image and button are all inert together. */
.product-card--ghost {
  opacity: .72;
  pointer-events: none;
  user-select: none;
}
.product-card--ghost a { cursor: default; }

/* "Show all N countries" / "Show fewer" toggle under the country picker.
   Bootstrap's link blue made it read as navigation sitting in a row of
   neutral chips, when it only expands the list next to it. Muted to match
   the chips, underlined so it still reads as clickable.
   --pz-text-muted is defined in the promos-modal scope this lives in, so
   dark mode follows without a second rule. */
[data-zone-chips-toggle] {
  color: var(--pz-text-muted, #6b7280);
  text-decoration: underline;
  text-underline-offset: 2px;
}
[data-zone-chips-toggle]:hover,
[data-zone-chips-toggle]:focus { color: var(--pz-text, inherit); }
@keyframes promos-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes promos-pop { from { transform: translateY(8px) scale(0.98); opacity: 0; } to { transform: translateY(0) scale(1); opacity: 1; } }

.promos-modal__header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 18px;
  border-bottom: 1px solid var(--pz-border);
}
.promos-modal__header-left {
  display: flex; align-items: center; gap: 12px; min-width: 0;
}
.promos-modal__swatch {
  width: 32px; height: 32px;
  border-radius: 7px;
  display: grid; place-items: center;
  color: #fff;
  font-size: 12px;
  font-weight: 600;
  flex-shrink: 0;
}
.promos-modal__title { font-size: 14px; font-weight: 600; line-height: 1.2; }
.promos-modal__subtitle {
  font-size: 12px;
  color: var(--pz-text-muted);
  margin-top: 2px;
}
.promos-modal__close {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 8px;
  cursor: pointer;
  color: var(--pz-text-muted);
  border-radius: 6px;
  display: grid;
  place-items: center;
}
.promos-modal__close:hover {
  background: var(--pz-surface-muted);
  color: var(--pz-text);
}

/* Inside the custom-units modal, mirror the product-edit modal's
   section colors. The cards' bg is dropped so only the --border-coloured
   outline separates each section (matches `.product-section`'s look),
   and form controls inside use the deeper `#161616` field surface that
   `.admin-shell[data-admin-shell] .form-control` applies in the product
   edit modal — without that, the units modal sits outside `.admin-shell`
   and the inputs would inherit the lighter `var(--card)` background,
   blending into the modal body. */
[data-theme="dark"] #modal-custom-units .card,
[data-theme="dark"] #modal-custom-units .card-body {
  background: #2a2a2a !important;
  border: 1px solid var(--border) !important;
  box-shadow: none !important;
}
[data-theme="dark"] #modal-custom-units .card-body {
  border: 0 !important; /* card already has the outer border */
}
[data-theme="dark"] #modal-custom-units .list-group-item {
  background-color: #2a2a2a !important;
}
/* Bootstrap's `.list-group-item` keeps its default near-white bg + light
   border in dark mode (the project uses [data-theme="dark"], not
   [data-bs-theme="dark"], so the Bootstrap dark tokens never kick in).
   Force them to match the rest of the modal. */
[data-theme="dark"] #modal-custom-units .list-group-item {
  background-color: transparent !important;
  border-color: var(--border) !important;
  color: var(--fg) !important;
}
[data-theme="dark"] #modal-custom-units .form-control,
[data-theme="dark"] #modal-custom-units .form-select {
  background-color: #161616 !important;
  border-color: var(--border) !important;
}
[data-theme="dark"] #modal-custom-units .form-control:focus,
[data-theme="dark"] #modal-custom-units .form-select:focus {
  background-color: #161616 !important;
  border-color: var(--primary, #10b981) !important;
}
/* `.form-control-sm` has Bootstrap's `min-height: calc(1.5em + 0.5rem
   + 2px)` (~32px) but `.btn-sm` is padding-only (~29px). When both sit
   in a `d-flex align-items-end` row, the button ends up ~3px shorter
   and its top doesn't line up with the input's. Pin the button's
   min-height to match the input's. */
#modal-custom-units .btn-sm {
  min-height: calc(1.5em + 0.5rem + 2px);
}

.promos-modal__body {
  padding: 18px;
  overflow: auto;
  display: flex;
  flex-direction: column;
  gap: 22px;
  flex: 1;
  min-height: 0; /* allow the body to shrink so overflow:auto kicks in */
}
.promos-modal__footer {
  display: flex; align-items: center;
  /* Cancel + Save (and any other action) cluster on the right inside
     `.promos-modal__footer-right`. A destructive Delete button — when
     present as a direct child of the footer — sits flush against the
     LEFT edge so it can't be hit by accident. `space-between` does the
     left/right split automatically. */
  justify-content: space-between;
  gap: 8px;
  padding: 18px;
  border-top: 1px solid var(--pz-border);
  background: var(--pz-surface-muted);
  flex-shrink: 0;
}
/* The right group always pushes itself to the right edge via auto-margin.
   With Delete present (left), space-between already does this; with Delete
   hidden (display:none in create mode), the auto-margin still pins the
   group right instead of falling back to start. */
.promos-modal__footer-right { margin-left: auto; }
/* The footer's Save button uses the brand colour so it aligns with the
   "+ New banner" action in the page header. */
.promos-modal__footer .promos-btn--primary {
  background: var(--pz-brand);
  border-color: var(--pz-brand);
  color: var(--pz-brand-fg);
}
.promos-modal__footer-right { display: flex; gap: 8px; }

/* ── Form sections inside the modal ── */
.promos-section { margin: 0; }
.promos-section__label {
  display: flex; align-items: baseline; gap: 10px;
  font-size: 11px;
  font-weight: 600;
  color: var(--pz-text-muted);
  letter-spacing: 0.6px;
  text-transform: uppercase;
  margin-bottom: 10px;
}
.promos-section__hint {
  text-transform: none;
  font-weight: 400;
  letter-spacing: 0;
  color: var(--pz-text-subtle);
}
.promos-row-fields {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
}
.promos-row-fields > * { flex: 1; min-width: 0; }
.promos-row-fields--narrow > .promos-field--w200 { flex: 0 0 200px; }
.promos-row-fields--narrow > .promos-field--w180 { flex: 0 0 180px; }
.promos-row-fields--narrow > .promos-field--w160 { flex: 0 0 160px; }
.promos-row-fields--narrow > .promos-field--w120 { flex: 0 0 120px; }
.promos-row-fields--narrow > .promos-field--w90  { flex: 0 0 90px; }

.promos-field__unit {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--pz-text-subtle);
  background: var(--pz-bg-subtle, #f5f5f4);
  border-radius: 4px;
}
.promos-field__unit:empty { display: none; }

/* Unit shown INSIDE an input on the right (a "%", "Ft", "$", "days"
   suffix), far more visible than a faint chip above the field. The
   input gets right padding so its value never runs under the suffix. */
.promos-input-affix { position: relative; }
.promos-input-affix > .promos-input { padding-right: 52px; }
.promos-input-affix__suffix {
  position: absolute;
  top: 1px; right: 1px; bottom: 1px;
  display: flex; align-items: center;
  padding: 0 11px;
  font-size: 12px; font-weight: 600;
  color: var(--pz-text-subtle);
  background: var(--pz-surface-muted, #f5f5f4);
  border-left: 1px solid var(--pz-border);
  border-radius: 0 6px 6px 0;
  pointer-events: none;
  white-space: nowrap;
}

.promos-field {
  display: flex; flex-direction: column; gap: 6px;
  flex: 1;
  min-width: 0;
}
.promos-field__head {
  display: flex; align-items: flex-end; justify-content: space-between;
  /* All field heads share a min-height so heads with a lang switcher
     don't push their input below heads that only have a label. The 22px
     matches the switcher's natural height (lang chips + arrow buttons). */
  min-height: 22px;
}
.promos-field__label { margin: 0; padding-bottom: 1px; }
.promos-field__label {
  font-size: 12px;
  font-weight: 500;
  color: var(--pz-text);
  margin: 0;
}
.promos-field__hint {
  font-size: 11px;
  color: var(--pz-text-subtle);
}

.promos-input,
.promos-select,
.promos-textarea {
  width: 100%;
  padding: 8px 10px;
  font-size: 13px;
  color: var(--pz-text);
  border: 1px solid var(--pz-border);
  border-radius: 6px;
  background: var(--pz-surface);
  font-family: inherit;
  box-sizing: border-box;
  outline: none;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.promos-input:focus,
.promos-select:focus,
.promos-textarea:focus,
.promos-input:focus-visible,
.promos-select:focus-visible,
.promos-textarea:focus-visible {
  /* Match the shop-wide focus state: brand border, no shadow ring. */
  border-color: var(--primary, var(--pz-brand)) !important;
  box-shadow: none !important;
  outline: 0;
}
.promos-textarea { resize: vertical; min-height: 80px; }
.promos-select-wrap { position: relative; }
.promos-select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 28px;
  cursor: pointer;
}
.promos-select-wrap::after {
  content: '';
  position: absolute;
  right: 12px;
  top: 50%;
  width: 8px; height: 8px;
  border-right: 1.5px solid var(--pz-text-muted);
  border-bottom: 1.5px solid var(--pz-text-muted);
  transform: translateY(-75%) rotate(45deg);
  pointer-events: none;
}

/* Color picker inline */
.promos-color {
  display: flex; align-items: center; gap: 8px;
}
.promos-color__swatch {
  width: 36px; height: 32px;
  padding: 2px;
  border: 1px solid var(--pz-border);
  border-radius: 6px;
  background: var(--pz-surface);
  cursor: pointer;
  flex-shrink: 0;
}
.promos-color__swatch::-webkit-color-swatch-wrapper { padding: 0; }
.promos-color__swatch::-webkit-color-swatch { border: 0; border-radius: 4px; }
.promos-color__hex {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 12px;
  text-transform: uppercase;
}

/* Checkbox cards (behaviour toggles). The checkbox sits on the same line
   as the main label ("Active"); the helper text flows directly below the
   label in the same text column, indented past the box.
   Structure:
     <label>
       <input>
       <span class="promos-check__body">
         <span class="promos-check__label">Active</span>
         <span class="promos-check__hint">Inactive banners are saved…</span>
       </span>
     </label>
*/
.promos-check {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  cursor: pointer;
  padding: 10px 12px;
  border: 1px solid var(--pz-border);
  border-radius: 8px;
  flex: 1;
  background: var(--pz-surface);
  transition: border-color 0.15s ease, background 0.15s ease;
}
.promos-check:hover { border-color: var(--pz-border-strong); }
.promos-check input {
  appearance: none;
  width: 16px; height: 16px;
  margin: 1px 0 0; /* nudge to baseline of the label */
  border-radius: 4px;
  border: 1.5px solid var(--pz-border-strong);
  background: var(--pz-surface);
  cursor: pointer;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  position: relative;
}
.promos-check input:checked {
  background: var(--pz-accent);
  border-color: var(--pz-accent);
}
.promos-check input:checked::after {
  content: '';
  width: 9px; height: 5px;
  border-left: 2px solid #fff;
  border-bottom: 2px solid #fff;
  transform: rotate(-45deg) translate(1px, -1px);
}
.promos-check:has(input:checked) {
  border-color: var(--pz-accent);
  background: var(--pz-accent-soft);
}
.promos-check__body {
  flex: 1;
  min-width: 0;
  display: block;
}
.promos-check__label {
  display: block;
  font-size: 13px;
  font-weight: 500;
  color: var(--pz-text);
  line-height: 1.35;
}
.promos-check__hint {
  display: block;
  font-size: 11.5px;
  color: var(--pz-text-muted);
  font-weight: 400;
  line-height: 1.45;
  margin-top: 2px;
}

/* Live preview tile */
.promos-preview {
  border: 1px solid var(--pz-border);
  border-radius: 8px;
  overflow: hidden;
  background: var(--pz-surface);
  margin-top: 12px;
}
.promos-preview__bar {
  padding: 12px 14px;
  text-align: center;
  font-size: 13.5px;
  font-weight: 500;
  line-height: 1.4;
  min-height: 40px;
  display: flex; align-items: center; justify-content: center;
}
.promos-preview__caption {
  background: var(--pz-surface-muted);
  padding: 5px 10px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.6px;
  color: var(--pz-text-subtle);
  text-transform: uppercase;
}

/* The language switcher (.az-lang-strip-wrap) inherits its standard
   page-wide styling here — no overrides — so it looks identical to the
   one used on Pages, Collections, Products, etc. The only modal-specific
   tweak: cap the strip width so ~3–4 language chips show with arrows for
   the rest, regardless of how many locales the shop has enabled. */
.promos-modal .az-lang-strip { max-width: 105px; }


/* ─────────────────────────────────────────────────────────────────
   Admin scrollbars (V2 design system)
   Same minimal, rounded, semi-transparent style as the sidebar nav.
   Dark thumb on light surfaces, light thumb on dark surfaces.
   Applied to: admin-main column, admin-main scroll area, every
   modal body, and any scrollable container inside an admin page.
   ───────────────────────────────────────────────────────────────── */

/* Light-mode admin: dark thumb on transparent track */
.admin-shell[data-admin-shell] .admin-main,
.admin-shell[data-admin-shell] .admin-main-col,
.admin-shell[data-admin-shell] .admin-main *,
.promos-modal__body,
.promos-modal__card *,
.shop-search-panel-results,
.lc-col-list {
  scrollbar-width: thin;
  scrollbar-color: rgba(0, 0, 0, 0.18) transparent;
}
.admin-shell[data-admin-shell] .admin-main::-webkit-scrollbar,
.admin-shell[data-admin-shell] .admin-main *::-webkit-scrollbar,
.promos-modal__body::-webkit-scrollbar,
.promos-modal__card *::-webkit-scrollbar {
  width: 6px;
  height: 6px;
}
.admin-shell[data-admin-shell] .admin-main::-webkit-scrollbar-track,
.admin-shell[data-admin-shell] .admin-main *::-webkit-scrollbar-track,
.promos-modal__body::-webkit-scrollbar-track,
.promos-modal__card *::-webkit-scrollbar-track {
  background: transparent;
}
.admin-shell[data-admin-shell] .admin-main::-webkit-scrollbar-thumb,
.admin-shell[data-admin-shell] .admin-main *::-webkit-scrollbar-thumb,
.promos-modal__body::-webkit-scrollbar-thumb,
.promos-modal__card *::-webkit-scrollbar-thumb {
  background: rgba(0, 0, 0, 0.18);
  border-radius: 999px;
  transition: background 0.2s ease;
}
.admin-shell[data-admin-shell] .admin-main::-webkit-scrollbar-thumb:hover,
.admin-shell[data-admin-shell] .admin-main *::-webkit-scrollbar-thumb:hover,
.promos-modal__body::-webkit-scrollbar-thumb:hover,
.promos-modal__card *::-webkit-scrollbar-thumb:hover {
  background: rgba(0, 0, 0, 0.32);
}
.admin-shell[data-admin-shell] .admin-main::-webkit-scrollbar-corner,
.promos-modal__body::-webkit-scrollbar-corner,
.promos-modal__card *::-webkit-scrollbar-corner {
  background: transparent;
}
/* The horizontally-scrolling tab row opts OUT of the admin's custom 6px
   scrollbar above (which `.admin-main *::-webkit-scrollbar` otherwise forces
   onto it — that was the bar still showing on the tab row on iOS). This must
   out-specify that rule, so it carries the full .admin-main ancestor chain. */
.admin-shell[data-admin-shell] .admin-main .promos-tabs::-webkit-scrollbar {
  display: none !important;
  width: 0 !important;
  height: 0 !important;
  -webkit-appearance: none;
  background: transparent;
}

/* Dark-mode admin: light thumb on transparent track */
[data-theme="dark"] .admin-shell[data-admin-shell] .admin-main,
[data-theme="dark"] .admin-shell[data-admin-shell] .admin-main-col,
[data-theme="dark"] .admin-shell[data-admin-shell] .admin-main *,
[data-theme="dark"] .promos-modal__body,
[data-theme="dark"] .promos-modal__card *,
[data-theme="dark"] .shop-search-panel-results,
[data-theme="dark"] .lc-col-list {
  scrollbar-color: rgba(255, 255, 255, 0.22) transparent;
}
[data-theme="dark"] .admin-shell[data-admin-shell] .admin-main::-webkit-scrollbar-thumb,
[data-theme="dark"] .admin-shell[data-admin-shell] .admin-main *::-webkit-scrollbar-thumb,
[data-theme="dark"] .promos-modal__body::-webkit-scrollbar-thumb,
[data-theme="dark"] .promos-modal__card *::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.22);
}
[data-theme="dark"] .admin-shell[data-admin-shell] .admin-main::-webkit-scrollbar-thumb:hover,
[data-theme="dark"] .admin-shell[data-admin-shell] .admin-main *::-webkit-scrollbar-thumb:hover,
[data-theme="dark"] .promos-modal__body::-webkit-scrollbar-thumb:hover,
[data-theme="dark"] .promos-modal__card *::-webkit-scrollbar-thumb:hover {
  background: rgba(255, 255, 255, 0.42);
}

/* Inline "required" tag after a field label, in place of an asterisk. The
   product form renders ~15 fields and exactly two are mandatory, so those two
   have to read as a different KIND of field rather than carry a mark you need
   to already know the convention for.

   Shares the danger red with validation errors on purpose: one colour for
   "this field wants something from you". The error state stays distinct
   because it also moves the field border and prints a message, neither of
   which this resting tag does.

   No text-transform: the string is translated, and lowercasing it would turn
   German "Pflichtfeld" into "pflichtfeld". Each locale carries its own casing. */
.form-required {
  font-size: .6875rem;
  font-weight: 400;
  /* Sits away from the label rather than reading as part of it. The template
     also has a literal space, so this is the extra gap on top of that. */
  margin-left: .35rem;
  color: #ef4444;
}
[data-theme="dark"] .form-required { color: #f87171; }

/* ─── Admin → Product edit (V2 redesign — collapsible sections) ──────────
   Reusable card pattern for the product create/edit form. Chevron sits to
   the LEFT of the section title (no leading icons), and each section
   persists its open/closed state in localStorage under
   `shopiza_psect_<key>`. The same pattern wraps Variants and Attributes;
   their existing inner-section JS still runs because the IDs are kept on
   the body element. */
.product-section {
  border: 1px solid var(--border, #dee2e6);
  border-radius: 8px;
  background: var(--card, #fff);
  margin-bottom: 14px;
  overflow: hidden;
}
.product-section__head {
  display: flex;
  align-items: stretch;
  width: 100%;
  background: #f5f5f4;
}
[data-theme="dark"] .product-section__head { background: #2a2a2a; }
.product-section__head-toggle {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 1 1 auto;
  min-width: 0;
  padding: 12px 16px;
  background: transparent;
  border: 0;
  cursor: pointer;
  text-align: left;
  font-family: inherit;
  font-size: 15px;
  color: inherit;
  user-select: none;
}
.product-section__head-toggle:focus { outline: none; }
.product-section__head-toggle:focus-visible { outline: 2px solid var(--primary, #10b981); outline-offset: -2px; }
.product-section__chev {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  font-size: 12px;
  color: var(--fg-muted, #6c757d);
  transition: transform .18s ease;
  flex: 0 0 auto;
}
.product-section.is-collapsed .product-section__chev {
  transform: rotate(-90deg);
}
.product-section__title {
  font-weight: 600;
  font-size: 15px;
  color: var(--fg, inherit);
  flex: 1 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.product-section__head-right {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px 8px 8px;
}
.product-section__body {
  padding: 14px 16px 16px;
  border-top: 1px solid var(--border, #e7e5e4);
}
.product-section.is-collapsed .product-section__body {
  display: none;
}

/* Product-image "Add image" button: text on desktop (icon hidden). On phone
   portrait the long upload hint + text button crowded out the section title
   and the button itself — so hide the hint, drop the button text and show
   just the upload icon, leaving room for "Product image" + a compact button. */
.img-add-btn__icon { display: none; }
@media (max-width: 576px) {
  .img-multi-upload .img-upload-hint { display: none; }
  .img-multi-upload .img-add-btn__text { display: none; }
  .img-multi-upload .img-add-btn__icon { display: inline-block; }
}

/* The product-name field's label row carries the language-switcher strip
   (~22px tall) with a 4px gap to the input, so the plain category label next
   to it sat higher and pushed its dropdown out of line. Give the category
   label the same header height + gap so the two controls align. */
.product-inline-label {
  display: flex;
  align-items: flex-end;
  min-height: 22px;
  margin-bottom: 4px;
}
/* Unit & quantities row: the Unit field's label sits in a flex header (label +
   "Edit units" link) whose rendered height is a pixel or two off the plain
   form-labels beside it. Rather than chase that, make the three columns equal
   height and pin every control to the bottom, so the dropdown lines up with the
   Min-qty / Step inputs exactly regardless of label-area height. */
.product-unit-head { margin-bottom: 0.375rem; }
.product-section__body .row:has(.product-unit-head) > [class*="col-"] {
  display: flex;
  flex-direction: column;
}
.product-section__body .row:has(.product-unit-head) > [class*="col-"] > .form-select,
.product-section__body .row:has(.product-unit-head) > [class*="col-"] > .form-control {
  margin-top: auto;
}

/* Multi-category picker (product form): a dropdown that shows the primary
   category when closed and a searchable checkbox tree when open. Indentation
   of nested categories is inline via padding-left on each .cat-check. */
.cat-select { position: relative; }
.cat-select__trigger {
  width: 100%;
  text-align: left;
  cursor: pointer;
  display: flex;
  align-items: center;
}
.cat-select__value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.cat-select__value--empty { color: var(--az-text-muted, #78716c); }
.cat-select__panel {
  /* Fixed + JS-positioned so it isn't clipped by the modal's overflow:hidden
     ancestors (.product-section / .promos-modal__body / __card). No ancestor has
     a transform, so fixed resolves against the viewport. top/left/width set by
     category-picker.js on open. */
  position: fixed;
  z-index: 2100;
  background: var(--card, #fff);
  border: 1px solid var(--border, #e7e5e4);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
  overflow: hidden;
}
.cat-select__search { padding: 8px; border-bottom: 1px solid var(--border, #e7e5e4); }
.cat-select__empty { padding: 10px 12px; font-size: 13px; color: var(--az-text-muted, #78716c); }
.cat-check-tree {
  max-height: 220px;
  overflow-y: auto;
  padding: 6px 8px;
}
.cat-check {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 3px 2px;
  margin: 0;
  cursor: pointer;
  font-size: 13px;
  color: var(--fg, #1c1917);
  border-radius: 5px;
}
.cat-check:hover { background: var(--hover, #f5f5f4); }
.cat-check__box { flex-shrink: 0; margin: 0; cursor: pointer; }
.cat-check__name { line-height: 1.3; }
/* Inline "Add category" button pinned below the (independently scrolling) tree. */
.cat-select__add {
  display: flex;
  align-items: center;
  gap: 7px;
  width: 100%;
  padding: 9px 12px;
  border: none;
  border-bottom: 1px solid var(--border, #e7e5e4);
  background: none;
  cursor: pointer;
  font-size: 13px;
  font-weight: 500;
  color: var(--primary, #10b981);
  text-align: left;
}
.cat-select__add:hover { background: var(--hover, #f5f5f4); }
[data-theme="dark"] .cat-select__panel { background: var(--card); border-color: var(--border); }
[data-theme="dark"] .cat-select__add { border-bottom-color: var(--border); }
[data-theme="dark"] .cat-select__search { border-bottom-color: var(--border); }
[data-theme="dark"] .cat-check:hover { background: var(--hover); }

/* SEO settings link in basic info section head */
.seo-settings-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--primary, #10b981);
  text-decoration: none;
  font-weight: 500;
  padding: 4px 8px;
  border-radius: 6px;
  transition: background 0.15s ease;
}
.seo-settings-link:hover { background: rgba(16, 185, 129, 0.08); color: var(--primary, #10b981); }
[data-theme="dark"] .seo-settings-link:hover { background: rgba(16, 185, 129, 0.14); }
.seo-drift-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #f59e0b;
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  cursor: help;
}

/* SEO modal — Google-style snippet preview */
.seo-snippet {
  background: var(--card, #fff);
  border: 1px solid var(--border, #e7e5e4);
  border-radius: 8px;
  padding: 14px 16px;
  margin-bottom: 18px;
  font-family: arial, sans-serif;
}
[data-theme="dark"] .seo-snippet {
  background: #1a1a1a;
  border-color: #2a2a2a;
}
.seo-snippet__url {
  font-size: 12px;
  color: #5f6368;
  line-height: 1.3;
  margin-bottom: 2px;
  word-break: break-all;
}
[data-theme="dark"] .seo-snippet__url { color: #9aa0a6; }
.seo-snippet__title {
  font-size: 18px;
  font-weight: 400;
  color: #1a0dab;
  line-height: 1.3;
  margin-bottom: 2px;
  cursor: pointer;
  word-break: break-word;
}
.seo-snippet__title:hover { text-decoration: underline; }
[data-theme="dark"] .seo-snippet__title { color: #8ab4f8; }
.seo-snippet__desc {
  font-size: 13px;
  color: #4d5156;
  line-height: 1.45;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  word-break: break-word;
}
[data-theme="dark"] .seo-snippet__desc { color: #bdc1c6; }

/* SEO field counters — colour graduates green → amber → red as the input
   crosses the recommended max. */
.seo-counter { font-variant-numeric: tabular-nums; }
.seo-counter.is-near { color: #f59e0b !important; }
.seo-counter.is-over { color: #ef4444 !important; }

/* Drift highlight on individual inputs */
.is-seo-drifted {
  border-color: #f59e0b !important;
  background: rgba(245, 158, 11, 0.05) !important;
}

[data-theme="dark"] .product-section {
  border-color: var(--border, #333);
  background: var(--card, #222);
}
[data-theme="dark"] .product-section__body {
  border-top-color: var(--border, #333);
}


/* Cap the lang switcher's strip width inside product sections so the
   prev/next arrows reliably appear when there are more locales than fit
   the visible strip. Without this cap, a strip in a flex row with no
   competing sibling (e.g. the Detailed description section) grows to its
   natural content width and the JS correctly decides no overflow scroll
   is needed — but the user expects the arrows to be visible whenever
   there are >~5 active locales. */
.product-section .az-lang-strip { max-width: 140px; }
.promos-modal .product-section .az-lang-strip { max-width: 105px; }

/* Rich-text + i18n: lang switcher moves into the rte-toolbar (cloned by
   richtext.js). The original strip-wrap is hidden so we don't show two
   copies. The clone sits flush right on the toolbar via margin-left:auto. */
[data-i18n-field-container].i18n-rte-mounted .az-lang-strip-wrap[data-orig-strip="1"] { display: none; }
/* When the strip's parent flex row had nothing else in it (e.g. the
   product Detailed description section, where the strip was the only
   child), collapse the empty row so we don't leave a 4px ghost gap above
   the editor. Rows that also held a label (e.g. category description)
   keep the label visible. */
[data-i18n-field-container].i18n-rte-mounted .d-flex:has(> .az-lang-strip-wrap[data-orig-strip="1"]:only-child) { display: none; }
.rte-toolbar__lang { margin-left: auto; }
/* Apply the same width cap inside the toolbar so the strip can overflow
   and the prev/next arrows surface as needed. */
.rte-toolbar .az-lang-strip { max-width: 140px; }
.promos-modal .rte-toolbar .az-lang-strip { max-width: 105px; }

/* ─────────────────────────────────────────────────────────────────
   Security Console — built on the design system tokens defined in
   `.promos-shell` (--pz-text, --pz-border, --pz-brand…). Adds a
   small set of `.sec-*` building blocks for the patterns the
   Security pages need (KPI tile, section heading, data table,
   styled form field, channel/severity pills, toggle, key/value
   readonly card). All values are scoped under `.promos-shell` so
   the rest of admin is unaffected.
   ───────────────────────────────────────────────────────────── */

/* Status pills the Security pages add on top of .promos-status. */
.promos-shell .promos-status--critical { background: #fee2e2; color: #991b1b; }
[data-theme="dark"] .promos-shell .promos-status--critical { background: rgba(239, 68, 68, 0.18); color: #fca5a5; }

/* Sub-nav (security tabs) — keep tab labels readable on mobile. */
.promos-shell .sec-tabs { flex-wrap: wrap; }

/* List rows: row swatches show on/off state instead of a colour. */
.promos-shell .sec-row__swatch {
  border-radius: 10px;
  font-size: 13px;
  font-weight: 500;
}
.promos-shell .sec-row__swatch.is-on  { background: var(--pz-accent-soft); color: var(--pz-accent-deep); }
.promos-shell .sec-row__swatch.is-off { background: var(--pz-surface-muted); color: var(--pz-text-subtle); }
.promos-shell .sec-muted { color: var(--pz-text-muted); }

/* Section heading inside a Security page (e.g. "Recent deliveries"). */
.promos-shell .sec-section { margin-top: 28px; }
.promos-shell .sec-section__title {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0;
  color: var(--pz-text);
  margin: 0 0 4px;
}
.promos-shell .sec-section__subtitle {
  font-size: 12.5px;
  color: var(--pz-text-muted);
  margin: 0 0 12px;
}

/* Data table — borrowed look from the design-system rows. */
.promos-shell .sec-table-card {
  background: var(--pz-surface);
  border: 1px solid var(--pz-border);
  border-radius: 12px;
  overflow: hidden;
}
.promos-shell .sec-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  color: var(--pz-text);
}
.promos-shell .sec-table thead th {
  text-align: left;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--pz-text-muted);
  background: var(--pz-surface-muted);
  border-bottom: 1px solid var(--pz-border);
  padding: 8px 14px;
}
.promos-shell .sec-table tbody td {
  padding: 10px 14px;
  border-bottom: 1px solid var(--pz-border);
  vertical-align: middle;
}
.promos-shell .sec-table tbody tr:last-child td { border-bottom: 0; }
.promos-shell .sec-table .text-end { text-align: right; }
.promos-shell .sec-table__when {
  color: var(--pz-text-muted);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  white-space: nowrap;
}
.promos-shell .sec-table__details {
  padding: 0 14px 10px !important;
  border-bottom: 1px solid var(--pz-border);
}
.promos-shell .sec-table__details details summary {
  font-size: 12px;
  color: var(--pz-text-muted);
  cursor: pointer;
  list-style: disclosure-closed;
}
.promos-shell .sec-table__details details[open] summary { margin-bottom: 6px; }
.promos-shell .sec-table__details pre {
  background: var(--pz-surface-muted);
  border: 1px solid var(--pz-border);
  border-radius: 8px;
  padding: 10px 12px;
  font-size: 12px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  white-space: pre-wrap;
  word-break: break-word;
  margin: 0;
  color: var(--pz-text);
}

/* Small chip / tag inside meta lines and table cells. */
.promos-shell .sec-tag {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  margin-right: 4px;
  border-radius: 999px;
  background: var(--pz-surface-muted);
  color: var(--pz-text-muted);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.02em;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.promos-shell .sec-tag--neutral {
  background: var(--pz-surface);
  border: 1px solid var(--pz-border);
}

/* Delivery / state pill (sent/partial/failed, ok/warn/bad). */
.promos-shell .sec-pill {
  display: inline-flex;
  align-items: center;
  padding: 3px 9px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.promos-shell .sec-pill--is-ok  { background: var(--pz-accent-soft); color: var(--pz-accent-deep); }
.promos-shell .sec-pill--is-warn { background: #fef3c7; color: #92400e; }
.promos-shell .sec-pill--is-bad  { background: #fee2e2; color: #991b1b; }
[data-theme="dark"] .promos-shell .sec-pill--is-warn { background: rgba(245, 158, 11, 0.15); color: #fbbf24; }
[data-theme="dark"] .promos-shell .sec-pill--is-bad  { background: rgba(239, 68, 68, 0.18); color: #fca5a5; }

/* A table cell whose primary text opens the row's edit modal. A real <button>
   so it is keyboard-reachable, styled back down to look like the surrounding
   text. Used by the Backlinks tab in the Growth Command Center. */
.promos-shell .promos-link-cell {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--pz-text);
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
.promos-shell .promos-link-cell:hover { color: var(--pz-accent-deep); text-decoration: underline; }
.promos-shell .promos-link-cell:focus-visible { outline: 2px solid var(--pz-accent-deep); outline-offset: 2px; border-radius: 3px; }

/* Form field layout used inside modals + Respond page. */
.promos-shell .sec-field { margin-bottom: 16px; }
.promos-shell .sec-field__label {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--pz-text);
  margin-bottom: 4px;
}
.promos-shell .sec-field__hint {
  font-size: 11.5px;
  color: var(--pz-text-muted);
  margin: 4px 0 0;
}
.promos-shell .sec-field__input,
.promos-modal .sec-field__input {
  width: 100%;
  font-size: 13px;
  padding: 8px 10px;
  background: var(--pz-surface);
  color: var(--pz-text);
  border: 1px solid var(--pz-border);
  border-radius: 8px;
  font-family: inherit;
}
.promos-shell .sec-field__input:focus,
.promos-modal .sec-field__input:focus {
  outline: none;
  border-color: var(--pz-brand);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--pz-brand) 18%, transparent);
}

/* Grids inside the modal body for paired fields. */
.promos-shell .sec-grid { display: grid; gap: 12px; }
.promos-shell .sec-grid--2 { grid-template-columns: 1fr 1fr; }
.promos-shell .sec-grid--3 { grid-template-columns: 1fr 1fr 1fr; }
@media (max-width: 640px) {
  .promos-shell .sec-grid--2, .promos-shell .sec-grid--3 { grid-template-columns: 1fr; }
}

/* Checkbox row (channels picker etc). */
.promos-shell .sec-checkrow { display: flex; flex-wrap: wrap; gap: 14px; align-items: center; }
.promos-shell .sec-check { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; cursor: pointer; }
.promos-shell .sec-check input[type="checkbox"] { accent-color: var(--pz-brand); }

/* Inline toggle switch — replaces Bootstrap form-switch. */
.promos-shell .sec-toggle { display: inline-flex; align-items: center; gap: 10px; cursor: pointer; user-select: none; }
.promos-shell .sec-toggle input { position: absolute; opacity: 0; pointer-events: none; }
.promos-shell .sec-toggle__track {
  width: 36px; height: 20px; background: var(--pz-surface-muted); border-radius: 999px;
  border: 1px solid var(--pz-border); position: relative; transition: background 0.15s ease;
}
.promos-shell .sec-toggle__knob {
  position: absolute; top: 1px; left: 1px;
  width: 16px; height: 16px; border-radius: 999px;
  background: var(--pz-surface); border: 1px solid var(--pz-border-strong);
  transition: transform 0.15s ease;
}
.promos-shell .sec-toggle input:checked + .sec-toggle__track { background: var(--pz-brand); border-color: var(--pz-brand); }
.promos-shell .sec-toggle input:checked + .sec-toggle__track .sec-toggle__knob { transform: translateX(16px); border-color: var(--pz-brand); }
.promos-shell .sec-toggle__label { font-size: 13px; color: var(--pz-text); }

/* Read-only fact card (e.g. "query_kind / match"). */
.promos-shell .sec-readonly {
  background: var(--pz-surface-muted);
  border: 1px solid var(--pz-border);
  border-radius: 10px;
  padding: 10px 12px;
}
.promos-shell .sec-readonly__label {
  font-size: 11px;
  font-weight: 600;
  color: var(--pz-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: 6px;
}
.promos-shell .sec-readonly__body { display: flex; flex-wrap: wrap; gap: 6px; }

/* Modal footer — left cluster for secondary destructive actions. */
.promos-modal .sec-modal__footer-left { display: inline-flex; gap: 8px; }

/* KPI tile — Overview / Auth pages. */
.promos-shell .sec-kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 14px;
  margin-bottom: 22px;
}
.promos-shell .sec-kpi {
  background: var(--pz-surface);
  border: 1px solid var(--pz-border);
  border-radius: 12px;
  padding: 14px 16px;
}
.promos-shell .sec-kpi__label {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--pz-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin: 0 0 6px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.promos-shell .sec-kpi__value {
  font-size: 28px;
  font-weight: 600;
  line-height: 1;
  color: var(--pz-text);
  font-variant-numeric: tabular-nums;
}
.promos-shell .sec-kpi__value.is-ok { color: var(--pz-accent-deep); }
.promos-shell .sec-kpi__value.is-bad { color: var(--pz-danger); }
.promos-shell .sec-kpi__value.is-muted { color: var(--pz-text-subtle); }
.promos-shell .sec-kpi__hint {
  margin-top: 6px;
  font-size: 12px;
  color: var(--pz-text-muted);
}
.promos-shell .sec-kpi__hint .sec-pill { margin-right: 0; }

/* Tag-like pills in KPI hints */
.promos-shell .sec-kpi__hint .sec-tag,
.promos-shell .sec-kpi__hint .sec-pill { display: inline-flex; }

/* Filter bar (Activity log). */
.promos-shell .sec-filter {
  background: var(--pz-surface);
  border: 1px solid var(--pz-border);
  border-radius: 12px;
  padding: 14px 16px;
  margin-bottom: 18px;
}
.promos-shell .sec-filter__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 10px;
  align-items: end;
}
.promos-shell .sec-filter__footer {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 12px;
  font-size: 12.5px;
  color: var(--pz-text-muted);
}
.promos-shell .sec-filter__count { margin-left: auto; }

/* Header pill (e.g. "Step-up required" on Respond page). */
.promos-shell .sec-header__pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-left: 12px;
  padding: 3px 10px;
  background: #fee2e2;
  color: #991b1b;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  vertical-align: middle;
}
[data-theme="dark"] .promos-shell .sec-header__pill { background: rgba(239, 68, 68, 0.18); color: #fca5a5; }

/* Action card — Respond page. */
.promos-shell .sec-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
  gap: 16px;
  margin-bottom: 18px;
}
.promos-shell .sec-card {
  background: var(--pz-surface);
  border: 1px solid var(--pz-border);
  border-radius: 12px;
  padding: 16px 18px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.promos-shell .sec-card__title {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  font-weight: 600;
  margin: 0;
}
.promos-shell .sec-card__title i { color: var(--pz-danger); }
.promos-shell .sec-card__hint {
  font-size: 12.5px;
  color: var(--pz-text-muted);
  margin: 0;
}
.promos-shell .sec-card__hint a { color: var(--pz-brand); text-decoration: none; }
.promos-shell .sec-card__hint a:hover { text-decoration: underline; }

/* Currently-blocked list inside the Respond card. */
.promos-shell .sec-blocklist {
  background: var(--pz-surface-muted);
  border: 1px solid var(--pz-border);
  border-radius: 10px;
  max-height: 240px;
  overflow-y: auto;
}
.promos-shell .sec-blocklist__head {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--pz-text-muted);
  padding: 8px 12px;
  border-bottom: 1px solid var(--pz-border);
}
.promos-shell .sec-blocklist__item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border-bottom: 1px solid var(--pz-border);
  font-size: 12.5px;
}
.promos-shell .sec-blocklist__item:last-child { border-bottom: 0; }
.promos-shell .sec-blocklist__main { flex: 1; min-width: 0; }
.promos-shell .sec-blocklist__ip {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-weight: 600;
  color: var(--pz-text);
}
.promos-shell .sec-blocklist__meta {
  font-size: 11.5px;
  color: var(--pz-text-muted);
}
.promos-shell .sec-blocklist__unblock {
  appearance: none;
  border: 1px solid var(--pz-border);
  background: var(--pz-surface);
  border-radius: 8px;
  width: 28px; height: 28px;
  color: var(--pz-text-muted);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.promos-shell .sec-blocklist__unblock:hover { color: var(--pz-danger); border-color: var(--pz-danger); }

/* Footer info box (e.g. "Coming soon" note on Respond). */
.promos-shell .sec-note {
  background: var(--pz-surface-muted);
  border: 1px dashed var(--pz-border);
  border-radius: 10px;
  padding: 10px 14px;
  font-size: 12.5px;
  color: var(--pz-text-muted);
  display: flex;
  align-items: center;
  gap: 8px;
}
.promos-shell .sec-note i { color: var(--pz-text-subtle); }

/* Help panel under the page header — describes what the page does,
   what each metric/term means, and when to act on it. Always visible
   so the operator can re-read it any time without clicking. */
.promos-shell .sec-help {
  background: var(--pz-surface-muted);
  border: 1px solid var(--pz-border);
  border-radius: 12px;
  padding: 14px 18px;
  margin-bottom: 22px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--pz-text);
}
.promos-shell .sec-help p { margin: 0 0 10px; }
.promos-shell .sec-help p:last-child { margin: 0; }
.promos-shell .sec-help strong { color: var(--pz-text); font-weight: 600; }
.promos-shell .sec-help em { font-style: italic; color: var(--pz-text); }
.promos-shell .sec-help code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  background: var(--pz-surface);
  border: 1px solid var(--pz-border);
  border-radius: 4px;
  padding: 1px 5px;
  color: var(--pz-text);
}

/* Click-to-expand rows on the Activity log table. The data row is the
   trigger; the next-sibling .sec-row-detail row is hidden by default. */
.promos-shell .sec-table tbody tr.sec-row-data {
  cursor: pointer;
  transition: background 0.12s ease;
}
.promos-shell .sec-table tbody tr.sec-row-data:hover { background: var(--pz-surface-muted); }
.promos-shell .sec-table tbody tr.sec-row-data.is-open { background: var(--pz-surface-muted); }
.promos-shell .sec-table tbody tr.sec-row-data.is-open td {
  border-bottom-color: transparent;
}
.promos-shell .sec-table tbody tr.sec-row-detail { display: none; background: var(--pz-surface-muted); }
.promos-shell .sec-table tbody tr.sec-row-detail.is-open { display: table-row; }
.promos-shell .sec-table tbody tr.sec-row-detail td {
  padding: 4px 14px 14px;
  border-bottom: 1px solid var(--pz-border);
}
.promos-shell .sec-chevron {
  display: inline-flex;
  width: 14px; height: 14px;
  transition: transform 0.15s ease;
  color: var(--pz-text-subtle);
}
.promos-shell tr.sec-row-data.is-open .sec-chevron { transform: rotate(90deg); color: var(--pz-text); }
.promos-shell .sec-detailgrid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px 16px;
  margin-bottom: 8px;
  font-size: 12px;
}
.promos-shell .sec-detailgrid > div { color: var(--pz-text); }
.promos-shell .sec-detailgrid > div.is-wide { grid-column: 1 / -1; }
.promos-shell .sec-detailgrid .sec-detailgrid__k { color: var(--pz-text-muted); margin-right: 4px; }

/* Security console tabs use the standard .promos-tabs underline style — the
   same modern tabs as Insights / Settings (a brand-coloured active underline,
   no box). `.sec-tabs` only keeps flex-wrap (above) so all 9 tabs stay visible
   on narrow viewports. */

/* Inline-action cluster on the right side of an alert-rule row.
   Each form lives next to a row-wide click target that opens the
   edit modal — `data-modal-skip` on this cluster prevents clicks on
   the toggle/test/edit buttons from also opening the modal. */
.promos-shell .sec-row__actions {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: 12px;
}
.promos-shell .sec-row__actions form { margin: 0; display: inline-flex; }
.promos-shell .sec-row__action-btn {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  height: 30px;
  padding: 0 12px;
  border-radius: 8px;
  border: 1px solid var(--pz-border);
  background: var(--pz-surface);
  color: var(--pz-text-muted);
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
  text-decoration: none;
}
.promos-shell .sec-row__action-btn:hover {
  background: var(--pz-surface-muted);
  color: var(--pz-text);
  border-color: var(--pz-border-strong);
  text-decoration: none;
}
.promos-shell .sec-row__action-btn--primary {
  border-color: var(--pz-brand);
  color: var(--pz-brand-fg);
  background: var(--pz-brand);
}
.promos-shell .sec-row__action-btn--primary:hover {
  color: var(--pz-brand-fg);
  background: var(--pz-brand);
  opacity: 0.92;
}

/* Row variant for the alerts page — uses a div (not <a>) so we can
   nest forms safely. Hover is still page-cursor pointer to read as
   clickable. */
.promos-shell .promos-row.sec-row {
  cursor: pointer;
}

/* Cloudflare Turnstile wrapper — center the (flexible) widget inside the
   auth-card and give it consistent vertical breathing room. Behaviour:
   - `data-appearance="interaction-only"` causes Cloudflare to hide the
     iframe once the challenge resolves silently, so for most visitors the
     wrap collapses to its margins only (no visible widget).
   - `data-size="flexible"` makes the widget fluid between 130-380 px so
     it sits naturally in a narrow form column. */
/* Column, so the challenge-error line sits UNDER the widget instead of
   becoming a second flex item beside it. */
.turnstile-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  margin: 14px 0 6px;
}
.turnstile-wrap .cf-turnstile { width: 100%; max-width: 360px; }
.turnstile-wrap [data-turnstile-error] { width: 100%; max-width: 360px; text-align: center; }

/* ── Checkout identify interstitial (anonymous visitor) ──────────────
   Three "How would you like to check out?" choice cards: guest / login /
   sign-up. Hover lifts the card; the leading icon sits in a soft tinted
   circle that picks up the shop's primary colour. */
.shop-checkout-identify-card {
  border: 1px solid var(--border, #e5e7eb);
  transition: transform 0.12s ease, box-shadow 0.12s ease, border-color 0.12s ease;
}
.shop-checkout-identify-card:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.06);
  border-color: var(--primary, #10b981);
}
.shop-checkout-identify-icon {
  width: 40px; height: 40px;
  flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 999px;
  background: color-mix(in oklab, var(--primary, #10b981) 12%, transparent);
  color: var(--primary, #10b981);
  font-size: 16px;
}
[data-theme="dark"] .shop-checkout-identify-card {
  background: var(--card, #222);
  border-color: var(--border, #333);
}
[data-theme="dark"] .shop-checkout-identify-card:hover {
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.3);
}

/* ── Shop-owner "please verify your email" banner ─────────────────
   Lives between the topbar and .admin-main inside .admin-main-col.
   Matches .admin-main's 22px horizontal gutter so it visually aligns
   with the page body. Resend button uses a custom color pair that
   stays readable on both the light yellow alert-warning AND the
   darker dark-mode alert variant Bootstrap renders. */
.admin-shell[data-admin-shell] .owner-verify-banner,
.admin-shell[data-admin-shell] .owner-verify-flash,
.admin-shell[data-admin-shell] .demo-admin-banner {
  /* The page boxes live inside .admin-main, whose scrollbar gutter eats into
     their right edge (22px padding + ~8px gutter). This banner sits ABOVE
     .admin-main, so its right margin needs the extra 8px to line its right edge
     up with the boxes. Left stays at the 22px .admin-main padding.
     The demo banner shares these selectors rather than repeating the numbers,
     so the two can never drift to different widths. */
  margin: 16px 30px 0 22px;
}
.admin-shell[data-admin-shell] .owner-verify-banner + .admin-main,
.admin-shell[data-admin-shell] .owner-verify-flash:last-of-type {
  /* When the banner is present the admin-main padding-top would stack
     on top of the banner's bottom margin — collapse to a single 16px
     gap by zeroing one side. */
}
.admin-shell[data-admin-shell] .owner-verify-banner,
.admin-shell[data-admin-shell] .demo-admin-banner {
  margin-bottom: 16px;
}
/* ≤991px: .admin-main padding drops to 16px and touch scrollbars overlay (take
   no space), so match both edges to 16px — the banner was sitting 6px too far
   in on each side. */
@media (max-width: 991px) {
  .admin-shell[data-admin-shell] .owner-verify-banner,
  .admin-shell[data-admin-shell] .owner-verify-flash,
  .admin-shell[data-admin-shell] .demo-admin-banner {
    margin-left: 16px;
    margin-right: 16px;
  }
}
/* The concise mobile line only shows on phone portrait (see media query). */
.owner-verify-title-mobile { display: none; }
/* Mobile portrait: strip the verify banner to the concise one-liner + resend
   button — drop the envelope icon, the full title and the long paragraph. */
@media (max-width: 576px) {
  .owner-verify-banner .fa-envelope,
  .owner-verify-banner .owner-verify-title-full,
  .owner-verify-banner .small { display: none; }
  .owner-verify-banner .owner-verify-title-mobile { display: block; }
  .admin-shell[data-admin-shell] .owner-verify-banner { padding: 10px 14px; margin-bottom: 12px; }
  /* Tighter gap between the message and the Resend button (was gap-3 = 1rem). */
  .owner-verify-banner .flex-column { gap: 0.4rem !important; }
}

/* Resend button — explicit colors so dark mode doesn't swallow the
   default btn-outline-dark border. Uses solid medium-gray fill on
   light mode (visible against yellow) and a lighter fill on dark mode
   (visible against the muted dark-warning background). */
.owner-verify-resend-btn {
  background: #1f2937;
  border: 1px solid #1f2937;
  color: #ffffff;
}
.owner-verify-resend-btn:hover,
.owner-verify-resend-btn:focus {
  background: #111827;
  border-color: #111827;
  color: #ffffff;
}
[data-theme="dark"] .owner-verify-resend-btn {
  background: var(--az-text, #e2e1de);
  border-color: var(--az-text, #e2e1de);
  color: var(--az-bg, #161616);
}
[data-theme="dark"] .owner-verify-resend-btn:hover,
[data-theme="dark"] .owner-verify-resend-btn:focus {
  background: #f0efed;
  border-color: #f0efed;
  color: #141414;
}

/* ── Storefront header brand fallback (no logo uploaded) ──────────
   Mirrors the admin sidebar's az-shop-badge + az-shop-name styling so
   admin and storefront feel like one brand. Triggered in layout.ejs
   when isShopFrontPage && !hasShopLogo via the `_useInitialFallback`
   variable. The badge uses the shop's --primary colour (set by the
   per-shop theme tokens further down the file). */
.shop-brand-fallback {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  /* No line-height: 1 — descenders (g, p, q, y, j) need vertical room below
     the baseline; the badge still centers via flex regardless. */
}
.shop-brand-fallback__badge {
  /* Sized to the header's .btn-sm controls so the badge and the menu / search
     / account / cart buttons read as one row of equally sized squares.
     Bootstrap's .btn-sm box is 8px vertical padding + a 21px line box + 2px
     border = 31px; there is no local override of .btn-sm to change that. */
  width: 31px;
  height: 31px;
  border-radius: 8px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  font-weight: 700;
  line-height: 1;          /* one letter, vertically centred — line-height: 1 is correct here */
  letter-spacing: 0;
  /* color comes from inline style="--primary-fg" so per-shop branding wins */
}
.shop-brand-fallback__name {
  font-size: 16px;
  font-weight: 600;
  color: var(--fg, #111827);
  letter-spacing: -0.2px;
  /* Tall enough to show descenders cleanly — 16 * 1.25 = 20px box leaves
     room for g/p/q/y/j without the ellipsis clipping them off. */
  line-height: 1.25;
  max-width: 240px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
@media (max-width: 480px) {
  .shop-brand-fallback__name { max-width: 160px; font-size: 15px; }
  /* The badge deliberately does NOT shrink here: the header buttons it is
     sized against keep their 31px box at every width, and a smaller badge
     beside them looked out of step. Only the name text scales down. */
}

/* ─── Page-block editor: feature-item icon grid picker ───
   Shown inside the Bootstrap .dropdown-menu in the editor's feature-
   list item rows. 6 columns × N rows. Each cell is a square button
   showing the actual FontAwesome glyph; hover lightens the cell,
   .is-selected gives it a coloured ring. */
.page-block-icon-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 4px;
}
.page-block-icon-grid-btn {
  width: 100%;
  aspect-ratio: 1;
  border: 1px solid transparent;
  background: transparent;
  border-radius: 4px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  color: var(--bs-body-color, #333);
  padding: 0;
}
.page-block-icon-grid-btn:hover {
  background: var(--bs-secondary-bg, #f0f0f0);
}
.page-block-icon-grid-btn.is-selected {
  background: var(--bs-primary-bg-subtle, #cfe2ff);
  border-color: var(--bs-primary, #0d6efd);
}

/* ─── Hero block default background pattern ───
   Applied to any hero slide with NO background image (the default a fresh shop
   ships with). Brand-tinted corner gradients + a static "confetti" field: most
   confetti are translucent grey (one cached SVG tile — reads on light AND
   dark), plus a handful of sparse pieces in the shop's --primary as tiny
   solid-colour gradient rects (SVGs in background-image can't read CSS vars, and
   gradients are far cheaper to repaint than a repeating mask). No JS, no
   animation — nothing to repaint after first paint. Theme-adaptive via
   --bg / --primary; degrades to the flat --bg surface without color-mix. */
.block-hero__slide--pattern {
  background-color: var(--bg, #f8f9fb);
  background-image:
    radial-gradient(115% 130% at 12% 15%, color-mix(in srgb, var(--primary, #9f50df) 22%, transparent), transparent 55%),
    radial-gradient(110% 120% at 88% 85%, color-mix(in srgb, var(--primary-2, #8155de) 18%, transparent), transparent 55%),
    url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%27320%27%20height%3D%27320%27%20viewBox%3D%270%200%20320%20320%27%3E%3Crect%20x%3D%2765.3%27%20y%3D%2767.0%27%20width%3D%2711.9%27%20height%3D%275.4%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%28-10.0%2071.3%2069.7%29%27%2F%3E%3Crect%20x%3D%27124.3%27%20y%3D%27168.0%27%20width%3D%2710.3%27%20height%3D%275.1%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%286.3%20129.5%20170.5%29%27%2F%3E%3Crect%20x%3D%27208.2%27%20y%3D%2767.2%27%20width%3D%279.7%27%20height%3D%274.7%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.13%29%27%20transform%3D%27rotate%28-42.8%20213.0%2069.6%29%27%2F%3E%3Crect%20x%3D%27249.4%27%20y%3D%27179.6%27%20width%3D%2712.6%27%20height%3D%275.4%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%28-59.7%20255.7%20182.3%29%27%2F%3E%3Crect%20x%3D%2769.8%27%20y%3D%2772.7%27%20width%3D%279.7%27%20height%3D%274.4%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%28-12.7%2074.6%2074.8%29%27%2F%3E%3Crect%20x%3D%27229.5%27%20y%3D%27228.0%27%20width%3D%278.1%27%20height%3D%273.9%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.13%29%27%20transform%3D%27rotate%2825.1%20233.6%20229.9%29%27%2F%3E%3Crect%20x%3D%2764.7%27%20y%3D%27233.3%27%20width%3D%278.2%27%20height%3D%275.9%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%28-67.1%2068.8%20236.3%29%27%2F%3E%3Crect%20x%3D%27179.3%27%20y%3D%27140.8%27%20width%3D%279.3%27%20height%3D%274.3%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%28-70.5%20183.9%20143.0%29%27%2F%3E%3Crect%20x%3D%2752.7%27%20y%3D%2785.8%27%20width%3D%279.0%27%20height%3D%275.2%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.13%29%27%20transform%3D%27rotate%28-29.8%2057.2%2088.4%29%27%2F%3E%3Crect%20x%3D%27181.3%27%20y%3D%27143.2%27%20width%3D%279.8%27%20height%3D%276.1%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%2865.9%20186.2%20146.2%29%27%2F%3E%3Crect%20x%3D%27215.7%27%20y%3D%2723.1%27%20width%3D%278.0%27%20height%3D%274.3%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%28-53.8%20219.6%2025.3%29%27%2F%3E%3Crect%20x%3D%2749.1%27%20y%3D%2787.9%27%20width%3D%279.0%27%20height%3D%274.2%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.13%29%27%20transform%3D%27rotate%28-49.5%2053.6%2090.0%29%27%2F%3E%3Crect%20x%3D%2757.4%27%20y%3D%27230.1%27%20width%3D%2710.2%27%20height%3D%275.1%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%2814.2%2062.6%20232.6%29%27%2F%3E%3Crect%20x%3D%27181.1%27%20y%3D%2726.0%27%20width%3D%278.1%27%20height%3D%274.3%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%2848.1%20185.1%2028.2%29%27%2F%3E%3Crect%20x%3D%2777.3%27%20y%3D%2794.7%27%20width%3D%2710.2%27%20height%3D%275.3%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.13%29%27%20transform%3D%27rotate%2814.3%2082.3%2097.3%29%27%2F%3E%3Crect%20x%3D%27234.5%27%20y%3D%27266.3%27%20width%3D%277.8%27%20height%3D%276.1%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%2820.8%20238.4%20269.4%29%27%2F%3E%3Crect%20x%3D%27104.1%27%20y%3D%27128.7%27%20width%3D%2711.7%27%20height%3D%276.0%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%2867.3%20109.9%20131.7%29%27%2F%3E%3Crect%20x%3D%27203.0%27%20y%3D%27184.8%27%20width%3D%279.9%27%20height%3D%274.5%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.13%29%27%20transform%3D%27rotate%28-69.8%20207.9%20187.0%29%27%2F%3E%3Crect%20x%3D%27122.9%27%20y%3D%27200.3%27%20width%3D%2712.4%27%20height%3D%274.5%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%2816.5%20129.1%20202.5%29%27%2F%3E%3Crect%20x%3D%2765.7%27%20y%3D%2764.7%27%20width%3D%279.9%27%20height%3D%276.0%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%2860.4%2070.7%2067.7%29%27%2F%3E%3Crect%20x%3D%2727.9%27%20y%3D%2719.3%27%20width%3D%279.9%27%20height%3D%275.5%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.13%29%27%20transform%3D%27rotate%286.7%2032.9%2022.0%29%27%2F%3E%3Crect%20x%3D%27215.5%27%20y%3D%27120.5%27%20width%3D%2711.2%27%20height%3D%274.9%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%28-13.0%20221.2%20123.0%29%27%2F%3E%3Crect%20x%3D%27212.8%27%20y%3D%2741.8%27%20width%3D%2711.4%27%20height%3D%273.7%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%28-54.3%20218.5%2043.6%29%27%2F%3E%3Crect%20x%3D%27135.2%27%20y%3D%27216.6%27%20width%3D%2710.6%27%20height%3D%275.2%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.13%29%27%20transform%3D%27rotate%28-48.5%20140.5%20219.2%29%27%2F%3E%3Crect%20x%3D%27217.7%27%20y%3D%27150.0%27%20width%3D%2711.6%27%20height%3D%274.3%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%28-13.4%20223.6%20152.1%29%27%2F%3E%3Crect%20x%3D%27137.5%27%20y%3D%27274.6%27%20width%3D%277.7%27%20height%3D%274.1%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%28-8.2%20141.3%20276.6%29%27%2F%3E%3Crect%20x%3D%27219.0%27%20y%3D%27278.0%27%20width%3D%279.4%27%20height%3D%276.0%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.13%29%27%20transform%3D%27rotate%28-19.7%20223.7%20281.0%29%27%2F%3E%3Crect%20x%3D%27123.6%27%20y%3D%27280.2%27%20width%3D%2711.8%27%20height%3D%274.6%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%28-59.6%20129.5%20282.5%29%27%2F%3E%3Crect%20x%3D%27163.1%27%20y%3D%2743.5%27%20width%3D%278.8%27%20height%3D%275.6%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%2822.0%20167.5%2046.3%29%27%2F%3E%3Crect%20x%3D%27157.9%27%20y%3D%2736.8%27%20width%3D%2710.5%27%20height%3D%274.4%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.13%29%27%20transform%3D%27rotate%28-68.2%20163.1%2039.0%29%27%2F%3E%3Crect%20x%3D%27252.4%27%20y%3D%2733.4%27%20width%3D%2711.6%27%20height%3D%274.4%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%2850.0%20258.3%2035.6%29%27%2F%3E%3Crect%20x%3D%2741.5%27%20y%3D%2775.9%27%20width%3D%278.3%27%20height%3D%275.4%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%28-21.5%2045.7%2078.7%29%27%2F%3E%3Crect%20x%3D%27259.4%27%20y%3D%2796.1%27%20width%3D%2712.0%27%20height%3D%276.0%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.13%29%27%20transform%3D%27rotate%28-38.2%20265.4%2099.1%29%27%2F%3E%3Crect%20x%3D%2748.4%27%20y%3D%27239.8%27%20width%3D%2711.8%27%20height%3D%275.6%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%2817.5%2054.3%20242.5%29%27%2F%3E%3Crect%20x%3D%27161.3%27%20y%3D%27208.9%27%20width%3D%277.4%27%20height%3D%276.1%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%2860.5%20165.0%20211.9%29%27%2F%3E%3Crect%20x%3D%2759.1%27%20y%3D%27102.4%27%20width%3D%277.1%27%20height%3D%274.9%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.13%29%27%20transform%3D%27rotate%28-63.1%2062.7%20104.8%29%27%2F%3E%3Crect%20x%3D%27202.9%27%20y%3D%27145.5%27%20width%3D%2712.9%27%20height%3D%274.6%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%287.5%20209.4%20147.8%29%27%2F%3E%3Crect%20x%3D%2716.9%27%20y%3D%27273.8%27%20width%3D%278.2%27%20height%3D%274.8%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%28-20.1%2021.0%20276.2%29%27%2F%3E%3Crect%20x%3D%27212.8%27%20y%3D%2727.7%27%20width%3D%278.7%27%20height%3D%274.1%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.13%29%27%20transform%3D%27rotate%28-25.1%20217.1%2029.7%29%27%2F%3E%3Crect%20x%3D%27207.9%27%20y%3D%27295.9%27%20width%3D%279.6%27%20height%3D%274.2%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%28-23.2%20212.7%20298.0%29%27%2F%3E%3Crect%20x%3D%27257.8%27%20y%3D%27155.2%27%20width%3D%2712.2%27%20height%3D%274.2%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%28-12.6%20263.9%20157.3%29%27%2F%3E%3Crect%20x%3D%27223.2%27%20y%3D%2794.1%27%20width%3D%2710.1%27%20height%3D%275.1%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.13%29%27%20transform%3D%27rotate%2848.7%20228.3%2096.7%29%27%2F%3E%3Crect%20x%3D%27119.5%27%20y%3D%27118.4%27%20width%3D%2711.7%27%20height%3D%276.4%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%2821.9%20125.4%20121.6%29%27%2F%3E%3Crect%20x%3D%27237.0%27%20y%3D%27256.8%27%20width%3D%278.4%27%20height%3D%274.5%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%2850.3%20241.2%20259.1%29%27%2F%3E%3Crect%20x%3D%27156.7%27%20y%3D%27113.8%27%20width%3D%2712.7%27%20height%3D%275.3%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.13%29%27%20transform%3D%27rotate%2829.2%20163.0%20116.4%29%27%2F%3E%3Crect%20x%3D%2724.2%27%20y%3D%27189.8%27%20width%3D%2710.9%27%20height%3D%276.2%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%28-14.8%2029.7%20192.9%29%27%2F%3E%3Crect%20x%3D%27119.5%27%20y%3D%27293.1%27%20width%3D%278.3%27%20height%3D%275.7%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%2821.2%20123.7%20295.9%29%27%2F%3E%3Crect%20x%3D%27130.2%27%20y%3D%27157.3%27%20width%3D%2711.9%27%20height%3D%273.7%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.13%29%27%20transform%3D%27rotate%28-55.0%20136.2%20159.1%29%27%2F%3E%3Crect%20x%3D%27276.2%27%20y%3D%27307.5%27%20width%3D%2710.6%27%20height%3D%276.3%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%28-12.6%20281.5%20310.7%29%27%2F%3E%3Crect%20x%3D%27302.6%27%20y%3D%2759.0%27%20width%3D%279.0%27%20height%3D%273.8%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%28-3.2%20307.1%2060.9%29%27%2F%3E%3Crect%20x%3D%27173.6%27%20y%3D%27138.3%27%20width%3D%2712.9%27%20height%3D%274.7%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.13%29%27%20transform%3D%27rotate%2840.9%20180.0%20140.6%29%27%2F%3E%3Crect%20x%3D%27221.2%27%20y%3D%2792.7%27%20width%3D%279.2%27%20height%3D%273.8%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%28-56.6%20225.7%2094.6%29%27%2F%3E%3Crect%20x%3D%2715.9%27%20y%3D%27178.0%27%20width%3D%2710.7%27%20height%3D%274.8%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%28-29.8%2021.2%20180.4%29%27%2F%3E%3Crect%20x%3D%2794.4%27%20y%3D%2790.1%27%20width%3D%2710.7%27%20height%3D%274.0%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.13%29%27%20transform%3D%27rotate%2845.5%2099.7%2092.1%29%27%2F%3E%3Crect%20x%3D%27205.6%27%20y%3D%27161.1%27%20width%3D%277.3%27%20height%3D%274.8%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%2816.0%20209.2%20163.5%29%27%2F%3E%3Crect%20x%3D%27279.4%27%20y%3D%27103.6%27%20width%3D%2713.0%27%20height%3D%274.8%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%28-2.6%20285.9%20106.0%29%27%2F%3E%3Crect%20x%3D%2755.0%27%20y%3D%27161.0%27%20width%3D%2712.1%27%20height%3D%274.9%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.13%29%27%20transform%3D%27rotate%28-36.8%2061.1%20163.4%29%27%2F%3E%3Crect%20x%3D%27187.4%27%20y%3D%27157.2%27%20width%3D%2712.0%27%20height%3D%274.2%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%2816.9%20193.3%20159.3%29%27%2F%3E%3Crect%20x%3D%27114.6%27%20y%3D%2757.1%27%20width%3D%2710.2%27%20height%3D%275.9%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%28-36.2%20119.7%2060.1%29%27%2F%3E%3Crect%20x%3D%27310.0%27%20y%3D%27223.0%27%20width%3D%277.7%27%20height%3D%275.4%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.13%29%27%20transform%3D%27rotate%2841.2%20313.9%20225.7%29%27%2F%3E%3Crect%20x%3D%27130.3%27%20y%3D%2751.5%27%20width%3D%277.4%27%20height%3D%274.9%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%28-38.8%20134.0%2054.0%29%27%2F%3E%3Crect%20x%3D%27250.5%27%20y%3D%2722.0%27%20width%3D%279.6%27%20height%3D%273.6%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%28-8.5%20255.3%2023.8%29%27%2F%3E%3Crect%20x%3D%27153.2%27%20y%3D%27117.1%27%20width%3D%2712.1%27%20height%3D%276.2%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.13%29%27%20transform%3D%27rotate%2838.6%20159.3%20120.2%29%27%2F%3E%3Crect%20x%3D%27271.3%27%20y%3D%27157.5%27%20width%3D%279.2%27%20height%3D%275.1%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%28-44.7%20276.0%20160.1%29%27%2F%3E%3Crect%20x%3D%2760.1%27%20y%3D%27261.2%27%20width%3D%279.5%27%20height%3D%274.7%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%2848.1%2064.8%20263.5%29%27%2F%3E%3Crect%20x%3D%2799.7%27%20y%3D%27112.4%27%20width%3D%277.1%27%20height%3D%274.4%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.13%29%27%20transform%3D%27rotate%28-68.8%20103.3%20114.7%29%27%2F%3E%3Crect%20x%3D%27130.5%27%20y%3D%27306.5%27%20width%3D%279.4%27%20height%3D%273.9%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%2866.4%20135.2%20308.4%29%27%2F%3E%3Crect%20x%3D%27227.3%27%20y%3D%27212.8%27%20width%3D%279.5%27%20height%3D%274.3%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%2862.5%20232.0%20215.0%29%27%2F%3E%3Crect%20x%3D%27200.4%27%20y%3D%2727.8%27%20width%3D%278.1%27%20height%3D%273.7%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.13%29%27%20transform%3D%27rotate%2827.5%20204.4%2029.6%29%27%2F%3E%3Crect%20x%3D%27249.7%27%20y%3D%27180.0%27%20width%3D%278.4%27%20height%3D%275.7%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%28-3.3%20253.9%20182.9%29%27%2F%3E%3Crect%20x%3D%27148.4%27%20y%3D%27154.8%27%20width%3D%279.1%27%20height%3D%274.0%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%286.5%20152.9%20156.8%29%27%2F%3E%3Crect%20x%3D%27266.4%27%20y%3D%2751.9%27%20width%3D%279.1%27%20height%3D%275.3%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.13%29%27%20transform%3D%27rotate%28-67.9%20270.9%2054.6%29%27%2F%3E%3Crect%20x%3D%27116.2%27%20y%3D%2730.1%27%20width%3D%278.4%27%20height%3D%276.3%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%2862.2%20120.4%2033.3%29%27%2F%3E%3Crect%20x%3D%27105.1%27%20y%3D%27135.6%27%20width%3D%279.8%27%20height%3D%274.2%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%28-44.9%20110.0%20137.7%29%27%2F%3E%3Crect%20x%3D%27112.7%27%20y%3D%27103.8%27%20width%3D%2711.2%27%20height%3D%274.2%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.13%29%27%20transform%3D%27rotate%2812.7%20118.3%20105.9%29%27%2F%3E%3Crect%20x%3D%27112.5%27%20y%3D%27191.5%27%20width%3D%279.7%27%20height%3D%276.1%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.16%29%27%20transform%3D%27rotate%28-70.7%20117.4%20194.6%29%27%2F%3E%3Crect%20x%3D%27175.8%27%20y%3D%27209.6%27%20width%3D%277.5%27%20height%3D%276.2%27%20fill%3D%27rgba%28128%2C128%2C128%2C0.10%29%27%20transform%3D%27rotate%2864.5%20179.5%20212.8%29%27%2F%3E%3C%2Fsvg%3E");
  background-size: 100% 100%, 100% 100%, 320px 320px;
  background-position: center, center, 0 0;
  background-repeat: no-repeat, no-repeat, repeat;
  border-radius: 8px;
}

/* The brand-coloured confetti. Painted as a --primary fill masked by a tile of
   tilted white rects: an SVG in background-image can't read CSS custom
   properties, and a gradient layer can't be rotated — masking is what lets the
   brand pieces tilt like the grey ones AND still track each shop's --primary.
   Static: rasterised once, no animation, nothing repaints after first paint.
   The slide is position:relative and its text column is position:relative too,
   so this ::before paints underneath the copy. */
.block-hero__slide--pattern::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: var(--primary, #9f50df);
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%271200%27%20height%3D%27320%27%20viewBox%3D%270%200%201200%20320%27%3E%3Crect%20x%3D%271029.1%27%20y%3D%27137.7%27%20width%3D%2711.2%27%20height%3D%275.2%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%28-61.2%201034.6%20140.3%29%27%2F%3E%3Crect%20x%3D%27460.3%27%20y%3D%27128.4%27%20width%3D%279.0%27%20height%3D%275.5%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%2856.0%20464.8%20131.1%29%27%2F%3E%3Crect%20x%3D%27356.5%27%20y%3D%27233.0%27%20width%3D%2711.6%27%20height%3D%274.5%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%28-52.6%20362.3%20235.3%29%27%2F%3E%3Crect%20x%3D%27954.0%27%20y%3D%27119.4%27%20width%3D%2712.5%27%20height%3D%274.9%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%28-54.1%20960.2%20121.8%29%27%2F%3E%3Crect%20x%3D%27417.8%27%20y%3D%2759.4%27%20width%3D%2710.3%27%20height%3D%276.4%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%2833.5%20422.9%2062.6%29%27%2F%3E%3Crect%20x%3D%27830.3%27%20y%3D%2782.0%27%20width%3D%2711.5%27%20height%3D%275.8%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%2832.4%20836.0%2084.9%29%27%2F%3E%3Crect%20x%3D%27623.2%27%20y%3D%27238.9%27%20width%3D%2712.8%27%20height%3D%275.9%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%28-21.1%20629.6%20241.9%29%27%2F%3E%3Crect%20x%3D%27661.2%27%20y%3D%27206.8%27%20width%3D%2712.1%27%20height%3D%275.0%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%286.2%20667.3%20209.3%29%27%2F%3E%3Crect%20x%3D%27524.7%27%20y%3D%27198.6%27%20width%3D%278.2%27%20height%3D%274.8%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%28-5.6%20528.8%20201.0%29%27%2F%3E%3Crect%20x%3D%27134.8%27%20y%3D%27256.9%27%20width%3D%2711.2%27%20height%3D%275.7%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%2814.1%20140.4%20259.8%29%27%2F%3E%3Crect%20x%3D%271047.5%27%20y%3D%2758.5%27%20width%3D%278.4%27%20height%3D%276.5%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%28-65.9%201051.7%2061.7%29%27%2F%3E%3Crect%20x%3D%27340.5%27%20y%3D%27224.3%27%20width%3D%2710.4%27%20height%3D%274.4%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%28-55.3%20345.7%20226.5%29%27%2F%3E%3Crect%20x%3D%27383.1%27%20y%3D%27273.6%27%20width%3D%2713.1%27%20height%3D%276.9%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%28-28.0%20389.7%20277.0%29%27%2F%3E%3Crect%20x%3D%27123.7%27%20y%3D%2786.5%27%20width%3D%278.2%27%20height%3D%275.1%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%2828.1%20127.8%2089.0%29%27%2F%3E%3Crect%20x%3D%27734.7%27%20y%3D%27196.5%27%20width%3D%2713.3%27%20height%3D%275.0%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%28-49.1%20741.3%20198.9%29%27%2F%3E%3Crect%20x%3D%27856.3%27%20y%3D%2784.5%27%20width%3D%278.3%27%20height%3D%274.2%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%2835.6%20860.4%2086.6%29%27%2F%3E%3C%2Fsvg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%271200%27%20height%3D%27320%27%20viewBox%3D%270%200%201200%20320%27%3E%3Crect%20x%3D%271029.1%27%20y%3D%27137.7%27%20width%3D%2711.2%27%20height%3D%275.2%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%28-61.2%201034.6%20140.3%29%27%2F%3E%3Crect%20x%3D%27460.3%27%20y%3D%27128.4%27%20width%3D%279.0%27%20height%3D%275.5%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%2856.0%20464.8%20131.1%29%27%2F%3E%3Crect%20x%3D%27356.5%27%20y%3D%27233.0%27%20width%3D%2711.6%27%20height%3D%274.5%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%28-52.6%20362.3%20235.3%29%27%2F%3E%3Crect%20x%3D%27954.0%27%20y%3D%27119.4%27%20width%3D%2712.5%27%20height%3D%274.9%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%28-54.1%20960.2%20121.8%29%27%2F%3E%3Crect%20x%3D%27417.8%27%20y%3D%2759.4%27%20width%3D%2710.3%27%20height%3D%276.4%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%2833.5%20422.9%2062.6%29%27%2F%3E%3Crect%20x%3D%27830.3%27%20y%3D%2782.0%27%20width%3D%2711.5%27%20height%3D%275.8%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%2832.4%20836.0%2084.9%29%27%2F%3E%3Crect%20x%3D%27623.2%27%20y%3D%27238.9%27%20width%3D%2712.8%27%20height%3D%275.9%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%28-21.1%20629.6%20241.9%29%27%2F%3E%3Crect%20x%3D%27661.2%27%20y%3D%27206.8%27%20width%3D%2712.1%27%20height%3D%275.0%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%286.2%20667.3%20209.3%29%27%2F%3E%3Crect%20x%3D%27524.7%27%20y%3D%27198.6%27%20width%3D%278.2%27%20height%3D%274.8%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%28-5.6%20528.8%20201.0%29%27%2F%3E%3Crect%20x%3D%27134.8%27%20y%3D%27256.9%27%20width%3D%2711.2%27%20height%3D%275.7%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%2814.1%20140.4%20259.8%29%27%2F%3E%3Crect%20x%3D%271047.5%27%20y%3D%2758.5%27%20width%3D%278.4%27%20height%3D%276.5%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%28-65.9%201051.7%2061.7%29%27%2F%3E%3Crect%20x%3D%27340.5%27%20y%3D%27224.3%27%20width%3D%2710.4%27%20height%3D%274.4%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%28-55.3%20345.7%20226.5%29%27%2F%3E%3Crect%20x%3D%27383.1%27%20y%3D%27273.6%27%20width%3D%2713.1%27%20height%3D%276.9%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%28-28.0%20389.7%20277.0%29%27%2F%3E%3Crect%20x%3D%27123.7%27%20y%3D%2786.5%27%20width%3D%278.2%27%20height%3D%275.1%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%2828.1%20127.8%2089.0%29%27%2F%3E%3Crect%20x%3D%27734.7%27%20y%3D%27196.5%27%20width%3D%2713.3%27%20height%3D%275.0%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%28-49.1%20741.3%20198.9%29%27%2F%3E%3Crect%20x%3D%27856.3%27%20y%3D%2784.5%27%20width%3D%278.3%27%20height%3D%274.2%27%20fill%3D%27%23fff%27%20transform%3D%27rotate%2835.6%20860.4%2086.6%29%27%2F%3E%3C%2Fsvg%3E");
  -webkit-mask-size: 1200px 320px;
          mask-size: 1200px 320px;
  -webkit-mask-position: 0 0;
          mask-position: 0 0;
  -webkit-mask-repeat: repeat;
          mask-repeat: repeat;
}

/* ─── Hero block track (multi-slide carousel) ───
   The track keeps overflow-x:auto in the inline style so touch
   swipe still works on mobile, but the native scrollbar is hidden
   on every viewport (Firefox `scrollbar-width: none` + WebKit
   pseudo-element). Desktop visitors navigate via the hover-shown
   .carousel-arrow buttons defined in the section above (shared
   with the collection-widget carousel). */
.block-hero__track,
.block-image__track {
  scrollbar-width: none;
}
.block-hero__track::-webkit-scrollbar,
.block-image__track::-webkit-scrollbar { display: none; }

/* Reveal the .carousel-arrow.is-visible inside a hero / image-slideshow
   on hover — mirrors the same hover rule the collection-widget has
   for its own carousel. */
.block-hero:hover .carousel-arrow.is-visible,
.block-hero .carousel-arrow.is-visible:focus-visible,
.block-image:hover .carousel-arrow.is-visible,
.block-image .carousel-arrow.is-visible:focus-visible {
  opacity: 1;
  pointer-events: auto;
}

/* ─── Hero block headings (views/partials/shop/blocks/_block_hero.ejs) ───
   Bootstrap's default h3 (1.75rem) was a touch too loud inside the
   hero pill — reads as 'heading' rather than the intended supporting
   subheading level. Trim it to 1.4rem. h1/h2 stay at default sizes. */
.block-hero__heading h3,
.block-hero__sub h3 {
  font-size: 1.4rem;
  line-height: 1.3;
  margin: 0;
}

/* ─── Hero block button (views/partials/shop/blocks/_block_hero.ejs) ───
   Three size variants — small / medium / large — that intentionally
   step UP from the Bootstrap defaults. 'medium' is roughly where
   Bootstrap's .btn-lg sits; 'large' is bigger still and a touch
   bolder. The hover/focus/active rules deliberately keep the SAME
   background colour as the resting state (Bootstrap's .btn-primary
   would otherwise re-paint with a shifted gradient on hover, which
   fights both the owner's chosen custom colour AND looks busy on a
   hero banner).

   Custom colours come in via inline CSS variables set by the partial
   when cta_bg is non-empty:
     style="--hero-btn-bg:#abc;--hero-btn-border:#abc;color:#fff;"
   When the variables aren't set the buttons fall through to the
   default brand gradient. */
.block-hero__btn.btn-primary,
.block-hero__btn.btn-primary:hover,
.block-hero__btn.btn-primary:focus,
.block-hero__btn.btn-primary:active,
.block-hero__btn.btn-primary:focus-visible {
  background: var(--hero-btn-bg, linear-gradient(135deg, var(--primary), var(--primary-2))) !important;
  border-color: var(--hero-btn-border, var(--primary)) !important;
  /* color is driven by the same CSS-variable pattern as bg so the
     partial can override it per-slide. Default white (the original
     hardcoded value); the partial sets --hero-btn-fg when the owner
     picks a button text colour or when a bg is set. */
  color: var(--hero-btn-fg, #fff) !important;
  box-shadow: none !important;
  outline: none !important;
  transform: none !important;
  transition: none !important;
}
/* Size variants. Padding + font-size chosen so 'medium' lines up with
   Bootstrap's old .btn-lg, and 'large' steps up another notch. */
.block-hero__btn--small {
  padding: 0.4rem 0.9rem;
  font-size: 0.95rem;
  font-weight: 500;
}
.block-hero__btn--medium {
  padding: 0.65rem 1.4rem;
  font-size: 1.2rem;
  font-weight: 500;
}
.block-hero__btn--large {
  padding: 0.95rem 1.9rem;
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: 0.005em;
}
/* The →-arrow icon sits inside the button, separated from the label
   by a fixed gap. Slightly smaller than the label text so the arrow
   reads as a directional cue rather than competing for attention.
   The icon has its OWN transition so it can slide right on hover —
   the parent .block-hero__btn has `transition: none` to suppress
   colour shift, but a child element's transition is independent. */
.block-hero__btn-icon {
  margin-left: 0.6em;
  font-size: 0.85em;
  display: inline-block;
  transition: transform 0.2s ease;
}
.block-hero__btn:hover .block-hero__btn-icon,
.block-hero__btn:focus .block-hero__btn-icon {
  transform: translateX(2px);
}

/* ══════════════════════════════════════════════════════════════════════
   Shop preview banner — shown on a not-live shop's storefront to its own
   logged-in owner/team (shopLiveGuard sets shopPreviewMode). Sticky slim
   bar directly under the storefront header. Visitors never see it — they
   get the coming-soon page instead.
   ══════════════════════════════════════════════════════════════════════ */
.shop-preview-banner {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 1050; /* the topmost bar: above the fixed header (1030) and the
                    announcement bar (1040). Everything else is pushed down
                    below it via --sz-preview-h (measured in the partial). */
  background: #1c1917;
  color: #fafaf9;
  font-size: 13px;
  line-height: 1.4;
}
.shop-preview-banner__inner {
  max-width: 1140px;
  margin: 0 auto;
  padding: 8px 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  flex-wrap: wrap;
  text-align: center;
}
.shop-preview-banner__dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: #f59e0b;
  flex: 0 0 auto;
}
[data-theme="dark"] .shop-preview-banner { background: #292524; }

/* When the preview banner is present, push the fixed header, the announcement
   bar and the main content down by its height so nothing hides beneath it. */
body:has(.shop-preview-banner) > header {
  top: calc(var(--sz-preview-h, 40px) + max(0px, var(--announcement-bar-h, 0px) - 1px));
}
body:has(.shop-preview-banner) .shop-announcement-bar {
  top: var(--sz-preview-h, 40px);
}
body:has(.shop-preview-banner) main,
body:has(.shop-preview-banner) main.container,
body:has(.shop-preview-banner) main.container.my-4 {
  padding-top: calc(var(--navbar-height, 64px) + var(--announcement-bar-h, 0px) + var(--sz-preview-h, 40px) + var(--header-gap, 12px)) !important;
  margin-top: 0 !important;
}
/* Sticky sidebars / summaries anchor below the header, so they get the offset too. */
body:has(.shop-preview-banner) .cart-summary-card {
  top: calc(80px + var(--announcement-bar-h, 0px) + var(--sz-preview-h, 40px));
}
body:has(.shop-preview-banner) .checkout-summary-card {
  top: calc(var(--navbar-height, 64px) + var(--header-gap, 12px) + var(--announcement-bar-h, 0px) + var(--sz-preview-h, 40px));
}
@media (min-width: 992px) {
  body:has(.shop-preview-banner) .shop-sidebar-wrap {
    top: calc(var(--navbar-height, 64px) + var(--header-gap, 12px) + var(--announcement-bar-h, 0px) + var(--sz-preview-h, 40px));
  }
}

/* ══════════════════════════════════════════════════════════════════════
   Onboarding setup guide — bottom-right pill + panel + welcome modal.
   Rendered OUTSIDE .promos-shell (sibling of .admin-shell), so the panel
   re-declares the --pz-* tokens exactly like .promos-modal__card does.
   z-order: pill/panel 1050 < promos-modal welcome 2000 < confetti 2500.
   ══════════════════════════════════════════════════════════════════════ */
.onboarding-pill {
  position: fixed;
  /* Same gap from the bottom/right edges as the settings floating Save bar
     (padding: 18px 22px) so the pill lines up with that Save button. */
  bottom: 18px;
  right: 22px;
  z-index: 1050;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 2.1rem;
  padding: 0 0.85rem;
  border: none;
  border-radius: 2rem;
  background: #1c1917;
  color: #fafaf9;
  font-family: Inter, system-ui, sans-serif;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.2);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.onboarding-pill:hover { transform: translateY(-1px); box-shadow: 0 10px 24px rgba(0, 0, 0, 0.26); }
.onboarding-pill__icon { font-size: 13px; line-height: 1; }
.onboarding-pill__count {
  background: #f59e0b;
  color: #1c1917;
  font-size: 11px;
  font-weight: 700;
  padding: 1px 7px;
  border-radius: 1rem;
}
/* Dark mode: #292524 was almost the page colour (barely visible). Mirror the
   light-mode contrast instead — a light pill with dark text so it pops. */
[data-theme="dark"] .onboarding-pill { background: #f5f5f4; color: #1c1917; }
/* Expanded: the panel takes the pill's corner, so hide the pill. */
.onboarding-widget.is-open .onboarding-pill { display: none; }
/* While the panel is open, the bug-report / powered-by floaters (rendered
   later in the DOM at the same z-plane) would paint over it — fade them out. */
body:has(.onboarding-widget.is-open) .shopiza-bug-btn {
  opacity: 0;
  pointer-events: none;
}

.onboarding-panel {
  position: fixed;
  bottom: 1.25rem;
  right: 1.25rem;
  z-index: 1050;
  width: 340px;
  max-width: calc(100vw - 2.5rem);
}
.onboarding-panel__body {
  /* Token re-declaration — this card renders outside .promos-shell. */
  --pz-bg: #fafaf9;
  --pz-surface: #ffffff;
  --pz-surface-muted: #f5f5f4;
  --pz-border: #e7e5e4;
  --pz-border-strong: #d6d3d1;
  --pz-text: #1c1917;
  --pz-text-muted: #78716c;
  --pz-text-subtle: #a8a29e;
  --pz-accent: #10b981;
  --pz-accent-soft: #ecfdf5;
  --pz-danger: #ef4444;
  --pz-brand: var(--primary, #10b981);
  --pz-brand-fg: var(--primary-fg, #ffffff);
  background: var(--pz-surface);
  color: var(--pz-text);
  border: 1px solid var(--pz-border);
  border-radius: 14px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.18);
  overflow: hidden;
  font-family: Inter, system-ui, sans-serif;
  animation: onboardingPanelIn 0.2s ease;
  display: flex;
  flex-direction: column;
  max-height: min(70vh, 640px);
}
[data-theme="dark"] .onboarding-panel__body {
  --pz-bg: #1a1a1a;
  --pz-surface: #222222;
  --pz-surface-muted: #2a2a2a;
  --pz-border: #333333;
  --pz-border-strong: #444444;
  --pz-text: #f3f4f6;
  --pz-text-muted: #94a3b8;
  --pz-text-subtle: #6b7280;
  --pz-accent-soft: rgba(16, 185, 129, 0.14);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
}
@keyframes onboardingPanelIn {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}
.onboarding-panelroot { display: flex; flex-direction: column; min-height: 0; }
.onboarding-loading { padding: 28px 16px; text-align: center; font-size: 13px; color: var(--pz-text-muted, #78716c); }

/* One-row header: title · count · minimize. */
.onboarding-panel__header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 11px 10px 11px 15px;
  background: var(--pz-bg);
}
.onboarding-panel__title { flex: 1; min-width: 0; font-size: 13.5px; font-weight: 700; letter-spacing: -0.1px; }
.onboarding-panel__count { font-size: 12px; font-weight: 700; color: var(--pz-text-muted); }
.onboarding-panel__min {
  border: none;
  /* Show the hover background by default (both themes — the token is
     theme-aware); hover still darkens the icon for feedback. */
  background: var(--pz-surface-muted);
  color: var(--pz-text-muted);
  padding: 3px 5px;
  cursor: pointer;
  border-radius: 6px;
  display: inline-flex;
}
.onboarding-panel__min:hover { background: var(--pz-surface-muted); color: var(--pz-text); }
/* Thin, text-free progress line directly under the header. */
.onboarding-progressbar { height: 3px; background: var(--pz-surface-muted); overflow: hidden; }
.onboarding-progressbar span { display: block; height: 100%; background: var(--pz-accent); transition: width 0.3s ease; }

.onboarding-panel__scroll { overflow-y: auto; padding: 8px; min-height: 0; }
/* Task row: deep-link (flex-1) + optional inline skip, one line. */
.onboarding-step {
  display: flex;
  align-items: center;
  gap: 4px;
  border: 1px solid var(--pz-border);
  border-radius: 9px;
  background: var(--pz-surface);
  margin-bottom: 5px;
  padding-right: 8px;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.onboarding-step:hover { border-color: var(--pz-border-strong); }
.onboarding-step.is-done { border-color: var(--pz-accent); background: var(--pz-accent-soft); }
.onboarding-step.is-skipped { opacity: 0.6; }
.onboarding-step__main {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 4px 9px 11px;
  text-decoration: none;
  color: inherit;
}
.onboarding-step__main:hover { color: inherit; }
.onboarding-step__check {
  flex: 0 0 auto;
  width: 19px;
  height: 19px;
  border-radius: 50%;
  border: 1.5px solid var(--pz-border-strong);
  display: grid;
  place-items: center;
  color: #ffffff;
  background: var(--pz-surface);
}
.onboarding-step.is-done .onboarding-step__check { background: var(--pz-accent); border-color: var(--pz-accent); }
.onboarding-step__title { font-size: 13px; font-weight: 600; line-height: 1.3; }
.onboarding-step__skip {
  border: none;
  background: transparent;
  color: var(--pz-text-subtle);
  font-size: 11.5px;
  font-weight: 600;
  padding: 4px 6px;
  border-radius: 6px;
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
}
.onboarding-step__skip:hover { color: var(--pz-text); background: var(--pz-surface-muted); }

.onboarding-panel__footer {
  padding: 10px 12px;
  border-top: 1px solid var(--pz-border);
  background: var(--pz-bg);
}
.onboarding-golive {
  width: 100%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: none;
  border-radius: 9px;
  padding: 9px 14px;
  background: var(--pz-brand);
  color: var(--pz-brand-fg);
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
}
.onboarding-golive:hover:not(:disabled) { filter: brightness(1.05); }
.onboarding-golive:disabled { opacity: 0.5; cursor: not-allowed; }
.onboarding-golive.is-busy { opacity: 0.7; cursor: wait; }
.onboarding-owneronly { font-size: 11.5px; color: var(--pz-text-muted); text-align: center; line-height: 1.5; }
.onboarding-live { display: flex; flex-direction: column; gap: 8px; }
.onboarding-live__note { font-size: 13px; font-weight: 700; color: var(--pz-accent); text-align: center; }
.onboarding-live__actions { display: flex; gap: 6px; justify-content: center; flex-wrap: wrap; }

/* Welcome modal — reuses .promos-modal scrim/card; card gets a tighter,
   celebratory layout. */
.onboarding-welcome__card { max-width: 480px; }
.onboarding-welcome__inner { padding: 36px 32px 28px; text-align: center; }
.onboarding-welcome__emoji { font-size: 44px; line-height: 1; margin-bottom: 14px; }
.onboarding-welcome__title { font-size: 21px; font-weight: 700; letter-spacing: -0.3px; margin: 0 0 10px; color: var(--pz-text); }
.onboarding-welcome__body { font-size: 14px; color: var(--pz-text-muted); line-height: 1.55; margin: 0 0 22px; }
.onboarding-welcome__actions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }

/* Deep-link section flash (data-onboard-target destinations). */
@keyframes onboardFlash {
  0% { box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.65); background-color: rgba(245, 158, 11, 0.10); }
  70% { box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.35); background-color: rgba(245, 158, 11, 0.05); }
  100% { box-shadow: 0 0 0 3px rgba(245, 158, 11, 0); background-color: transparent; }
}
.onboard-flash {
  animation: onboardFlash 2s ease-out;
  border-radius: 10px;
}

/* Mobile: panel becomes a bottom sheet; welcome modal a full-width sheet. */
@media (max-width: 576px) {
  .onboarding-pill {
    /* Match the mobile save bar gap (padding: 12px 16px); keep the safe-area
       inset so it clears the home indicator. */
    bottom: calc(12px + env(safe-area-inset-bottom, 0px));
    right: 16px;
    height: 2.1rem;
    font-size: 13px;
  }
  /* The open panel is a full-width bottom sheet here — the pill would sit
     on top of its footer and steal taps from the Go-live button. */
  .onboarding-widget.is-open .onboarding-pill { display: none; }
  .onboarding-panel {
    left: 0;
    right: 0;
    bottom: 0;
    width: auto;
    max-width: none;
  }
  .onboarding-panel__body {
    border-radius: 16px 16px 0 0;
    border-left: none;
    border-right: none;
    border-bottom: none;
    max-height: 85dvh;
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  .promos-modal.onboarding-welcome[data-open="true"] { padding: 0; align-items: end; }
  .onboarding-welcome__card {
    max-width: none;
    width: 100%;
    max-height: 92dvh;
    border-radius: 16px 16px 0 0;
    align-self: end;
  }
  .onboarding-welcome__inner { padding: 30px 22px calc(24px + env(safe-area-inset-bottom, 0px)); }
}


/* ── Favicon field + interactive crop modal ─────────────────────────── */
.favicon-field__preview {
  width: 48px; height: 48px; border-radius: 8px;
  border: 1px solid var(--border, #dee2e6);
  background: var(--card, #fff);
  object-fit: contain; display: block; padding: 4px;
}
.favicon-field__placeholder {
  width: 48px; height: 48px; border-radius: 8px;
  border: 1px dashed var(--border, #ced4da);
  display: flex; align-items: center; justify-content: center;
  color: var(--muted, #adb5bd);
}
[data-favicon-field][data-has-favicon="1"] .favicon-field__placeholder { display: none; }
[data-favicon-field][data-has-favicon="0"] .favicon-field__preview { display: none; }

#faviconCropModal {
  display: none; position: fixed; inset: 0; z-index: 2200;
  align-items: center; justify-content: center; padding: 16px;
}
#faviconCropModal .fav-crop-backdrop { position: absolute; inset: 0; background: rgba(0,0,0,0.55); }
#faviconCropModal .fav-crop-dialog {
  position: relative; z-index: 1;
  background: var(--card, #fff); color: var(--fg, #212529);
  border: 1px solid var(--border, #dee2e6); border-radius: 12px;
  box-shadow: 0 20px 60px rgba(0,0,0,0.35);
  width: min(560px, 100%); max-height: 92vh; overflow: auto; padding: 20px;
}
#faviconCropModal .fav-crop-stage {
  position: relative; display: inline-block; max-width: 100%;
  line-height: 0; touch-action: none; user-select: none; -webkit-user-select: none;
  /* Clips the crop box's dimming shadow to the image.
     .fav-crop-box dims everything outside the selection with a 9999px spread
     shadow; without this it escaped the image and washed the whole dialog
     (title, preview, buttons) in 45% black, so the modal looked disabled and
     only the crop area read as active. The handles overhang by 8px and can
     clip when the selection is dragged flush to an edge, which is a far
     smaller cost than a dialog that looks broken. */
  overflow: hidden;
}
#faviconCropModal [data-fav-crop-image] {
  display: block; max-width: 100%; max-height: 46vh; width: auto; height: auto;
  background-image:
    linear-gradient(45deg, #e9ecef 25%, transparent 25%),
    linear-gradient(-45deg, #e9ecef 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #e9ecef 75%),
    linear-gradient(-45deg, transparent 75%, #e9ecef 75%);
  background-size: 16px 16px;
  background-position: 0 0, 0 8px, 8px -8px, -8px 0;
}
#faviconCropModal .fav-crop-box {
  position: absolute; display: none; box-sizing: border-box;
  border: 2px solid #fff; cursor: move;
  box-shadow: 0 0 0 1px rgba(0,0,0,0.45), 0 0 0 9999px rgba(0,0,0,0.45);
}
#faviconCropModal .fav-crop-handle {
  position: absolute; width: 14px; height: 14px;
  background: #fff; border: 1px solid rgba(0,0,0,0.5); border-radius: 2px;
}
#faviconCropModal .fav-crop-handle[data-fav-handle="nw"] { top: -8px; left: -8px; cursor: nwse-resize; }
#faviconCropModal .fav-crop-handle[data-fav-handle="ne"] { top: -8px; right: -8px; cursor: nesw-resize; }
#faviconCropModal .fav-crop-handle[data-fav-handle="sw"] { bottom: -8px; left: -8px; cursor: nesw-resize; }
#faviconCropModal .fav-crop-handle[data-fav-handle="se"] { bottom: -8px; right: -8px; cursor: nwse-resize; }
#faviconCropModal .fav-crop-preview {
  width: 64px; height: 64px; border: 1px solid var(--border, #dee2e6);
  border-radius: 8px; background: #fff;
}


/* ── Home block: categories grid ─────────────────────────────────────
   Moved here from an inline <style> in
   views/partials/shop/blocks/_block_categories_grid.ejs. That nonce-gated
   inline style was dropped whenever the shop container re-rendered via
   innerHTML (e.g. after add-to-cart): the re-fetched partial carries a FRESH
   CSP nonce that no longer matches the live document, so the browser blocked
   the <style>, the grid lost display:grid, and every card ballooned to full
   width. In app.css (loaded once in <head>) it survives every re-render. */
.block-categories-grid__grid { display: grid; gap: 1rem; }
.block-category-card-link { display: block; }
.block-categories-grid__grid--large,
.block-categories-grid__grid--medium,
.block-categories-grid__grid--small  { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 768px) {
  .block-categories-grid__grid--large  { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .block-categories-grid__grid--medium { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .block-categories-grid__grid--small  { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}
@media (min-width: 992px) {
  .block-categories-grid__grid--large  { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .block-categories-grid__grid--medium { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .block-categories-grid__grid--small  { grid-template-columns: repeat(8, minmax(0, 1fr)); }
}

/* ── Home block: feature list (responsive overrides) ─────────────────
   Same reason as above — moved from the inline <style> in
   _block_feature_list.ejs so the mobile/tablet column overrides survive an
   add-to-cart re-render. The base grid is an inline style="" on the element
   (allowed via style-src-attr 'unsafe-inline'), so only these overrides — and
   the paragraph resets — needed relocating. The !important keeps them winning
   over that inline base grid-template-columns. */
.block-feature-list__text p:last-child { margin-bottom: 0; }
.block-feature-list__text p { margin-bottom: 0.25rem; }
@media (max-width: 991.98px) {
  .block-feature-list__grid { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }
}
@media (max-width: 479.98px) {
  .block-feature-list__grid { grid-template-columns: 1fr !important; }
}


/* Admin toast — bottom-centre, mirrors the storefront add-to-cart toast. Used
   e.g. after saving a category so you keep your scroll position on a long list. */
.admin-toast {
  position: fixed;
  bottom: 1.5rem;
  left: 50%;
  transform: translateX(-50%);
  background: var(--card);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  padding: 0.75rem 1.25rem;
  box-shadow: 0 6px 20px rgba(0,0,0,.18);
  font-size: 0.875rem;
  font-weight: 500;
  z-index: 9999;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  animation: adminToastIn .25s ease, adminToastOut .4s ease 2.4s forwards;
}
.admin-toast i { color: var(--primary-text, var(--primary)); }
.admin-toast.admin-toast-error i { color: #dc3545; }
.admin-toast.admin-toast-error { border-color: #dc3545; }
@keyframes adminToastIn { from { opacity: 0; transform: translate(-50%, 14px); } to { opacity: 1; transform: translate(-50%, 0); } }
@keyframes adminToastOut { to { opacity: 0; transform: translate(-50%, 14px); } }

/* Sortable table headers (admin products list). A faded fa-sort hints the
   column is clickable; the active column shows a solid caret (up/down). */
.sortable-col { cursor: pointer; user-select: none; white-space: nowrap; }
.sortable-col .sort-th { display: inline-flex; align-items: center; gap: 0.35rem; }
.sortable-col .sort-ind { font-size: 0.8em; opacity: 0.35; transition: opacity 0.12s ease; }
.sortable-col .sort-ind.active { opacity: 1; color: var(--primary-text, var(--primary)); }
.sortable-col:hover .sort-ind { opacity: 0.7; }
.sortable-col:hover .sort-ind.active { opacity: 1; }
.sortable-col:focus-visible { outline: 2px solid var(--primary-text, var(--primary)); outline-offset: -2px; }
/* Two-line "Modified" cell: date over time, kept on their own lines. */
.product-modified { line-height: 1.25; white-space: nowrap; }


/* Briefly highlight a just-created category row (scroll-to + flash). Uses an
   inset box-shadow fill so it never fights the row background classes. */
@keyframes categoryFlashKf {
  0%, 60% { box-shadow: inset 0 0 0 2000px color-mix(in srgb, var(--primary-text, #6c63ff) 22%, transparent), inset 0 0 0 2px var(--primary-text, var(--primary)); }
  100%    { box-shadow: inset 0 0 0 2000px transparent, inset 0 0 0 2px transparent; }
}
.category-item .category-flash { animation: categoryFlashKf 2.4s ease; border-radius: 6px; }


/* Categories tree: collapse-all hides every nested (sub-)category, leaving only
   the top-level rows. Toggled by the header button in the categories admin. */
[data-categories-list].tree-collapsed .category-item .category-item { display: none; }


/* ════════════════════════════════════════════════════════════════════
   Landing 2026-07 redesign (lp2-*) — sections below the checkout
   spotlight: bento vignettes, feature ticker, free-forever, pricing,
   final CTA. Light mode only for now (dark-mode pass comes later).
   Animations intentionally have NO reduced-motion gating (owner
   decision 2026-07-20 — they must always play).
   ════════════════════════════════════════════════════════════════════ */

.lp2-band,
.lp2-veil,
.lp2-modal {
  --lp2-ink: #1b1526;
  --lp2-muted: #6b6478;
  --lp2-faint: #9a90ac;
  --lp2-soft: #8a819a;
  --lp2-body: #4a4258;
  --lp2-border: #ebe6f3;
  --lp2-border2: #e7e1f1;
  --lp2-tile: #f7f4fc;
  --lp2-tint: #faf8fd;
  --lp2-purple: #7a3bb8;
  --lp2-purple-bg: #f4ecfc;
  --lp2-purple-bg2: #f6f0fd;
  --lp2-dark: #1b1526;
  --lp2-green: #2e9e63;
  --lp2-accent: var(--primary, #9f50df);
  --lp2-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
}

/* ── Keyframes (from the design concept) ── */
@keyframes lp2Grow { 0% { transform: scaleY(.25); } 100% { transform: scaleY(1); } }
@keyframes lp2Shimmer { 0% { transform: translateX(-130%); } 60%, 100% { transform: translateX(260%); } }
@keyframes lp2OrderIn {
  0%, 12% { opacity: 0; transform: translateY(12px); }
  22%, 78% { opacity: 1; transform: translateY(0); }
  90%, 100% { opacity: 0; transform: translateY(-4px); }
}
@keyframes lp2CycleHi {
  0%, 13% { background: var(--lp2-langhi-bg, #f1e7fb); color: var(--lp2-langhi-fg, #7a3bb8); }
  19%, 100% { background: transparent; color: var(--lp2-soft, #8a819a); }
}
@keyframes lp2Marq { 0% { transform: translateX(0); } 100% { transform: translateX(-50%); } }
@keyframes lp2Floaty { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-6px); } }
@keyframes lp2FadeCycle {
  0%, 26% { opacity: 1; transform: translateY(0); }
  33%, 93% { opacity: 0; transform: translateY(-8px); }
  100% { opacity: 1; transform: translateY(0); }
}
@keyframes lp2Blip { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.7); opacity: .45; } }
@keyframes lp2CartGroup { 0%, 20% { transform: translateY(40px); } 30%, 100% { transform: translateY(0); } }
@keyframes lp2CartRow1 { 0%, 3% { opacity: 0; transform: translateY(6px); } 9%, 100% { opacity: 1; transform: translateY(0); } }
@keyframes lp2CartRow2 { 0%, 28% { opacity: 0; transform: translateY(6px); } 36%, 100% { opacity: 1; transform: translateY(0); } }
@keyframes lp2CartRow3 { 0%, 48% { opacity: 0; transform: translateY(6px); } 56%, 100% { opacity: 1; transform: translateY(0); } }
@keyframes lp2StampIn {
  0%, 70% { opacity: 0; transform: scale(1.6) rotate(-8deg); }
  78%, 100% { opacity: 1; transform: scale(1) rotate(-8deg); }
}

/* ── Bands ──
   Exactly TWO section backgrounds across the whole landing, alternating
   strictly (owner request 2026-07-21): "normal" = the raw page gradient +
   dots (transparent band), "alternate" (--tint) = one translucent milky
   veil. Order: hero N, logos A, storefront N, products A, checkout N,
   dashboard A, worldwide N, sell&grow A, ticker N, free-forever A,
   pricing N, final CTA A. */
.lp2-band { padding: 4.5rem 0; background: transparent; color: var(--lp2-ink); }
.lp2-band--tint { background: rgba(255, 255, 255, .58); }
.lp2-band--ticker { padding: 3rem 0 3.5rem; }

/* Veil variant for sections that live INSIDE the page container (the demo
   spotlights + logo strip): paints the same full-bleed alternate band via
   a pseudo-element so the section's markup/indentation stays untouched. */
.lp2-veil { position: relative; }
.lp2-veil::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(50% - 50vw);
  width: 100vw;
  z-index: -1;
  pointer-events: none;
}
.lp2-veil--tint::before { background: rgba(255, 255, 255, .58); }

/* ── Section grid: sticky head + bento ── */
.lp2-grid { display: grid; grid-template-columns: 360px 1fr; gap: 44px; align-items: start; }
.lp2-head { position: sticky; top: calc(var(--navbar-height, 64px) + var(--announcement-bar-h, 0px) + 24px); }
.lp2-grid--flip { grid-template-columns: 1fr 360px; }
.lp2-grid--flip .lp2-head { order: 2; }
/* Fallback values so the pill also works outside .lp2-band/.lp2-veil scopes
   (it's used in the hero too, which defines none of the --lp2-* vars).
   align-self keeps it from stretching inside flex-column parents.
   Fixed height + flex centering + line-height:1 makes every pill exactly the
   same size with the text dead-centered; the 1px top padding optically
   compensates for uppercase glyphs sitting above the em-box center. */
.lp2-eyebrow {
  display: inline-flex; align-items: center; justify-content: center;
  height: 29px; padding: 1px 13px 0; box-sizing: border-box; line-height: 1;
  font-size: 12.5px; font-weight: 700; color: var(--lp2-purple, #7a3bb8);
  letter-spacing: .12em; text-transform: uppercase;
  background: #ecdcf9; border-radius: 999px; align-self: flex-start;
  /* Own composited layer: sections above have fractional heights, so pills
     land on sub-pixel Y offsets — rasterized in-page, edge AA and text
     baseline rounding then differ per pill (pills LOOKED different heights
     with text 1px off-center in some). As a layer, every pill rasterizes in
     its own local (integer) coordinates — identical for all — and Chrome
     pixel-aligns the layer when compositing. */
  transform: translateZ(0);
}
.lp2-title { margin: 14px 0 0; font-size: 36px; font-weight: 700; line-height: 1.08; letter-spacing: -.02em; text-wrap: pretty; color: var(--lp2-ink); }
.lp2-lead { margin: 14px 0 0; font-size: 15.5px; color: var(--lp2-muted); line-height: 1.5; }

/* Even out the ragged edge on all landing copy (better line breaks, no
   orphan words) — the tasteful alternative to justification; degrades to
   normal wrapping where text-wrap:pretty is unsupported. */
.lp2-lead,
.lp2-card-text,
.lp2-dark-text,
.lp2-strip-text,
.lp2-final-sub,
.lp2-modal-desc,
.lp2-idea-link,
.landing-hero-subtitle,
.landing-spotlight p,
.landing-bullets li,
.landing-demo p { text-wrap: pretty; }

/* ── Bento cards ── */
.lp2-bento { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
.lp2-card {
  background: #fff; border: 1px solid var(--lp2-border); border-radius: 16px;
  padding: 18px; display: flex; flex-direction: column; gap: 12px; min-width: 0;
}
.lp2-card--wide { grid-column: span 2; }
.lp2-card--dark { background: var(--lp2-dark); border: 0; color: #fff; padding: 22px; }
.lp2-card-title { font-weight: 700; font-size: 15px; color: var(--lp2-ink); }
.lp2-card-text { font-size: 13px; color: var(--lp2-muted); margin-top: 2px; }
.lp2-dark-title { font-weight: 700; font-size: 16px; }
.lp2-dark-text { font-size: 13.5px; color: #a99cc4; margin-top: 4px; }
.lp2-viz {
  background: var(--lp2-tile); border-radius: 11px; height: 100px;
  display: flex; align-items: center; justify-content: center; overflow: hidden;
}
.lp2-viz-caption { font-size: 12.5px; color: var(--lp2-muted); font-weight: 600; }

/* Wide inline strips ("Also on board", "Speed is SEO") */
.lp2-strip { flex-direction: row; align-items: center; gap: 16px; padding: 18px 20px; }
.lp2-blip { width: 8px; height: 8px; border-radius: 50%; background: var(--lp2-accent); flex: 0 0 auto; animation: lp2Blip 2.4s ease infinite; }
.lp2-strip-strong { font-weight: 700; color: var(--lp2-ink); font-size: 15px; }
.lp2-strip-text { font-size: 13.5px; color: var(--lp2-muted); line-height: 1.5; }

/* ── Dashboard vignettes ── */
.lp2-analytics { display: grid; grid-template-columns: 1fr 1.2fr; gap: 22px; align-items: center; }
.lp2-analytics-kpi { font-size: 26px; font-weight: 700; margin-top: 6px; }
.lp2-analytics-delta { font-size: 13px; color: #7ee2a8; font-weight: 600; }
.lp2-bars { display: flex; align-items: flex-end; gap: 8px; height: 84px; }
.lp2-bar { flex: 1; border-radius: 5px 5px 2px 2px; transform-origin: bottom; animation: lp2Grow 2.4s ease infinite alternate; }
.lp2-bar:nth-child(1) { height: 44%; background: #4a3a68; animation-delay: -0.2s; }
.lp2-bar:nth-child(2) { height: 62%; background: #5b4880; animation-delay: -0.7s; }
.lp2-bar:nth-child(3) { height: 50%; background: #4a3a68; animation-delay: -1.1s; }
.lp2-bar:nth-child(4) { height: 78%; background: #8355c9; animation-delay: -1.5s; }
.lp2-bar:nth-child(5) { height: 96%; background: var(--lp2-accent); animation-delay: -1.9s; }

.lp2-orders { flex-direction: column; justify-content: center; align-items: stretch; gap: 6px; padding: 0 14px; }
.lp2-order-row {
  display: flex; justify-content: space-between; gap: 8px; font-size: 11.5px; font-weight: 600;
  color: var(--lp2-body); background: #fff; border: 1px solid var(--lp2-border2);
  border-radius: 7px; padding: 7px 11px; white-space: nowrap;
}
.lp2-order-row--new { animation: lp2OrderIn 4.5s ease infinite; }
.lp2-order-ok { color: var(--lp2-green); }
.lp2-order-new { color: var(--lp2-purple); }

.lp2-csv { gap: 12px; }
.lp2-csv-file { font-family: var(--lp2-mono); font-size: 12.5px; color: var(--lp2-body); background: #fff; border: 1px solid var(--lp2-border2); border-radius: 8px; padding: 8px 12px; }
.lp2-csv-arrow { color: var(--lp2-purple); font-size: 18px; animation: lp2Floaty 3s ease infinite; }
.lp2-csv-count { font-size: 12.5px; font-weight: 600; color: var(--lp2-body); background: #fff; border: 1px solid var(--lp2-border2); border-radius: 8px; padding: 8px 12px; }

/* ── Worldwide vignettes ── */
.lp2-langs { gap: 6px; flex-wrap: wrap; padding: 0 12px; font-size: 12px; font-weight: 600; }
.lp2-langs span { padding: 4px 9px; border-radius: 7px; color: var(--lp2-soft); animation: lp2CycleHi 6s infinite; }
.lp2-langs span:nth-child(1) { animation-delay: 0s; }
.lp2-langs span:nth-child(2) { animation-delay: -5s; }
.lp2-langs span:nth-child(3) { animation-delay: -4s; }
.lp2-langs span:nth-child(4) { animation-delay: -3s; }
.lp2-langs span:nth-child(5) { animation-delay: -2s; }
.lp2-langs span:nth-child(6) { animation-delay: -1s; }

.lp2-fx { position: relative; }
.lp2-fx span { position: absolute; font-size: 30px; font-weight: 700; color: var(--lp2-ink); animation: lp2FadeCycle 6s infinite; }
.lp2-fx span:nth-child(1) { animation-delay: 0s; }
.lp2-fx span:nth-child(2) { animation-delay: -4s; opacity: 0; }
.lp2-fx span:nth-child(3) { animation-delay: -2s; opacity: 0; }

.lp2-theme-split { border-radius: 11px; height: 100px; overflow: hidden; display: grid; grid-template-columns: 1fr 1fr; border: 1px solid var(--lp2-border); }
/* The tile literally depicts the light theme, so its ink stays dark in BOTH
   page themes (a themed var here made the sun go light-on-light in dark mode). */
.lp2-theme-light { background: #fdfcff; display: grid; place-items: center; font-size: 20px; color: #1b1526; }
.lp2-theme-dark { background: var(--lp2-dark); color: #fff; display: grid; place-items: center; font-size: 20px; }

.lp2-domain-pill {
  background: #fff; border: 1px solid var(--lp2-border2); border-radius: 999px; padding: 9px 16px;
  font-family: var(--lp2-mono); font-size: 13px; color: var(--lp2-body);
  display: flex; align-items: center; gap: 8px;
}
.lp2-domain-lock { width: 8px; height: 10px; border: 1.5px solid var(--lp2-green); border-radius: 3px 3px 2px 2px; display: inline-block; }

.lp2-oss { flex-direction: row; display: grid; grid-template-columns: 1fr 1fr; gap: 18px; align-items: center; padding: 18px 20px; }
.lp2-oss-chips { display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; font-size: 11.5px; font-weight: 600; }
.lp2-oss-chips span { background: var(--lp2-tile); border-radius: 7px; padding: 6px 10px; color: var(--lp2-body); }
.lp2-oss-chips .lp2-oss-chip--ok { background: var(--lp2-purple-bg); color: var(--lp2-purple); }

/* ── Sell & grow vignettes ── */
.lp2-cart { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; align-items: center; }
.lp2-cart-stage { position: relative; height: 114px; overflow: hidden; }
.lp2-cart-rows { display: flex; flex-direction: column; gap: 6px; animation: lp2CartGroup 8s ease infinite; }
.lp2-cart-row {
  display: flex; align-items: center; gap: 10px; background: #2a2138; border-radius: 9px;
  padding: 7px 12px; font-size: 12.5px; line-height: 1.3; color: #cfc6e0; white-space: nowrap; min-width: 0;
}
.lp2-cart-row strong { color: #fff; font-weight: 600; }
.lp2-cart-row--1 { animation: lp2CartRow1 8s ease infinite; }
.lp2-cart-row--2 { animation: lp2CartRow2 8s ease infinite; }
.lp2-cart-row--3 { animation: lp2CartRow3 8s ease infinite; }
.lp2-cart-dot { width: 7px; height: 7px; border-radius: 50%; background: #b7abcd; flex: 0 0 auto; }
.lp2-cart-dot--pulse { background: #e3c8fb; animation: lp2Blip 2s ease infinite; }
.lp2-cart-dot--ok { background: #7ee2a8; }
.lp2-cart-code { font-family: var(--lp2-mono); font-size: 11.5px; color: #e9d4fd; border: 1px dashed #a586cf; border-radius: 6px; padding: 1px 8px; }
.lp2-cart-won { color: #7ee2a8; font-weight: 600; }
.lp2-stamp {
  position: absolute; right: 10px; top: 50%; margin-top: -11px;
  border: 2px solid #7ee2a8; color: #7ee2a8; font-weight: 700; font-size: 10.5px;
  letter-spacing: .12em; padding: 3px 8px; border-radius: 7px; animation: lp2StampIn 8s ease infinite;
}

.lp2-cko { flex-direction: column; align-items: stretch; justify-content: center; gap: 6px; padding: 0 16px; }
.lp2-cko-fields { display: flex; gap: 6px; }
.lp2-cko-fields span { flex: 1; background: #fff; border: 1px solid var(--lp2-border2); border-radius: 7px; padding: 6px 10px; font-size: 11px; color: var(--lp2-soft); white-space: nowrap; overflow: hidden; }
.lp2-cko-pay {
  position: relative; background: var(--lp2-dark); color: #fff; font-weight: 600; font-size: 12.5px;
  padding: 8px; border-radius: 8px; overflow: hidden; text-align: center;
}
.lp2-cko-pay::after {
  content: ""; position: absolute; top: 0; left: 0; width: 40%; height: 100%;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,.22), transparent);
  animation: lp2Shimmer 3.2s ease-in-out -1.4s infinite;
}

.lp2-coupon {
  position: relative; overflow: hidden; background: #fff; border: 1.5px dashed #b98ae0;
  border-radius: 10px; padding: 10px 17px; font-family: var(--lp2-mono); font-size: 14px; color: var(--lp2-purple);
}
.lp2-coupon::after {
  content: ""; position: absolute; top: 0; left: 0; width: 40%; height: 100%;
  background: linear-gradient(100deg, transparent, rgba(159,80,223,.16), transparent);
  animation: lp2Shimmer 3.2s ease-in-out infinite;
}

.lp2-emails { flex-direction: column; align-items: stretch; justify-content: center; gap: 6px; padding: 0 14px; }
.lp2-emails > div { background: #fff; border: 1px solid var(--lp2-border2); border-radius: 7px; padding: 7px 11px; font-size: 11.5px; font-weight: 600; color: var(--lp2-body); white-space: nowrap; overflow: hidden; }
.lp2-email-live { color: var(--lp2-green); }
.lp2-email-draft { color: var(--lp2-soft); }

.lp2-banner-viz { padding: 0 14px; }
.lp2-banner-pill {
  background: var(--lp2-accent); color: #fff; border-radius: 9px; padding: 9px 14px;
  font-size: 12px; font-weight: 600; width: 100%; text-align: center; animation: lp2Floaty 4s ease infinite;
}

.lp2-seo { display: grid; grid-template-columns: 1.1fr 1fr; gap: 10px; }
.lp2-seo-pane { background: var(--lp2-tile); border-radius: 11px; padding: 14px 16px; display: flex; flex-direction: column; gap: 7px; min-width: 0; }
.lp2-seo-label { font-size: 10.5px; font-weight: 700; color: var(--lp2-faint); letter-spacing: .08em; text-transform: uppercase; }
.lp2-serp { background: #fff; border: 1px solid var(--lp2-border2); border-radius: 9px; padding: 11px 14px; display: flex; flex-direction: column; gap: 5px; }
.lp2-serp-title { font-size: 12.5px; font-weight: 600; color: var(--lp2-purple); }
.lp2-serp-url { font-size: 10.5px; color: var(--lp2-green); font-family: var(--lp2-mono); }
.lp2-serp-line { height: 4px; border-radius: 2px; background: #efeaf6; width: 85%; }
.lp2-serp-line--short { width: 60%; }
.lp2-seo-ok { font-size: 11px; font-weight: 600; color: var(--lp2-green); font-family: var(--lp2-mono); }
.lp2-seo-item { font-size: 12.5px; font-weight: 600; color: var(--lp2-body); }
.lp2-seo-good { color: var(--lp2-green); }
.lp2-seo-warn { color: #e9b23c; }
.lp2-seo-tip {
  align-self: flex-start; background: var(--lp2-purple-bg); color: var(--lp2-purple);
  border-radius: 7px; padding: 5px 9px; font-size: 11.5px; font-weight: 600; animation: lp2Floaty 4s ease infinite;
}

.lp2-speed { gap: 14px; }
.lp2-speed-gauge {
  width: 58px; height: 58px; border-radius: 50%; border: 4px solid var(--lp2-green);
  display: grid; place-items: center; font-weight: 700; font-size: 17px; color: var(--lp2-green); background: #fff;
}

.lp2-mobile { gap: 16px; }
.lp2-phone {
  width: 44px; height: 76px; border: 2px solid #cfc4e2; border-radius: 10px; background: #fff;
  padding: 7px 6px; display: flex; flex-direction: column; gap: 4px; animation: lp2Floaty 4s ease infinite;
}
.lp2-phone-hero { height: 14px; border-radius: 4px; background: var(--lp2-accent); opacity: .85; }
.lp2-phone-line { height: 5px; border-radius: 2px; background: #efeaf6; }
.lp2-phone-line--short { width: 70%; }
.lp2-phone-cta { margin-top: auto; height: 9px; border-radius: 4px; background: var(--lp2-dark); }

/* ── Feature ticker ── */
.lp2-ticker-caption { text-align: center; font-family: var(--lp2-mono); font-size: 12.5px; color: var(--lp2-faint); margin-bottom: 18px; }
.lp2-ticker-caption--bottom { margin: 18px 0 0; }
.lp2-ticker { overflow: hidden; }
.lp2-ticker + .lp2-ticker { margin-top: 12px; }
.lp2-ticker-row { display: flex; gap: 12px; width: max-content; animation: lp2Marq 32s linear infinite; }
.lp2-ticker-row--reverse { animation: lp2Marq 40s linear infinite reverse; }
.lp2-ticker:hover .lp2-ticker-row { animation-play-state: paused; }
.lp2-chip {
  background: var(--lp2-tile); border-radius: 12px; padding: 11px 17px;
  font-size: 13.5px; font-weight: 600; color: var(--lp2-body); white-space: nowrap;
}
.lp2-chip--purple { background: var(--lp2-purple-bg2); color: var(--lp2-purple); }
.lp2-chip--dark { background: var(--lp2-dark); color: #fff; }
.lp2-chip--soon { background: transparent; border: 1.5px dashed #cbb8e6; color: var(--lp2-purple); }
.lp2-soon-badge {
  background: var(--lp2-purple); color: #fff; font-size: 9.5px; font-weight: 700;
  padding: 3px 8px; border-radius: 999px; letter-spacing: .08em; margin-right: 9px;
  vertical-align: 1px; text-transform: uppercase;
}
.lp2-idea-link {
  border: 0; background: none; padding: 0; cursor: pointer;
  font-family: var(--lp2-mono); font-size: 12.5px; color: var(--lp2-purple);
  text-decoration: underline; text-underline-offset: 3px;
}
.lp2-idea-link:hover, .lp2-idea-link:focus { color: #5c2b8d; }
.lp2-demo-wrap { margin-top: 3rem; }
/* The demo-shop box uses the same veil shade as the alternate bands
   (matches the "Free forever" section right below it) and the same border
   color as the lp2 cards (var(--border) looked off next to them). */
.lp2-demo-wrap .landing-demo { background: rgba(255, 255, 255, .58); border-color: var(--lp2-border); }

/* ── Free forever ── */
.lp2-free-hero { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; align-items: center; }
.lp2-free-price { font-size: 32px; font-weight: 700; letter-spacing: -.02em; }
.lp2-free-per { font-size: 15px; color: #a99cc4; font-weight: 600; letter-spacing: 0; }
.lp2-free-checks { display: flex; flex-direction: column; gap: 7px; font-size: 13.5px; }
.lp2-check { color: #7ee2a8; }
.lp2-upgrade { height: 88px; gap: 12px; font-weight: 700; font-size: 16px; color: var(--lp2-ink); }
.lp2-upgrade-from { background: #fff; border: 1px solid var(--lp2-border2); border-radius: 9px; padding: 8px 13px; }
.lp2-upgrade-arrow { color: var(--lp2-purple); animation: lp2Floaty 3s ease infinite; }
.lp2-upgrade-to { background: var(--lp2-accent); color: #fff; border-radius: 9px; padding: 8px 13px; }
.lp2-swap { height: 88px; gap: 12px; font-weight: 600; font-size: 13px; color: var(--lp2-body); }
.lp2-swap-pill { background: #fff; border: 1px solid var(--lp2-border2); border-radius: 999px; padding: 8px 14px; }
.lp2-swap-arrows { color: var(--lp2-purple); font-size: 17px; }

/* ── Pricing ── */
.lp2-band--pricing .lp2-pricing-head { text-align: center; max-width: 760px; margin: 0 auto 36px; }
#plans { scroll-margin-top: calc(var(--navbar-height, 64px) + var(--announcement-bar-h, 0px) + 16px); }
/* overflow: clip (NOT hidden) rounds the bottom corners over the tinted Pro
   column while keeping the sticky header working — clip does not create a
   scroll container, hidden would. */
.lp2-pt { background: #fff; border: 1px solid var(--lp2-border); border-radius: 18px; overflow: clip; }
/* Sticky offset: the navbar's real rendered height is ~6px less than the
   --navbar-height var (measured 58 vs 64 on 2026-07-21), so tuck the header
   7px higher — otherwise rows peek through the gap while scrolling. The
   sticky lives on the WRAP (outside the mobile horizontal scroller) so it
   works against the page on every viewport; the head inside is translated
   with the body's scrollLeft by JS. */
.lp2-pt-headwrap {
  position: sticky; top: calc(var(--navbar-height, 64px) + var(--announcement-bar-h, 0px) - 7px); z-index: 5;
  background: #fff; border-bottom: 1px solid var(--lp2-border); overflow: clip;
}
/* Left-only row padding: the left inset disappears into the feature column's
   text, but a right inset would sit visibly AFTER the Max column and make it
   read wider than the other tiers — so the last track ends flush. */
.lp2-pt-head {
  display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr; padding: 0 0 0 20px;
  background: #fff; will-change: transform;
}
.lp2-pt-toggle-save.lp2-pt-toggle-save--short { display: none; }
.lp2-cta-short { display: none; }
/* Zero-lag header sync while the body scrolls horizontally on mobile:
   a scroll-driven animation runs on the compositor, so the head moves in
   the SAME frame as the table (the JS scroll listener stays as fallback
   for browsers without animation-timeline; JS sets --lp2-pt-maxscroll). */
@supports (animation-timeline: scroll()) {
  .lp2-pt { timeline-scope: --lp2ptx; }
  .lp2-pt-body { scroll-timeline-name: --lp2ptx; scroll-timeline-axis: x; }
  .lp2-pt-head { animation: lp2HeadSync linear both; animation-timeline: --lp2ptx; }
}
@keyframes lp2HeadSync {
  from { transform: translateX(0); }
  to { transform: translateX(calc(0px - var(--lp2-pt-maxscroll, 0px))); }
}
.lp2-pt-corner { padding: 16px 8px; display: flex; flex-direction: column; justify-content: space-between; align-items: flex-start; gap: 10px; }
.lp2-pt-toggle { display: inline-flex; align-items: center; background: #f4f1f9; border-radius: 999px; padding: 3px; gap: 2px; }
.lp2-pt-toggle-btn {
  border: 0; background: transparent; border-radius: 999px; padding: 6px 13px;
  font-size: 12px; font-weight: 600; color: var(--lp2-muted); cursor: pointer; white-space: nowrap;
}
.lp2-pt-toggle-btn.is-active { background: #fff; box-shadow: 0 1px 3px rgba(27,21,38,.12); font-weight: 700; color: var(--lp2-ink); }
.lp2-pt-toggle-save {
  color: #fff; font-weight: 700; font-size: 10px;
  background: #2e9e63; padding: 2px 7px; border-radius: 999px;
  display: inline-block; line-height: 1.4; vertical-align: middle;
  margin-left: 4px;
}
/* The badge's gap to the button's right edge matches its top/bottom gap
   (the button's vertical padding) instead of the full text padding. */
.lp2-pt-toggle-btn:has(.lp2-pt-toggle-save) { padding-right: 6px; }
.lp2-pt-corner-label { font-weight: 700; font-size: 16px; color: var(--lp2-ink); }
.lp2-pt-plan { padding: 16px 8px; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; gap: 4px; }
.lp2-pt-plan--pro { background: #f9f4fe; padding-top: 12px; }
.lp2-pt-popular {
  background: var(--lp2-accent); color: #fff; font-size: 10px; font-weight: 700;
  padding: 2px 8px; border-radius: 999px; letter-spacing: .05em; text-transform: uppercase;
}
.lp2-pt-plan-name { font-weight: 700; font-size: 17.5px; line-height: 1.2; color: var(--lp2-ink); }
.lp2-pt-price { font-size: 24px; line-height: 1.15; font-weight: 700; color: var(--lp2-ink); }
.lp2-pt-per { font-size: 12px; color: var(--lp2-faint); font-weight: 600; }
/* "billed as EUR 84.00/year" under the price. Deliberately NOT faint: the table
   defaults to the yearly tab, where the headline reads "EUR 7/mo" — a figure
   nobody can actually pay, since the real monthly price is EUR 9. This line is
   what makes the default view honest, so it has to be comfortably readable
   rather than technically present. Body colour, not the faint grey. */
.lp2-pt-note { font-size: 12px; line-height: 1.3; color: var(--lp2-body); font-weight: 600; min-height: 16px; text-align: center; }
.lp2-pt[data-mode="yearly"] .lp2-var--m { display: none; }
.lp2-pt[data-mode="monthly"] .lp2-var--y { display: none; }
.lp2-pt-cta {
  font-weight: 600; font-size: 12.5px; padding: 8px 16px; border-radius: 9px; margin-top: 5px;
  text-decoration: none; transition: filter .15s ease, background .15s ease;
}
.lp2-pt-cta--ghost { border: 1px solid #e0daea; color: var(--lp2-body); }
.lp2-pt-cta--ghost:hover { background: var(--lp2-tile); color: var(--lp2-ink); }
.lp2-pt-cta--accent { background: var(--lp2-accent); color: #fff; }
.lp2-pt-cta--dark { background: var(--lp2-dark); color: #fff; }
.lp2-pt-cta--accent:hover, .lp2-pt-cta--dark:hover { filter: brightness(1.12); color: #fff; }
/* Pro/Max CTAs while paid plans are not purchasable: original colours, dimmed,
   with the "not allowed" cursor and a native title tooltip carrying the reason.
   `filter: none !important` beats the --accent/--dark hover brightness rule. */
.lp2-pt-cta.is-disabled { opacity: .5; cursor: not-allowed; }
.lp2-pt-cta.is-disabled:hover { filter: none !important; }
.lp2-pt-group { display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr; padding: 0 0 0 20px; }
.lp2-pt-group-name { padding: 20px 8px 8px; font-size: 11px; font-weight: 700; color: var(--lp2-faint); letter-spacing: .1em; text-transform: uppercase; }
.lp2-pt-row {
  display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr; padding: 0 0 0 20px;
  border-top: 1px solid #f5f2f9; font-size: 13.5px; align-items: stretch;
}
.lp2-pt-feature { padding: 11px 8px; font-weight: 600; color: var(--lp2-ink); }
.lp2-pt-cell { padding: 11px 8px; text-align: center; color: var(--lp2-body); font-weight: 600; }
.lp2-pt-c--pro { background: #f9f4fe; }
.lp2-pt-unlimited { display: flex; justify-content: center; align-items: center; gap: 14px; margin-top: 4rem; flex-wrap: wrap; }
.lp2-pt-unlimited-q { font-size: 15.5px; font-weight: 600; color: var(--lp2-body); }
.lp2-pt-unlimited-btn {
  border: 1px solid #cbb8e6; color: var(--lp2-purple); font-weight: 600; font-size: 14px;
  padding: 10px 20px; border-radius: 10px; text-decoration: none;
  background: none; cursor: pointer; font-family: inherit;
}
.lp2-contact-hp { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }
.lp2-contact-email { margin-bottom: 10px; }
.lp2-pt-unlimited-btn:hover { background: var(--lp2-purple-bg2); color: var(--lp2-purple); }
.lp2-pt-fineprint { text-align: center; margin-top: 14px; font-family: var(--lp2-mono); font-size: 11.5px; color: var(--lp2-faint); }

/* ── Final CTA ──
   Just another alternate band (bg comes from .lp2-band--tint) with
   centered content. */
.lp2-final { padding: 56px 0; text-align: center; }
.lp2-final-title { margin: 0; font-size: 34px; font-weight: 700; letter-spacing: -.02em; color: var(--lp2-ink); }
.lp2-final-sub { margin: 14px 0 0; color: var(--lp2-muted); font-size: 15.5px; }
/* Only positioning — look/animation comes from .landing-btn-primary. */
.lp2-final-cta { margin-top: 18px; }

/* ── Platform contact inbox (/admin/emails, superadmin) ── */
.pce-row { display: flex; justify-content: space-between; gap: 16px; text-decoration: none; color: inherit; }
.pce-row-main { min-width: 0; }
.pce-row-top { display: flex; align-items: center; gap: 8px; }
.pce-row-email { font-weight: 600; font-size: 14px; }
.pce-row-subject { font-size: 13.5px; margin-top: 2px; }
.pce-row-preview { font-size: 12.5px; color: var(--muted); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 640px; }
.pce-row-meta { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; flex: 0 0 auto; font-size: 12px; color: var(--muted); }
.pce-chip { font-size: 10.5px; font-weight: 700; padding: 2px 8px; border-radius: 999px; text-transform: uppercase; letter-spacing: .04em; }
.pce-chip--due { background: rgba(159, 80, 223, .14); color: var(--primary-text, var(--primary)); }
.pce-chip--closed { background: var(--hover); color: var(--muted); }
.pce-back { color: var(--muted); text-decoration: none; }
.pce-back:hover { color: var(--fg); }
.pce-thread { display: flex; flex-direction: column; gap: 12px; max-width: 760px; }
.pce-msg { border: 1px solid var(--border); border-radius: 12px; padding: 12px 16px; background: var(--card); }
.pce-msg--outbound { border-color: color-mix(in srgb, var(--primary) 35%, var(--border)); background: color-mix(in srgb, var(--primary) 6%, var(--card)); margin-left: 40px; }
.pce-msg--inbound { margin-right: 40px; }
.pce-msg-head { display: flex; justify-content: space-between; gap: 12px; font-size: 12px; color: var(--muted); margin-bottom: 6px; }
.pce-msg-body { font-size: 13.5px; white-space: pre-wrap; word-break: break-word; }
/* Sanitized HTML mail. `white-space: pre-wrap` is wrong here — the markup already
   carries its own line breaks, and pre-wrap would preserve every newline in the
   source as a blank line. Marketing mail is built on wide fixed-width tables, so
   the container scrolls rather than stretching the admin layout. */
.pce-msg-body--html { white-space: normal; overflow-x: auto; }
.pce-msg-body--html table { width: 100% !important; max-width: 100% !important; border-collapse: collapse; }
.pce-msg-body--html td, .pce-msg-body--html th { padding: 2px 4px; }
.pce-msg-body--html p { margin: 0 0 .5rem; }
.pce-msg-body--html a { color: var(--primary-text, var(--primary)); text-decoration: underline; }
.pce-msg-body--html h1, .pce-msg-body--html h2, .pce-msg-body--html h3 { font-size: 1.05rem; margin: .6rem 0 .35rem; }
.pce-replybox { max-width: 760px; margin-top: 20px; }

/* ── Suggest-a-feature popup ── */
.lp2-modal {
  position: fixed; inset: 0; z-index: 5000; background: rgba(27,21,38,.55);
  display: flex; align-items: center; justify-content: center; padding: 16px;
}
.lp2-modal.d-none { display: none; }
.lp2-modal-inner {
  background: #fff; border-radius: 16px; width: 100%; max-width: 480px;
  box-shadow: 0 24px 64px rgba(27,21,38,.28); overflow: hidden;
}
.lp2-modal-header {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 14px 20px; border-bottom: 1px solid var(--lp2-border); color: var(--lp2-ink);
}
.lp2-modal-header .fa-lightbulb { color: var(--lp2-accent); }
.lp2-modal-close {
  border: 0; background: none; font-size: 24px; line-height: 1; color: var(--lp2-faint);
  cursor: pointer; padding: 0 2px;
}
.lp2-modal-close:hover { color: var(--lp2-ink); }
.lp2-modal-body { padding: 18px 20px 20px; }
.lp2-modal-desc { font-size: 13.5px; color: var(--lp2-muted); margin: 0 0 12px; }
/* Links in the modal copy (the mailto address) read in the text's own color,
   just underlined — the default link blue is barely readable on the dark modal. */
.lp2-modal-desc a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.lp2-modal-desc a:hover { color: var(--lp2-ink); }
.lp2-modal-success { background: #e8f7ee; color: #1c7a47; border-radius: 9px; padding: 10px 14px; font-size: 13.5px; font-weight: 600; }
.lp2-modal-error { color: #b3261e; font-size: 13px; margin-top: 8px; }
.lp2-modal-actions { display: flex; justify-content: flex-end; margin-top: 12px; }
.lp2-modal-submit {
  border: 0; background: var(--lp2-accent); color: #fff; font-weight: 600; font-size: 13.5px;
  padding: 9px 18px; border-radius: 9px; cursor: pointer; transition: filter .15s ease;
}
.lp2-modal-submit:hover { filter: brightness(1.12); }
.lp2-modal-submit:disabled { opacity: .6; cursor: default; }

/* ── Responsive ── */
@media (max-width: 991.98px) {
  .lp2-band { padding: 3rem 0; }
  .lp2-grid, .lp2-grid--flip { grid-template-columns: 1fr; gap: 28px; }
  .lp2-head { position: static; }
  .lp2-grid--flip .lp2-head { order: 0; }
  .lp2-title { font-size: 28px; }

  /* Pricing table, compact: fixed narrow columns so the feature column and
     the Free tier fit a phone viewport together; the body scrolls
     horizontally for Pro/Max while the headwrap stays page-sticky. */
  .lp2-pt-body { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .lp2-pt-rows { min-width: 565px; }
  .lp2-pt-head { min-width: 565px; }
  /* minmax: columns hit their minimums on phones (scroll for the rest) but
     grow to fill the row on wider viewports — no dead slack after Max. */
  .lp2-pt-head, .lp2-pt-group, .lp2-pt-row { grid-template-columns: minmax(175px, 1.4fr) repeat(3, minmax(126px, 1fr)); padding: 0 0 0 12px; }
  .lp2-pt-toggle-save.lp2-pt-toggle-save--full { display: none; }
  .lp2-pt-toggle-save.lp2-pt-toggle-save--short { display: inline-block; }
  .lp2-pt-toggle-btn { padding: 5px 9px; font-size: 11px; }
  .lp2-pt-toggle-btn:has(.lp2-pt-toggle-save) { padding-right: 5px; }
  .lp2-pt-corner { padding: 12px 6px; }
  .lp2-pt-corner-label { font-size: 14px; }
  .lp2-pt-plan { padding: 12px 6px; }
  .lp2-pt-plan-name { font-size: 15px; }
  .lp2-pt-price { font-size: 20px; }
  /* Still the smallest text in the card, but not so small it stops being a real
     disclosure. See the note on .lp2-pt-note above. */
  .lp2-pt-note { font-size: 11px; }
  .lp2-pt-cta { padding: 6px 10px; font-size: 11.5px; }
  .lp2-pt-row { font-size: 12.5px; }
  .lp2-pt-feature { padding: 9px 6px; }
  .lp2-pt-cell { padding: 9px 6px; }
  .lp2-pt-group-name { padding: 16px 6px 6px; font-size: 10px; }
}
@media (max-width: 767.98px) {
  /* Full-bleed pricing table: no side gaps on phones. */
  .lp2-pt {
    margin-left: calc(50% - 50vw);
    margin-right: calc(50% - 50vw);
    border-radius: 0;
    border-left: 0;
    border-right: 0;
  }
}
@media (max-width: 576px) {
  /* Hero copy is centered on phones — center its eyebrow pill too. */
  .landing-hero-copy .lp2-eyebrow { align-self: center; }
  /* No Shopiza S badge in the footer on phones. */
  footer .az-brand-badge { display: none; }
  /* Center all section texts (and their pills) on phones. The bullet lists
     center as a block but keep their own text left-aligned. */
  .lp2-head { text-align: center; }
  .landing-spotlight [class*="col-lg-5"] { text-align: center; }
  .landing-spotlight .landing-bullets { display: inline-block; text-align: left; }
  /* Wider side gutters for the TEXT parts only (heads, spotlight copy,
     lists, ticker captions) — boxes/vignettes/demos keep their size. */
  .lp2-head,
  .lp2-pricing-head,
  .landing-spotlight [class*="col-lg-5"] { padding-left: 28px; padding-right: 28px; }
  .lp2-ticker-caption { padding: 0 28px; }
  /* Pricing Free CTA: short label so the button stays one line. */
  .lp2-pt .lp2-cta-full { display: none; }
  .lp2-pt .lp2-cta-short { display: inline; }
  .lp2-pt-cta { white-space: nowrap; }
  /* Demo-shop box: centered contents on phones. */
  .lp2-demo-wrap .landing-demo { align-items: center; text-align: center; }
}
@media (max-width: 575.98px) {
  .lp2-bento { grid-template-columns: 1fr; }
  .lp2-card--wide { grid-column: auto; }
  .lp2-analytics, .lp2-cart, .lp2-oss, .lp2-free-hero, .lp2-seo { grid-template-columns: 1fr; }
  .lp2-oss-chips { justify-content: flex-start; }
  .lp2-title { font-size: 24px; }
  .lp2-final-title { font-size: 28px; }
  .lp2-strip { flex-direction: row; }
}

/* The final dark CTA band sits flush on the footer border: on the landing,
   kill main's bottom margin (my-4) and the footer's top margin (mt-5) so no
   page-background strip shows between the band and the footer. */
body.is-landing-page main.container { margin-bottom: 0 !important; }
body.is-landing-page footer { margin-top: 0 !important; }

/* ── lp2 dark mode ──
   The palette flips via the --lp2-* vars; below that, only the hardcoded
   light backgrounds (white cards/panels, pills, chips, pricing surfaces,
   modal) get explicit dark values. Band veils stay translucent so the dark
   gradient + dot pattern show through, same as light mode. */
[data-theme="dark"] .lp2-band,
[data-theme="dark"] .lp2-veil,
[data-theme="dark"] .lp2-modal {
  --lp2-ink: #efeaf8;
  --lp2-muted: #a79dbd;
  --lp2-faint: #837a99;
  --lp2-soft: #948aa8;
  --lp2-body: #d4cbe6;
  --lp2-border: rgba(255, 255, 255, .09);
  --lp2-border2: rgba(255, 255, 255, .13);
  --lp2-tile: rgba(255, 255, 255, .05);
  --lp2-purple: #cfa9f2;
  --lp2-purple-bg: rgba(159, 80, 223, .20);
  --lp2-purple-bg2: rgba(159, 80, 223, .15);
  --lp2-green: #5fd598;
  --lp2-langhi-bg: rgba(159, 80, 223, .28);
  --lp2-langhi-fg: #e3ccfa;
}
/* Two-shade alternation in dark: normal = raw dark gradient (transparent),
   alternate = one darker veil. */
[data-theme="dark"] .lp2-band { background: transparent; }
[data-theme="dark"] .lp2-band--tint { background: rgba(8, 5, 18, .42); }
[data-theme="dark"] .lp2-veil--tint::before { background: rgba(8, 5, 18, .42); }

/* Cards & panels */
[data-theme="dark"] .lp2-card { background: rgba(24, 17, 40, .85); }
[data-theme="dark"] .lp2-card--dark { background: #221a38; }
[data-theme="dark"] .lp2-order-row,
[data-theme="dark"] .lp2-csv-file,
[data-theme="dark"] .lp2-csv-count,
[data-theme="dark"] .lp2-cko-fields span,
[data-theme="dark"] .lp2-emails > div,
[data-theme="dark"] .lp2-serp,
[data-theme="dark"] .lp2-domain-pill,
[data-theme="dark"] .lp2-upgrade-from,
[data-theme="dark"] .lp2-swap-pill,
[data-theme="dark"] .lp2-speed-gauge,
[data-theme="dark"] .lp2-coupon { background: #241b3d; }
[data-theme="dark"] .lp2-coupon { border-color: rgba(185, 138, 224, .55); }
[data-theme="dark"] .lp2-cko-pay { background: #0c0716; border: 1px solid rgba(255, 255, 255, .14); }
[data-theme="dark"] .lp2-serp-line { background: rgba(255, 255, 255, .12); }
[data-theme="dark"] .lp2-phone { background: #241b3d; border-color: rgba(207, 196, 226, .35); }
[data-theme="dark"] .lp2-phone-line { background: rgba(255, 255, 255, .14); }
[data-theme="dark"] .lp2-phone-cta { background: rgba(255, 255, 255, .28); }

/* Eyebrow pills */
[data-theme="dark"] .lp2-eyebrow { background: rgba(159, 80, 223, .24); color: #d9bdf7; }

/* Ticker */
[data-theme="dark"] .lp2-chip--dark { background: #efe9f8; color: #1b1526; }
[data-theme="dark"] .lp2-chip--soon { border-color: rgba(176, 124, 232, .45); }
[data-theme="dark"] .lp2-soon-badge { background: #8d55c8; color: #fff; }

/* Demo-shop box matches the dark alternate band */
[data-theme="dark"] .lp2-demo-wrap .landing-demo { background: rgba(8, 5, 18, .42); }

/* Pricing table */
[data-theme="dark"] .lp2-pt { background: rgba(23, 16, 38, .92); }
[data-theme="dark"] .lp2-pt-headwrap,
[data-theme="dark"] .lp2-pt-head { background: #191129; }
[data-theme="dark"] .lp2-pt-plan--pro,
[data-theme="dark"] .lp2-pt-c--pro { background: rgba(159, 80, 223, .10); }
[data-theme="dark"] .lp2-pt-toggle { background: rgba(255, 255, 255, .08); }
[data-theme="dark"] .lp2-pt-toggle-btn.is-active { background: #55447a; box-shadow: 0 1px 3px rgba(0, 0, 0, .4); color: #fff; }
[data-theme="dark"] .lp2-pt-row { border-top-color: rgba(255, 255, 255, .06); }
[data-theme="dark"] .lp2-pt-cta--ghost { border-color: rgba(255, 255, 255, .18); }
[data-theme="dark"] .lp2-pt-cta--dark { background: #efe9f8; color: #1b1526; }
[data-theme="dark"] .lp2-pt-cta--dark:hover { color: #1b1526; filter: brightness(.94); }
[data-theme="dark"] .lp2-pt-unlimited-btn { border-color: rgba(176, 124, 232, .40); }

/* Suggest popup */
[data-theme="dark"] .lp2-modal { background: rgba(0, 0, 0, .65); }
[data-theme="dark"] .lp2-modal-inner { background: #1e1631; }
[data-theme="dark"] .lp2-modal-success { background: rgba(46, 158, 99, .16); color: #7ee2a8; }
[data-theme="dark"] .lp2-modal-error { color: #f3a49e; }


/* ── Drag-to-reorder rows (admin list tables) ──
   The "#" column: a 2x3 dot grip that reads as draggable, and a row that
   lifts while it is being moved. Deliberately low-contrast until hover so a
   long table doesn't turn into a field of dots. */
/* The "#" header and the grip below it share this class and are both inline
   content, so centring the column lines the symbol up directly over the dots
   instead of leaving it hanging at the cell's left padding. */
.attr-order-col { width: 38px; text-align: center; padding-right: 0.25rem !important; padding-left: 0.25rem !important; }
.attr-order-col .sort-th { justify-content: center; }
.attr-drag-handle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 2px 4px;
  border-radius: 4px;
  color: var(--az-text-muted, #8a8a8a);
  opacity: 0.5;
  cursor: grab;
  touch-action: none;   /* let the pointer handler own vertical drags */
  transition: opacity 0.12s ease, background 0.12s ease;
}
tr:hover .attr-drag-handle { opacity: 1; }
.attr-drag-handle:hover { background: var(--az-surface-muted, rgba(0,0,0,0.05)); }
.attr-drag-handle:focus-visible { outline: 2px solid var(--primary, #9f50df); outline-offset: 1px; opacity: 1; }
.attr-drag-handle:active { cursor: grabbing; }
tr.is-row-dragging {
  background: var(--az-surface-muted, rgba(0,0,0,0.04));
  box-shadow: 0 6px 16px rgba(0,0,0,0.14);
  position: relative;
  z-index: 2;
}
[data-theme="dark"] tr.is-row-dragging { box-shadow: 0 6px 16px rgba(0,0,0,0.5); }
/* The manual column shows no arrow — there is no direction to a custom order. */
th[data-sort-manual] .sort-ind { display: none; }
th[data-sort-manual].is-active .sort-th { color: var(--primary-text, var(--primary)); }


/* Filter value that would return nothing with the current selection. Greyed
   rather than removed: a missing option reads as "not stocked here", a visible
   (0) reads as "not with what you picked" — only the second is recoverable.
   A CHECKED value is never disabled, so a visitor can always undo their way
   out of a zero-result selection. */
.shop-filter-options .form-check.is-unavailable { opacity: 0.7; }
.shop-filter-options .form-check.is-unavailable .form-check-label { cursor: not-allowed; }
/* The grid dims slightly while a burst of filter clicks settles, so the page
   shows it heard you without yanking the results away mid-choice. */
#shopProductGrid.is-filter-pending { opacity: 0.65; transition: opacity 0.15s ease; }


/* Light mode: the filter checkboxes shipped with Bootstrap's #dee2e6 border,
   which nearly disappears against the white sidebar card, so an empty box read
   as absent rather than unticked. 20% darker (#b2b5b8) gives the outline
   enough weight to scan down a list of them. Dark mode is left alone: the same
   border already sits against a dark panel and reads fine. */
:root:not([data-theme="dark"]) #shopFilters .form-check-input:not(:checked) {
  border-color: #b2b5b8;
}

/* ─────────────────────────────────────────────────────────────
   Marketing "Tudástár" (guides) + localized product landing pages.
   Public pages on shopiza.io (views/tudastar/*). Themed via the
   existing --primary / --fg / --muted / --border / --card vars, so
   dark mode works with no extra rules.
   ───────────────────────────────────────────────────────────── */
.guide-page { padding: 32px 0 80px; }
.guide-breadcrumb { display: flex; gap: 8px; align-items: center; font-size: 13px; color: var(--muted); margin: 4px 0 24px; }
.guide-breadcrumb a { color: var(--muted); text-decoration: none; }
.guide-breadcrumb a:hover { color: var(--fg); }

.guide-article { max-width: 760px; margin: 0 auto; }
.guide-article h1 { font-weight: 800; letter-spacing: -0.02em; line-height: 1.1; margin: 0 0 0.5em; font-size: clamp(28px, 4vw, 40px); }
.guide-article h2 { font-weight: 700; letter-spacing: -0.01em; margin: 1.7em 0 0.4em; font-size: 1.55rem; }
.guide-article h3 { font-weight: 700; margin: 1.3em 0 0.3em; font-size: 1.15rem; }
.guide-article p, .guide-article li { font-size: 1.05rem; line-height: 1.72; color: var(--fg); }
.guide-article ul, .guide-article ol { padding-left: 1.3em; margin: 0 0 1em; }
.guide-article li { margin: 6px 0; }
.guide-article a { color: var(--primary, #9f50df); text-underline-offset: 2px; }

.guide-lede { font-size: 1.22rem; line-height: 1.6; color: var(--muted); margin: 0 0 1.1em; }
.guide-note { font-size: 0.92rem; color: var(--muted); }
.guide-callout { background: var(--hover, #f3f4f6); border-radius: 12px; padding: 16px 18px; margin: 22px 0; }
.guide-callout p { font-size: 1rem; }

.guide-cta { display: inline-block; background: var(--primary, #9f50df); color: var(--primary-fg, #fff); padding: 12px 24px; border-radius: 10px; font-weight: 600; text-decoration: none; border: 1px solid transparent; }
.guide-cta:hover { filter: brightness(1.05); color: var(--primary-fg, #fff); }
.guide-cta--ghost { background: transparent; color: var(--fg); border-color: var(--border, #e5e7eb); }
.guide-cta--ghost:hover { color: var(--fg); border-color: var(--primary, #9f50df); }
/* CTA sits inside .guide-article, whose `a { color: var(--primary) }` rule
   otherwise wins and paints the label purple-on-purple (only readable on
   hover). Re-assert the button's own foreground with higher specificity. */
.guide-article a.guide-cta, .guide-article a.guide-cta:hover { color: var(--primary-fg, #fff); }
.guide-article a.guide-cta--ghost { color: var(--fg); }

/* Fixed layout + 100% width so the multi-column tables always fit the reading
   column — no horizontal scrollbar. Cells wrap instead. */
.guide-table-scroll { margin: 18px 0; }
.guide-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; table-layout: fixed; }
.guide-table th, .guide-table td { border-bottom: 1px solid var(--border, #e5e7eb); padding: 9px 10px; text-align: left; vertical-align: top; word-break: break-word; }
.guide-table th:first-child, .guide-table td:first-child { width: 26%; }
.guide-table thead th { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.03em; color: var(--muted); }

/* Hub */
.guide-hub h1 { font-weight: 800; letter-spacing: -0.02em; font-size: clamp(26px, 4vw, 36px); margin: 0 0 0.3em; }
.guide-hub-list { display: grid; gap: 14px; margin-top: 22px; }
.guide-hub-card { display: block; border: 1px solid var(--border, #e5e7eb); border-radius: 14px; padding: 18px 20px; text-decoration: none; color: inherit; background: var(--card, #fff); }
.guide-hub-card:hover { border-color: var(--primary, #9f50df); }
.guide-hub-card h3 { margin: 0 0 4px; font-size: 1.1rem; font-weight: 700; }
.guide-hub-card p { margin: 0; color: var(--muted); font-size: 0.95rem; }

/* Landing */
.guide-page--landing { padding-top: 12px; }
.guide-hero { text-align: center; max-width: 780px; margin: 0 auto; padding: 40px 0 8px; }
.guide-eyebrow { font-size: 12px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: var(--primary, #9f50df); margin: 0 0 10px; }
.guide-hero h1 { font-weight: 800; letter-spacing: -0.02em; line-height: 1.08; font-size: clamp(30px, 5vw, 48px); margin: 0 0 0.35em; }
.guide-hero .lead { font-size: 1.25rem; line-height: 1.5; color: var(--muted); margin: 0 auto 1.2em; max-width: 620px; }
.guide-hero-cta { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }

.guide-features { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 18px; margin: 40px auto; max-width: 1000px; }
.guide-feature { background: var(--card, #fff); border: 1px solid var(--border, #e5e7eb); border-radius: 14px; padding: 22px; }
.guide-feature h3 { font-size: 1.05rem; font-weight: 700; margin: 0 0 6px; }
.guide-feature p { color: var(--muted); margin: 0; font-size: 0.98rem; line-height: 1.5; }

.guide-band, .guide-steps, .guide-faq { max-width: 760px; margin: 40px auto; }
.guide-band h2, .guide-steps h2, .guide-faq h2 { font-weight: 700; font-size: 1.5rem; margin: 0 0 0.5em; }
.guide-why { list-style: none; padding: 0; display: grid; gap: 10px; }
.guide-why li { font-size: 1.05rem; }
.guide-steps ol { padding-left: 1.2em; }
.guide-steps li { font-size: 1.05rem; margin: 8px 0; }
.guide-faq h3 { font-size: 1.08rem; font-weight: 700; margin: 1.1em 0 0.2em; }
.guide-faq p { color: var(--fg); }
.guide-final-cta { text-align: center; max-width: 640px; margin: 56px auto 0; padding: 40px 24px; border: 1px solid var(--border, #e5e7eb); border-radius: 18px; background: var(--card, #fff); }
.guide-final-cta h2 { font-weight: 800; letter-spacing: -0.01em; margin: 0 0 0.6em; }

/* ─── Custom-plan pricing sheet (/admin/pricing-sheet, superadmin) ───
   Doubles as the printable PDF: the browser's "Save as PDF" is the generator,
   so there is no headless-Chromium dependency on the server for one page. */
.psheet { background: var(--card); border: 1px solid var(--border); border-radius: 12px; }
.psheet-inner { max-width: 820px; margin: 0 auto; padding: 40px 44px 48px; }
.psheet-head { border-bottom: 2px solid var(--border); padding-bottom: 14px; margin-bottom: 22px; }
.psheet-head h1 { font-size: 1.45rem; font-weight: 700; margin: 0 0 6px; }
.psheet-meta { font-size: .82rem; color: var(--muted); margin: 0; line-height: 1.5; }
.psheet section { margin-bottom: 26px; }
.psheet h2 { font-size: 1.1rem; font-weight: 700; margin: 0 0 10px; }
.psheet h3 { font-size: .95rem; font-weight: 700; margin: 18px 0 7px; }
.psheet p, .psheet li { font-size: .875rem; line-height: 1.6; }
.psheet ul { padding-left: 20px; margin-bottom: 10px; }
.psheet-price { font-size: 1rem !important; }
.psheet-note { font-size: .8rem !important; color: var(--muted); }
.psheet-sub { color: var(--muted); font-size: .78rem; }
.psheet-table { width: 100%; border-collapse: collapse; margin: 10px 0 12px; font-size: .82rem; }
.psheet-table th, .psheet-table td { border: 1px solid var(--border); padding: 6px 9px; text-align: left; }
.psheet-table thead th { background: var(--bg); font-weight: 700; }
.psheet-table--narrow { max-width: 460px; }
.psheet-foot { border-top: 1px solid var(--border); padding-top: 12px; font-size: .78rem; color: var(--muted); line-height: 1.6; }

@media print {
  /* Strip the admin shell so the sheet prints as a standalone document. */
  .az-sidebar, .az-topbar, .psheet-toolbar, footer,
  [data-az-sidebar], .admin-shell__sidebar, .admin-shell__topbar { display: none !important; }
  .admin-shell, .admin-main, main, body { margin: 0 !important; padding: 0 !important; background: #fff !important; }
  /* THE reason a long sheet printed as a single clipped page. The admin shell is
     a fixed-viewport layout: `body { overflow: hidden }`, `#main-content
     { height: 100vh }`, `.admin-shell { height: 100dvh }`, with .admin-main
     scrolling internally. Printing renders only what fits inside that fixed box,
     so everything below the fold is discarded rather than flowing onto page 2.
     Unwind the whole chain so the document is a normal, arbitrarily tall block.
     `display: block` matters too: the shell is a flex row, and flex children with
     constrained heights clip instead of paginating. */
  html, body,
  #main-content,
  .admin-shell[data-admin-shell],
  .admin-main-col,
  .admin-main {
    height: auto !important;
    min-height: 0 !important;
    max-height: none !important;
    overflow: visible !important;
    display: block !important;
    position: static !important;
  }
  .psheet { border: 0 !important; border-radius: 0 !important; background: #fff !important; box-shadow: none !important; }
  .psheet-inner { max-width: none; padding: 0; }
  /* Force readable black-on-white regardless of the admin's dark mode. */
  .psheet, .psheet * { color: #000 !important; }
  .psheet-meta, .psheet-note, .psheet-sub, .psheet-foot { color: #444 !important; }
  .psheet-table th, .psheet-table td { border-color: #999 !important; }
  .psheet-table thead th { background: #f2f2f2 !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  /* Don't split a heading from the text it introduces, or a table across pages. */
  .psheet h2, .psheet h3 { break-after: avoid; page-break-after: avoid; }
  .psheet-table { break-inside: avoid; page-break-inside: avoid; }
  @page { margin: 16mm; }
}

/* Contact-inbox compose panel + attachment inputs (superadmin /admin/emails). */
.pce-compose { border: 1px solid var(--border); border-radius: 12px; background: var(--card); padding: 16px 18px; margin-bottom: 18px; }
.pce-file { max-width: 320px; }
.pce-msg-atts { margin-top: 8px; display: flex; flex-wrap: wrap; gap: 8px; }
.pce-att { font-size: .78rem; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; padding: 3px 8px; }
.pce-att i { opacity: .6; margin-right: 3px; }
.pce-row-thread-of { font-size: .72rem; color: var(--muted); margin-top: 1px; }
.pce-msg-subject { font-weight: 700; font-size: .88rem; margin-bottom: 6px; }

/* Unread threads in the superadmin contact inbox. Bold subject + a dot, and
   they sort to the top (see listThreads) regardless of age. */
.pce-row--unread .pce-row-subject { font-weight: 700; }
.pce-row--unread .pce-row-email { font-weight: 600; }
.pce-unread-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--primary-solid, var(--primary)); display: inline-block; flex: 0 0 8px; }

/* Visitor context strip on a contact thread (form-created threads only). */
.pce-visitor-meta { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.pce-vm { font-size: .78rem; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; padding: 3px 9px; }
.pce-vm-label { color: var(--muted); }

/* ══════════════════════════════════════════════════════════════════════════
   Post-checkout thank-you page (.typ-*)
   Rendered by views/partials/shop/_order_placed.ejs at /checkout?ok=placed.
   Visual language follows the landing page's checkout demo: an animated tick,
   a map of the delivery address, and the order summary beside it.
   ══════════════════════════════════════════════════════════════════════════ */
/* Full content width, matching the checkout form this page replaces — no
   max-width of its own, so the page lands on the same right edge as the
   header's "Secure checkout" badges. */
.typ { display: block; --typ-ok: #16a34a; --typ-ok-fg: #15803d; }
[data-theme="dark"] .typ { --typ-ok: #22c55e; --typ-ok-fg: #4ade80; }

/* ─── Reassurance banner + ticket-stub order number ─── */
.typ-banner {
  display: flex;
  align-items: stretch;
  border: 1px solid color-mix(in srgb, var(--typ-ok) 32%, transparent);
  border-radius: 12px;
  background: color-mix(in srgb, var(--typ-ok) 8%, var(--card));
  overflow: hidden;
  margin-bottom: 0.85rem;
}
.typ-banner-main {
  flex: 1 1 auto; min-width: 0;
  display: flex; align-items: center; gap: 0.85rem;
  padding: 1rem 1.15rem;
}
.typ-banner-text { min-width: 0; }
/* The stub is torn off the banner along a dashed rule, like a ticket. */
.typ-stub {
  flex: 0 0 auto;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 2px;
  padding: 0.9rem 1.5rem;
  border-left: 2px dashed color-mix(in srgb, var(--typ-ok) 45%, transparent);
}
.typ-stub-k {
  font-size: 0.6875rem; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--typ-ok-fg);
}
.typ-stub-v { font-size: 1.5rem; font-weight: 800; letter-spacing: .1em; color: var(--fg); }

.typ-check { display: inline-block; flex: 0 0 auto; }
.typ-check svg { width: 64px; height: 64px; display: block; }
.typ-check-ring {
  fill: none; stroke: #16a34a; stroke-width: 3;
  stroke-dasharray: 145; stroke-dashoffset: 145;
  animation: typDraw .55s ease forwards;
}
.typ-check-tick {
  fill: none; stroke: #16a34a; stroke-width: 4.5;
  stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 44; stroke-dashoffset: 44;
  animation: typDraw .4s .42s ease forwards;
}
.typ-check { animation: typPop .5s cubic-bezier(.34, 1.56, .64, 1); }
@keyframes typDraw { to { stroke-dashoffset: 0; } }
@keyframes typPop { 0% { transform: scale(.6); opacity: 0; } 100% { transform: scale(1); opacity: 1; } }

.typ-title { font-size: 1.5rem; font-weight: 800; color: var(--fg); margin: 0; line-height: 1.15; }
.typ-sub { font-size: 0.875rem; color: var(--muted); margin: 0.3rem 0 0; }
.typ-mail { font-size: 0.8125rem; color: var(--muted); margin: 0.2rem 0 0; }
.typ-mail i { opacity: .65; margin-right: .3rem; }
.typ-demo-cta { margin-top: 0.85rem; }

/* Banner text fades up in sequence behind the tick. */
.typ-title, .typ-sub, .typ-mail, .typ-demo-cta, .typ-stub { animation: typRise .45s ease both; }
.typ-title { animation-delay: .25s; }
.typ-sub { animation-delay: .33s; }
.typ-mail { animation-delay: .41s; }
.typ-stub { animation-delay: .45s; }
.typ-demo-cta { animation-delay: .53s; }
@keyframes typRise { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

/* ─── Dashboard grid ─── */
/* Mobile first: one column, ordered where → when/how → what. From sm the two
   stat tiles pair up; from lg the map takes a tall left column spanning both
   rows and the summary fills the row under the stats (the wireframe's
   1.4fr 1fr 1fr). */
.typ-grid { display: grid; grid-template-columns: 1fr; gap: 0.85rem; align-items: start; }
@media (min-width: 576px) {
  .typ-grid { grid-template-columns: 1fr 1fr; }
  .typ-tile-map, .typ-sum, .typ-tile-notes { grid-column: 1 / -1; }
}
@media (min-width: 992px) {
  /* Map takes half the content width; the two stat tiles split the rest. */
  .typ-grid { grid-template-columns: 2fr 1fr 1fr; align-items: stretch; }
  .typ-tile-map { grid-column: 1; grid-row: 1 / 3; }
  .typ-sum { grid-column: 2 / 4; grid-row: 2; }
  .typ-tile-notes { grid-column: 1 / -1; }
  /* No address to map: the stats and summary share a plain two-column grid. */
  .typ-grid--nomap { grid-template-columns: 1fr 1fr; }
  .typ-grid--nomap .typ-sum { grid-column: 1 / -1; grid-row: auto; }
}

/* ─── Cards ─── */
.typ-card {
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--card);
  padding: 1rem 1.1rem;
  /* Gaps come from the grid, not from the cards. */
  margin-bottom: 0;
  /* The banner owns the opening beat; the tiles follow it in. */
  animation: typRise .45s .55s ease both;
}
.typ-card-h {
  font-size: 0.8125rem; font-weight: 700; color: var(--fg);
  text-transform: uppercase; letter-spacing: .04em;
  margin-bottom: 0.75rem;
}
.typ-card-h i { color: var(--primary-text, var(--primary)); margin-right: .35rem; }
.typ-sum-n { color: var(--muted); font-weight: 400; }

/* ─── Delivery / payment stat tiles ─── */
/* Lead with the one fact that matters, detail underneath. */
.typ-stat { display: flex; flex-direction: column; gap: 0.3rem; }
.typ-stat-k {
  font-size: 0.8125rem; font-weight: 700; color: var(--muted);
  text-transform: uppercase; letter-spacing: .04em;
}
.typ-stat-k i { color: var(--primary-text, var(--primary)); margin-right: .35rem; opacity: .8; }
.typ-stat-v { font-size: 1.0625rem; font-weight: 700; color: var(--fg); line-height: 1.25; }
.typ-stat-s { font-size: 0.75rem; color: var(--muted); }

/* ─── Map tile ─── */
/* The map fills the tile edge to edge, so the card carries no padding of its
   own. On lg it stretches to match the stats + summary stacked beside it. */
.typ-tile-map { padding: 0; overflow: hidden; display: flex; flex-direction: column; }
.typ-map {
  position: relative;
  height: 240px;
  overflow: hidden;
  background: var(--bg);
}
@media (min-width: 992px) {
  .typ-map { height: auto; flex: 1 1 auto; min-height: 240px; }
}
.typ-map-art { width: 100%; height: 100%; display: block; }
.typ-map iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
/* Once the real embed is in, the drawn stand-in and its overlays step aside. */
.typ-map--live .typ-map-art,
.typ-map--live .typ-pin,
.typ-map--live .typ-map-card { display: none; }

.typ-map-ground { fill: #e9eef2; }
.typ-map-park { fill: #dfe9e2; }
.typ-map-water { fill: #dbe8ef; }
.typ-map-road { stroke: #ffffff; stroke-width: 9; stroke-linecap: round; fill: none; }
.typ-map-lane { stroke: #f3f6f8; stroke-width: 4; fill: none; }
.typ-map-blocks { fill: #d7dfe5; }
.typ-map-route {
  stroke: var(--primary-solid, var(--primary));
  stroke-width: 4; stroke-linecap: round; stroke-linejoin: round;
  opacity: .55;
  stroke-dasharray: 300; stroke-dashoffset: 300;
  animation: typRoute 1.1s .7s ease forwards;
}
@keyframes typRoute { to { stroke-dashoffset: 0; } }

[data-theme="dark"] .typ-map-ground { fill: #232833; }
[data-theme="dark"] .typ-map-park { fill: #26302a; }
[data-theme="dark"] .typ-map-water { fill: #202c36; }
[data-theme="dark"] .typ-map-road { stroke: #39404d; }
[data-theme="dark"] .typ-map-lane { stroke: #2f3540; }
[data-theme="dark"] .typ-map-blocks { fill: #2b313c; }

.typ-pin {
  position: absolute; left: 48.5%; top: 47%;
  font-size: 30px; color: var(--primary-solid, var(--primary));
  opacity: 0; transform: translate(-50%, -190%) scale(.6);
  filter: drop-shadow(0 3px 5px rgba(16, 24, 40, .35));
  animation: typPin .6s 1.5s cubic-bezier(.34, 1.56, .64, 1) forwards;
}
.typ-pin::after {
  content: ""; position: absolute; left: 50%; bottom: -4px;
  width: 26px; height: 9px; border-radius: 50%;
  background: color-mix(in srgb, var(--primary-solid, var(--primary)) 30%, transparent);
  transform: translateX(-50%);
}
@keyframes typPin { to { opacity: 1; transform: translate(-50%, -100%) scale(1); } }

/* Click-to-load: without marketing consent the Google embed only loads when
   the customer asks for it, so no third party sees the address by default. */
.typ-map-load {
  position: absolute; right: 12px; top: 12px;
  display: flex; flex-direction: column; align-items: flex-start; gap: 1px;
  background: var(--card); color: var(--fg);
  border: 1px solid var(--border); border-radius: 8px;
  padding: 7px 11px; cursor: pointer;
  box-shadow: 0 6px 18px rgba(16, 24, 40, .14);
  font-size: 0.8125rem; font-weight: 600; text-align: left;
  animation: typRise .4s 1.9s ease both;
}
.typ-map-load i { color: var(--primary-text, var(--primary)); margin-right: .3rem; }
.typ-map-load small { font-size: 0.6875rem; font-weight: 400; color: var(--muted); }
.typ-map-load:hover { border-color: var(--primary); }

/* Address card sitting on the drawn map. The live Google embed labels the
   location itself, so this steps aside once the real map loads. */
.typ-map-card {
  position: absolute; left: 12px; bottom: 12px;
  max-width: calc(100% - 24px);
  background: var(--card); border: 1px solid var(--border);
  border-radius: 8px; padding: 8px 11px;
  display: flex; flex-direction: column; gap: 1px;
  box-shadow: 0 6px 18px rgba(16, 24, 40, .14);
  animation: typRise .4s 1.75s ease both;
}
.typ-map-card b { font-size: 0.8125rem; color: var(--fg); }
.typ-map-card em { font-size: 0.75rem; color: var(--muted); font-style: normal; }
.typ-notes { font-size: 0.875rem; color: var(--muted); white-space: pre-wrap; margin: 0; }

/* ─── Order summary ─── */
.typ-lines { margin-bottom: 0.9rem; }
.typ-line {
  display: flex; align-items: flex-start; gap: 0.65rem;
  padding: 0.55rem 0;
}
.typ-line + .typ-line { border-top: 1px solid var(--border); }
.typ-line-img {
  position: relative; flex: 0 0 44px; width: 44px; height: 44px;
  border-radius: 6px; border: 1px solid var(--border);
  background: var(--bg) center / cover no-repeat;
  display: flex; align-items: center; justify-content: center;
  color: var(--muted);
}
.typ-line-qty {
  position: absolute; top: -6px; right: -6px;
  min-width: 18px; height: 18px; padding: 0 4px;
  border-radius: 9px;
  background: var(--primary-solid, var(--primary));
  color: var(--primary-fg, #fff);
  font-size: 0.6875rem; font-weight: 700; font-style: normal;
  display: flex; align-items: center; justify-content: center;
}
.typ-line-b { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.typ-line-b b { font-size: 0.875rem; font-weight: 600; color: var(--fg); }
.typ-line-b em { font-size: 0.75rem; color: var(--muted); font-style: normal; }
.typ-line-p { flex: 0 0 auto; text-align: right; font-size: 0.875rem; color: var(--fg); white-space: nowrap; }
.typ-line-p em { display: block; font-size: 0.6875rem; color: var(--muted); font-style: normal; }

.typ-row {
  display: flex; justify-content: space-between; gap: 1rem;
  font-size: 0.875rem; color: var(--muted);
  padding: 0.22rem 0;
}
.typ-row b { color: var(--fg); font-weight: 500; }
.typ-row--muted, .typ-row--muted b { font-size: 0.8125rem; color: var(--muted); }
.typ-row--disc, .typ-row--disc b { color: #16a34a; }
.typ-free { color: #16a34a; }
.typ-total {
  display: flex; justify-content: space-between; gap: 1rem;
  margin-top: 0.6rem; padding-top: 0.6rem;
  border-top: 1px solid var(--border);
  font-size: 1rem; font-weight: 700; color: var(--fg);
}

/* Mobile: the stub folds under the banner and its dashed rule turns
   horizontal, running the label and number along one line. */
@media (max-width: 575.98px) {
  .typ-banner { flex-direction: column; }
  .typ-banner-main { padding: 0.9rem 1rem; }
  .typ-stub {
    flex-direction: row; justify-content: space-between; align-items: center;
    padding: 0.5rem 1rem;
    border-left: 0;
    border-top: 2px dashed color-mix(in srgb, var(--typ-ok) 45%, transparent);
  }
  .typ-stub-v { font-size: 1.25rem; }
  .typ-title { font-size: 1.3rem; }
  .typ-check svg { width: 52px; height: 52px; }
  .typ-map { height: 200px; }
  .typ-map-load { right: 8px; top: 8px; }
  .typ-map-card { left: 8px; bottom: 8px; }
}


/* ══════════════════════════════════════════════════════════════════════════
   Header / toolbar icon alignment
   Optical corrections, not layout fixes: these glyphs each sit a pixel or two
   off their button's visual centre because of the whitespace baked into the
   icon itself, which no amount of flex centring can fix.
   ══════════════════════════════════════════════════════════════════════════ */

/* The sun glyph carries more padding below its rays than above, so it reads
   low in the button. Only the sun is affected: dark mode is when it shows. */
[data-theme="dark"] .theme-icon svg { transform: translateY(-2px); }

/* Shared dropdown chevron. The account and sort dropdowns were drawing
   Bootstrap's CSS triangle while the language switcher used a stroked
   chevron; this gives all of them the same mark. */
.dd-chevron-toggle { display: inline-flex; align-items: center; }
.dd-chevron-toggle.dropdown-toggle::after { display: none !important; }
.dd-chevron {
  width: 10px;
  height: 10px;
  flex-shrink: 0;
  margin-left: 0.3em;
  transition: transform 0.18s;
}
.dd-chevron-toggle[aria-expanded="true"] .dd-chevron { transform: rotate(180deg); }

/* One pixel more air than Bootstrap's ms-1 / me-1 (0.25rem) between a
   leading icon and its label. */
.dd-icon-gap { margin-left: 5px; }
.dd-icon-gap-r { margin-right: 5px; }

/* The 3x3 grid glyph is drawn slightly left of and above its own box. Desktop
   only: the mobile rendering already sits right, so it gets no transform. */
@media (min-width: 992px) {
  .layout-btn[data-layout="grid"] i { transform: translate(1px, 0); }
}


/* ══════════════════════════════════════════════════════════════════════════
   Catalog toolbar + header control polish
   ══════════════════════════════════════════════════════════════════════════ */

/* The filter button and the sort dropdown sit in the same row as the view
   toggle, so they take the same surface: white in light, --az-card in dark. */
.shop-catalog-toolbar .btn-outline-secondary,
.shop-catalog-toolbar .dropdown > .btn-outline-secondary {
  background-color: var(--card);
}
/* ...but a resting background at this specificity (0,3,0) outranks Bootstrap's
   own .btn-outline-secondary:hover (0,2,0), so hover silently stopped painting
   and these buttons stayed white while every other outline button filled grey.
   Restating the interaction states at matching specificity is the fix; they
   defer to Bootstrap's variables so they follow the button variant if it is
   ever restyled. Same trap as the storefront header controls. */
.shop-catalog-toolbar .btn-outline-secondary:hover,
.shop-catalog-toolbar .dropdown > .btn-outline-secondary:hover,
.shop-catalog-toolbar .btn-outline-secondary:focus-visible,
.shop-catalog-toolbar .dropdown > .btn-outline-secondary:focus-visible {
  background-color: var(--bs-btn-hover-bg);
  color: var(--bs-btn-hover-color);
}
.shop-catalog-toolbar .btn-outline-secondary:active,
.shop-catalog-toolbar .dropdown > .btn-outline-secondary:active,
.shop-catalog-toolbar .btn-outline-secondary.show,
.shop-catalog-toolbar .dropdown > .btn-outline-secondary.show {
  background-color: var(--bs-btn-active-bg);
  color: var(--bs-btn-active-color);
}

/* Below the toolbar, match the gap ABOVE it (main's --header-gap clearance)
   instead of Bootstrap's mb-3 1rem. */
@media (max-width: 991.98px) {
  .shop-catalog-toolbar { margin-bottom: var(--header-gap, 12px) !important; }
}

/* Header buttons that contain only an icon are square. Their width was
   content-driven, so a 14px glyph and a 16px glyph produced different widths
   in the same row. 31px matches the .btn-sm box height. */
.shop-menu-toggle,
.shop-search-toggle,
#themeToggle,
.shop-cart-btn,
/* Drawer header: the close button and the theme toggle are the same
   icon-only control, so they take the same square box as the header's. */
.shop-drawer-close,
#themeToggleMobile {
  /* Explicit box, not padding-driven: as flex containers these buttons take
     their height from the glyph (14-16px) rather than the 21px line box, so
     they came out shorter than their text-bearing siblings. */
  /* !important on the BOX only. Something in the header cascade was still
     widening the cart button, and these four are a closed set, so pinning the
     size is safer than guessing at the source.
     display must NEVER be !important here: these buttons carry Bootstrap's
     own d-lg-none / d-none d-lg-inline-flex, and forcing display broke both,
     leaking the menu toggle onto desktop and the theme toggle onto mobile. */
  width: 31px !important;
  height: 31px !important;
  padding: 0 !important;
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Centres the glyph when a d-* utility forces inline-block instead. */
  line-height: 29px;
  text-align: center;
  flex: 0 0 auto;
}
/* The account button only loses its label below md, so it is only square there. */
@media (max-width: 767.98px) {
  .shop-account-btn {
    width: 31px;
    height: 31px;
    padding: 0;
    box-sizing: border-box;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
  }
}

/* Moon glyph (light mode) reads low and slightly large next to the other
   header icons. */
[data-theme="light"] .theme-icon svg { width: 15px; height: 15px; transform: translateY(-1px); }

/* Sidebar-to-content gap matches the gap above the content (--header-gap),
   rather than Bootstrap's g-4 24px gutter. Only the horizontal gutter moves;
   the vertical one still separates the rail from the grid when they stack. */
/* Horizontal gap between the rail and the grid. Deliberately a literal, not
   derived from --header-gap: that token is the fixed-header clearance, and
   the two are free to differ. */
.shop-catalog-row { --bs-gutter-x: 15px; }

/* Desktop clears the fixed header by 3px more than phones do. Everything under
   the header resolves this single token — main's padding, all four sticky
   rails, and SIDEBAR_HEAD_GAP in navigation.js — so the rail, the toolbar and
   the grid all move together, and the pinned rail keeps the same position once
   scrolled. Phones stay on 12px. */
@media (min-width: 992px) {
  :root { --header-gap: 15px; }
}

/* Catalog toolbar never wraps a button's label under its icon. The sort label
   is the widest thing in the row and varies by language, so when it grew the
   filter button got squeezed and broke onto a second line. */
.shop-catalog-toolbar .btn { white-space: nowrap; }
/* Drop the word and keep the icon ONLY when the row genuinely cannot fit it.
   A viewport breakpoint is the wrong tool: what decides the fit is the width
   of the current sort label, which varies by language and by which option is
   selected, so a 390px phone can have room while a 430px one does not.
   initCatalogToolbarFit() in navigation.js measures and sets .is-tight.
   The label carries its own leading margin, so hiding it removes the gap. */
.shop-catalog-toolbar.is-tight .shop-filter-label { display: none; }

/* Arrow slot in the filters card header, split out of the title button so
   "Clear all" can sit between the two. Same rotation rules apply: they match
   .shop-filter-chev anywhere inside the card. */
.shop-sidebar-chevbtn {
  padding: 0;
  background: none;
  border: 0;
  color: inherit;
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  cursor: pointer;
}

/* Per-breakpoint icon nudges. The header's theme toggle and language switcher
   are d-none below lg, so on phones these target their copies in the drawer,
   which sit a pixel differently against that panel's type. */
@media (max-width: 991.98px) {
  /* Sun 1px lower than the desktop -2px. */
  [data-theme="dark"] .theme-icon svg { transform: translateY(-1px); }
  /* Globe 1px higher than the desktop +1px. */
  .lc-trigger-globe { transform: translateY(0); }
}

/* The x glyph sits a touch high in its box. */
.shop-drawer-close i { transform: translateY(1px); }

/* The x inside the search field is a dismiss affordance, not an action: it
   holds its resting appearance on hover, focus and active, so neither the
   border nor the glyph shifts under the cursor. Resting colours are restated
   per theme so nothing about the idle state changes. */
#shopSearchClose,
#shopSearchClose:hover,
#shopSearchClose:focus,
#shopSearchClose:focus-visible,
#shopSearchClose:active {
  background-color: var(--card) !important;
  border-color: var(--btn-outline-border, var(--search-border, #6c757d)) !important;
  color: #6c757d !important;
  box-shadow: none !important;
}
[data-theme="dark"] #shopSearchClose,
[data-theme="dark"] #shopSearchClose:hover,
[data-theme="dark"] #shopSearchClose:focus,
[data-theme="dark"] #shopSearchClose:focus-visible,
[data-theme="dark"] #shopSearchClose:active {
  color: var(--fg) !important;
}

/* Held for the single frame a theme swap takes (see the toggle in
   layout.ejs). Colour-bearing properties like border-color and box-shadow are
   transitioned, so flipping the theme animated every one of them from its old
   colour to its new one and they read as a flash of black or white. This is
   NOT a reduced-motion gate: it lasts one frame and applies to everyone. */
:root.theme-switching *,
:root.theme-switching *::before,
:root.theme-switching *::after {
  transition: none !important;
}

/* Secondary price lines (the net/gross counterpart and the per-unit price).
   Carries the display the d-block utility used to provide, so it can be
   hidden below without fighting an !important. */
.product-card-price-alt { display: block; }

/* List view on phones: a row is one line of information. Keep the main price,
   the name and nothing that makes the row taller or noisier. */
@media (max-width: 991.98px) {
  .product-grid.layout-list .product-card-price-alt,
  .product-grid.layout-list .product-card-sku {
    display: none;
  }
  /* One line, clipped with an ellipsis rather than wrapping. min-width on the
     body is what lets a flex item shrink below its content and actually clip. */
  .product-grid.layout-list .product-card-body { min-width: 0; }
  .product-grid.layout-list .product-card-title {
    display: block;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
}

/* Phones, list view: two columns, two bands. The photo and the name/price
   share the top; the quantity field sits under the photo at the same width,
   with add-to-cart beside it under the price.

   The quantity field lives at body > form > action-row, so nothing in CSS can
   pull it into the photo's column from there. display:contents lifts the
   body's children into the card's own grid, which makes the arrangement
   expressible without touching markup that grid view also renders.

   Rows are explicit on purpose: every row here would otherwise be implicit,
   and negative line numbers (-1, -2) resolve against the EXPLICIT grid, so
   they would place the form somewhere it was never meant to go. The middle
   row takes 1fr so the price sits directly under the name and the slack
   collects below it, rather than being shared out between the two. */
@media (max-width: 991.98px) {
  .product-grid.layout-list { --lm-img: 100px; }

  .product-grid.layout-list .product-card {
    display: grid;
    grid-template-columns: var(--lm-img) minmax(0, 1fr);
    /* A 1fr spacer above and below the text pair. They take equal shares of
       whatever the photo leaves over, which is what centres the name and price
       against it — two items in separate rows cannot be centred as a pair any
       other way without a wrapper element. */
    grid-template-rows: 1fr auto auto auto 1fr auto;
    column-gap: 9px;
    /* No row-gap: it would apply to every row boundary, including the spacers.
       The two gaps that matter are set as margins instead. */
    row-gap: 0;
    /* The body carried the inner padding; display:contents drops its box, so
       the card takes it over. */
    padding: 8px;
  }

  .product-grid.layout-list .product-card-body { display: contents; }

  .product-grid.layout-list .product-card-img {
    grid-column: 1;
    grid-row: 1 / 6;
    width: var(--lm-img);
    min-width: var(--lm-img);
    margin: 0;
    align-self: start;
  }
  .product-grid.layout-list .product-card-title {
    grid-column: 2;
    grid-row: 2;
    align-self: end;
    margin-bottom: 0;
  }
  .product-grid.layout-list .product-card-price-block {
    grid-column: 2;
    grid-row: 4;
    align-self: start;
  }

  /* Conditional content. With no Net/Gross switch the price block is a line
     shorter, and that freed line goes to the attribute tags — the same tags
     desktop shows. Safe to key off because priceSwitchEnabled and
     displayTaxRate are page-level, not per-product: within a shop either every
     row has the switch or none does, so rows stay uniform either way.
     :has() with nothing but "no" booleans left leaves them hidden. */
  .product-grid.layout-list .product-card:not(:has(.price-mode-switch))
    .product-card-attrs:has(.product-card-attr:not([data-bool="no"])) {
    display: flex;
    grid-column: 2;
    grid-row: 3;
    align-self: start;
    margin: 4px 0 3px;
    /* One row only. Whatever does not fit runs off the end and fades out
       rather than being cut mid-tag, which reads as "there is more" instead of
       as a rendering fault.

       The mask is unconditional, and does not need to be conditional: it fades
       the last stretch of the CONTAINER, so when the tags end before that
       point there is nothing there to fade. min-width lets the grid item
       shrink below its content, which is what makes it clip at all. */
    flex-wrap: nowrap;
    min-width: 0;
    overflow: hidden;
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
    mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
  }

  /* Bottom band spans both columns; the row inside it lines the quantity field
     up with the photo above it. */
  .product-grid.layout-list .product-card-body > form[data-shop-ajax],
  .product-grid.layout-list .product-card--has-variants .card-options-btn {
    grid-column: 1 / -1;
    grid-row: 6;
    /* Stands clear of the block above rather than sitting flush under the
       photo. !important because the form carries Bootstrap's mt-auto, which is
       itself !important and was silently winning — the gap read as 0. */
    margin-top: 9px !important;
  }
  .product-grid.layout-list .product-card-action-row {
    margin-top: 0;
    /* Matches the grid's column-gap so add-to-cart lines up with the text
       column above it, not just with the quantity field beside it. */
    gap: 9px;
  }
  .product-grid.layout-list .product-card-action-row .qty-control {
    flex: 0 0 var(--lm-img);
    width: var(--lm-img);
  }

  /* The label is hidden below 768px because a phone GRID tile has no room for
     it. A list row's button spans the whole right column, so it does — the
     viewport query cannot tell the two layouts apart, this can. */
  .product-grid.layout-list .card-atc-label {
    display: inline;
  }

  /* Optical alignment against the price figure beside it. */
  .product-grid.layout-list .price-vat-badge {
    transform: translateY(-1px);
  }

  /* More air under the switch, so it reads as its own control rather than as
     part of the price beneath it. Qualified by the price block because a
     later, unscoped list-view rule pins this to 2px and would otherwise win
     on source order. */
  .product-grid.layout-list .product-card-price-block .price-mode-switch--compact {
    margin-bottom: 6px;
  }

  /* The photo is 100px here, roughly two thirds of the desktop tile, so the
     badges scale with it: smaller type, tighter box, and pinned nearer the
     corner so they take less of the picture. */
  .product-grid.layout-list .product-badge {
    font-size: 0.5625rem;
    /* Vertical padding in px, not rem: 0.1rem is 1.6px and the box needed to
       come down by a whole pixel, which fractional padding cannot express
       cleanly at this size. Uneven top/bottom sits the label a pixel lower in
       the box without changing the box's own height. */
    padding: 2px 0.3rem 1px;
    border-radius: 0.2rem;
    top: 0.3rem;
    left: 0.3rem;
  }
  .product-grid.layout-list .product-badge-featured {
    top: auto;
    bottom: 0.3rem;
  }
}

/* List view on desktop: a row is wide enough for two lines of information.
   The SKU joins the name in brackets rather than taking a line of its own, and
   the short description gets the second line, clipped to it.

   The SKU rides on the title as an attribute so the DOM order stays
   name → description → sku, which is what grid view lays out. Reordering the
   markup to put the two in a wrapper would have moved the description in grid
   view too. Guarded on [data-sku] so a shop with SKUs hidden renders no
   empty brackets. */
@media (min-width: 992px) {
  .product-grid.layout-list .product-card-title[data-sku]::after {
    content: " (" attr(data-sku) ")";
    font-size: 0.75rem;
    font-weight: 400;
    color: var(--muted);
  }
  .product-grid.layout-list .product-card-sku { display: none; }

  /* Overrides the blanket hide a few rules up, which still applies to phones
     and to the attribute list in every size. */
  .product-grid.layout-list .product-card-short-desc {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 1;
    overflow: hidden;
    /* The gap below belongs to the attribute chips, not to this element. It
       used to live here as a 10px bottom margin, which meant the chips only
       cleared the title when a short description happened to sit between them
       -- with the owner's short-description setting off they landed 1px under
       the name. Moved to .product-card-attrs below, so the spacing holds
       whichever of the two is present. */
    margin-bottom: 0;
  }
  /* Lets the body shrink below its content, which is what makes the clamp
     actually clip instead of widening the row. */
  .product-grid.layout-list .product-card-body { min-width: 0; }

  /* Shown unconditionally here; the shared block above supplies the look. */
  .product-grid.layout-list .product-card-attrs:has(.product-card-attr:not([data-bool="no"])) {
    display: flex;
    /* Nudge, not margin, for the same reason as the price line: the text side
       already fills the image column exactly. */
    transform: translateY(-3px);
    /* Own the gap above rather than inheriting it from whatever happens to
       precede. 10px lands the chips 11px clear of the element above them,
       which is what the short-description case already read as, and now it
       reads the same with that setting off. */
    margin-top: 10px;
  }

  /* The switch moves in front of the price instead of sitting on its own line
     above it. A list row has width to spare and one line fewer keeps the row
     compact. Vertical margins go: they were spacing two stacked lines. */
  .product-grid.layout-list .product-card-price-block {
    display: flex;
    align-items: center;
    gap: 8px;
    /* Nudged, not margined: the text side already fills the image column
       exactly, so 3px of real height would push the row past it and reopen
       the gaps above and below the photo. */
    transform: translateY(3px);
  }

  /* A desktop row runs close to the image height, so any slack is a few px.
     Centring splits it evenly rather than dropping all of it under the photo.
     Phones stay top-aligned, where the text stack is far taller. */
  .product-grid.layout-list .product-card-img { align-self: center; }

  /* 10px to match the image's own inset, so the space above and below the
     photo reads the same as the space to its left. At 14px the text side ran a
     few px taller than the image column on rows carrying the qty + add-to-cart
     form, which pushed those gaps out to 13. */
  .product-grid.layout-list .product-card-body {
    padding-top: 10px;
    padding-bottom: 10px;
  }
  .product-grid.layout-list .price-mode-switch--compact {
    margin-top: 0;
    margin-bottom: 0;
  }

  /* Optical alignment against the price figure they sit beside. All nudges,
     never margins: the text side already fills the image column exactly. */
  .product-grid.layout-list .product-card-price > .price-vat-badge {
    transform: translateY(-1px);
  }
  .product-grid.layout-list .product-card-price > .badge-primary {
    transform: translateY(-3px);
    /* The gap to the price is the row's 6px column-gap PLUS this badge's own
       me-1 (4px), so 10px. Trimmed to 8. !important to beat the utility. */
    margin-right: 2px !important;
  }

  /* The whole price reads as one line: what you pay, what it was, what it works
     out to per unit. Markup order is [-16%][was]<br>[now][/unit], written for
     grid view where the struck price sits above; here order puts the price you
     pay first and the comparison after it. The <br> is a flex item, hence the
     display:none rather than a wrap rule. The base order:3 catches the unit
     label, which has no class of its own to target. */
  .product-grid.layout-list .product-card-price {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: 6px;
    row-gap: 2px;
  }
  .product-grid.layout-list .product-card-price br { display: none; }
  .product-grid.layout-list .product-card-price > * { order: 3; }
  .product-grid.layout-list .product-card-price > .badge-primary { order: 1; }
  .product-grid.layout-list .product-card-price > [data-card-price-value] { order: 2; }
  .product-grid.layout-list .product-card-price > .price-vat-badge { order: 4; }
  .product-grid.layout-list .product-card-price > .text-decoration-line-through { order: 5; }
  .product-grid.layout-list .product-card-price > .product-card-price-alt { order: 6; }
  /* column-gap already spaces these, and the tag's own trailing margin is
     measured for an inline run, not a flex row. */
  .product-grid.layout-list .product-card-price > *:has(+ .price-vat-badge) {
    margin-right: 0;
  }

}

/* ══════════════════════════════════════════════════════════════════════════
   List view: option picker for products with variants
   Grid view keeps its inline dropdowns; a list ROW is too short to hold a
   stack of selects, so the row offers one button that opens a sheet instead.
   ══════════════════════════════════════════════════════════════════════════ */
.card-options-btn { display: none; }
.product-grid.layout-list .product-card--has-variants > .product-card-body > form[data-shop-ajax] { display: none; }
.product-grid.layout-list .product-card--has-variants .card-options-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 100%;
}

/* Centre the whole text + action stack against the photo.
   .product-card-body is already justify-content:center, but the action was
   pinned to the bottom regardless: the form carries Bootstrap's .mt-auto and
   Select options had margin-top:auto, either of which eats all the free space
   and leaves only the name/price to centre in what is left.

   Grid view genuinely needs that push, so buttons line up across cards whose
   text runs to different lengths. A list row is one card per row, so there is
   nothing to line up with and the group should simply sit level with the
   image. !important because .mt-auto is a Bootstrap utility and nothing but
   another !important can outrank it. */
.product-grid.layout-list .product-card-body > form { margin-top: 0 !important; }
.product-grid.layout-list .product-card--has-variants .card-options-btn {
  margin-top: 0;
  /* Sat 4px above the photo's bottom edge while the add-to-cart row on every
     other card sat flush with it, so a variant row read as short next to its
     neighbours. Measured at exactly -4px on every variant card.

     Nudge, not margin, for the same reason as the attribute chips above: the
     text column already fills the image column exactly, so a margin would grow
     the row rather than move the button inside it. */
  transform: translateY(4px);
}

/* A desktop row is wide, so a full-width action row stretched the add-to-cart
   button into a 545px bar. Cap it. One token drives both the row and the
   options button that replaces it on variant products, so the two can never
   drift apart; and because the row is flex with a fixed-width stepper, the
   button simply absorbs whatever the stepper leaves. The value is what a phone
   row already works out to, where the proportions read correctly. */
@media (min-width: 992px) {
  .product-grid.layout-list {
    --list-action-w: 254px;
  }
  /* fit-content with a floor: normally all of them land on the token, so they
     match; a language with a long label grows its own button rather than
     clipping it, since the label is nowrap + overflow:hidden. */
  .product-grid.layout-list .product-card-action-row,
  .product-grid.layout-list .product-card--has-variants .card-options-btn,
  .product-grid.layout-list .product-add-btn {
    width: fit-content;
    min-width: var(--list-action-w);
  }
  /* .product-add-btn is the no-quantity-field markup: a bare w-100 button with
     no .product-card-action-row around it, so the cap above never reached it
     and turning the quantity field off stretched add-to-cart into a full-width
     bar while Select options next to it stayed at 254px. Same token, so all
     three widths move together. !important only because .w-100 is a Bootstrap
     utility; the min-width above does the actual sizing. */
  .product-grid.layout-list .product-add-btn { width: fit-content !important; }
  /* The label is hidden between 992 and 1400px because a GRID column is too
     narrow for it there. A list row is the full card width, so it has room —
     the viewport query cannot tell the two layouts apart, this can. */
  .product-grid.layout-list .card-atc-label {
    display: inline;
  }
}

/* Inside the sheet the clone is a plain panel: no card frame, no hover lift,
   and the dropdowns come back because it is no longer a list row. */
.product-card--in-picker {
  box-shadow: none !important;
  border-radius: 0;
  transform: none !important;
  flex-direction: column;
  height: auto;
}
.product-card--in-picker:hover { box-shadow: none !important; transform: none !important; }
.product-card--in-picker .card-variant-selectors { display: block; }
.product-card--in-picker > .product-card-body > form[data-shop-ajax] { display: block; }
/* The sheet is sized to the photo: one token drives the photo's width and the
   dialog's, so the window is exactly as wide as the picture plus the body's
   own 6px inset. That also puts the same 6px above the photo as beside it,
   instead of the photo floating in the middle of a 500px dialog. */
.shop-options-modal { --picker-img-w: 240px; --picker-inset: 17px; }
.product-card--in-picker .product-card-img {
  width: 100%;
  max-width: var(--picker-img-w);
  margin: 0 auto;
  border-radius: 8px;
}
.shop-options-modal .modal-dialog {
  /* The inset on both sides plus the content box's own 1px borders, so the
     photo renders at exactly --picker-img-w rather than under it. */
  max-width: calc(var(--picker-img-w) + 2 * var(--picker-inset) + 2px);
  /* Bootstrap only centres the dialog from 576px up; below that it ships a
     flat margin: 0.5rem, which a fixed-width dialog turns into a sheet stuck
     against the left edge. */
  margin-left: auto;
  margin-right: auto;
}

/* One inset for everything in the sheet, on all four sides. The panel's body
   used to supply the bottom gap and to indent the name, price and selects
   further than the photo above them. Its top padding stays: that one is not
   an outer inset but the space between the photo and the name. */
.product-card--in-picker > .product-card-body {
  padding: 14px 0 0;
}
/* The sheet's only inset, matching on all four sides. */
.shop-options-modal .modal-body { padding: var(--picker-inset); }

/* ─── Net / Gross switch ─── */
/* Same segmented-control language as the catalog view toggle: joined, square
   inner corners, rounded outer edges. */
.price-mode-switch { display: inline-flex; flex: 0 0 auto; }
.price-mode-btn {
  background: var(--card);
  border: 1px solid var(--btn-outline-border, var(--border));
  color: var(--muted);
  height: 31px;
  padding: 0 10px;
  font-size: 0.8125rem;
  font-weight: 500;
  border-radius: 0;
  cursor: pointer;
  transition: all 0.15s ease;
  position: relative;
  white-space: nowrap;
}
.price-mode-btn + .price-mode-btn { margin-left: -1px; }
.price-mode-btn:first-child { border-top-left-radius: 0.5rem; border-bottom-left-radius: 0.5rem; }
.price-mode-btn:last-child { border-top-right-radius: 0.5rem; border-bottom-right-radius: 0.5rem; }
.price-mode-btn:hover { color: var(--fg); z-index: 1; }
.price-mode-btn.active {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--primary-fg);
  z-index: 1;
}
/* On the product page it sits on its own line above the price. */
.product-detail-price-row + .price-mode-switch,
.price-mode-switch { margin-bottom: 0; }
.product-detail-page .price-mode-switch { margin-bottom: 0.5rem; }

/* Compact variant for the product card: the control sits above the price, so
   it has to read as a small secondary affordance, not another button row. */
/* Sits a little lower so it is not crowded against the line above it. The
   price block below follows it down by the same amount. */
.price-mode-switch--compact { margin-top: 6px; margin-bottom: 4px; }
.price-mode-switch--compact .price-mode-btn {
  height: 22px;
  padding: 0 7px;
  font-size: 0.6875rem;
  border-radius: 0;
}
.price-mode-switch--compact .price-mode-btn:first-child {
  border-top-left-radius: 6px; border-bottom-left-radius: 6px;
}
.price-mode-switch--compact .price-mode-btn:last-child {
  border-top-right-radius: 6px; border-bottom-right-radius: 6px;
}
/* In list view the row is tight; keep it on the same line as the price block. */
.product-grid.layout-list .price-mode-switch--compact { margin-bottom: 2px; }

/* "+27% VAT" reads as a small tag beside the price rather than trailing muted
   text, so it is legible as a qualifier without competing with the figure. */
.price-vat-badge {
  display: inline-block;
  /* No left margin: an inline margin applies at the start of a wrapped line
     too, so a long price that pushed the tag onto its own line left it
     indented. The markup already has a space before the tag, and a space at a
     line break hangs on the previous line instead of indenting the next one,
     which gives the gap only where it is wanted. */
  padding: 1px 4px;
  border-radius: 6px;
  background: var(--hover, rgba(0, 0, 0, .05));
  border: 1px solid var(--btn-outline-border, var(--border));
  color: var(--muted);
  font-size: 0.6875rem;
  font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
  vertical-align: middle;
}
/* The gap in front of the tag rides on the element BEFORE it, never on the tag
   itself: a trailing margin hangs off the end of a line, so when a long price
   pushes the tag onto its own line the tag still starts flush with the edge.
   A left margin on the tag would apply at that line start and indent it. */
:is(.product-card-price, .product-detail-price) > *:has(+ .price-vat-badge) {
  margin-right: 3px;
}

/* The product page's price is much larger, so the tag scales with it. */
.product-detail-price .price-vat-badge { font-size: 0.75rem; padding: 2px 5px; }

/* ─── Settings -> Design pointer card ───────────────────────────────────────
   Branding (colour, logo, favicon, storefront theme) moved to /admin/design on
   2026-08-04, where it can be previewed live. This card is what replaced those
   controls in the Shop tab. Deliberately a LINK and not a duplicate set of
   inputs: two places to set one value is how they drift apart. */
.settings-brand-pointer {
  display: flex;
  gap: .85rem;
  padding: 1rem;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--bg);
}
.settings-brand-pointer__icon {
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 9px;
  background: var(--primary-subtle, rgba(99, 102, 241, .08));
  color: var(--primary);
}
.settings-brand-pointer__body { min-width: 0; }
.settings-brand-pointer__title {
  font-weight: 600;
  font-size: .9375rem;
  margin-bottom: .25rem;
}
.settings-brand-pointer__text {
  font-size: .8125rem;
  line-height: 1.45;
  color: var(--muted, #6b7280);
  margin: 0 0 .75rem;
}
