:root {
  --color-bg: #f7f8fa;
  --color-surface: #ffffff;
  --color-surface-alt: #ffffff;
  --color-border: #e4e7ec;
  --color-text: #16181d;
  --color-text-muted: #5d6b82;
  /* Round 25 accessibility: text-only variants of the brand colors. Measured with the WCAG
     contrast formula, the brand orange #ff5a1f on white is 3.12:1 and the warning amber
     #f79009 is 2.35:1 — both fail the 4.5:1 minimum for normal-size text, and the amber is
     the colour of every star rating on the site (63 failing instances measured across 8
     pages). These darker variants pass (4.77:1 and 5.02:1) while still reading as the same
     brand hue. They are used ONLY where the colour is text on a light surface — buttons,
     fills, borders and gradients keep the original brand orange, so the brand is unchanged. */
  --color-primary-text: #c2410c;   /* 5.18:1 on white, 4.60:1 on --tag-bg */
  --color-warning-text: #b45309;   /* 5.02:1 on white */
  --color-success-text: #067647;   /* 5.20:1 on --tag-success-bg */
  --color-danger-text:  #b42318;   /* 5.75:1 on --tag-danger-bg, 6.57:1 on white */
  --color-primary: #ff5a1f;
  --color-primary-dark: #e04a13;
  --color-primary-light: #ff7a45;
  --color-success: #12b76a;
  --color-warning: #f79009;
  --color-danger: #f04438;
  --color-media-bg: #f0f1f3;
  --color-scrollbar-thumb: #d0d5dd;
  --color-scrollbar-thumb-hover: #98a2b3;
  --color-scrollbar-track: #eceef2;
  --skeleton-a: #eee;
  --skeleton-b: #f5f5f5;
  --tag-bg: #eef2f6;
  --tag-success-bg: #e7f9f0;
  --tag-warning-bg: #fef3e2;
  --tag-danger-bg: #fdeceb;
  --tag-sponsored-bg: #eef4ff;
  --tag-sponsored-color: #3651d4;
  --tag-bestseller-bg1: #fff3d6;
  --tag-bestseller-bg2: #ffe4b3;
  --tag-bestseller-color: #92450a;
  --alert-error-bg: #fdeceb;
  --alert-error-border: #f6c8c4;
  --alert-success-bg: #e7f9f0;
  --alert-success-border: #b7ecd3;
  --alert-info-bg: #eef4ff;
  --alert-info-border: #c7d6fb;
  --alert-info-color: #3651d4;
  --celebrate-bg1: #fff7ed;
  --celebrate-bg2: #ffe8d9;
  --celebrate-border: #ffd7b3;
  --terms-link-color: #2563eb;
  --terms-link-hover: #1d4ed8;
  --radius: 10px;
  --radius-lg: 16px;
  --shadow: 0 1px 2px rgba(16,24,40,0.06), 0 1px 3px rgba(16,24,40,0.08);
  --shadow-md: 0 4px 12px rgba(16,24,40,0.08), 0 2px 4px rgba(16,24,40,0.06);
  --shadow-lg: 0 12px 32px rgba(16,24,40,0.14), 0 4px 10px rgba(16,24,40,0.08);
  --gradient-primary: linear-gradient(135deg, var(--color-primary), var(--color-primary-dark));
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --header-h: 64px;
}

/* ─── Dark mode ───
   Real theme switching (round 11 — "it should work not just fake display"): every color used
   anywhere below is one of the custom properties above, so redefining them here restyles the
   ENTIRE site, not just a couple of showcase elements. Two ways in:
   1. Explicit user choice — ux.js sets data-theme="dark"|"light" on <html> and persists it.
   2. "System" (the default until the user picks otherwise) — no data-theme attribute at all,
      so the OS-level prefers-color-scheme media query below decides, and updates live if the
      OS theme changes while the tab is open (see ux.js's matchMedia change listener). */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --color-bg: #0e1013;
    --color-surface: #181b21;
    --color-surface-alt: #22262e;
    --color-border: #2b2f38;
    --color-text: #edeef1;
    --color-text-muted: #9aa1ad;
  --color-primary-text: #ff8a5c;
  --color-warning-text: #f7b955;
  --color-success-text: #5be3a5;
  --color-danger-text:  #ff9a91;
    --color-primary-text: #ff8a5c;
    --color-warning-text: #f7b955;
    --color-success-text: #5be3a5;
    --color-danger-text:  #ff9a91;
    --color-media-bg: #23262e;
    --color-scrollbar-thumb: #3a3e48;
    --color-scrollbar-thumb-hover: #4c515c;
    --color-scrollbar-track: #22252c;
    --skeleton-a: #23262e;
    --skeleton-b: #2b2f38;
    --tag-bg: #242832;
    --tag-success-bg: rgba(18,183,106,0.16);
    --tag-warning-bg: rgba(247,144,9,0.16);
    --tag-danger-bg: rgba(240,68,56,0.18);
    --tag-sponsored-bg: rgba(54,81,212,0.2);
    --tag-sponsored-color: #93a9ff;
    --tag-bestseller-bg1: rgba(255,196,64,0.2);
    --tag-bestseller-bg2: rgba(255,163,26,0.14);
    --tag-bestseller-color: #ffbf6b;
    --alert-error-bg: rgba(240,68,56,0.14);
    --alert-error-border: rgba(240,68,56,0.4);
    --alert-success-bg: rgba(18,183,106,0.14);
    --alert-success-border: rgba(18,183,106,0.4);
    --alert-info-bg: rgba(54,81,212,0.18);
    --alert-info-border: rgba(54,81,212,0.45);
    --alert-info-color: #93a9ff;
    --celebrate-bg1: rgba(255,90,31,0.12);
    --celebrate-bg2: rgba(255,90,31,0.06);
    --celebrate-border: rgba(255,90,31,0.4);
    --terms-link-color: #6ea1f7;
    --terms-link-hover: #93bbfa;
    --shadow: 0 1px 2px rgba(0,0,0,0.35), 0 1px 3px rgba(0,0,0,0.4);
    --shadow-md: 0 4px 14px rgba(0,0,0,0.45), 0 2px 6px rgba(0,0,0,0.35);
    --shadow-lg: 0 14px 34px rgba(0,0,0,0.55), 0 4px 12px rgba(0,0,0,0.4);
  }
}
:root[data-theme="dark"] {
  --color-bg: #0e1013;
  --color-surface: #181b21;
  --color-surface-alt: #22262e;
  --color-border: #2b2f38;
  --color-text: #edeef1;
  --color-text-muted: #9aa1ad;
  --color-media-bg: #23262e;
  --color-scrollbar-thumb: #3a3e48;
  --color-scrollbar-thumb-hover: #4c515c;
  --color-scrollbar-track: #22252c;
  --skeleton-a: #23262e;
  --skeleton-b: #2b2f38;
  --tag-bg: #242832;
  --tag-success-bg: rgba(18,183,106,0.16);
  --tag-warning-bg: rgba(247,144,9,0.16);
  --tag-danger-bg: rgba(240,68,56,0.18);
  --tag-sponsored-bg: rgba(54,81,212,0.2);
  --tag-sponsored-color: #93a9ff;
  --tag-bestseller-bg1: rgba(255,196,64,0.2);
  --tag-bestseller-bg2: rgba(255,163,26,0.14);
  --tag-bestseller-color: #ffbf6b;
  --alert-error-bg: rgba(240,68,56,0.14);
  --alert-error-border: rgba(240,68,56,0.4);
  --alert-success-bg: rgba(18,183,106,0.14);
  --alert-success-border: rgba(18,183,106,0.4);
  --alert-info-bg: rgba(54,81,212,0.18);
  --alert-info-border: rgba(54,81,212,0.45);
  --alert-info-color: #93a9ff;
  --celebrate-bg1: rgba(255,90,31,0.12);
  --celebrate-bg2: rgba(255,90,31,0.06);
  --celebrate-border: rgba(255,90,31,0.4);
  --terms-link-color: #6ea1f7;
  --terms-link-hover: #93bbfa;
  --shadow: 0 1px 2px rgba(0,0,0,0.35), 0 1px 3px rgba(0,0,0,0.4);
  --shadow-md: 0 4px 14px rgba(0,0,0,0.45), 0 2px 6px rgba(0,0,0,0.35);
  --shadow-lg: 0 14px 34px rgba(0,0,0,0.55), 0 4px 12px rgba(0,0,0,0.4);
}
html { transition: background-color 0.2s var(--ease); }
body, .site-header, .card, .form-card, .modal-overlay .form-card { transition: background-color 0.2s var(--ease), border-color 0.2s var(--ease), color 0.2s var(--ease); }

* { box-sizing: border-box; }

/* Round 25 — systemic fix for a bug this codebase has now hit five separate times.
   The browser hides [hidden] elements via a USER-AGENT rule, and a user-agent rule loses to
   ANY author rule of equal-or-higher specificity regardless of source order. So the moment a
   class sets `display` (e.g. `.promo-count{display:inline-flex}`), setting `el.hidden = true`
   from JS silently stops hiding that element — it keeps rendering, usually as an empty shell.
   Measured instances before this rule: the empty "•" bullet on the home page's Deals tile,
   the categories page's filtered section, both search filter-count badges, and the used-
   marketplace infinite-scroll sentinel. One author-origin rule fixes the whole class of bug
   and stops it recurring. `!important` is deliberate: it has to beat every later display rule.
   Nothing here relies on a [hidden] element still having layout — the two places that need a
   visually-hidden-but-present element (custom-select.js's real <select>, the OTP input) use
   opacity/clip on purpose, precisely because `hidden` would exclude them from form submission. */
[hidden] { display: none !important; }
html { scroll-behavior: smooth; }
html, body { margin: 0; padding: 0; }
/* Mobile browsers auto-inflate text size on narrow viewports ("font boosting") independent of
   any px value here — this is the actual cause of a correctly-sized mobile layout still reading
   as "zoomed in". Locking it off makes 1px in this file render as exactly 1px on a real phone. */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  /* Round 24: the repeated "feels empty / just white" complaint is really about this line —
     every page sat on one flat solid color with zero visual interest. A fixed, very subtle
     brand-tinted wash (same rgba(255,90,31,…) recipe round 22's .section-tinted already uses,
     just fainter and attached to the whole viewport) gives every page ambient color for free,
     with no per-template changes needed. Kept faint enough that text contrast is untouched. */
  background:
    radial-gradient(circle at 8% 0%, rgba(255,90,31,0.05), transparent 38%),
    radial-gradient(circle at 100% 18%, rgba(255,90,31,0.04), transparent 40%),
    var(--color-bg);
  background-attachment: fixed;
  color: var(--color-text);
  line-height: 1.5;
}
:root[data-theme="dark"] body {
  background:
    radial-gradient(circle at 8% 0%, rgba(255,90,31,0.10), transparent 38%),
    radial-gradient(circle at 100% 18%, rgba(255,90,31,0.08), transparent 40%),
    var(--color-bg);
  background-attachment: fixed;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) body {
    background:
      radial-gradient(circle at 8% 0%, rgba(255,90,31,0.10), transparent 38%),
      radial-gradient(circle at 100% 18%, rgba(255,90,31,0.08), transparent 40%),
      var(--color-bg);
    background-attachment: fixed;
  }
}
a { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }
button, input, select, textarea { font: inherit; color: inherit; }
/* Round 12 fix: inputs/selects/textareas with no explicit background (e.g. inline JS-rendered
   fields not wrapped in .field or .search-form) fell back to the browser's default white
   background while inheriting the theme's text color — invisible text in dark mode. Setting
   both here covers every input site-wide; more specific rules (.field input, .search-form
   input, etc.) still apply on top where they exist. */
input, select, textarea { background: var(--color-surface); color: var(--color-text); }
input[type="checkbox"], input[type="radio"] { background: initial; }

::selection { background: var(--color-primary); color: #fff; }
/* Round 27: the scrollbar was a near-invisible light-gray hairline — easy to miss entirely,
   especially for older eyes, and it gave no sense of how long a page is. Now brand-orange,
   wider, with a visible track so the full scroll range reads at a glance. Styled for both
   WebKit/Blink (Chrome, Edge, Safari) and Firefox (scrollbar-color/scrollbar-width). */
::-webkit-scrollbar { width: 14px; height: 14px; }
::-webkit-scrollbar-track { background: var(--color-scrollbar-track); border-radius: 999px; }
::-webkit-scrollbar-thumb {
  background: var(--gradient-primary); border-radius: 999px; border: 3px solid var(--color-bg);
  min-height: 42px;
}
::-webkit-scrollbar-thumb:hover { background: var(--color-primary-dark); }
::-webkit-scrollbar-thumb:active { background: var(--color-primary-dark); }
html { scrollbar-color: var(--color-primary) var(--color-scrollbar-track); scrollbar-width: auto; }

.skip-link {
  position: absolute; left: 50%; transform: translate(-50%, -140%); z-index: 400;
  background: var(--color-surface); color: var(--color-text); border: 2px solid var(--color-primary);
  border-radius: 0 0 10px 10px; padding: 12px 22px; font-weight: 700; box-shadow: var(--shadow-lg);
  transition: transform 0.18s var(--ease);
}
.skip-link:focus { transform: translate(-50%, 0); outline: none; }

:focus-visible {
  outline: 2px solid var(--color-primary); outline-offset: 2px; border-radius: 4px;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; scroll-behavior: auto !important; }
}

.container { max-width: 1200px; margin: 0 auto; padding: 0 20px; }

/* ─── Keyframe animations ─── */
@keyframes fadeInUp { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: translateY(0); } }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes scaleIn { from { opacity: 0; transform: scale(0.94); } to { opacity: 1; transform: scale(1); } }
@keyframes popIn { 0% { opacity: 0; transform: scale(0.7); } 70% { transform: scale(1.06); } 100% { opacity: 1; transform: scale(1); } }
@keyframes slideDown { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } }
@keyframes badgePulse { 0% { transform: scale(1); } 30% { transform: scale(1.35); } 100% { transform: scale(1); } }
@keyframes softBounce { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-6px); } }
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes shimmerSweep { 0% { transform: translateX(-100%); } 100% { transform: translateX(200%); } }

.reveal { opacity: 0; transform: translateY(16px); transition: opacity 0.5s var(--ease), transform 0.5s var(--ease); }
.reveal.is-visible { opacity: 1; transform: translateY(0); }

main.container { animation: fadeIn 0.35s var(--ease); }

/* ─── Header ─── */
/* ─── Round 32: top announce bar — real color on every page before anything else loads ─── */
.announce-bar {
  background: var(--gradient-primary); color: #fff; font-size: 12.5px; font-weight: 700;
  display: flex; align-items: center; justify-content: center; gap: 22px;
  padding: 7px 14px; letter-spacing: 0.1px; white-space: nowrap; overflow-x: auto; scrollbar-width: none;
}
.announce-bar::-webkit-scrollbar { display: none; }
.announce-bar span { flex-shrink: 0; }
@media (max-width: 640px) { .announce-bar { justify-content: flex-start; font-size: 11.5px; gap: 16px; } }

.site-header {
  position: sticky; top: 0; z-index: 50;
  background: var(--color-surface); border-bottom: 1px solid var(--color-border);
  height: var(--header-h);
  transition: box-shadow 0.25s var(--ease), border-color 0.25s var(--ease);
}
.site-header.is-scrolled { box-shadow: var(--shadow-md); border-bottom-color: transparent; }
.header-inner { display: flex; align-items: center; gap: 24px; height: 100%; }
.logo {
  font-size: 22px; font-weight: 800; color: var(--color-text); white-space: nowrap;
  transition: transform 0.2s var(--ease);
  display: inline-flex; align-items: center; gap: 7px;
}
.logo:hover { transform: translateY(-1px); }
.logo-icon { width: 46px; height: 44px; object-fit: contain; display: block; flex-shrink: 0; }
@media (max-width: 640px) { .logo-icon { width: 38px; height: 36px; } }
.logo-text { white-space: nowrap; }
.logo span {
  background: var(--gradient-primary); -webkit-background-clip: text; background-clip: text; color: transparent;
}
.search-form { flex: 1; display: flex; max-width: 480px; }
.search-form input {
  flex: 1; padding: 9px 14px; border: 1px solid var(--color-border); border-radius: var(--radius) 0 0 var(--radius);
  border-right: none; outline: none; transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
  background: var(--color-surface); color: var(--color-text);
}
.search-form input:focus { border-color: var(--color-primary); box-shadow: 0 0 0 3px rgba(255,90,31,0.12); }
.search-form button {
  padding: 0 16px; border: 1px solid var(--color-border); background: var(--color-bg);
  border-radius: 0 var(--radius) var(--radius) 0; cursor: pointer; transition: background 0.15s var(--ease), transform 0.15s var(--ease);
}
.search-form button:hover { background: var(--color-primary); color: #fff; border-color: var(--color-primary); }
.search-form button:active { transform: scale(0.94); }
.main-nav { display: flex; align-items: center; gap: 18px; font-size: 14px; font-weight: 500; white-space: nowrap; }
.main-nav a {
  color: var(--color-text-muted); position: relative; padding-bottom: 3px; transition: color 0.15s var(--ease);
}
.main-nav a::after {
  content: ""; position: absolute; left: 0; right: 100%; bottom: -2px; height: 2px;
  background: var(--color-primary); border-radius: 2px; transition: right 0.25s var(--ease);
}
.main-nav a:hover { color: var(--color-text); }
.main-nav a:hover::after { right: 0; }
.nav-cta { color: var(--color-primary-text) !important; }
.cart-link { position: relative; }
.badge {
  position: absolute; top: -8px; right: -14px; background: var(--color-primary); color: #fff;
  font-size: 11px; font-weight: 700; padding: 1px 6px; border-radius: 999px;
  box-shadow: 0 2px 6px rgba(255,90,31,0.5);
}
.badge.is-pulsing { animation: badgePulse 0.4s var(--ease); }

/* ─── Credits nav pill (Round 22 — "make credits more visible" beyond the profile page) ─── */
.nav-credits-pill {
  display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; border-radius: 999px;
  background: var(--tag-bg); border: 1px solid var(--color-border); font-size: 12px; font-weight: 700;
  color: var(--color-text); transition: border-color 0.15s var(--ease), transform 0.15s var(--ease), background 0.15s var(--ease), color 0.15s var(--ease);
}
.nav-credits-pill:hover { border-color: var(--color-primary); color: var(--color-primary-text); background: rgba(255,90,31,0.08); transform: translateY(-1px); }
/* Round 24: found while fixing the nav overflow — this pill's own `display: flex` beat the
   browser's default `[hidden]{display:none}` rule (any author-stylesheet rule outranks a
   user-agent rule regardless of specificity/order), so it was showing an empty "💎 —" pill to
   logged-out guests this whole time despite the `hidden` attribute being correctly set. Real
   bug, not cosmetic — fixed the same way `.modal-overlay` already guards against this. */
.nav-credits-pill[hidden] { display: none; }

/* ─── Round 24: account dropdown — collapses Messages/Favorites/Orders/My Store/My
   listings/Profile/Logout (+ lang/theme) out of the top-level nav, which was overflowing
   off-screen for any fully signed-in seller. Same visual language as custom-select.js's
   .cs-popup (shadow-lg, 10px radius, fade+scale) for consistency, but plain CSS — this menu
   doesn't need JS positioning since it's a simple two-item-wide trigger, not a page-embedded
   filter control. */
.account-menu { position: relative; }
.account-menu-trigger {
  display: flex; align-items: center; gap: 7px; padding: 6px 10px 6px 6px; border: 1px solid var(--color-border);
  border-radius: 999px; background: var(--color-surface); font: inherit; font-weight: 600; color: var(--color-text);
  cursor: pointer; box-shadow: var(--shadow); transition: background 0.15s var(--ease), border-color 0.15s var(--ease), box-shadow 0.15s var(--ease), transform 0.15s var(--ease);
}
.account-menu-trigger:hover { border-color: var(--color-primary); box-shadow: var(--shadow-md); transform: translateY(-1px); }
.account-menu-trigger[aria-expanded="true"] { background: var(--color-bg); border-color: var(--color-primary); box-shadow: 0 0 0 3px rgba(255,90,31,0.14); }
.account-avatar {
  display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px;
  border-radius: 50%; background: var(--gradient-primary); color: #fff; font-size: 13px; font-weight: 800;
  flex-shrink: 0;
}
.account-name { max-width: 90px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.account-menu-trigger .cs-chevron { font-size: 10px; color: var(--color-text-muted); transition: transform 0.2s var(--ease); }
.account-menu-trigger[aria-expanded="true"] .cs-chevron { transform: rotate(180deg); }
.account-menu-popup {
  position: absolute; top: calc(100% + 8px); inset-inline-end: 0; z-index: 300; min-width: 210px;
  background: var(--color-surface); border: 1px solid var(--color-border); border-radius: 10px;
  box-shadow: var(--shadow-lg); padding: 6px; display: flex; flex-direction: column; gap: 1px;
  animation: accountMenuIn 0.16s var(--ease);
}
/* Round 25: the exact same bug as the credits-pill/guest-nav-group fix earlier this round —
   this rule's own `display: flex` beat the browser's default `[hidden]{display:none}`, so
   setting the JS `.hidden` property back to true had no visual effect at all: the menu opened
   fine but could never close (click the trigger again, click outside, Escape — none of it did
   anything visible). This is the real, reported "stays open" bug. */
.account-menu-popup[hidden] { display: none; }
@keyframes accountMenuIn { from { opacity: 0; transform: scale(0.96) translateY(-4px); } to { opacity: 1; transform: scale(1) translateY(0); } }
.account-menu-popup a {
  display: block; padding: 9px 10px; border-radius: 7px; font-size: 14px; font-weight: 500;
  color: var(--color-text); white-space: nowrap; transition: background 0.12s var(--ease);
}
.account-menu-popup a:hover { background: var(--color-bg); color: var(--color-primary-text); }
.account-menu-popup a.account-menu-logout:hover { color: var(--color-danger, #e5484d); }
.account-menu-divider { height: 1px; background: var(--color-border); margin: 5px 2px; }
.account-menu-settings { display: flex; align-items: center; justify-content: space-between; padding: 4px 10px 2px; gap: 10px; }
.account-menu-settings .lang-switch { font-size: 13px; }
.guest-nav-group { display: flex; align-items: center; gap: 14px; }
.guest-nav-group[hidden] { display: none; }

/* ─── Mobile nav: hamburger + off-canvas drawer + bottom tab bar ───
   Desktop keeps the original inline .main-nav row; only the ≤860px query below turns it into
   a slide-in drawer. These base rules stay inert on desktop (display:none / not rendered)
   and just define the pieces the media query switches on. */
.mobile-menu-toggle {
  display: none; flex-direction: column; align-items: center; justify-content: center;
  width: 40px; height: 40px; padding: 0; margin-inline-end: 2px; flex-shrink: 0;
  background: transparent; border: none; border-radius: var(--radius); cursor: pointer;
  transition: background 0.15s var(--ease);
}
.mobile-menu-toggle:hover { background: var(--color-bg); }
.hamburger-bar {
  display: block; width: 20px; height: 2px; margin: 2.5px 0; border-radius: 2px;
  background: var(--color-text); transition: transform 0.2s var(--ease), opacity 0.2s var(--ease);
}
.mobile-menu-toggle[aria-expanded="true"] .hamburger-bar:nth-child(1) { transform: translateY(5px) rotate(45deg); }
.mobile-menu-toggle[aria-expanded="true"] .hamburger-bar:nth-child(2) { opacity: 0; }
.mobile-menu-toggle[aria-expanded="true"] .hamburger-bar:nth-child(3) { transform: translateY(-5px) rotate(-45deg); }
.mobile-nav-close, .nav-backdrop, .bottom-tab-bar { display: none; }
.nav-backdrop[hidden] { display: none; }
body.nav-drawer-open { overflow: hidden; }

@media (prefers-reduced-motion: reduce) { .account-menu-popup { animation: none; } }

@media (max-width: 860px) {
  .mobile-menu-toggle { display: inline-flex; }
  .header-inner { flex-wrap: wrap; height: auto; padding: 10px 16px; gap: 10px; }
  .site-header { height: auto; }
  .search-form { order: 3; max-width: none; width: 100%; }

  /* Round 28 mobile pass: the nav used to wrap inline into the header (Round 27's fix below,
     now superseded), which meant 8-13 links pushed the header to 2-3 lines tall — on a real
     390px phone, over 300px of pure chrome before any page content appeared. It's an
     off-canvas drawer instead now: the header goes back to one short row (hamburger + logo),
     and every nav link/account item lives in a slide-in panel, opened by the hamburger
     (app.js) and closed via the ✕ button, the backdrop, an Escape press, or following any
     link inside it. Slides in from the inline-end edge so it's the right side in LTR (English)
     and the left side in RTL (Arabic) without duplicating the rule. */
  .nav-backdrop {
    display: block; position: fixed; inset: 0; z-index: 199;
    background: rgba(15,17,21,0.5); animation: fadeIn 0.2s var(--ease);
  }
  .nav-backdrop[hidden] { display: none; }
  .main-nav {
    position: fixed; top: 0; inset-inline-end: 0; z-index: 200;
    height: 100vh; height: 100dvh; width: min(320px, 86vw); overflow-y: auto;
    flex-direction: column; align-items: stretch; gap: 2px; white-space: normal;
    background: var(--color-surface); box-shadow: var(--shadow-lg);
    padding: 64px 18px 24px;
    transform: translateX(100%); transition: transform 0.28s var(--ease);
  }
  [dir="rtl"] .main-nav { transform: translateX(-100%); }
  .main-nav.is-open { transform: translateX(0); }
  /* Round 27 phone-usability fix (still applies inside the drawer): nav links need a real
     ~44px tap target, not a 24px-tall inline link. */
  .main-nav > a, .guest-nav-group > a { padding: 13px 4px; border-bottom: 1px solid var(--color-border); }
  .main-nav > a::after { display: none; }
  .guest-nav-group { flex-direction: column; align-items: stretch; gap: 2px; }
  .account-menu, .account-menu-trigger { width: 100%; }
  .account-menu-trigger { justify-content: flex-start; }
  .account-menu-popup { position: static; margin-top: 4px; padding: 0; border: none; box-shadow: none; animation: none; }
  .mobile-nav-close {
    display: flex !important; align-items: center; justify-content: center;
    position: absolute; top: 12px; inset-inline-end: 12px; width: 36px; height: 36px;
    background: var(--color-bg); border: 1px solid var(--color-border); border-radius: 50%;
    font-size: 15px; color: var(--color-text); cursor: pointer;
  }
  .theme-toggle button { width: 36px; height: 36px; }
  .sell-choice-card { grid-column: 1 / -1; }

  /* Bottom tab bar: the primary app-shell nav on a phone (Home/Categories/Sell/Cart/Account),
     same destinations as the drawer above so nothing here needs its own auth logic beyond the
     existing data-auth-only / data-guest-only gate. Fixed, so page content needs matching
     bottom padding — added on body below — to avoid the last row of content sitting under it. */
  .bottom-tab-bar {
    display: flex; position: fixed; left: 0; right: 0; bottom: 0; z-index: 150;
    background: var(--color-surface); border-top: 1px solid var(--color-border);
    box-shadow: 0 -2px 12px rgba(16,24,40,0.06);
    padding: 6px 4px calc(6px + env(safe-area-inset-bottom, 0px));
  }
  .bottom-tab-item {
    flex: 1; display: flex; flex-direction: column; align-items: center; gap: 2px;
    padding: 4px 2px; border-radius: var(--radius);
    color: var(--color-text-muted); font-size: 11px; font-weight: 600;
    transition: color 0.15s var(--ease), background 0.15s var(--ease);
  }
  .bottom-tab-item:hover, .bottom-tab-item:active { color: var(--color-primary-text); background: var(--color-bg); }
  .bottom-tab-item[hidden] { display: none; }
  .bottom-tab-icon { position: relative; font-size: 20px; line-height: 1; }
  .bottom-tab-sell .bottom-tab-icon {
    display: flex; align-items: center; justify-content: center; margin-bottom: 1px;
    width: 30px; height: 30px; border-radius: 50%; background: var(--gradient-primary);
    color: #fff; font-size: 16px;
  }
  #bottom-cart-badge {
    position: absolute; top: -6px; right: -10px; background: var(--color-primary); color: #fff;
    font-size: 10px; font-weight: 700; padding: 0 4px; border-radius: 999px; line-height: 1.4;
  }
  body { padding-bottom: 62px; }
}
@media (max-width: 860px) and (prefers-reduced-motion: reduce) {
  .main-nav { transition: none; }
  .nav-backdrop { animation: none; }
}
.lang-switch { font-weight: 600; }

/* ─── Layout helpers ─── */
main.container { min-height: calc(100vh - var(--header-h) - 80px); padding-top: 24px; padding-bottom: 60px; }
.page-title { font-size: 26px; font-weight: 800; margin: 0 0 4px; }
.page-subtitle { color: var(--color-text-muted); margin: 0 0 24px; }
.section { margin-bottom: 40px; }
/* Round 26: section headings were 18px with a 4px hairline — visually almost nothing, which is
   part of why the page read as undifferentiated. Bigger, heavier, with a real gradient bar. */
.section-title {
  font-size: 22px; font-weight: 800; margin: 0 0 16px; position: relative; padding-inline-start: 15px;
  letter-spacing: -0.2px;
}
.section-title::before {
  content: ""; position: absolute; inset-inline-start: 0; top: 2px; bottom: 2px; width: 6px;
  border-radius: 999px; background: var(--gradient-primary);
}
@media (max-width: 560px) { .section-title { font-size: 19px; } }

/* ─── Round 22 "not just everything in white" pass: alternating tinted section bands ───
   Reuses the same rgba(255,90,31, …) tint recipe the hero banner / focus rings / file-dropzone
   hover already use elsewhere in this file — no new colors invented, just applied to more
   surfaces so the page isn't flat white-on-gray end to end. Bleeds to the container's own edges
   via a negative margin equal to .container's horizontal padding (20px) so it reads as a true
   background band, not another card floating inside the page. */
.section-tinted {
  background: linear-gradient(180deg, rgba(255,90,31,0.06), rgba(255,90,31,0));
  border-radius: var(--radius-lg); padding: 22px 20px 26px; margin-left: -20px; margin-right: -20px;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .section-tinted { background: linear-gradient(180deg, rgba(255,90,31,0.12), rgba(255,90,31,0)); }
}
:root[data-theme="dark"] .section-tinted { background: linear-gradient(180deg, rgba(255,90,31,0.12), rgba(255,90,31,0)); }

/* ─── Home promo tiles (Round 26 — the "it still feels empty and white" fix) ───
   Three rounds of faint ambient tints didn't solve the real problem, which was density and
   color BLOCKS: Amazon.eg and dubizzle both open on a wall of saturated colored tiles, while
   this page opened on one 320px near-empty white card with huge dead margins. This is that
   wall: five real, clickable tiles, each a saturated gradient with its own identity, each
   linking to a real page in the app and (where JS fills one in) carrying a real live count. */
.promo-grid {
  display: grid; grid-template-columns: 1.7fr 1fr 1fr 1fr 1fr; gap: 14px; margin-bottom: 26px;
}
.promo-tile {
  position: relative; overflow: hidden; border-radius: var(--radius-lg); padding: 20px 18px;
  min-height: 232px; display: flex; flex-direction: column; color: #fff;
  box-shadow: 0 6px 20px rgba(17,20,28,0.16);
  animation: promoTileIn 0.5s var(--ease) backwards;
  transition: transform 0.28s var(--ease), box-shadow 0.28s var(--ease);
}
.promo-tile:nth-child(2) { animation-delay: 0.06s; }
.promo-tile:nth-child(3) { animation-delay: 0.12s; }
.promo-tile:nth-child(4) { animation-delay: 0.18s; }
.promo-tile:nth-child(5) { animation-delay: 0.24s; }
@keyframes promoTileIn { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: translateY(0); } }
.promo-tile:hover { transform: translateY(-6px); box-shadow: 0 16px 34px rgba(17,20,28,0.26); }

/* Five distinct saturated identities — the point is that the row reads as color, not as white
   cards with a colored detail. All five carry white text, so they're identical in dark mode. */
.promo-tile-1 { background: linear-gradient(145deg, #ff6a2b 0%, #e33c1c 55%, #c02b34 100%); }
.promo-tile-2 { background: linear-gradient(150deg, #0fb5a4 0%, #068d93 60%, #0a6d82 100%); }
.promo-tile-3 { background: linear-gradient(150deg, #6b62f5 0%, #5039cf 60%, #3c2a9e 100%); }
.promo-tile-4 { background: linear-gradient(150deg, #ff5d8f 0%, #e5326f 60%, #b81e5b 100%); }
.promo-tile-5 { background: linear-gradient(150deg, #ffb524 0%, #f08b05 60%, #cf6a07 100%); }

.promo-tile-hero { padding: 26px 24px; }
.promo-eyebrow {
  font-size: 12px; font-weight: 800; letter-spacing: 0.9px; text-transform: uppercase;
  opacity: 0.92; margin-bottom: 10px;
}
.promo-title { font-size: 20px; font-weight: 800; line-height: 1.22; margin-bottom: 6px; }
.promo-tile-hero .promo-title { font-size: 31px; max-width: 92%; }
.promo-sub { font-size: 13px; line-height: 1.45; opacity: 0.94; max-width: 94%; }
.promo-tile-hero .promo-sub { font-size: 15px; max-width: 78%; }
.promo-count {
  margin-top: auto; padding-top: 12px; font-size: 13px; font-weight: 800;
  display: inline-flex; align-items: center; gap: 6px;
}
.promo-count::before { content: "●"; font-size: 8px; opacity: 0.85; }
.promo-badge {
  align-self: flex-start; background: rgba(255,255,255,0.22); backdrop-filter: blur(2px);
  border-radius: 999px; padding: 4px 11px; font-size: 12px; font-weight: 800; margin-bottom: 10px;
  letter-spacing: 0.3px;
}
.promo-cta {
  margin-top: 14px; align-self: flex-start; background: #fff; color: #17181c; border-radius: 999px;
  padding: 9px 18px; font-size: 13.5px; font-weight: 800; box-shadow: 0 4px 12px rgba(0,0,0,0.18);
  transition: transform 0.18s var(--ease), box-shadow 0.18s var(--ease);
}
.promo-tile:hover .promo-cta { transform: translateY(-2px); box-shadow: 0 8px 18px rgba(0,0,0,0.26); }
/* The oversized emoji is decoration, not content (aria-hidden in the markup) — it gives each
   tile a focal point the way the product photography does on Amazon's banners. */
.promo-art {
  position: absolute; inset-inline-end: -6px; bottom: -14px; font-size: 76px; line-height: 1;
  opacity: 0.26; transform: rotate(-10deg); pointer-events: none;
  transition: transform 0.4s var(--ease), opacity 0.4s var(--ease);
}
.promo-tile-hero .promo-art { font-size: 108px; bottom: -20px; }
.promo-tile:hover .promo-art { transform: rotate(-4deg) scale(1.12); opacity: 0.34; }

@media (max-width: 1080px) {
  .promo-grid { grid-template-columns: 1fr 1fr; }
  .promo-tile-hero { grid-column: 1 / -1; min-height: 200px; }
  .promo-tile { min-height: 176px; }
}
@media (max-width: 560px) {
  /* Round 27: stacked one-per-row, the five tiles were ~900px of banner to scroll past before
     a single product appeared on a phone. They're a swipeable row instead now — the same five
     real tiles, one screen instead of five, which is exactly how Amazon's phone banners behave.
     Scroll-snap so each tile lands squarely, and the scrollbar is hidden since the partially
     visible next tile is the affordance. */
  .promo-grid {
    display: flex; gap: 12px; overflow-x: auto; scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch; padding-bottom: 6px;
    margin-left: -20px; margin-right: -20px; padding-left: 20px; padding-right: 20px;
  }
  .promo-grid::-webkit-scrollbar { display: none; }
  .promo-grid { scrollbar-width: none; }
  .promo-tile { flex: 0 0 84%; scroll-snap-align: center; min-height: 190px; }
  .promo-tile-hero .promo-title { font-size: 25px; max-width: 100%; }
  .promo-tile-hero .promo-sub { max-width: 100%; }
}
@media (prefers-reduced-motion: reduce) { .promo-tile { animation: none; } }

/* ─── Grid / cards ─── */
.grid {
  display: grid; gap: 16px;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
}
.card {
  background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius);
  overflow: hidden; box-shadow: var(--shadow); display: flex; flex-direction: column;
  transition: transform 0.25s var(--ease), box-shadow 0.25s var(--ease), border-color 0.25s var(--ease);
}
/* Warm-tinted lift instead of a neutral gray shadow — reads as the card "coming alive" rather
   than just moving up a few pixels. */
.card:hover { transform: translateY(-6px); box-shadow: 0 14px 30px rgba(255,90,31,0.22), var(--shadow-lg); border-color: rgba(255,90,31,0.35); }
/* Round 26: product tiles sat on flat gray, which is most of why grids of them read as "white
   and empty" — Amazon's product tiles all sit on a warm tinted panel. Same idea here, with the
   tint rotating through the app's existing tag-token palette by card position so a row of six
   reads as colorful rather than as six identical gray squares. The photo itself covers the tile
   once it loads; the tint is what shows behind a transparent PNG, a still-loading image, and
   the emoji placeholder cards. */
.card-media {
  aspect-ratio: 1 / 1; background: var(--color-media-bg); display: flex; align-items: center;
  justify-content: center; overflow: hidden; position: relative;
}
.grid > *:nth-child(5n+1) .card-media { background: linear-gradient(160deg, rgba(255,90,31,0.16), rgba(255,90,31,0.05)); }
.grid > *:nth-child(5n+2) .card-media { background: linear-gradient(160deg, rgba(54,81,212,0.14), rgba(54,81,212,0.04)); }
.grid > *:nth-child(5n+3) .card-media { background: linear-gradient(160deg, rgba(18,183,106,0.14), rgba(18,183,106,0.04)); }
.grid > *:nth-child(5n+4) .card-media { background: linear-gradient(160deg, rgba(229,50,111,0.14), rgba(229,50,111,0.04)); }
.grid > *:nth-child(5n+5) .card-media { background: linear-gradient(160deg, rgba(247,144,9,0.16), rgba(247,144,9,0.05)); }
.card-media img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.4s var(--ease); }
.card:hover .card-media img { transform: scale(1.08); }
/* Round 32: swipeable multi-photo cards — a real horizontal scroller (native touch swipe, no
   JS drag math), one real photo per screen, with dots showing position. A single-photo product
   never gets this markup at all (see components.js), so nothing changes for most cards. */
.card-media-swipe {
  position: absolute; inset: 0; display: flex; overflow-x: auto; scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch; scrollbar-width: none;
}
.card-media-swipe::-webkit-scrollbar { display: none; }
.card-media-swipe img {
  flex: 0 0 100%; width: 100%; height: 100%; object-fit: cover; scroll-snap-align: start;
}
.card-media-dots {
  position: absolute; bottom: 8px; left: 50%; transform: translateX(-50%); z-index: 2;
  display: flex; gap: 4px; pointer-events: none;
}
.card-media-dot {
  width: 5px; height: 5px; border-radius: 50%; background: rgba(255,255,255,0.55);
  box-shadow: 0 0 2px rgba(0,0,0,0.3); transition: background 0.2s var(--ease), width 0.2s var(--ease);
}
.card-media-dot.active { background: #fff; width: 14px; border-radius: 3px; }
.card-media .placeholder-icon { font-size: 40px; opacity: 0.35; transition: transform 0.3s var(--ease); }
.card:hover .card-media .placeholder-icon { transform: scale(1.1) rotate(-4deg); }
.card-body { padding: 12px 14px; display: flex; flex-direction: column; gap: 4px; flex: 1; }
.card-title { font-size: 14px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.card-store { font-size: 12px; color: var(--color-text-muted); }
.card-price { font-size: 15px; font-weight: 800; color: var(--color-text); }
.card-price small { font-weight: 500; font-size: 12px; color: var(--color-text-muted); }
.card-link { display: block; }
.rating { font-size: 12px; color: var(--color-warning-text); }

/* ─── Round 29: real mobile app density, not a shrunk desktop page ───
   The actual bug users were seeing: .grid's auto-fill with a 210px minimum NEVER fits two
   across a real phone width (a 375px screen minus 2×20px container padding leaves 335px —
   210×2=420 doesn't fit — so auto-fill collapses to exactly one column), and .section's 40px
   gaps plus the 20px edge padding are desktop measurements carried over unchanged. The result
   was one giant stretched card per row with huge dead margins — reads as a compressed desktop
   layout because structurally it *was* the desktop layout, just narrower. Amazon/Noon/dubizzle
   all run a dense 2-column product grid with tight edge margins on a phone; this is that. */
@media (max-width: 480px) {
  /* Real phones (not just narrow desktop windows): scale the whole page down slightly so it
     reads as a native app's density rather than a website at 100%. This is a uniform zoom, not
     more per-element tweaking — every font, icon and spacing shrinks together, proportionally. */
  body { zoom: 0.92; }
}
@media (max-width: 640px) {
  .container { padding: 0 14px; }
  .section { margin-bottom: 26px; }
  .section-tinted { margin-left: -14px; margin-right: -14px; padding: 18px 14px 22px; }
  .grid { grid-template-columns: repeat(2, 1fr); gap: 10px; }
  .card-body { padding: 9px 10px; gap: 3px; }
  .card-title { font-size: 13px; }
  .card-store { font-size: 11px; }
  .card-price { font-size: 14px; }
  .card-media .placeholder-icon { font-size: 32px; }
  .card:hover { transform: none; } /* hover-lift is a desktop affordance; on touch it just looks like an accidental jump */
  main.container { padding-top: 14px; }
  .page-title { font-size: 22px; }
  .page-subtitle { margin-bottom: 16px; }

  /* Round 31: the promo hero / trust pills / category badges were still sized for a desktop
     first-screen (31px hero title, 190px-tall tiles, 46px round badges) — correct pixel values,
     but on a 335px-wide screen they eat the whole viewport before any product is visible, which
     reads as "zoomed in" even though nothing is actually mis-scaled. Tightened to real phone-app
     density (compare Talabat/Noon's home header, which is ~120px tall, not ~200px). */
  .promo-grid { margin-bottom: 18px; }
  .promo-tile { min-height: 148px; padding: 16px 15px; }
  .promo-tile-hero { min-height: 148px; padding: 18px 17px; }
  .promo-tile-hero .promo-title { font-size: 21px; }
  .promo-tile-hero .promo-sub { font-size: 13px; }
  .promo-title { font-size: 16px; }
  .promo-sub { font-size: 12px; }
  .promo-eyebrow { font-size: 11px; margin-bottom: 6px; }
  .promo-cta { padding: 7px 14px; font-size: 12.5px; margin-top: 10px; }
  .promo-art { font-size: 56px; }
  .promo-tile-hero .promo-art { font-size: 76px; }

  .trust-bar { gap: 8px; margin-bottom: 22px; }
  .trust-tile { padding: 6px 14px 6px 6px; font-size: 12px; gap: 7px; }
  .trust-tile .trust-icon { width: 26px; height: 26px; font-size: 13px; }

  .category-icon-grid { grid-template-columns: repeat(4, 1fr); gap: 8px; }
  .category-icon-card { padding: 10px 4px; gap: 4px; }
  .category-icon-badge { width: 38px; height: 38px; font-size: 18px; }
  .category-icon-name { font-size: 11px; }

  .section-title { font-size: 16px; }
}

/* ─── Buttons / forms ─── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 10px 18px; border-radius: var(--radius); font-weight: 600; font-size: 14px;
  border: 1px solid transparent; cursor: pointer; transition: transform 0.15s var(--ease), box-shadow 0.15s var(--ease), background 0.15s var(--ease), border-color 0.15s var(--ease);
  position: relative; overflow: hidden;
}
.btn:hover:not(:disabled) { transform: translateY(-1px); }
.btn:active:not(:disabled) { transform: translateY(0) scale(0.97); }
.btn-primary { background: var(--gradient-primary); color: #fff; box-shadow: 0 2px 8px rgba(255,90,31,0.28); }
.btn-primary:hover:not(:disabled) { box-shadow: 0 6px 16px rgba(255,90,31,0.38); }
.btn-secondary { background: var(--color-surface); border-color: var(--color-border); color: var(--color-text); }
.btn-secondary:hover:not(:disabled) { border-color: var(--color-text-muted); box-shadow: var(--shadow); }
.identifier-tab.active { background: var(--color-primary-text); border-color: var(--color-primary-text); color: #fff; }
.btn-danger { background: var(--color-surface-alt); border-color: var(--color-danger); color: var(--color-danger); }
.btn-danger:hover:not(:disabled) { background: var(--color-danger); color: #fff; }
.btn-block { width: 100%; }
.btn-sm { padding: 6px 12px; font-size: 13px; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
/* Round 22 fix: a disabled .btn-primary previously just sat at 50% opacity — its orange
   gradient stayed vivid enough that shoppers read it as a live, clickable button (the exact
   "Add to Cart looks enabled while Buy Now looks disabled" complaint), even though the
   `disabled` attribute was already correctly set. Flattening it to the same neutral/gray
   treatment as a disabled secondary button makes "not clickable yet" obvious regardless of
   which button variant it is. */
.btn-primary:disabled { background: var(--color-border); color: var(--color-text-muted); box-shadow: none; opacity: 1; }

/* Ripple feedback on click, triggered by ux.js */
.btn .btn-ripple {
  position: absolute; border-radius: 50%; background: rgba(255,255,255,0.55);
  transform: scale(0); animation: btnRipple 0.55s var(--ease); pointer-events: none;
}
@keyframes btnRipple { to { transform: scale(2.6); opacity: 0; } }

.field { margin-bottom: 16px; display: flex; flex-direction: column; gap: 6px; }
/* .field's flex column defaults every direct-child flex item to align-items:stretch — fine for
   inputs (already width:100% anyway) but it also silently stretched any <button class="btn">
   placed directly inside a .field (e.g. store/products.html's "+ Add variant"/"+ Add
   specification" buttons) into a full-width bar with none of .btn's usual pill/inline look, even
   though the class was applied correctly. Round 22 fix: let a directly-nested .btn size to its
   own content again, like every other .btn on the site. */
.field > .btn { align-self: flex-start; }
.field label { font-size: 13px; font-weight: 600; }
.field input, .field select, .field textarea {
  padding: 10px 12px; border: 1px solid var(--color-border); border-radius: 8px; outline: none; width: 100%;
  background: var(--color-surface); transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
.field input:focus, .field select:focus, .field textarea:focus { border-color: var(--color-primary); box-shadow: 0 0 0 3px rgba(255,90,31,0.12); }
.field-error { color: var(--color-danger); font-size: 12px; animation: slideDown 0.2s var(--ease); }
.form-card {
  background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius);
  padding: 28px; max-width: 460px; margin: 0 auto; box-shadow: var(--shadow-md);
  animation: fadeInUp 0.4s var(--ease);
}
.form-card.wide { max-width: 720px; }
.checkbox-row { display: flex; align-items: center; gap: 8px; font-size: 13px; }
.checkbox-row input { width: auto; }

/* ─── Custom form controls (Round 18 — "cool design not the default browser controls") ───
   Native <select>/<input type=checkbox|radio> get a hand-drawn look via appearance:none plus a
   background-image arrow (select) or a ::after pseudo-element mark (checkbox/radio). The
   underlying element is still a real, focusable <select>/<input>, so keyboard nav, screen
   readers and native form validation all keep working exactly as before — this is styling only.
   Applied as GLOBAL base selectors (select / input[type=checkbox] / input[type=radio]) rather
   than scoped to .field, since an audit of every template found zero admin filter dropdowns and
   only a dozen <select>s total (see search/categories/products/used/kyc/register) — nothing
   that going global would break — so every existing and future form picks this up automatically. */
:root {
  --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%23667085' stroke-width='1.6' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%239aa1ad' stroke-width='1.6' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  }
}
:root[data-theme="dark"] {
  --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%239aa1ad' stroke-width='1.6' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
select:not([multiple]) {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  padding: 10px 12px; padding-inline-end: 34px; border: 1px solid var(--color-border); border-radius: 8px;
  background-color: var(--color-surface); background-image: var(--select-arrow);
  background-repeat: no-repeat; background-position: right 12px center; background-size: 11px 7px;
  cursor: pointer; outline: none; transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
[dir="rtl"] select:not([multiple]) { background-position: left 12px center; }
select:not([multiple]):focus { border-color: var(--color-primary); box-shadow: 0 0 0 3px rgba(255,90,31,0.12); }
select:not([multiple]):disabled { opacity: 0.6; cursor: not-allowed; }
/* .field select already gets border/padding/width from ".field input, .field select, .field
   textarea" above — restate just the end-padding + arrow here (later in source, same
   specificity) so the arrow never sits under the option text inside a .field. */
.field select:not([multiple]) {
  padding-inline-end: 34px; background-image: var(--select-arrow); background-repeat: no-repeat;
  background-position: right 12px center; background-size: 11px 7px;
}
[dir="rtl"] .field select:not([multiple]) { background-position: left 12px center; }

input[type="checkbox"], input[type="radio"] {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  width: 18px; height: 18px; min-width: 18px; min-height: 18px; margin: 0; padding: 0;
  border: 1.5px solid var(--color-border); background: var(--color-surface);
  display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
  position: relative; cursor: pointer; vertical-align: middle;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
input[type="checkbox"] { border-radius: 5px; }
input[type="radio"] { border-radius: 50%; }
input[type="checkbox"]:checked, input[type="radio"]:checked { background: var(--gradient-primary); border-color: var(--color-primary); }
input[type="checkbox"]:checked::after {
  content: ""; position: absolute; left: 5px; top: 2px; width: 5px; height: 9px;
  border: solid #fff; border-width: 0 2px 2px 0; transform: rotate(45deg);
}
input[type="radio"]:checked::after { content: ""; position: absolute; width: 8px; height: 8px; border-radius: 50%; background: #fff; }
input[type="checkbox"]:focus-visible, input[type="radio"]:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(255,90,31,0.16); }
input[type="checkbox"]:disabled, input[type="radio"]:disabled { opacity: 0.55; cursor: not-allowed; }
/* Checkboxes/radios placed inside a .field (e.g. store/products.html's "Halal Certified"
   toggle) would otherwise inherit .field input's width:100%/padding:10px 12px/8px-radius —
   restate the compact square shape here (higher specificity than ".field input" so it wins). */
.field input[type="checkbox"] { width: 18px; height: 18px; padding: 0; border: 1.5px solid var(--color-border); border-radius: 5px; }
.field input[type="radio"] { width: 18px; height: 18px; padding: 0; border: 1.5px solid var(--color-border); border-radius: 50%; }
/* Native <input type=date> (register.html's signup DOB step, store/kyc.html's seller DOB) picks
   up the same border/padding/background as every other .field input already — this just makes
   the browser's own calendar icon + popup follow the site's light/dark theme instead of always
   rendering as if the page were light. */
input[type="date"] { color-scheme: light; cursor: pointer; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) input[type="date"] { color-scheme: dark; } }
:root[data-theme="dark"] input[type="date"] { color-scheme: dark; }
/* Round 18 sweep: the min/max price filter fields (categories/search/used browse) are plain
   type=number inputs — drop the browser's default up/down spinner arrows so they read as the
   same clean bordered field as everything else instead of a leftover native control. */
input[type="number"] { -moz-appearance: textfield; }
input[type="number"]::-webkit-outer-spin-button, input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none; margin: 0;
}

/* ─── Status / badges ─── */
.tag {
  display: inline-block; padding: 3px 10px; border-radius: 999px; font-size: 12px; font-weight: 700;
  background: var(--tag-bg); color: var(--color-text-muted);
}
.tag-success { background: var(--tag-success-bg); color: var(--color-success-text); }
.tag-warning { background: var(--tag-warning-bg); color: var(--color-warning); }
.tag-danger { background: var(--tag-danger-bg); color: var(--color-danger); }

/* ─── Alerts ─── */
.alert { padding: 12px 16px; border-radius: 8px; font-size: 14px; margin-bottom: 16px; animation: slideDown 0.25s var(--ease); }
.alert-error { background: var(--alert-error-bg); color: var(--color-danger); border: 1px solid var(--alert-error-border); }
.alert-success { background: var(--alert-success-bg); color: var(--color-success); border: 1px solid var(--alert-success-border); }
.alert-info { background: var(--alert-info-bg); color: var(--alert-info-color); border: 1px solid var(--alert-info-border); }

/* ─── Empty states ─── */
.empty-state { text-align: center; padding: 60px 20px; color: var(--color-text-muted); animation: fadeIn 0.4s var(--ease); }
.empty-state .empty-icon { font-size: 42px; margin-bottom: 12px; animation: softBounce 2.4s ease-in-out infinite; display: inline-block; }
.empty-state h3 { color: var(--color-text); margin: 0 0 6px; }

/* ─── Tables ─── */
table.data-table { width: 100%; border-collapse: collapse; background: var(--color-surface); border-radius: var(--radius); overflow: hidden; }
table.data-table th, table.data-table td { padding: 10px 14px; border-bottom: 1px solid var(--color-border); text-align: left; font-size: 14px; }
/* Round 18: product.html's "compare prices across stores" rows navigate to that listing's
   product page on click (see loadCompare) — hint it's clickable like any other card. */
tr.compare-row { cursor: pointer; transition: background 0.12s var(--ease); }
tr.compare-row:hover, tr.compare-row:focus-visible { background: var(--color-bg); outline: none; }
table.data-table th { background: var(--color-bg); font-weight: 700; font-size: 12px; text-transform: uppercase; color: var(--color-text-muted); }
.table-wrap { overflow-x: auto; border: 1px solid var(--color-border); border-radius: var(--radius); }
.table-wrap table.data-table { border: none; }

/* ─── Stat tiles ─── */
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 14px; margin-bottom: 28px; }
.stat-tile { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius); padding: 16px; }
.stat-tile .stat-label { font-size: 12px; color: var(--color-text-muted); font-weight: 600; text-transform: uppercase; }
.stat-tile .stat-value { font-size: 24px; font-weight: 800; margin-top: 4px; }

/* ─── Cart / checkout ─── */
.store-group { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius); margin-bottom: 16px; overflow: hidden; }
.store-group-header { padding: 12px 16px; background: var(--color-bg); font-weight: 700; font-size: 14px; border-bottom: 1px solid var(--color-border); }
.cart-item { display: flex; gap: 12px; padding: 14px 16px; border-bottom: 1px solid var(--color-border); align-items: center; }
.cart-item:last-child { border-bottom: none; }
.cart-item img, .cart-item .placeholder-icon { width: 60px; height: 60px; object-fit: cover; border-radius: 8px; background: var(--color-media-bg); }
.cart-item .placeholder-icon { display: flex; align-items: center; justify-content: center; font-size: 22px; opacity: 0.4; }
.cart-item-info { flex: 1; min-width: 0; }
.cart-item-info .name { font-weight: 600; font-size: 14px; }
.cart-item-info .attrs { font-size: 12px; color: var(--color-text-muted); }
.qty-control { display: flex; align-items: center; gap: 8px; }
.qty-control button { width: 28px; height: 28px; border-radius: 6px; border: 1px solid var(--color-border); background: var(--color-surface-alt); cursor: pointer; }
.summary-row { display: flex; justify-content: space-between; padding: 6px 0; font-size: 14px; }
.summary-row.total { font-weight: 800; font-size: 17px; border-top: 1px solid var(--color-border); margin-top: 8px; padding-top: 12px; }
.summary-card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius); padding: 20px; }

/* ─── Two-column layout ─── */
.layout-2col { display: grid; grid-template-columns: 2fr 1fr; gap: 24px; align-items: start; }
@media (max-width: 860px) { .layout-2col { grid-template-columns: 1fr; } }

/* ─── Product detail ─── */
.product-detail { display: grid; grid-template-columns: 1fr 1fr; gap: 40px; }
@media (max-width: 860px) { .product-detail { grid-template-columns: 1fr; } }
.gallery-main { aspect-ratio: 1/1; background: var(--color-media-bg); border-radius: var(--radius); overflow: hidden; margin-bottom: 10px; display: flex; align-items: center; justify-content: center; box-shadow: var(--shadow); position: relative; touch-action: pan-y; }
.gallery-main img { width: 100%; height: 100%; object-fit: cover; animation: fadeIn 0.3s var(--ease); }
.gallery-dots { position: absolute; bottom: 12px; left: 50%; transform: translateX(-50%); display: flex; gap: 5px; }
.gallery-dot { width: 6px; height: 6px; border-radius: 50%; background: rgba(255,255,255,0.55); box-shadow: 0 0 2px rgba(0,0,0,0.3); transition: background 0.2s var(--ease), width 0.2s var(--ease); }
.gallery-dot.active { background: #fff; width: 16px; border-radius: 3px; }
@media (min-width: 861px) { .gallery-dots { display: none; } } /* desktop has the click-thumbnail row — dots are the phone-swipe affordance */
/* Round 33: "the image is too large on phone" — a full-bleed square photo (up to ~340px tall)
   pushed the price/add-to-cart button off the first screen entirely. Capped narrower and
   centered, like Noon/Amazon's phone product page, so title/price/button are visible without
   scrolling past the photo first. */
@media (max-width: 640px) {
  .gallery-main { max-width: 62%; margin-left: auto; margin-right: auto; }
}
.gallery-arrow {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  width: 34px; height: 34px; border-radius: 50%; border: none; cursor: pointer;
  background: rgba(20,22,26,0.4); color: #fff; font-size: 20px; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  transition: background 0.15s var(--ease), transform 0.15s var(--ease);
}
.gallery-arrow:hover { background: rgba(20,22,26,0.65); transform: translateY(-50%) scale(1.08); }
.gallery-arrow-prev { inset-inline-start: 10px; }
.gallery-arrow-next { inset-inline-end: 10px; }
@media (max-width: 640px) { .gallery-arrow { display: none; } } /* touch swipe replaces it on phone */
.gallery-thumbs { display: flex; gap: 8px; }
/* Round 25 a11y: the thumbnails are <button> elements now (keyboard-operable), with the image
   inside as decoration. Styles moved to the button so the look is identical. */
.gallery-thumbs .gallery-thumb { width: 60px; height: 60px; padding: 0; background: none; border-radius: 6px; cursor: pointer; border: 2px solid transparent; transition: border-color 0.15s var(--ease), transform 0.15s var(--ease); overflow: hidden; line-height: 0; }
.gallery-thumbs .gallery-thumb img { width: 100%; height: 100%; object-fit: cover; }
.gallery-thumbs .gallery-thumb:hover { transform: translateY(-2px); }
.gallery-thumbs .gallery-thumb.active { border-color: var(--color-primary); }
.variant-options { display: flex; flex-wrap: wrap; gap: 8px; }
.variant-chip { padding: 8px 14px; border: 1px solid var(--color-border); border-radius: 8px; cursor: pointer; font-size: 13px; transition: border-color 0.15s var(--ease), transform 0.15s var(--ease), color 0.15s var(--ease); }
.variant-chip:hover { transform: translateY(-1px); border-color: var(--color-text-muted); }
.variant-chip.active { border-color: var(--color-primary); color: var(--color-primary-text); font-weight: 700; }
.variant-chip-disabled { cursor: not-allowed; opacity: .5; text-decoration: line-through; }
.variant-chip-disabled:hover { transform: none; border-color: var(--color-border); }
/* ─── Round 22: separate Color/Size attribute rows (was one flat chip per full combination) ─── */
.variant-attr-row { margin-bottom: 14px; }
.variant-chip-color { padding-inline-start: 8px; display: inline-flex; align-items: center; }
.variant-swatch {
  display: inline-block; width: 14px; height: 14px; border-radius: 50%; margin-inline-end: 7px;
  vertical-align: middle; border: 1px solid var(--color-border); flex-shrink: 0;
  box-shadow: inset 0 0 0 1px rgba(0,0,0,0.05);
}

/* ─── Footer ─── */
.site-footer { border-top: 1px solid var(--color-border); padding: 28px 0; color: var(--color-text-muted); font-size: 13px; text-align: center; }
.footer-links { display: flex; justify-content: center; gap: 18px; flex-wrap: wrap; margin-bottom: 12px; font-weight: 600; }
.footer-links a { transition: color 0.15s var(--ease); }
.footer-links a:hover { color: var(--color-primary-text); }
.footer-socials { display: flex; justify-content: center; gap: 14px; margin-bottom: 12px; }
.footer-socials a {
  width: 34px; height: 34px; border-radius: 50%; border: 1px solid var(--color-border);
  display: flex; align-items: center; justify-content: center; font-size: 15px;
  transition: transform 0.2s var(--ease), border-color 0.2s var(--ease), box-shadow 0.2s var(--ease);
}
.footer-socials a:hover { border-color: var(--color-primary); color: var(--color-primary); transform: translateY(-3px) scale(1.08); box-shadow: var(--shadow-md); }

/* ─── Cookie consent banner (Trust/Legal/Compliance) ─── */
.cookie-banner {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 500;
  background: var(--color-surface); border-top: 1px solid var(--color-border);
  box-shadow: 0 -4px 16px rgba(16,24,40,0.10); padding: 14px 16px;
}
.cookie-banner-inner {
  max-width: 980px; margin: 0 auto; display: flex; align-items: center;
  justify-content: space-between; gap: 16px; flex-wrap: wrap;
}
.cookie-banner-inner p { margin: 0; font-size: 13px; color: var(--color-text-muted); flex: 1; min-width: 240px; }
.cookie-banner-actions { display: flex; gap: 10px; flex: none; }

/* ─── Halal Use Reminders ─── */
.tag-halal { background: color-mix(in srgb, #0f9d58 15%, transparent); color: #0f9d58; }
.halal-reminder {
  display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--color-text-muted);
  background: color-mix(in srgb, #0f9d58 8%, transparent); border: 1px solid color-mix(in srgb, #0f9d58 25%, transparent);
  border-radius: 10px; padding: 10px 14px; margin: 10px 0;
}

/* ─── Terms link, price/discount, sponsored ─── */
.terms-link { color: var(--terms-link-color); font-weight: 600; text-decoration: underline; }
.terms-link:hover { color: var(--terms-link-hover); }
.card-price-old { text-decoration: line-through; color: var(--color-text-muted); font-weight: 500; font-size: 12px; margin-inline-start: 6px; }
.tag-discount { background: var(--tag-danger-bg); color: var(--color-danger-text); }
.tag-sponsored { background: var(--tag-sponsored-bg); color: var(--tag-sponsored-color); font-size: 10px; }
.tag-bestseller { background: linear-gradient(100deg, var(--tag-bestseller-bg1), var(--tag-bestseller-bg2)); color: var(--tag-bestseller-color); }
.urgency-badge { animation: urgentPulse 1.6s ease-in-out infinite; }
@keyframes urgentPulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.55; } }

/* ─── Home tabs ─── */
.home-tabs { display: flex; gap: 8px; flex-wrap: wrap; margin: 0 0 18px; border-bottom: 1px solid var(--color-border); }
.home-tabs button {
  padding: 10px 4px; margin-inline-end: 22px; border: none; background: none; cursor: pointer;
  font-size: 14px; font-weight: 600; color: var(--color-text-muted); border-bottom: 2px solid transparent;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease);
}
.home-tabs button:hover { color: var(--color-text); }
.home-tabs button.active { color: var(--color-primary); border-bottom-color: var(--color-primary); }

/* ─── Recent searches ─── */
.recent-searches { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 20px; }
.recent-searches .chip { padding: 6px 12px; border-radius: 999px; background: var(--color-bg); border: 1px solid var(--color-border); font-size: 13px; }
.recent-searches .chip a { font-weight: 600; }
.recent-searches .clear-history { font-size: 13px; color: var(--color-danger); font-weight: 600; cursor: pointer; }

/* ─── Promote inline form ─── */
.promote-box { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-top: 8px; }
.promote-box select { padding: 6px 10px; border-radius: 8px; border: 1px solid var(--color-border); }

/* ─── Misc ─── */
.skeleton { background: linear-gradient(90deg, var(--skeleton-a) 25%, var(--skeleton-b) 37%, var(--skeleton-a) 63%); background-size: 400% 100%; animation: skeleton-loading 1.4s ease infinite; border-radius: 6px; }
@keyframes skeleton-loading { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }
.flex-between { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.mt-0 { margin-top: 0; }
.text-muted { color: var(--color-text-muted); }
.pill-nav { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 20px; }
.pill-nav a, .pill-nav button { padding: 7px 14px; border-radius: 999px; border: 1px solid var(--color-border); background: var(--color-surface-alt); font-size: 13px; font-weight: 600; cursor: pointer; transition: transform 0.15s var(--ease), background 0.15s var(--ease), color 0.15s var(--ease), border-color 0.15s var(--ease); }
.pill-nav a:hover, .pill-nav button:hover { transform: translateY(-1px); border-color: var(--color-text-muted); }
.pill-nav a.active, .pill-nav button.active { background: var(--color-text); color: #fff; border-color: var(--color-text); }

.qty-control button { transition: transform 0.15s var(--ease), border-color 0.15s var(--ease); }
.qty-control button:hover { border-color: var(--color-primary); color: var(--color-primary); }
.qty-control button:active { transform: scale(0.9); }

/* ─── Modal overlays ─── */
/* Centers the modal only while it's actually shown. Relying on the [hidden] attribute
   alone to toggle visibility (instead of also setting inline display:flex) keeps the
   browser's default `[hidden] { display: none }` rule in control, so the modal can
   never be accidentally visible on page load. */
.modal-overlay:not([hidden]) { display: flex; animation: fadeIn 0.2s var(--ease); }
.modal-overlay:not([hidden]) .form-card { animation: popIn 0.3s var(--ease); }

/* ─── Loading spinner ─── */
.spinner {
  display: inline-block; width: 18px; height: 18px; border-radius: 50%;
  border: 2.5px solid rgba(255,255,255,0.4); border-top-color: #fff; animation: spin 0.7s linear infinite;
}
.spinner.spinner-dark { border-color: rgba(22,24,29,0.15); border-top-color: var(--color-primary); }

/* ─── Scroll progress bar (ux.js) ─── */
.scroll-progress {
  position: fixed; top: 0; left: 0; height: 3px; width: 0%; z-index: 60;
  background: var(--gradient-primary); transition: width 0.1s linear;
}

/* ─── Back-to-top button (ux.js) ─── */
.back-to-top {
  position: fixed; bottom: 24px; inset-inline-end: 24px; width: 44px; height: 44px; border-radius: 50%;
  background: var(--color-surface); border: 1px solid var(--color-border); box-shadow: var(--shadow-lg);
  display: flex; align-items: center; justify-content: center; cursor: pointer; font-size: 16px;
  opacity: 0; transform: translateY(12px) scale(0.9); pointer-events: none;
  transition: opacity 0.25s var(--ease), transform 0.25s var(--ease), border-color 0.2s var(--ease), color 0.2s var(--ease);
  z-index: 55; color: var(--color-text-muted);
}
.back-to-top.is-visible { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; }
.back-to-top:hover { border-color: var(--color-primary); color: var(--color-primary); transform: translateY(-3px) scale(1.05); }

/* ─── Order-placed celebration (ux.js confetti + order_detail.html banner) ─── */
.celebrate-banner {
  background: linear-gradient(120deg, var(--celebrate-bg1), var(--celebrate-bg2)); border: 1px solid var(--celebrate-border); border-radius: var(--radius-lg);
  padding: 24px 28px; margin-bottom: 24px; text-align: center; animation: popIn 0.4s var(--ease);
}
.celebrate-banner h2 { margin: 0 0 4px; font-size: 22px; }
.celebrate-banner p { margin: 0; color: var(--color-text-muted); }
.confetti-piece {
  position: fixed; top: -12px; width: 8px; height: 14px; z-index: 3000; pointer-events: none;
  animation: confettiFall linear forwards;
}
@keyframes confettiFall {
  to { transform: translateY(105vh) rotate(540deg); opacity: 0.9; }
}

/* ─── Toast notifications (ux.js) ─── */
.toast-stack {
  position: fixed; bottom: 20px; right: 20px; z-index: 2000; display: flex; flex-direction: column; gap: 10px;
  max-width: 340px;
}
[dir="rtl"] .toast-stack { right: auto; left: 20px; }
.toast {
  background: var(--color-surface); border: 1px solid var(--color-border); border-radius: 10px;
  padding: 12px 16px; box-shadow: var(--shadow-lg); font-size: 13px; font-weight: 600;
  animation: fadeInUp 0.3s var(--ease); display: flex; align-items: center; gap: 8px;
}
.toast.toast-success { border-inline-start: 3px solid var(--color-success); }
.toast.toast-error { border-inline-start: 3px solid var(--color-danger); }
.toast.toast-leaving { animation: fadeIn 0.25s var(--ease) reverse forwards; }

/* ─── Theme toggle (round 11) ─── */
.theme-toggle {
  display: flex; align-items: center; border: 1px solid var(--color-border); border-radius: 999px;
  padding: 3px; gap: 2px; background: var(--color-bg); flex-shrink: 0;
}
.theme-toggle button {
  border: none; background: none; cursor: pointer; width: 28px; height: 28px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center; font-size: 13px; color: var(--color-text-muted);
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.theme-toggle button.active { background: var(--color-surface); color: var(--color-primary-text); box-shadow: var(--shadow); }
.theme-toggle button:hover { color: var(--color-primary-text); }

/* ─── Slogan badge ─── */
.slogan-badge {
  display: inline-block; font-size: 12px; font-weight: 700; color: var(--color-primary-text);
  background: var(--tag-bg); padding: 2px 10px; border-radius: 999px; margin-inline-start: 8px;
  vertical-align: middle; letter-spacing: 0.2px; white-space: nowrap;
}
@media (max-width: 640px) { .slogan-badge { display: none; } }
.hero-slogan {
  font-weight: 700; color: var(--color-primary); font-size: 14px; margin: 0 0 6px;
  letter-spacing: 0.3px; text-transform: uppercase; animation: fadeInUp 0.4s var(--ease);
}

/* ─── Infinite scroll sentinel (round 12) ─── */
.scroll-sentinel { height: 40px; display: flex; align-items: center; justify-content: center; margin-top: 8px; }
.scroll-sentinel::after {
  content: ""; width: 22px; height: 22px; border-radius: 50%; border: 3px solid var(--color-border);
  border-top-color: var(--color-primary); opacity: 0; transition: opacity 0.2s var(--ease); animation: spin 0.7s linear infinite;
}
.scroll-sentinel.is-loading-more::after { opacity: 1; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ─── Category icon grid (home page top-6 + "view all" page, round 12) ─── */
.category-icon-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); gap: 12px;
}
.category-icon-card {
  display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center;
  background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius);
  padding: 16px 8px; color: var(--color-text); position: relative; overflow: hidden;
  transition: transform 0.2s var(--ease), box-shadow 0.2s var(--ease), border-color 0.2s var(--ease);
  animation: fadeInUp 0.5s var(--ease) backwards;
}
.category-icon-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); border-color: var(--color-primary); }
.category-icon-badge {
  width: 46px; height: 46px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
  font-size: 22px; background: var(--color-bg); transition: transform 0.2s var(--ease);
}
.category-icon-card:hover .category-icon-badge { transform: scale(1.1) rotate(-4deg); }
.category-icon-name { font-size: 13px; font-weight: 600; }
.category-icon-count {
  font-size: 12px; color: var(--color-text-muted); background: var(--color-bg); border-radius: 999px;
  padding: 1px 8px; position: absolute; top: 8px; right: 8px;
}

/* ─── Trust bar (home page — 3 static badges, round 12 simplified, round 25 colorized) ─── */
.trust-bar {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 14px;
  margin: 0 0 36px; text-align: center;
}
.trust-tile {
  background: var(--color-surface); border: 1px solid var(--color-border); border-radius: 999px;
  padding: 9px 20px 9px 9px; display: flex; align-items: center; gap: 10px; font-size: 13px; font-weight: 600;
  color: var(--color-text-muted); transition: transform 0.2s var(--ease), box-shadow 0.2s var(--ease), border-color 0.2s var(--ease);
  animation: fadeInUp 0.5s var(--ease) backwards;
}
.trust-tile:hover { transform: translateY(-4px) scale(1.03); box-shadow: var(--shadow-md); border-color: var(--color-primary); color: var(--color-text); }
.trust-tile .trust-icon {
  font-size: 17px; width: 34px; height: 34px; border-radius: 50%; display: flex; align-items: center;
  justify-content: center; flex-shrink: 0; animation: trustIconBreathe 3.2s ease-in-out infinite;
}
/* Same semantic-color tokens the app already uses for tags/badges — not new colors, just
   applied here so the trust bar isn't three identical white pills. */
.trust-tile-0 .trust-icon { background: var(--tag-success-bg); }
.trust-tile-1 .trust-icon { background: var(--tag-sponsored-bg); }
.trust-tile-2 .trust-icon { background: var(--tag-warning-bg); }
.trust-tile-0:hover { border-color: #12b76a; }
.trust-tile-1:hover { border-color: var(--tag-sponsored-color); }
.trust-tile-2:hover { border-color: #f79009; }
.trust-tile:nth-child(2) .trust-icon { animation-delay: 0.4s; }
.trust-tile:nth-child(3) .trust-icon { animation-delay: 0.8s; }
@keyframes trustIconBreathe { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.1); } }
.trust-tile .trust-value { font-size: 18px; font-weight: 800; }
.trust-tile .trust-label { font-size: 12px; color: var(--color-text-muted); font-weight: 600; }
.trust-tile:hover .trust-label { color: var(--color-text); }
@media (prefers-reduced-motion: reduce) { .trust-tile .trust-icon { animation: none; } }

/* ─── Favorite / follow heart toggle ─── */
.fav-toggle-btn {
  position: absolute; top: 8px; inset-inline-end: 8px; width: 32px; height: 32px; border-radius: 50%;
  border: none; background: rgba(255,255,255,0.9); color: var(--color-text-muted); font-size: 15px;
  display: flex; align-items: center; justify-content: center; cursor: pointer; z-index: 2;
  box-shadow: var(--shadow); transition: transform 0.2s var(--ease), color 0.2s var(--ease), background 0.2s var(--ease);
}
:root[data-theme="dark"] .fav-toggle-btn { background: rgba(24,27,33,0.85); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .fav-toggle-btn { background: rgba(24,27,33,0.85); } }
.fav-toggle-btn:hover { transform: scale(1.12); }
.fav-toggle-btn.is-favorited { color: var(--color-danger); }
.fav-toggle-btn.is-favorited svg, .fav-toggle-btn.is-favorited { animation: popIn 0.3s var(--ease); }
.remove-fav-btn { margin-top: 8px; width: 100%; }

/* ─── Recent-search dropdown (header search box, round 11) ─── */
.search-form { position: relative; }
.search-suggest {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0; background: var(--color-surface);
  border: 1px solid var(--color-border); border-radius: var(--radius); box-shadow: var(--shadow-lg);
  z-index: 70; overflow: hidden; animation: slideDown 0.15s var(--ease);
}
.search-suggest-header {
  display: flex; justify-content: space-between; align-items: center; padding: 8px 12px;
  font-size: 11px; font-weight: 700; text-transform: uppercase; color: var(--color-text-muted);
  border-bottom: 1px solid var(--color-border);
}
.search-suggest-header .clear-history { cursor: pointer; color: var(--color-danger); font-weight: 600; text-transform: none; font-size: 12px; }
.search-suggest-item {
  display: flex; align-items: center; gap: 8px; padding: 9px 12px; cursor: pointer; font-size: 13px;
  transition: background 0.12s var(--ease);
}
.search-suggest-item:hover, .search-suggest-item.active { background: var(--color-bg); }
.search-suggest-item .remove-item { margin-inline-start: auto; color: var(--color-text-muted); font-size: 11px; padding: 2px 6px; border-radius: 4px; }
.search-suggest-item .remove-item:hover { color: var(--color-danger); }

/* ─── Seller multi-image inputs (up to 15, round 11) ─── */
.image-url-row { display: flex; gap: 6px; margin-bottom: 6px; align-items: center; }
.image-url-row input { flex: 1; }
.image-url-row .remove-image-row { flex-shrink: 0; width: 34px; height: 34px; border-radius: 8px; border: 1px solid var(--color-border); background: var(--color-surface); cursor: pointer; color: var(--color-danger); }
.image-url-preview { width: 34px; height: 34px; border-radius: 6px; object-fit: cover; background: var(--color-media-bg); flex-shrink: 0; }
.image-count-hint { font-size: 12px; color: var(--color-text-muted); margin: -2px 0 8px; }

/* ─── OTP / verification-code digit boxes (Round 16) ─── */
@keyframes otpShake {
  10%, 90% { transform: translateX(-1px); }
  20%, 80% { transform: translateX(2px); }
  30%, 50%, 70% { transform: translateX(-4px); }
  40%, 60% { transform: translateX(4px); }
}
.otp-boxes { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin: 4px 0 2px; width: auto; }
/* Specificity note: the generic ".field input" rule (0,1,1) sets width:100% on every input
   inside a .field, which — combined with this container's flex-wrap — was stretching each
   digit box to fill the row and forcing them onto their own line each (i.e. rendering the
   "horizontal" OTP boxes vertically stacked). ".otp-boxes .otp-box" (0,2,0) outranks
   ".field input" (0,1,1) so the fixed box width below always wins over the generic input rule. */
.otp-boxes .otp-box {
  width: 44px; height: 52px; flex: 0 0 auto; text-align: center; font-size: 20px; font-weight: 800;
  border: 1.5px solid var(--color-border); border-radius: 10px; background: var(--color-surface);
  color: var(--color-text); outline: none; padding: 0;
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease), transform 0.15s var(--ease);
}
.otp-boxes .otp-box:focus { border-color: var(--color-primary); box-shadow: 0 0 0 3px rgba(255,90,31,0.16); transform: translateY(-1px); }
.otp-boxes.otp-shake { animation: otpShake 0.4s var(--ease); }
.otp-boxes.otp-shake .otp-box { border-color: var(--color-danger); }
/* Sized so an 8-digit code (TOTP MFA, login.html) still fits on one row inside a 375px-wide
   phone viewport: container padding (20px×2) + .form-card padding (28px×2) leaves ~279px, and
   8 boxes × 30px + 7 gaps × 4px = 268px fits with room to spare; flex-wrap above is kept only
   as a safety net for narrower/zoomed viewports. */
@media (max-width: 400px) {
  .otp-boxes .otp-box { width: 30px; height: 38px; font-size: 15px; }
  .otp-boxes { gap: 4px; }
}

/* ─── Inline filter bar: categories.html + used/browse.html (Round 20 fix) ───
   These pages' filter row previously used bare, unclassed <select>/<input> elements with only
   inline `style="width:110px"` — outside the global select/checkbox rules' reach for the price
   inputs and missing the card/pill treatment search.html's .filters-panel already had, so it
   rendered as plain default-browser boxes even though the rest of the app didn't. This wraps the
   same row in a proper card with the same price-pill styling as search.html, reusing existing
   tokens/classes rather than inventing a new visual language. */
.filter-bar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-lg);
  padding: 14px 16px; box-shadow: var(--shadow); margin-bottom: 18px; animation: fadeInUp 0.35s var(--ease);
}
.filter-bar-label {
  font-size: 13px; font-weight: 600; color: var(--color-text-muted); display: flex; align-items: center; gap: 8px;
}
.filter-bar select { min-width: 150px; }
.filter-bar .filters-price-row { width: 250px; }
.filter-bar-check {
  font-size: 13px; font-weight: 600; color: var(--color-text-muted);
  display: flex; align-items: center; gap: 7px; cursor: pointer; user-select: none;
  transition: color 0.15s var(--ease);
}
.filter-bar-check:hover { color: var(--color-text); }
.filter-bar .btn { transition: transform 0.15s var(--ease), box-shadow 0.15s var(--ease); }
.filter-bar .btn:hover { transform: translateY(-1px); }

/* ─── Search filters panel: sidebar on desktop, slide-in drawer on mobile (Round 16) ─── */
.search-layout { display: grid; grid-template-columns: 260px 1fr; gap: 24px; align-items: start; }
.filters-toggle-btn { display: none; }
.filters-backdrop { display: none; }
.filters-panel {
  background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-lg);
  padding: 18px; position: sticky; top: calc(var(--header-h) + 16px); box-shadow: var(--shadow);
}
/* Round 18 "cool design" pass: each filter gets its own labeled group, separated by a hairline
   divider (a subtle card-within-a-card, using the same --color-border token as the rest of the
   design system) instead of one flat stack of controls. */
.filters-panel .filters-group { padding-bottom: 16px; margin-bottom: 16px; border-bottom: 1px solid var(--color-border); }
.filters-panel .filters-group:last-of-type { border-bottom: none; margin-bottom: 0; padding-bottom: 0; }
.filters-panel h3 {
  display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.5px; color: var(--color-text-muted); margin: 0 0 10px;
}
.filters-panel h3:first-child { margin-top: 0; }
.filters-panel .field { margin-bottom: 0; }
/* Round 22 redesign: two distinct compact pills (one per input) instead of both numbers sharing
   one pill — round 21's shared-pill fix solved the background-color mismatch, but a single
   :focus-within ring around the WHOLE row (both fields at once) still read as one boxy control,
   especially now that main.css's global `:focus-visible` outline made the mismatch between "the
   entire row glows" and "only one field is actually focused" more obvious. Splitting into two
   real pills means only the field you're actually typing in lights up. */
.filters-price-row { display: flex; align-items: center; gap: 8px; }
.filters-price-field {
  display: flex; align-items: center; gap: 5px; background: var(--color-surface);
  border: 1px solid var(--color-border); border-radius: 8px; padding: 7px 10px;
  flex: 1; min-width: 0; transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
.filters-price-field:focus-within { border-color: var(--color-primary); box-shadow: 0 0 0 2px rgba(255,90,31,0.12); }
.filters-price-currency {
  font-size: 10.5px; font-weight: 800; color: var(--color-text-muted); flex-shrink: 0;
  letter-spacing: 0.3px; text-transform: uppercase;
}
.filters-price-row input {
  width: 0; flex: 1; min-width: 0; text-align: start; border: none; background: transparent;
  padding: 1px 0; font-size: 13px;
}
.filters-price-row input:focus { box-shadow: none; outline: none; }
.filters-price-row .filters-price-sep { color: var(--color-text-muted); font-size: 13px; flex-shrink: 0; }
.filters-panel-header { display: none; align-items: center; justify-content: space-between; margin-bottom: 14px; padding-bottom: 14px; border-bottom: 1px solid var(--color-border); }
.filters-panel-footer { display: flex; align-items: center; justify-content: space-between; margin-top: 4px; }
.filters-clear-link {
  font-size: 12px; color: var(--color-danger-text); font-weight: 600; cursor: pointer;
  display: inline-flex; align-items: center; gap: 5px; transition: opacity 0.15s var(--ease);
}
.filters-clear-link[data-inactive="1"] { opacity: 0.45; pointer-events: none; }
.filters-close-btn { border: none; background: none; font-size: 20px; line-height: 1; cursor: pointer; color: var(--color-text-muted); padding: 4px 6px; }
/* Active-filter-count badge: shown next to "Clear all" inside the panel and on the mobile
   "Filters" toggle button so it's obvious at a glance that filters are narrowing the results. */
.filters-count-badge {
  display: inline-flex; align-items: center; justify-content: center; min-width: 17px; height: 17px;
  padding: 0 5px; border-radius: 999px; background: var(--gradient-primary); color: #fff;
  font-size: 10px; font-weight: 800; line-height: 1; margin-inline-start: 6px;
}
@media (max-width: 860px) {
  .search-layout { grid-template-columns: 1fr; }
  .filters-toggle-btn { display: inline-flex; }
  .filters-panel-header { display: flex; }
  .filters-panel {
    position: fixed; top: 0; bottom: 0; inset-inline-end: 0; width: min(320px, 88vw); z-index: 220;
    border-radius: 0; overflow-y: auto; box-shadow: var(--shadow-lg); transform: translateX(100%);
    transition: transform 0.28s var(--ease); border-top: none; border-bottom: none; border-inline-end: none;
  }
  [dir="rtl"] .filters-panel { transform: translateX(-100%); }
  .filters-panel.is-open { transform: translateX(0); }
  .filters-backdrop {
    display: block; position: fixed; inset: 0; background: rgba(16,24,40,0.45); z-index: 210;
    opacity: 0; pointer-events: none; transition: opacity 0.25s var(--ease);
  }
  .filters-backdrop.is-open { opacity: 1; pointer-events: auto; }
}

/* ─── Category hierarchy directory (Round 16 — "make sure ALL categories are shown") ─── */
.category-group {
  background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius);
  padding: 14px 16px; margin-bottom: 12px; transition: border-color 0.2s var(--ease), box-shadow 0.2s var(--ease);
}
.category-group:hover { border-color: var(--color-primary); box-shadow: var(--shadow); }
.category-group-header { display: flex; align-items: center; gap: 10px; font-weight: 700; margin-bottom: 10px; }
.category-group-icon {
  width: 32px; height: 32px; border-radius: 50%; background: var(--color-bg);
  display: flex; align-items: center; justify-content: center; font-size: 16px; flex-shrink: 0;
}
.category-group-children { display: flex; flex-wrap: wrap; gap: 8px; }
.category-chip {
  padding: 6px 12px; border-radius: 999px; border: 1px solid var(--color-border); background: var(--color-bg);
  font-size: 13px; font-weight: 600; color: var(--color-text); transition: transform 0.15s var(--ease), border-color 0.15s var(--ease), color 0.15s var(--ease);
}
.category-chip:hover { transform: translateY(-2px); border-color: var(--color-primary); color: var(--color-primary); }

/* ─── A few more micro-interactions (Round 16) ─── */
.theme-toggle button:hover { transform: scale(1.12); }
.otp-resend-link, #otp-resend-link { transition: color 0.15s var(--ease); }
@keyframes addedPop { 0% { transform: scale(1); } 35% { transform: scale(0.94); } 65% { transform: scale(1.05); } 100% { transform: scale(1); } }
.btn.is-added { animation: addedPop 0.4s var(--ease); }

/* ─── DM attachments (round 11) ─── */
.bubble img.msg-attachment-img { max-width: 220px; border-radius: 10px; display: block; margin-top: 4px; cursor: pointer; }
.bubble .msg-attachment-file {
  display: flex; align-items: center; gap: 8px; margin-top: 4px; padding: 8px 10px; border-radius: 8px;
  background: rgba(0,0,0,0.06); font-size: 13px; font-weight: 600; text-decoration: none; color: inherit;
}
.bubble-row.mine .msg-attachment-file { background: rgba(255,255,255,0.18); color: #fff; }
.composer-attach-btn {
  flex-shrink: 0; width: 40px; height: 40px; border-radius: 50%; border: 1px solid var(--color-border);
  background: var(--color-surface); cursor: pointer; font-size: 16px; display: flex; align-items: center; justify-content: center;
}
.composer-attach-btn:hover { border-color: var(--color-primary); color: var(--color-primary); }
.attach-preview-bar {
  display: flex; align-items: center; gap: 8px; padding: 6px 12px; font-size: 12px; color: var(--color-text-muted);
  border-top: 1px solid var(--color-border); background: var(--color-bg);
}
.attach-preview-bar .remove-attach { color: var(--color-danger); cursor: pointer; font-weight: 600; }

/* ─── Round 22: REAL custom dropdown popups (custom-select.js) ───
   Round 18 only ever restyled a <select>'s CLOSED box (appearance:none + a background-image
   arrow, above) — the OPEN popup list is native browser chrome in every engine and can't be
   themed with CSS at all, which is exactly what screenshots kept catching. custom-select.js
   progressively enhances every non-multiple <select> into this trigger + popup pair while the
   original <select> stays in the DOM (invisible, non-interactive) as the real value/events
   source — see that file's header comment for the full architecture. Every color here is an
   existing --color-* token so this automatically follows light/dark theme and RTL like the rest
   of the page; no new tokens, no new keyframes needed (fade+scale uses plain transitions). */
.cs-select-wrap { position: relative; display: inline-block; max-width: 100%; min-width: 120px; vertical-align: middle; }
.field .cs-select-wrap { display: block; width: 100%; }
.filter-bar .cs-select-wrap { min-width: 150px; }
/* Round 27 (real mobile bug): the register page's Day/Month/Year row set flex sizes with
   inline styles on the original <select>s, but custom-select.js hides those and renders a
   .cs-select-wrap in their place — which carries a 120px min-width, so three of them plus
   gaps overflowed a 390px phone screen and pushed the Year picker off the edge, making the
   whole page scroll sideways. Verified on a 390px viewport before and after. */
.dob-select-row { flex-wrap: wrap; }
.dob-select-row .cs-select-wrap { flex: 1 1 88px; min-width: 0; }
.promote-box .cs-select-wrap { min-width: 0; }
.promote-box .cs-trigger { width: auto; }

.cs-trigger {
  width: 100%; box-sizing: border-box; display: flex; align-items: center; justify-content: space-between;
  gap: 10px; padding: 10px 12px; border: 1px solid var(--color-border); border-radius: 8px;
  background: var(--color-surface); color: var(--color-text); font: inherit; text-align: start;
  cursor: pointer; outline: none; min-height: 41px;
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease), background 0.15s var(--ease);
}
.cs-trigger:hover:not(:disabled) { border-color: var(--color-text-muted); }
.cs-trigger:focus-visible { border-color: var(--color-primary); box-shadow: 0 0 0 3px rgba(255,90,31,0.12); outline: none; }
.cs-select-wrap.is-open .cs-trigger { border-color: var(--color-primary); box-shadow: 0 0 0 3px rgba(255,90,31,0.12); }
.cs-trigger:disabled { opacity: 0.6; cursor: not-allowed; }
.cs-trigger-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cs-trigger-label.is-placeholder { color: var(--color-text-muted); }
.cs-chevron { flex-shrink: 0; color: var(--color-text-muted); transition: transform 0.2s var(--ease); }
.cs-select-wrap.is-open .cs-chevron { transform: rotate(180deg); }

/* The popup is portaled to <body> (position:fixed, JS-positioned against the trigger's live
   rect) rather than nested inside .cs-select-wrap — several of this app's real dropdowns
   (search.html's filters sidebar, its mobile slide-in variant, categories/used sort rows) sit
   inside ancestors with their own overflow/scroll or stacking context (.filters-panel scrolls
   on mobile, has its own z-index), which would silently clip or misplace an absolutely-nested
   popup — the exact "looks custom but broken in the one place that matters" failure mode this
   round is explicitly trying to avoid. Fixed positioning sidesteps all of that. */
.cs-popup {
  position: fixed; z-index: 300; background: var(--color-surface); border: 1px solid var(--color-border);
  border-radius: 10px; box-shadow: var(--shadow-lg); padding: 6px; max-height: 300px; overflow-y: auto;
  transform-origin: top center; opacity: 0; transform: scale(0.96) translateY(-4px);
  transition: opacity 0.16s var(--ease), transform 0.16s var(--ease); pointer-events: none;
}
.cs-popup.is-open { opacity: 1; transform: scale(1) translateY(0); pointer-events: auto; }
.cs-popup.cs-popup-above { transform-origin: bottom center; }
.cs-optgroup-label {
  padding: 8px 10px 4px; font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.4px; color: var(--color-text-muted);
}
.cs-option {
  display: flex; align-items: center; gap: 8px; padding: 9px 10px; border-radius: 7px;
  font-size: 14px; line-height: 1.3; cursor: pointer; color: var(--color-text);
  transition: background 0.12s var(--ease);
}
.cs-option:hover, .cs-option.is-highlighted { background: var(--color-bg); }
.cs-option.is-selected { font-weight: 700; color: var(--color-primary); }
.cs-option-text { overflow: hidden; text-overflow: ellipsis; }
.cs-option-check { margin-inline-start: auto; opacity: 0; color: var(--color-primary); font-weight: 800; flex-shrink: 0; }
.cs-option.is-selected .cs-option-check { opacity: 1; }
.cs-option.is-disabled { opacity: 0.5; cursor: default; pointer-events: none; }
@media (prefers-reduced-motion: reduce) { .cs-popup { transition: none; } }

/* ─── Round 22: seller product-form variants + photo upload redesign ─── */
.variant-row {
  background: var(--color-surface); border: 1px solid var(--color-border); border-radius: 10px;
  padding: 10px 12px; margin-bottom: 8px;
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease), opacity 0.2s var(--ease), transform 0.2s var(--ease);
}
.variant-row:hover { border-color: var(--color-text-muted); box-shadow: var(--shadow); }
.pf-row-enter { opacity: 0; transform: translateY(-6px) scale(0.98); }
.pf-row-enter-active { opacity: 1; transform: none; }
.pf-row-leaving { opacity: 0 !important; transform: translateY(-6px) scale(0.98) !important; }
#pf-variants-wrap:empty::after, #pf-add-variant-btn { transition: transform 0.15s var(--ease); }

.file-dropzone-wrap { position: relative; display: block; }
.file-input-native-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
.file-dropzone {
  display: flex; align-items: center; justify-content: center; gap: 10px; flex-direction: column;
  border: 1.5px dashed var(--color-border); border-radius: var(--radius); background: var(--color-bg);
  padding: 22px 16px; cursor: pointer; text-align: center;
  transition: border-color 0.15s var(--ease), background 0.15s var(--ease), transform 0.1s var(--ease);
}
.file-dropzone:hover, .file-dropzone.is-dragover {
  border-color: var(--color-primary); background: rgba(255,90,31,0.06);
}
.file-dropzone:active { transform: scale(0.99); }
.file-dropzone .file-dropzone-icon { font-size: 26px; opacity: 0.75; transition: transform 0.2s var(--ease); }
.file-dropzone:hover .file-dropzone-icon { transform: translateY(-2px) scale(1.08); }
.file-dropzone .file-dropzone-text {
  font-size: 12px; color: var(--color-text-muted); font-weight: 600; word-break: break-all; max-width: 100%;
}
.file-dropzone.has-file { border-style: solid; border-color: var(--color-success); background: var(--tag-success-bg); }
.file-dropzone.has-file .file-dropzone-text { color: var(--color-text); }
/* Compact row variant — used next to an existing thumbnail preview (store/products.html's main
   product photo), where a full-height dropzone card would be redundant with the preview image. */
.file-dropzone-compact { flex-direction: row; padding: 12px 14px; gap: 8px; }
.file-dropzone-compact .file-dropzone-icon { font-size: 18px; }
.file-dropzone-compact .file-dropzone-text { font-size: 12px; text-align: start; }

/* ─── Profile page redesign (Round 22 — "visible and tidy and understandable") ─── */
.profile-header { margin-bottom: 20px; }
.profile-col-main, .profile-col-side { display: flex; flex-direction: column; gap: 20px; }
.profile-col-main .form-card.profile-card { margin: 0; max-width: none; width: 100%; }
.profile-card h2.section-title, .profile-side-card h2.section-title { display: flex; align-items: center; gap: 8px; }
.profile-side-card { padding: 18px; }

/* ShopAll Credits card — moved up top and made visually prominent per this round's "make
   credits more visible" ask. The accent wash is the same rgba(255,90,31, …) tint recipe used
   elsewhere in this file (hero banner, focus rings) — no new color invented. */
.profile-credits-card {
  padding: 22px 24px; margin-bottom: 24px; border: 1px solid var(--color-border); border-radius: var(--radius-lg);
  background: linear-gradient(120deg, rgba(255,90,31,0.10), rgba(255,90,31,0.02)); box-shadow: var(--shadow-md);
  animation: fadeInUp 0.4s var(--ease);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .profile-credits-card { background: linear-gradient(120deg, rgba(255,90,31,0.18), rgba(255,90,31,0.04)); }
}
:root[data-theme="dark"] .profile-credits-card { background: linear-gradient(120deg, rgba(255,90,31,0.18), rgba(255,90,31,0.04)); }
.profile-credits-top { display: flex; align-items: center; gap: 14px; margin-bottom: 10px; }
.profile-credits-icon {
  width: 48px; height: 48px; border-radius: 50%; background: var(--gradient-primary); color: #fff;
  display: flex; align-items: center; justify-content: center; font-size: 22px; flex-shrink: 0;
  box-shadow: 0 4px 12px rgba(255,90,31,0.32);
}
.profile-section-eyebrow { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.4px; color: var(--color-text-muted); }
.profile-credits-balance { font-size: 30px; font-weight: 800; line-height: 1.2; }
.profile-credits-copy { font-size: 12.5px; line-height: 1.65; margin: 0 0 10px; }
.profile-credits-ledger-toggle summary { cursor: pointer; font-weight: 700; font-size: 13px; color: var(--color-primary); list-style: none; }
.profile-credits-ledger-toggle summary::-webkit-details-marker { display: none; }
.profile-credits-ledger-toggle summary::before { content: "▸ "; display: inline-block; transition: transform 0.15s var(--ease); }
.profile-credits-ledger-toggle[open] summary::before { transform: rotate(90deg); }

.profile-pref-row { display: flex; flex-wrap: wrap; gap: 28px; align-items: flex-start; }
.profile-pref-label { font-size: 12px; font-weight: 700; color: var(--color-text-muted); margin-bottom: 8px; text-transform: uppercase; letter-spacing: 0.3px; }

.profile-danger-card {
  margin: 28px 0 0; max-width: none; width: 100%;
  border-color: var(--color-danger); background: var(--tag-danger-bg);
}
.profile-danger-title { color: var(--color-danger); }
.profile-danger-title::before { background: var(--color-danger); }
.profile-danger-list { font-size: 13px; color: var(--color-text-muted); margin: 6px 0 16px; padding-inline-start: 20px; line-height: 1.7; }
@media (max-width: 860px) { .profile-col-main .form-card.profile-card { max-width: 100%; } }
