/* ==========================================================================
   Rush Wristband - Global Stylesheet
   Modern, clean, responsive design inspired by tablecoversnow.com
   ========================================================================== */

/* ----- 0. i18n cloak — hide body until either the inline <head> boot
            script (600 ms timeout) or main.js's applyTranslations() removes
            the .i18n-cloak class. The 800 ms CSS fallback is just a final
            safety net in case both fail (e.g. JS blocked). ----- */
html.i18n-cloak body { visibility: hidden; }
@keyframes i18n-cloak-safety {
  to { visibility: visible !important; }
}
html.i18n-cloak body { animation: i18n-cloak-safety 0s linear 0.8s forwards; }

/* ----- 1. Design tokens ----- */
:root {
  /* ---------- Colour: primary palette ---------- */
  --color-primary: #1e3a8a;
  --color-primary-dark: #0f172a;
  --color-primary-light: #3b82f6;
  --color-primary-soft: #eff4ff;

  --color-accent: #ea580c;
  --color-accent-dark: #c2410c;
  --color-accent-light: #fb923c;
  --color-accent-soft: #fff1e6;

  /* Teal — Step tabs, secondary toggles, customize-card buttons */
  --color-teal: #4ea4ad;
  --color-teal-dark: #3b8a93;
  --color-teal-soft: #e6f3f5;
  --color-teal-line: #c7e3e7;

  /* Status colours */
  --color-success: #059669;
  --color-success-soft: #d1fae5;
  --color-danger: #dc2626;
  --color-danger-soft: #fee2e2;
  --color-warning: #d97706;
  --color-warning-soft: #fef3c7;
  --color-info: #2563eb;
  --color-info-soft: #dbeafe;

  /* Step header strip (matches reference site) */
  --color-step-strip-top: #e2edf5;
  --color-step-strip-bot: #d6e4f0;

  /* ---------- Colour: text / surface ---------- */
  --color-text: #0f172a;
  --color-text-muted: #475569;
  --color-text-light: #94a3b8;
  --color-border: #e6e7eb;
  --color-border-strong: #d1d5db;
  --color-border-focus: #93c5fd;

  --color-bg: #ffffff;
  --color-surface: #ffffff;
  --color-surface-alt: #f8fafc;
  --color-bg-alt: #fafaf7;
  --color-bg-warm: #fdf8f3;
  --color-bg-dark: #0f172a;
  --color-bg-darker: #0b1120;
  --color-overlay: rgba(15, 23, 42, 0.55);

  /* ---------- Shadow scale (5 stops + 1 inset, 1 brand glow) ---------- */
  --shadow-xs: 0 1px 2px rgba(15, 23, 42, 0.04);
  --shadow-sm: 0 2px 4px rgba(15, 23, 42, 0.05), 0 1px 2px rgba(15, 23, 42, 0.04);
  --shadow-md: 0 4px 12px rgba(15, 23, 42, 0.07), 0 2px 4px rgba(15, 23, 42, 0.04);
  --shadow-lg: 0 12px 28px -8px rgba(15, 23, 42, 0.14), 0 4px 12px rgba(15, 23, 42, 0.05);
  --shadow-xl: 0 24px 48px -12px rgba(15, 23, 42, 0.18), 0 8px 16px -8px rgba(15, 23, 42, 0.08);
  --shadow-2xl: 0 40px 80px -20px rgba(15, 23, 42, 0.25);
  --shadow-inset: inset 0 1px 2px rgba(15, 23, 42, 0.06);
  --shadow-glow-accent: 0 8px 24px -8px rgba(234, 88, 12, 0.45);
  --shadow-glow-teal:   0 8px 24px -8px rgba(78, 164, 173, 0.4);

  /* ---------- Border radius scale ---------- */
  --radius-xs: 4px;
  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 16px;
  --radius-xl: 22px;
  --radius-2xl: 28px;
  --radius-pill: 999px;

  /* ---------- Spacing scale (4-px base, 8 stops) ---------- */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 64px;
  --space-9: 96px;

  /* ---------- Font ---------- */
  --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
               'Noto Sans SC', 'PingFang SC', 'Hiragino Sans GB',
               'Microsoft YaHei', '微软雅黑', sans-serif;
  --font-display: 'Inter', 'Noto Sans SC', 'PingFang SC',
                  'Microsoft YaHei', sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas,
               'Liberation Mono', monospace;
  --letter-tight: -0.02em;
  --letter-snug:  -0.01em;
  --letter-wide:   0.06em;

  /* ---------- Layout ---------- */
  --container-max: 1280px;
  --container-pad: clamp(16px, 4vw, 32px);
  --header-height: 72px;

  /* ---------- Transitions / easing ---------- */
  --ease-out:  cubic-bezier(0.16, 1, 0.3, 1);
  --ease-pop:  cubic-bezier(0.34, 1.56, 0.64, 1);
  --ease-soft: cubic-bezier(0.4, 0, 0.2, 1);
  --transition-fast: 0.12s var(--ease-soft);
  --transition: all 0.2s var(--ease-soft);
  --transition-slow: 0.35s var(--ease-out);
  --transition-pop:  0.4s var(--ease-pop);
}

/* CJK languages typically need slightly looser line-height + a different
   font preference; <html data-lang="zh"> is set by applyTranslations(). */
html[data-lang="zh"] body { line-height: 1.7; }

/* Arabic support.

   Only the variables and inherited text are covered here. 177 rules in this
   sheet name Inter inside a `font:` shorthand instead of reading --font-sans,
   and there is no way to reach them from one place: an @font-face claiming the
   name 'Inter' for just the Arabic code blocks was tried, and even with
   unicode-range confining it to U+0600 and up it changed Latin and CJK
   rendering on every page (measured: 4815 pixels on pages/about.html, gone the
   moment the rule was removed — see scripts/_bisect-css-diff.py). Declaring an
   @font-face for a family name replaces how that name resolves, full stop.

   So those 177 declarations let Arabic glyphs fall through to the generic
   `sans-serif`, which maps to a face with Arabic coverage on all three
   platforms (Segoe UI / Arial on Windows, SF Arabic via Helvetica on Apple,
   Noto Naskh Arabic via Roboto on Android). Legible, just not a chosen Naskh.
   Getting the preferred face everywhere means converting those shorthands to
   var(--font-sans), which is worth doing on its own merits but is a separate
   change with its own regression run.

   Arabic sets dots and diacritics above and below the baseline, so it wants
   more leading than Latin at the same size. */
html[data-lang="ar"] {
  --font-sans: 'SF Arabic', 'Segoe UI', 'Noto Sans Arabic', 'Noto Naskh Arabic',
               'Geeza Pro', Tahoma, Arial, sans-serif;
  --font-display: 'SF Arabic', 'Segoe UI', 'Noto Sans Arabic',
                  'Noto Naskh Arabic', Tahoma, Arial, sans-serif;
}
html[data-lang="ar"] body { line-height: 1.8; }

/* ----- 2. Reset & base ----- */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.6;
  color: var(--color-text);
  background: var(--color-bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

img { max-width: 100%; height: auto; display: block; }

a {
  color: var(--color-primary);
  text-decoration: none;
  transition: var(--transition);
}
a:hover { color: var(--color-primary-light); }

button {
  font-family: inherit;
  cursor: pointer;
  border: none;
  background: none;
}

h1, h2, h3, h4, h5, h6 {
  margin: 0 0 0.5em;
  font-weight: 700;
  line-height: 1.2;
  color: var(--color-text);
  letter-spacing: var(--letter-snug);
  font-family: var(--font-display);
}

h1 {
  font-size: clamp(2.25rem, 4.6vw, 3.5rem);
  font-weight: 800;
  letter-spacing: var(--letter-tight);
  line-height: 1.1;
}
h2 {
  font-size: clamp(1.75rem, 3.2vw, 2.5rem);
  font-weight: 700;
  letter-spacing: var(--letter-tight);
  line-height: 1.15;
}
h3 { font-size: 1.3rem; }
h4 { font-size: 1.1rem; }

::selection { background: var(--color-accent-soft); color: var(--color-accent-dark); }

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

p { margin: 0 0 1em; }

ul { padding-inline-start: 1.5em; margin: 0 0 1em; }

.container {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 24px;
}

/* ----- 3. Buttons ----- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 12px 24px;
  border-radius: var(--radius-md);
  font-weight: 600;
  font-size: 0.95rem;
  text-align: center;
  transition: var(--transition);
  white-space: nowrap;
  cursor: pointer;
}

.btn-primary {
  background: var(--color-accent);
  color: #1c1917;
  box-shadow: var(--shadow-sm);
}
.btn-primary:hover {
  background: var(--color-accent-dark);
  color: #fff;
  transform: translateY(-1px);
  box-shadow: var(--shadow-md);
}

.btn-secondary {
  background: var(--color-primary);
  color: #fff;
}
.btn-secondary:hover {
  background: var(--color-primary-dark);
  color: #fff;
}

.btn-outline {
  background: transparent;
  color: var(--color-primary);
  border: 1.5px solid var(--color-primary);
}
.btn-outline:hover {
  background: var(--color-primary);
  color: #fff;
}

.btn-ghost {
  background: transparent;
  color: var(--color-text);
  padding: 8px 16px;
}
.btn-ghost:hover { background: var(--color-bg-alt); }

.btn-block { display: flex; width: 100%; }

/* ----- 4a. Top utility bar (language switcher) ----- */
.topbar {
  background: #0f172a;
  color: #cbd5e1;
  font-size: 0.78rem;
  padding: 6px 0;
  border-bottom: 1px solid rgba(255,255,255,0.06);
  position: sticky;
  top: 0;
  z-index: 60;
}
.topbar-inner {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  min-height: 24px;
}
.topbar-info {
  color: #94a3b8;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
/* About / Contact live up here, next to the language picker, the way
   order-card.com keeps its account and contact links out of the product bar.
   The nav below is for products only. */
.topbar-links {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-inline-start: auto;
}
.topbar-links a {
  color: #cbd5e1;
  padding: 4px 8px;
  border-radius: 4px;
  font-weight: 500;
}
.topbar-links a:hover { color: #fff; background: rgba(255, 255, 255, 0.08); }
.topbar-links a.active { color: #fff; font-weight: 700; }
/* (removed: .topbar-cs WhatsApp + chat icon group)
   order-card.com does not show support icons in the topbar — chat
   is exclusively the Tawk.to bubble in the bottom-right. The icon
   styles previously here were dropped along with the markup
   injector that produced them. */

/* ----- 4b. Limited-time offer strip + offer modal -----
   Injected by js/promo.js between the sticky .topbar and the sticky .header,
   from whatever campaign the admin 优惠码 page has flagged. Deliberately NOT
   sticky: it would push the header's sticky offset (top: 36px) off by its own
   height, which varies with the locale's wording. It scrolls away instead and
   the topbar keeps the offer one click away. */
.promo-bar {
  background: linear-gradient(90deg, var(--color-accent) 0%, var(--color-accent-dark) 100%);
  color: #fff;
  font-size: 0.82rem;
  /* Fixed line box so the strip is the same height in every locale — CJK and
     Latin glyphs report different metrics and the bar wobbled by a pixel. */
  line-height: 22px;
  padding: 7px 0;
}
.promo-bar__inner {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 4px 12px;
  text-align: center;
}
.promo-bar__tag {
  background: rgba(255, 255, 255, 0.22);
  border-radius: 999px;
  padding: 2px 9px;
  font-size: 0.7rem;
  font-weight: 700;
  /* Its own line box, so the pill's padding does not make the strip taller
     than the 22px text row beside it. */
  line-height: 18px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
}
.promo-bar__text { font-weight: 600; }
/* Equally spaced fragments read as one run-on sentence, so the code is fenced
   off with a middot. It rides on the fragment itself rather than sitting
   between them, so hiding the fragment hides its divider. */
.promo-bar__code::before {
  content: "\00b7";
  margin-inline-end: 12px;
  opacity: 0.55;
}
.promo-bar__code {
  /* "Code" and its chip are one unit and never break apart. */
  white-space: nowrap;
}
.promo-bar__code b {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  letter-spacing: 0.06em;
  border: 1px dashed rgba(255, 255, 255, 0.7);
  border-radius: 4px;
  padding: 2px 9px;
  margin-inline-start: 6px;
}
.promo-bar__details {
  background: none;
  border: 0;
  color: #fff;
  font: inherit;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
  /* Set apart from the offer itself: it is an aside, not part of the sentence. */
  margin-inline-start: 6px;
  padding: 0;
  white-space: nowrap;
}
.promo-bar__details:hover { opacity: 0.85; }
@media (max-width: 700px) {
  .promo-bar { font-size: 0.76rem; }
  /* Once the strip can wrap, a divider can land at the start of a line. The
     gaps do the separating from here down. */
  .promo-bar__code::before { content: none; }
  .promo-bar__details { margin-inline-start: 0; }
}

/* Shared shell for the small print and the homepage offer card. */
.promo-modal {
  position: fixed;
  inset: 0;
  z-index: 900;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.promo-modal__backdrop {
  position: absolute;
  inset: 0;
  background: var(--color-overlay);
}
.promo-modal__card {
  position: relative;
  background: var(--color-surface);
  border-radius: 14px;
  box-shadow: var(--shadow-lg);
  max-width: 460px;
  width: 100%;
  max-height: calc(100vh - 40px);
  overflow-y: auto;
  padding: 26px 26px 22px;
}
.promo-modal__x {
  position: absolute;
  top: 8px;
  inset-inline-end: 12px;
  background: none;
  border: 0;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--color-text-light);
  cursor: pointer;
}
.promo-modal__x:hover { color: var(--color-text); }
.promo-modal__title {
  margin: 0 0 12px;
  font-size: 1.05rem;
  padding-inline-end: 24px;
}
.promo-modal__list {
  margin: 0 0 18px;
  padding-inline-start: 20px;
  color: var(--color-text-muted);
  font-size: 0.86rem;
  line-height: 1.6;
}
.promo-modal__list li { margin-bottom: 6px; }
.promo-modal__ok { width: 100%; justify-content: center; }

/* The homepage announcement: one card, one number, one code to copy. */
.promo-pop { text-align: center; }
.promo-pop__tag {
  display: inline-block;
  background: var(--color-accent-soft);
  color: var(--color-accent-dark);
  border-radius: 999px;
  padding: 3px 12px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.promo-pop__title {
  margin: 14px 0 6px;
  font-size: 2rem;
  line-height: 1.1;
  letter-spacing: -0.01em;
}
/* The discount inside that headline: the number is what sells the card, so it
   is set larger than the words around it and in the brand red. Injected by
   promo.js, which keeps the whole of it — "10%", "9 折", "€20" — in here so no
   unit is left behind at sentence size. */
.promo-pop__off {
  /* Red rather than the brand orange: the CTA right below it is orange, and
     two warm tones that close together read as an accident. */
  color: var(--color-danger);
  font-size: 1.35em;
  font-weight: 800;
  /* Optical alignment: a much larger run inside a line of text otherwise
     drags the baseline of the whole heading down. */
  line-height: 1;
  display: inline-block;
  vertical-align: -0.04em;
}
.promo-pop__sub {
  margin: 0 0 16px;
  color: var(--color-text-muted);
  font-size: 0.9rem;
}
.promo-pop__code {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  width: 100%;
  background: var(--color-surface-alt);
  border: 1px dashed var(--color-border-strong);
  border-radius: 10px;
  padding: 12px 14px;
  margin-bottom: 12px;
  cursor: pointer;
}
.promo-pop__code:hover { border-color: var(--color-accent); }
.promo-pop__code span {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 1.15rem;
  font-weight: 700;
  letter-spacing: 0.1em;
}
.promo-pop__code em {
  font-style: normal;
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-accent-dark);
}
@media (max-width: 480px) {
  .promo-pop__title { font-size: 1.6rem; }
  .promo-pop__off { font-size: 2.1rem; }
}

/* =====================================================================
   Language switcher — Order-Card-style dropdown.
   Markup is enhanced at runtime by enhanceLangSwitchers() in main.js:
     .lang-switcher
       button.lang-trigger      ← globe + current language name + caret
       div.lang-menu             ← absolutely positioned, hidden by default
         button.lang-btn × 5
   Pre-JS fallback (no .lang-switcher--enhanced class yet) keeps the
   original inline 5-button look so the page is usable even with JS off.
   ===================================================================== */
.lang-switcher {
  display: inline-flex;
  gap: 2px;
  align-items: center;
  position: relative;
}
/* Pre-enhancement (fallback) sizing for the legacy label. */
.lang-switcher:not(.lang-switcher--enhanced) .lang-label {
  color: #64748b;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  margin-inline-end: 6px;
}
.lang-switcher:not(.lang-switcher--enhanced) .lang-btn {
  background: transparent;
  color: #94a3b8;
  font-size: 0.72rem;
  font-weight: 700;
  padding: 4px 10px;
  border-radius: 4px;
  letter-spacing: 0.06em;
  cursor: pointer;
  border: none;
  font-family: inherit;
}
.lang-switcher:not(.lang-switcher--enhanced) .lang-btn.active {
  color: #fff;
  background: var(--color-accent);
}

/* ----- Dropdown trigger (collapsed state) -----
   Sits in the dark topbar so we use the accent orange as the pill
   colour: globe + name + caret all share the same white text on an
   accent background so it reads as a real "language picker" call-to-
   action rather than blending into the topbar. Right-aligned to its
   container, full-name text means widths vary (English vs Français) —
   `max-content` keeps it snug. */
.lang-trigger {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 14px;
  background: var(--color-accent);
  border: 1px solid rgba(255, 255, 255, 0.25);
  cursor: pointer;
  color: #fff;
  font-family: inherit;
  font-size: 0.88rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  border-radius: 999px;
  box-shadow: 0 2px 6px rgba(234, 88, 12, 0.35);
  transition: background 0.18s ease, box-shadow 0.18s ease, transform 0.18s ease;
}
.lang-trigger:hover {
  background: var(--color-accent-dark, #c2410c);
  box-shadow: 0 4px 12px rgba(234, 88, 12, 0.45);
  transform: translateY(-1px);
}
.lang-trigger:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}
.lang-trigger-icon {
  display: inline-flex;
  color: #fff;
  opacity: 0.95;
}
.lang-trigger-name { line-height: 1; color: #fff; }
.lang-trigger-caret {
  font-size: 0.75rem;
  color: #fff;
  opacity: 0.9;
  transition: transform 0.18s ease;
}
.lang-switcher.is-open .lang-trigger { box-shadow: 0 4px 12px rgba(234, 88, 12, 0.5); }
.lang-switcher.is-open .lang-trigger-caret { transform: rotate(180deg); }

/* ----- Dropdown menu (expanded state) -----
   Right-aligned under the trigger so the menu doesn't push off the
   viewport on narrow topbars. Rows show the short code on the left
   (tiny, subdued) and the full native language name on the right
   (bold), with the currently active row painted in the accent
   colour. */
.lang-menu {
  position: absolute;
  top: calc(100% + 8px);
  inset-inline-end: 0;
  inset-inline-start: auto;
  min-width: 180px;
  padding: 6px 0;
  background: #fff;
  border: 1px solid #e5e7eb;
  border-radius: 10px;
  box-shadow: 0 12px 32px rgba(15, 23, 42, 0.18), 0 4px 10px rgba(15, 23, 42, 0.08);
  display: none;
  z-index: 1000;
  /* Was `overflow: hidden`, but the dropdown now lists 13 locales and
     can comfortably exceed a phone's viewport. Cap it at 60vh and let
     the list scroll inside the rounded card so the corners stay clipped
     and the bottom rows never disappear under the fold. */
  max-height: 60vh;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.lang-switcher.is-open .lang-menu { display: block; }
.lang-menu .lang-btn {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 9px 16px;
  background: transparent;
  border: none;
  border-inline-start: 3px solid transparent;
  font-family: inherit;
  font-size: 0.92rem;
  color: #1f2937;
  text-align: start;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.lang-menu .lang-btn .lang-btn-code {
  flex: 0 0 28px;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: #94a3b8;
  text-transform: uppercase;
}
.lang-menu .lang-btn .lang-btn-name {
  flex: 1 1 auto;
  font-weight: 600;
  color: inherit;
}
.lang-menu .lang-btn:hover {
  background: #fff7ed;
  border-inline-start-color: var(--color-accent);
}
.lang-menu .lang-btn:hover .lang-btn-code { color: var(--color-accent); }
.lang-menu .lang-btn.is-current,
.lang-menu .lang-btn.active {
  background: rgba(234, 88, 12, 0.08);
  border-inline-start-color: var(--color-accent);
}
.lang-menu .lang-btn.is-current .lang-btn-code,
.lang-menu .lang-btn.active .lang-btn-code,
.lang-menu .lang-btn.is-current .lang-btn-name,
.lang-menu .lang-btn.active .lang-btn-name {
  color: var(--color-accent);
  font-weight: 800;
}

/* =====================================================================
   Currency switcher (sits to the LEFT of the language switcher in
   topbar). Visual conventions mirror .lang-switcher so the two
   controls feel like siblings: same pill shape, same dropdown panel,
   same hover treatments. We use a slate-blue accent instead of the
   orange to visually distinguish "what language?" from "what currency?"
   without screaming for attention.
   ===================================================================== */
.currency-switcher {
  display: inline-flex;
  position: relative;
  align-items: center;
  margin-inline-end: 8px;
}
.currency-trigger {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  background: #1e293b;
  border: 1px solid rgba(255, 255, 255, 0.18);
  cursor: pointer;
  color: #fff;
  font-family: inherit;
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  border-radius: 999px;
  box-shadow: 0 2px 6px rgba(15, 23, 42, 0.35);
  transition: background 0.18s ease, box-shadow 0.18s ease, transform 0.18s ease;
}
.currency-trigger:hover {
  background: #0f172a;
  box-shadow: 0 4px 12px rgba(15, 23, 42, 0.45);
  transform: translateY(-1px);
}
.currency-trigger:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}
.currency-trigger-icon {
  font-weight: 800;
  font-size: 0.9rem;
  line-height: 1;
}
.currency-trigger-name { line-height: 1; }
.currency-trigger-caret {
  font-size: 0.72rem;
  opacity: 0.85;
  transition: transform 0.18s ease;
}
.currency-switcher.is-open .currency-trigger { box-shadow: 0 4px 12px rgba(15, 23, 42, 0.5); }
.currency-switcher.is-open .currency-trigger-caret { transform: rotate(180deg); }

.currency-menu {
  position: absolute;
  top: calc(100% + 8px);
  inset-inline-end: 0;
  min-width: 220px;
  padding: 6px 0;
  background: #fff;
  border: 1px solid #e5e7eb;
  border-radius: 10px;
  box-shadow: 0 12px 32px rgba(15, 23, 42, 0.18), 0 4px 10px rgba(15, 23, 42, 0.08);
  display: none;
  z-index: 1000;
  max-height: 60vh;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.currency-switcher.is-open .currency-menu { display: block; }
.currency-menu .currency-btn {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 14px;
  background: transparent;
  border: none;
  border-inline-start: 3px solid transparent;
  font-family: inherit;
  font-size: 0.92rem;
  color: #1f2937;
  text-align: start;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.currency-menu .currency-btn-code {
  flex: 0 0 36px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: #94a3b8;
}
.currency-menu .currency-btn-name {
  flex: 1 1 auto;
  font-weight: 600;
}
.currency-menu .currency-btn-symbol {
  flex: 0 0 auto;
  font-weight: 700;
  color: #64748b;
  font-size: 0.95rem;
}
.currency-menu .currency-btn:hover {
  background: #f1f5f9;
  border-inline-start-color: #1e293b;
}
.currency-menu .currency-btn:hover .currency-btn-code,
.currency-menu .currency-btn:hover .currency-btn-symbol {
  color: #1e293b;
}
.currency-menu .currency-btn.is-current {
  background: rgba(30, 41, 59, 0.06);
  border-inline-start-color: #1e293b;
}
.currency-menu .currency-btn.is-current .currency-btn-code,
.currency-menu .currency-btn.is-current .currency-btn-name,
.currency-menu .currency-btn.is-current .currency-btn-symbol {
  color: #1e293b;
  font-weight: 800;
}
@media (max-width: 600px) {
  .currency-switcher { margin-inline-end: 4px; }
  .currency-trigger { padding: 5px 10px; font-size: 0.78rem; }
  .currency-trigger-icon { font-size: 0.85rem; }
}
@media (max-width: 600px) {
  .topbar-info { display: none; }
  .topbar-inner { justify-content: flex-end; }
}

/* ----- 5. Header ----- */
.header {
  position: sticky;
  top: 36px;
  z-index: 50;
  background: #fff;
  border-bottom: 1px solid var(--color-border);
  box-shadow: var(--shadow-sm);
}

.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: var(--header-height);
  gap: 24px;
}

.logo {
  display: flex;
  align-items: center;
  gap: 10px;
  font-weight: 800;
  font-size: 1.35rem;
  color: var(--color-primary-dark);
  letter-spacing: -0.02em;
  /* "Rush Wristband" broke across two lines as soon as the nav row grew. */
  white-space: nowrap;
  flex-shrink: 0;
}
.logo:hover { color: var(--color-primary); }
/* The brand mark. Height is fixed so the header keeps its rhythm; width
   follows the artwork, which is wider than tall. */
.logo-img {
  height: 36px;
  width: auto;
  flex-shrink: 0;
  display: block;
}

.main-nav {
  display: flex;
  align-items: center;
  gap: 2px;
  flex: 1 1 auto;
  min-width: 0;
  margin-inline-start: 20px;
  /* Deliberately NOT a scroll container. It used to be `overflow-x: auto` with
     the scrollbar hidden, which sounds like graceful degradation and is not:
     when the links did not fit, the last one was sliced mid-word with nothing
     to say it could be scrolled to. In German that swallowed About and Contact
     entirely. The categories now live in one dropdown so the row fits in every
     language, and `visible` is required anyway — an overflow container clips
     the absolutely positioned menu. scripts/_measure-nav-overflow.py guards the
     fit, since English still looks fine long after German has broken. */
  overflow: visible;
}
.main-nav a {
  padding: 8px 6px;
  border-radius: var(--radius-sm);
  color: var(--color-text);
  font-weight: 500;
  /* Ten items sit in this row (eight product families plus Home and All
     products), and German labels are the longest of the six locales, so the
     type is a notch smaller than the body copy and the families carry shorter
     navbar.* labels here than in their panels. scripts/_verify-mega-nav.py
     guards the fit down to 961px — English still looks fine long after German
     has broken. */
  font-size: 0.85rem;
  white-space: nowrap;
  flex: 0 0 auto;
}
.main-nav a:hover {
  background: var(--color-bg-alt);
  color: var(--color-primary);
}
.main-nav a.active {
  color: var(--color-primary);
  font-weight: 600;
}

/* ----- Product menus in the primary nav -----
   Six product groups sit in the bar, each opening a panel that lists every
   style in that group — 74 links in total, the way order-card.com lays its
   catalogue out. Conventions follow the language switcher so the components
   feel related: `.is-open` on the wrapper, a white card, and click to toggle
   rather than hover, because a hover menu leaves a touch user holding
   something they have no way to dismiss.

   Every trigger stays a real link to that group's listing page and JS only
   intercepts the click above the mobile breakpoint, so the catalogue is still
   reachable before main.js runs or if it fails. Each panel repeats the
   destination as its section heading, which is otherwise unreachable for a
   mouse user once the click is intercepted.

   A panel hangs off its own trigger and is only as wide as its content: the
   families range from three styles to twenty-one, and a one-column panel
   stretched across the header read as a bug — a name in the top-left corner of
   an otherwise empty white sheet. Width is capped so the widest panel wraps
   instead of leaving the viewport, and main.js nudges a panel left when its
   trigger sits far enough right that even a capped panel would overhang. */
.nav-dropdown { display: block; }
.nav-dropdown-trigger {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.nav-dropdown-caret {
  font-size: 0.75rem;
  opacity: 0.9;
  transition: transform 0.18s ease;
}
.nav-dropdown-menu {
  display: flex;
  flex-direction: column;
}
.nav-dropdown.is-open .nav-dropdown-caret { transform: rotate(180deg); }
.nav-mega-col { display: flex; flex-direction: column; }
.nav-mega-head {
  font-weight: 700;
  color: var(--color-primary-dark);
}

@media (min-width: 961px) {
  /* Two rows, as on order-card.com: brand and account icons up top, the whole
     product bar underneath. Eight groups plus Home and All products cannot
     share a row with the logo and the icons — German ran them into each other
     at 1440px, never mind 1024 — and a wrapped .header-inner gets us there
     without touching the markup on 117 pages. */
  :root { --header-height: 118px; }
  .header-inner {
    position: relative;
    flex-wrap: wrap;
    align-content: center;
    height: auto;
    min-height: var(--header-height);
    row-gap: 0;
    padding-top: 12px;
  }
  .main-nav {
    order: 3;
    flex: 1 0 100%;
    justify-content: center;
    /* Every link already carries its own side padding; the extra gap is what
       pushed German and French past a 961px viewport. */
    gap: 0;
    margin: 10px 0 0;
    padding-top: 6px;
    border-top: 1px solid var(--color-border);
  }
  .nav-dropdown { flex: 0 0 auto; position: relative; }
  .nav-mega-menu {
    position: absolute;
    top: calc(100% + 8px);
    inset-inline-start: 0;
    width: max-content;
    /* Wide enough for the five material columns of the luggage tag panel, the
       widest we have; anything wider wraps rather than leaves the viewport. */
    max-width: min(94vw, 1260px);
    display: none;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 10px 26px;
    padding: 20px 24px 22px;
    background: #fff;
    border: 1px solid #e5e7eb;
    border-radius: 12px;
    box-shadow: 0 12px 32px rgba(15, 23, 42, 0.18), 0 4px 10px rgba(15, 23, 42, 0.08);
    z-index: 1000;
    max-height: min(72vh, 560px);
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  .nav-dropdown.is-open .nav-mega-menu { display: flex; }
  .nav-mega-col {
    flex: 0 1 218px;
    min-width: 0;
    gap: 1px;
  }
  .nav-mega-menu a {
    display: block;
    padding: 7px 10px;
    border-radius: var(--radius-sm);
    /* Style names run long ("Vintage Crazy Horse Leather Luggage Tag"), so let
       them wrap inside the column instead of widening it. */
    white-space: normal;
    line-height: 1.35;
  }
  .nav-mega-menu a:hover {
    background: #fff7ed;
    color: var(--color-accent);
  }
  .nav-mega-head {
    margin-bottom: 4px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--color-border);
    border-radius: 0;
    font-size: 0.9rem;
  }
  .nav-mega-head:hover { background: none; }
  .nav-mega-menu a:not(.nav-mega-head) {
    font-size: 0.85rem;
    color: var(--color-text-muted);
  }
}

/* Just above the drawer breakpoint the row is within ~12px of the edge in
   German and French, which is close enough that a different font stack could
   push it over. Give those widths a smaller step so the margin is comfortable
   everywhere; wider screens keep the roomier type. */
@media (min-width: 961px) and (max-width: 1140px) {
  .main-nav a { font-size: 0.8rem; padding: 8px 5px; }
}

/* Wider than that there is room to spare, so the bar reads at close to
   body-copy size instead of the compromise the narrow widths force. Kept as its
   own step rather than raising the base size, because the base is what the
   mobile drawer uses and the 961-1140px band above must stay at 0.8rem.

   Starts at 1200px, NOT at 1141px where the squeeze ends: German is the widest
   locale and at this size its row is ~1010px, which leaves ~120px inside a
   1200px viewport — the same margin the narrow band was tuned to keep, and the
   reason that band exists at all (a different font stack eats a thin margin).
   At 1141px the same row would have had ~60px, half of it. Measure with
   scripts/_probe-nav-headroom.py before widening this further, and note it
   reports the margin on ONE side: the row is centred, so the slack left before
   anything collides is twice the figure it prints. */
@media (min-width: 1200px) {
  .main-nav a { font-size: 0.95rem; padding: 9px 7px; }
}

.header-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}
/* The newer product pages ship a static account link and ALSO get the control
   account.js injects, so their header carried two identical person icons — on a
   phone that pushed the row 56px past the viewport and left the menu button off
   screen. Hide the static one once the real control is in; with JS off it stays
   as the fallback it was meant to be. */
.header-actions:has([data-acct-ctl]) > a.icon-btn[href$="account-dashboard.html"] {
  display: none;
}

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  color: var(--color-text);
  position: relative;
}
.icon-btn:hover { background: var(--color-bg-alt); }
.icon-btn .badge {
  position: absolute;
  top: 2px;
  inset-inline-end: 2px;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  border-radius: 9px;
  background: var(--color-accent);
  color: #1c1917;
  font-size: 0.7rem;
  font-weight: 700;
  display: grid;
  place-items: center;
}

.menu-toggle {
  display: none;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-sm);
}
.menu-toggle:hover { background: var(--color-bg-alt); }

@media (max-width: 960px) {
  .main-nav {
    position: fixed;
    /* --pe-drawer-top is the header's measured bottom edge, set by main.js when
       the drawer opens. This was `inset: var(--header-height) 0 0 0`, which
       assumed the header started at the top of the viewport; with a 43px sticky
       topbar above it the drawer rode 44px too high and covered the second line
       of the logo. The fallback tracks .header's own sticky offset. */
    top: var(--pe-drawer-top, calc(36px + var(--header-height)));
    inset-inline-end: 0;
    bottom: 0;
    inset-inline-start: 0;
    background: #fff;
    flex-direction: column;
    align-items: stretch;
    padding: 16px;
    margin: 0;
    overflow-x: hidden;
    overflow-y: auto;
    transform: translateX(-100%);
    transition: transform 0.25s ease;
  }
  .main-nav.open { transform: translateX(0); }
  .main-nav a {
    padding: 14px 16px;
    border-bottom: 1px solid var(--color-border);
    border-radius: 0;
  }
  /* An accordion, not the old flat list: 74 style links in one column would be
     a drawer the length of six screens. The panels collapse only once main.js
     has marked the nav as enhanced, so with JS broken the drawer degrades to
     that flat list rather than to six dead rows. */
  .nav-dropdown-trigger {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
  .nav-mega-menu { padding-inline-start: 12px; }
  .nav-mega-head { color: var(--color-text-muted); font-size: 0.8rem; }
  .main-nav.is-enhanced .nav-dropdown:not(.is-open) .nav-mega-menu {
    display: none;
  }
  .menu-toggle { display: inline-flex; }
}

/* On a phone the row holds the brand plus four controls, and the wordmark was
   wide enough to push the mark off the left edge. */
@media (max-width: 620px) {
  .logo { font-size: 1.05rem; gap: 8px; }
  .logo-img { height: 30px; }
  .header-inner { gap: 10px; }
}
/* A 390px phone is the common case and the row was ~8px over it, which shows up
   as a page that scrolls sideways. The controls give up the slack, not the
   wordmark. */
@media (max-width: 430px) {
  .header-actions { gap: 2px; }
  .header-actions .icon-btn { width: 36px; }
}
/* Smallest phones still in use (iPhone SE, 360px Androids): the controls have
   nothing left to give, so the wordmark takes the last step down. */
@media (max-width: 380px) {
  .logo { font-size: 0.95rem; }
  .logo-img { height: 26px; }
}

/* ----- 6. Hero ----- */
/* DEPRECATED: legacy homepage .hero — replaced by .category-banner--all.
   Kept for now so future pages can reuse the styles if needed; the homepage
   no longer renders these elements. Safe to remove in a later cleanup. */
.hero {
  position: relative;
  background: linear-gradient(135deg, #fafaf7 0%, #f5f3ee 100%);
  padding: 72px 0 96px;
  overflow: hidden;
  border-bottom: 1px solid var(--color-border);
}
.hero::before {
  content: '';
  position: absolute;
  top: -120px; inset-inline-end: -120px;
  width: 420px; height: 420px;
  background: radial-gradient(circle, rgba(234, 88, 12, 0.08) 0%, transparent 70%);
  pointer-events: none;
}
.hero::after {
  content: '';
  position: absolute;
  bottom: -160px; inset-inline-start: -160px;
  width: 380px; height: 380px;
  background: radial-gradient(circle, rgba(30, 58, 138, 0.06) 0%, transparent 70%);
  pointer-events: none;
}
.hero-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 48px;
  align-items: center;
  position: relative;
}
.hero-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 14px;
  background: var(--color-accent-soft);
  color: var(--color-accent-dark);
  border: 1px solid rgba(234, 88, 12, 0.18);
  border-radius: 99px;
  font-size: 0.82rem;
  font-weight: 600;
  margin-bottom: 20px;
}
.hero h1 {
  font-size: clamp(2.25rem, 5vw, 3.5rem);
  margin-bottom: 16px;
  font-weight: 800;
}
.hero p.lead {
  font-size: 1.15rem;
  color: var(--color-text-muted);
  margin-bottom: 32px;
  max-width: 520px;
}
.hero-cta {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 32px;
}
.hero-trust {
  display: flex;
  flex-wrap: wrap;
  gap: 24px;
  font-size: 0.9rem;
  color: var(--color-text-muted);
}
.hero-trust span {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.hero-trust svg { color: var(--color-success); flex-shrink: 0; }

.hero-visual {
  position: relative;
  aspect-ratio: 1 / 1;
  max-width: 520px;
  margin-inline-start: auto;
}
.hero-visual .visual-card {
  position: absolute;
  background: #fff;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.hero-visual .vc-1 {
  top: 0; inset-inline-start: 0;
  width: 60%; height: 60%;
}
.hero-visual .vc-2 {
  bottom: 0; inset-inline-end: 0;
  width: 55%; height: 55%;
}
.hero-visual .vc-3 {
  top: 30%; inset-inline-end: 10%;
  width: 35%; height: 35%;
  border-radius: 50%;
}
.hero-visual img {
  width: 100%; height: 100%;
  object-fit: cover;
}

@media (max-width: 800px) {
  .hero { padding: 48px 0 64px; }
  .hero-grid { grid-template-columns: 1fr; gap: 32px; }
  .hero-visual { max-width: 360px; margin: 0 auto; }
}

/* ----- 7. Sections ----- */
.section { padding: 72px 0; }
.section-alt { background: var(--color-bg-alt); }
.section-dark { background: var(--color-bg-dark); color: #e2e8f0; }
.section-dark h2, .section-dark h3 { color: #fff; }

.section-header {
  text-align: center;
  max-width: 720px;
  margin: 0 auto 48px;
}
.section-header h2 {
  font-size: clamp(1.75rem, 3vw, 2.5rem);
  margin-bottom: 12px;
}
.section-header p {
  color: var(--color-text-muted);
  font-size: 1.05rem;
  margin: 0;
}

@media (max-width: 600px) {
  .section { padding: 48px 0; }
  .section-header { margin-bottom: 32px; }
}

/* ----- 8. Category cards (homepage) ----- */
.category-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 24px;
}
/* Four per row on desktop; an incomplete final row (e.g. a 5th card)
   stays centred instead of hugging the left edge. */
.category-grid > .category-card { flex: 0 1 calc(25% - 18px); }
@media (max-width: 1100px) { .category-grid > .category-card { flex-basis: calc(50% - 12px); } }
@media (max-width: 600px)  { .category-grid > .category-card { flex-basis: 100%; } }

.category-card {
  position: relative;
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: var(--transition);
  text-decoration: none;
  color: inherit;
}
.category-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lg);
  border-color: var(--color-primary-light);
}
.category-card .card-image {
  aspect-ratio: 4/3;
  background: var(--color-bg-alt);
  overflow: hidden;
}
.category-card .card-image img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 0.4s ease;
}
.category-card:hover .card-image img { transform: scale(1.05); }
.category-card .card-body {
  padding: 24px;
  flex: 1;
  display: flex;
  flex-direction: column;
}
.category-card h3 {
  margin: 0 0 8px;
  font-size: 1.4rem;
}
.category-card .card-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 0.875rem;
  color: var(--color-text-muted);
}
.category-card .price-from {
  color: var(--color-accent-dark);
  font-weight: 700;
  font-size: 1.05rem;
}

/* ----- 8b. Homepage product groups -----
   The three biggest families are shown style by style on the homepage. The
   cards are .product-card, exactly as on the listing pages, so the two can
   never drift apart visually; only the group header belongs to the homepage.
   Unlike the listing pages the whole card stays ONE anchor — those pages swap
   the <a> for a <div> in JS and inject their own inner link, and the homepage
   has no listing mode to do that — so the anchor needs its text styles reset
   and the "Customise" button is a <span> that only looks like one. */
/* Five cards a row, which needs more width than the 1280px the rest of the
   site is built on — so this ONE section reaches further into the margins.
   Scoped to #products, which only exists on the homepage. */
#products > .container { max-width: 1560px; }
.home-family { margin-bottom: 56px; }
.home-family:last-of-type { margin-bottom: 0; }
/* Group heading centred over its row of cards, with "see all" pulled out to
   the right edge so centring the title does not push it off-centre. */
.home-family__head {
  position: relative;
  margin: 0 auto 22px;
  padding-bottom: 12px;
  text-align: center;
  border-bottom: 1px solid var(--color-border);
}
.home-family__head h3 {
  margin: 0;
  font-size: clamp(1.5rem, 2.4vw, 2rem);
  font-weight: 700;
  letter-spacing: -0.01em;
}
.home-family__all {
  position: absolute;
  inset-inline-end: 0;
  bottom: 14px;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--color-primary);
  text-decoration: none;
  white-space: nowrap;
}
.home-family__all:hover { text-decoration: underline; }
a.product-card { text-decoration: none; color: inherit; }
/* Five per row, and the cards fill the row rather than sitting at a fixed
   280px — but this stays the listing pages' centred flex row (inherited from
   .product-grid) instead of becoming a real grid, because a family whose count
   is not a multiple of five ends on a short row, and grid would leave that row
   hard against the left edge with a hole beside it. Flex centres it.

   So the count per row is set by the flex-basis, one value per breakpoint:
   (100% − the gaps) ÷ n. Two extra pixels come off each basis so sub-pixel
   rounding can never push the last card of a row onto the next one; at these
   widths that is invisible. */
.home-family .product-grid {
  gap: 22px;
  max-width: none;
}
.home-family .product-grid > .product-card {
  flex: 0 0 calc((100% - 90px) / 5);
  max-width: none;
}
@media (max-width: 1400px) {
  .home-family .product-grid > .product-card { flex-basis: calc((100% - 68px) / 4); }
}
@media (max-width: 1120px) {
  .home-family .product-grid > .product-card { flex-basis: calc((100% - 46px) / 3); }
}
@media (max-width: 820px) {
  .home-family .product-grid > .product-card { flex-basis: calc((100% - 24px) / 2); }
  /* No room beside a centred title at this width: the link goes under it. */
  .home-family__all {
    position: static;
    display: inline-block;
    margin-top: 8px;
  }
}
@media (max-width: 520px) {
  .home-family .product-grid > .product-card { flex-basis: 100%; }
}

/* ----- 8c. Homepage "other products" tiles -----
   Everything not promoted above keeps a single tile: name and call to action
   on the left, category photo on the right. */
.home-other { margin-top: 64px; }
.home-other > h3 {
  margin: 0 0 20px;
  text-align: center;
  /* Same size as the family headings above — it is the same rank of heading. */
  font-size: clamp(1.5rem, 2.4vw, 2rem);
  font-weight: 700;
  letter-spacing: -0.01em;
}
/* Three across, leftover tiles on the last row sit in the middle — same
   centering as .product-grid above. 1fr columns would pin a short last
   row to the left, which is why this is flex rather than grid. */
.other-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 28px;
  margin: 0 auto;
}
.other-grid > .other-card {
  flex: 0 1 calc((100% - 56px) / 3);
  min-width: 0;
  max-width: calc((100% - 56px) / 3);
}
@media (max-width: 900px) {
  .other-grid > .other-card {
    flex-basis: calc((100% - 28px) / 2);
    max-width: calc((100% - 28px) / 2);
  }
}
@media (max-width: 560px) {
  .other-grid > .other-card {
    flex-basis: 100%;
    max-width: 100%;
  }
}
.other-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  /* Floor, not a fixed height: a long name wraps and the tile grows. */
  min-height: 136px;
  padding-inline-start: 26px;
  background: var(--color-bg-alt);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  transition: var(--transition);
}
.other-card:hover {
  border-color: var(--color-primary-light);
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}
.other-card__body {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
  padding: 18px 0;
}
.other-card__name { font-weight: 600; font-size: 1.12rem; }
.other-card__cta {
  font-size: 0.9rem;
  color: var(--color-text-muted);
  text-decoration: underline;
}
/* The photo runs the full height of the tile, whatever the name does to it. */
.other-card__img { flex: none; align-self: stretch; width: 176px; }
.other-card__img img { display: block; width: 100%; height: 100%; object-fit: cover; }
@media (max-width: 400px) {
  .other-card { padding-inline-start: 18px; }
  .other-card__img { width: 128px; }
}

/* ----- 9. Product grid (sub-category list on product pages) ----- */
.product-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 24px;
  margin-inline-start: auto;
  margin-inline-end: auto;
  max-width: 1280px;
}
.product-grid > .product-card {
  flex: 0 0 280px;
  max-width: 320px;
}

.product-card {
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
  transition: var(--transition);
}
.product-card:hover {
  border-color: var(--color-primary-light);
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}
.product-card .card-image {
  aspect-ratio: 1/1;
  background: var(--color-bg-alt);
  overflow: hidden;
  position: relative;
}
.product-card .card-image img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 0.4s ease;
}
.product-card:hover .card-image img { transform: scale(1.04); }
.product-card .badge-sale {
  position: absolute;
  top: 12px;
  inset-inline-start: 12px;
  background: var(--color-danger);
  color: #fff;
  font-size: 0.7rem;
  font-weight: 700;
  padding: 4px 8px;
  border-radius: 4px;
  letter-spacing: 0.05em;
}
.product-card .card-body {
  padding: 16px;
  display: flex;
  flex-direction: column;
  flex: 1;
  gap: 8px;
}
.product-card h3 {
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
  min-height: 2.4em;
  line-height: 1.2;
}
.product-card .meta {
  font-size: 0.8rem;
  color: var(--color-text-muted);
}
.product-card .rating {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.8rem;
  color: var(--color-text-muted);
}
.product-card .rating .stars { color: var(--color-accent); letter-spacing: 1px; }
.product-card .price {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin-top: auto;
}
.product-card .price-current {
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--color-text);
}
.product-card .price-prefix,
.product-card .price-suffix {
  font-size: 0.8rem;
  color: var(--color-text-muted);
}
/* ----- 10. Page header (sub-category pages) ----- */
.page-header {
  background: linear-gradient(135deg, #fafaf7 0%, #f5f3ee 100%);
  padding: 56px 0 40px;
  border-bottom: 1px solid var(--color-border);
}

/* ============================================================
   .category-banner — premium hero strip used at the very top of
   every product-listing page (PVC Cards / Lanyards / Wristbands)
   and on the homepage. The right two-thirds is a pre-composed
   collage of REAL Step-1 product photos (built by
   scripts/build-category-banners.py) — left ~40% is kept clean
   by a cream-to-transparent gradient so the heading stays
   crisp at every viewport width.

   One image per surface, swapped via the modifier class:
     .category-banner--wristbands     -> hero/category-banner-wristbands.jpg
     .category-banner--lanyards       -> hero/category-banner-lanyards.jpg
     .category-banner--idcards        -> hero/category-banner-idcards.jpg
     .category-banner--handsanitizers -> hero/category-banner-handsanitizers.jpg
     .category-banner--finder-tags    -> hero/category-banner-finder-tags.jpg
     .category-banner--all            -> hero/category-banner-all.jpg
   ============================================================ */
.category-banner {
  position: relative;
  background-color: #faf4e8;
  border-bottom: 1px solid var(--color-border);
  overflow: hidden;
}
.category-banner .container { position: relative; z-index: 2; }
.category-banner__inner {
  padding: 60px 0 72px;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: center;
  min-height: 380px;
}
.category-banner .breadcrumb { margin-bottom: 18px; }
.category-banner__text { max-width: 540px; }
.category-banner__eyebrow {
  display: inline-block;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--color-accent, #00b3a4);
  margin-bottom: 14px;
}
.category-banner__h1 {
  font-size: clamp(1.85rem, 3.4vw, 2.7rem);
  font-weight: 800;
  line-height: 1.12;
  letter-spacing: -0.01em;
  color: #1f2937;
  margin: 0 0 14px;
  /* Allow long CJK / hyphen-free strings (Chinese / German compound words)
     to break inside the text column so they never push past the cream area. */
  overflow-wrap: anywhere;
  word-break: break-word;
}
.category-banner__tagline {
  display: inline-block;
  background: #1f2937;
  color: #fff;
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 8px 16px;
  border-radius: 6px;
  margin-bottom: 16px;
}
.category-banner__lead {
  font-size: 1.02rem;
  line-height: 1.55;
  color: #4b5563;
  margin: 6px 0 10px;
}
.category-banner__sub {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--color-accent, #00b3a4);
}

/* ---------- Copy over a busy or dark photo ----------
   Set on the banner when the visible photo has no clear area to hold text —
   the lapel-pin wall being the case that forced this. Since nothing may be
   laid over the photo, the type has to carry its own contrast, so it flips to
   white and gets a dark edge drawn around it.

   The edge is what makes this work rather than the colour. That photo is not
   uniformly dark: it runs from near-black backing (luma 3) up to pale enamel
   and chrome (155), so white alone drops to about 2.8:1 wherever a light pin
   sits behind a stroke — under the 4.5:1 floor. The ring of shadow means each
   letter supplies its own dark background for the width of its own outline, so
   the contrast the eye actually resolves is white against that ring, not white
   against whichever pin happens to be there.

   Four hard 1px offsets draw the outline; the blurred pair behind them lift
   the whole block off the busiest patches. text-stroke would be tidier but
   thickens the strokes from the inside, which closes up dense CJK glyphs at
   this size — 消毒瓶 turns to mush — so the outline is drawn outside the
   letterform instead. */
.category-banner.is-photo-busy .category-banner__h1,
.category-banner.is-photo-busy .category-banner__lead {
  color: #fff;
  text-shadow:
    1px 1px 0 rgba(17, 20, 24, 0.92),
    -1px 1px 0 rgba(17, 20, 24, 0.92),
    1px -1px 0 rgba(17, 20, 24, 0.92),
    -1px -1px 0 rgba(17, 20, 24, 0.92),
    0 2px 6px rgba(17, 20, 24, 0.85),
    0 4px 18px rgba(17, 20, 24, 0.7);
}
/* The lead is smaller and lighter than the h1, so it is the first thing to
   fail; give it the same white rather than the muted grey it uses on cream. */
.category-banner.is-photo-busy .category-banner__lead { color: #f8fafc; }

/* The supporting lines need it too. They are easy to forget because the
   homepage hero has none of them — it uses a pill-shaped eyebrow that carries
   its own white background — but the lapel-pin LISTING page puts all three
   over the same photo, where muted grey and mid-teal disappear entirely.
   The accent-coloured pair keep their hue, lightened to hold up against a
   photo rather than cream; going white would flatten the hierarchy that tells
   these apart from the lead. */
.category-banner.is-photo-busy .breadcrumb,
.category-banner.is-photo-busy .breadcrumb a,
.category-banner.is-photo-busy .breadcrumb .sep {
  color: #e5e7eb;
  text-shadow:
    1px 1px 0 rgba(17, 20, 24, 0.92),
    -1px 1px 0 rgba(17, 20, 24, 0.92),
    1px -1px 0 rgba(17, 20, 24, 0.92),
    -1px -1px 0 rgba(17, 20, 24, 0.92),
    0 2px 6px rgba(17, 20, 24, 0.85);
}
.category-banner.is-photo-busy .breadcrumb a:hover { color: #fff; }
.category-banner.is-photo-busy .category-banner__eyebrow,
.category-banner.is-photo-busy .category-banner__sub {
  color: #5eead4;
  text-shadow:
    1px 1px 0 rgba(17, 20, 24, 0.92),
    -1px 1px 0 rgba(17, 20, 24, 0.92),
    1px -1px 0 rgba(17, 20, 24, 0.92),
    -1px -1px 0 rgba(17, 20, 24, 0.92),
    0 2px 6px rgba(17, 20, 24, 0.85);
}
/* Cross-fade the colour with the photo, so the type does not snap to white a
   beat before the dark slide has finished arriving. Matches the slide's own
   opacity transition. */
.category-banner .category-banner__h1,
.category-banner .category-banner__lead {
  transition: color 700ms ease-in-out, text-shadow 700ms ease-in-out;
}
@media (prefers-reduced-motion: reduce) {
  .category-banner .category-banner__h1,
  .category-banner .category-banner__lead { transition: none; }
}

/* Homepage variant — taller, with extra breathing room for CTAs +
   trust badges. Used together with .category-banner--carousel. */
.category-banner--lg .category-banner__inner {
  padding: 84px 0 96px;
  min-height: 480px;
}
.category-banner--lg .category-banner__text { max-width: 560px; }
.category-banner--lg .category-banner__h1 {
  /* Capped lower than before — at full-HD viewports the headline used
     to clamp to ~57px which overflowed the cream column with long CJK
     copy like “为欧洲市场打造的定制工牌卡、挂绳、手环与消毒瓶”. */
  font-size: clamp(2rem, 3.8vw, 2.9rem);
}
.category-banner--lg .category-banner__lead {
  font-size: 1.05rem;
  margin: 8px 0 22px;
  max-width: 520px;
}
.category-banner--lg .hero-cta { margin: 12px 0 26px; }
.category-banner--lg .hero-trust { margin-top: 4px; }

/* --- per-category background images (desktop: products on right) ---
   The banner JPGs are editorial product photography (1600x540) with
   the products composed on the right two-thirds against a soft cream
   paper background, with the left third left intentionally empty as
   negative space for the headline. The CSS overlay gradient just adds
   a SHORT cream wash over the leftmost 36% to guarantee heading
   contrast on any viewport. */
.category-banner--wristbands {
  background-image:
    linear-gradient(90deg, rgba(250, 245, 233, 0.94) 0%,
                            rgba(250, 245, 233, 0.55) 18%,
                            rgba(250, 245, 233, 0) 36%),
    url('../images/hero/category-banner-wristbands.jpg?v=hero-9');
  background-position: center right;
  background-size: cover;
  background-repeat: no-repeat;
}
.category-banner--lanyards {
  background-image:
    linear-gradient(90deg, rgba(250, 245, 233, 0.94) 0%,
                            rgba(250, 245, 233, 0.55) 18%,
                            rgba(250, 245, 233, 0) 36%),
    url('../images/hero/category-banner-lanyards.jpg?v=hero-9');
  background-position: center right;
  background-size: cover;
  background-repeat: no-repeat;
}
.category-banner--idcards {
  background-image:
    linear-gradient(90deg, rgba(250, 245, 233, 0.94) 0%,
                            rgba(250, 245, 233, 0.55) 18%,
                            rgba(250, 245, 233, 0) 36%),
    url('../images/hero/category-banner-idcards.jpg?v=hero-9');
  background-position: center right;
  background-size: cover;
  background-repeat: no-repeat;
}
.category-banner--handsanitizers {
  background-image:
    linear-gradient(90deg, rgba(250, 245, 233, 0.94) 0%,
                            rgba(250, 245, 233, 0.55) 18%,
                            rgba(250, 245, 233, 0) 36%),
    url('../images/hero/category-banner-handsanitizers.jpg?v=hs-hero-3');
  background-position: center right;
  background-size: cover;
  background-repeat: no-repeat;
}
.category-banner--baghangers {
  background-image:
    linear-gradient(90deg, rgba(250, 245, 233, 0.94) 0%,
                            rgba(250, 245, 233, 0.55) 18%,
                            rgba(250, 245, 233, 0) 36%),
    url('../images/hero/category-banner-baghangers.jpg?v=bh-hero-2');
  background-position: center right;
  background-size: cover;
  background-repeat: no-repeat;
}
.category-banner--scarves {
  background-image:
    linear-gradient(90deg, rgba(250, 245, 233, 0.94) 0%,
                            rgba(250, 245, 233, 0.55) 18%,
                            rgba(250, 245, 233, 0) 36%),
    url('../images/hero/category-banner-scarves.jpg?v=sc-hero-2');
  background-position: center right;
  background-size: cover;
  background-repeat: no-repeat;
}
.category-banner--buttons {
  background-image:
    linear-gradient(90deg, rgba(250, 245, 233, 0.94) 0%,
                            rgba(250, 245, 233, 0.55) 18%,
                            rgba(250, 245, 233, 0) 36%),
    url('../images/hero/category-banner-buttons.jpg?v=bt-hero-1');
  background-position: center right;
  background-size: cover;
  background-repeat: no-repeat;
}
/* No cream wash, unlike its neighbours here. Their photos leave the left side
   empty for the copy, so a wash over that side hides nothing; this one is a
   full-bleed wall of pins, so the same wash painted out the left half of the
   photograph. The copy carries its own contrast instead — the page sets
   .is-photo-busy on the section. */
.category-banner--lapelpins {
  background-image: url('../images/hero/category-banner-lapelpins.jpg?v=lp-hero-5');
  background-position: center right;
  background-size: cover;
  background-repeat: no-repeat;
}
.category-banner--luggage-tags {
  background-image:
    linear-gradient(90deg, rgba(250, 245, 233, 0.94) 0%,
                            rgba(250, 245, 233, 0.55) 18%,
                            rgba(250, 245, 233, 0) 36%),
    url('../images/hero/category-banner-luggage-tags.jpg?v=lt-hero-1');
  background-position: center right;
  background-size: cover;
  background-repeat: no-repeat;
}
.category-banner--milk-frothers,
.category-banner--finder-tags {
  /* Full-bleed like lapel pins: the photo covers the whole banner, including
     the copy side. These shots are light, so the default dark type stays. */
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
}
.category-banner--milk-frothers {
  background-image: url('../images/hero/category-banner-milk-frothers.jpg?v=mf-hero-8');
}
.category-banner--finder-tags {
  background-image: url('../images/hero/category-banner-finder-tags.jpg?v=fnd-banner-11');
}
.category-banner--finder-tags .pd-price-block { margin: 10px 0 16px; }
/* Homepage all-categories hero. Two stacked background layers:
     1. a cream→transparent gradient (cover, full-bleed) that keeps the
        left-hand headline readable
     2. the composite product photo (cover, anchored RIGHT) so it fills
        the banner at full size — NOT shrunk.
   The photo was widened to 4:1 (cream added on the LEFT, see
   scripts/_widen-all-banner.ps1). Because it's now wider than the banner
   at every common viewport, `cover` crops the expendable LEFT cream
   instead of the products — so the cards / lanyards / wristbands /
   bottles / wheat props on the right always stay fully visible and large.
   The added cream carries the photo's own paper texture, so the seam is
   invisible (and falls under the headline gradient anyway). */
.category-banner--all {
  background-image:
    linear-gradient(90deg, rgba(250, 245, 233, 0.94) 0%,
                            rgba(250, 245, 233, 0.55) 18%,
                            rgba(250, 245, 233, 0) 36%),
    url('../images/hero/category-banner-all.jpg?v=hero-25');
  background-position: center, right center;
  background-size: cover, cover;
  background-repeat: no-repeat, no-repeat;
}

/* ---------- Homepage hero carousel ----------
   Same banner, but the photo rotates through the three families the homepage
   sells style by style. The photo cannot stay on the section's own
   background-image (there is only one per element to cross-fade), so each
   slide is a stacked layer and the cream scrim that keeps the headline
   readable moves onto ::after, above the slides and below .container (z-index
   2). The first slide carries .is-active in the markup, so with JS off the
   hero is simply the static banner it was before. */
.category-banner--carousel { background-color: #faf4e8; }
.hero-slides {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
}
.hero-slide {
  position: absolute;
  inset: 0;
  background-position: right center;
  background-size: cover;
  background-repeat: no-repeat;
  opacity: 0;
  /* The change has to be felt, not just noticed after the fact: a long
     dissolve, plus the incoming photo settling from a hair over full size, so
     there is something moving for the whole of it. The zoom is small on
     purpose — it ends at exactly scale(1), so the photo spends its dwell time
     unscaled and pin-sharp, and only the couple of seconds of the handover
     are soft. */
  transform: scale(1.035);
  transition: opacity 1300ms ease-in-out, transform 1600ms ease-out;
}
.hero-slide.is-active { opacity: 1; transform: scale(1); }
/* No scrim over the photo.
   There used to be one: a near-opaque cream wash across the whole copy column
   so the dark text stayed readable whatever was behind it. Three of the four
   slides are shot for this layout — their left ~45% is empty backdrop and the
   products sit on the right — so on those the wash covered nothing but blank
   cream and cost nothing visible. On the lapel-pin shot, which is a full-bleed
   wall of pins with no clear side, it painted over half the photograph, and
   that was the half the visitor saw first. Readability now comes from the type
   itself (below), which costs nothing on the pale slides and leaves every
   photo whole. */
/* Arrows and dots are injected by hero-carousel.js, so they only exist when
   something can actually drive them. */
.hero-arrow {
  position: absolute;
  z-index: 3;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  padding: 0;
  border: 1px solid rgba(31, 41, 55, 0.10);
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.82);
  color: #1f2937;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: var(--transition);
}
.hero-arrow--prev { inset-inline-start: 18px; }
.hero-arrow--next { inset-inline-end: 18px; }
.hero-arrow:hover {
  background: #fff;
  color: var(--color-primary);
  box-shadow: var(--shadow-md);
}
.hero-arrow:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}
.hero-dots {
  position: absolute;
  z-index: 3;
  left: 50%;
  bottom: 18px;
  transform: translateX(-50%);
  display: flex;
  gap: 8px;
}
.hero-dot {
  width: 30px;
  height: 4px;
  padding: 0;
  border: 0;
  border-radius: 2px;
  background: rgba(31, 41, 55, 0.22);
  cursor: pointer;
  transition: background 200ms ease, transform 200ms ease;
}
.hero-dot:hover { background: rgba(31, 41, 55, 0.45); }
.hero-dot.is-active { background: var(--color-primary); transform: scaleY(1.5); }
.hero-dot:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 3px;
}
/* On a narrow screen the headline already covers most of the banner, so the
   controls shrink and tuck in rather than crowd the copy. */
@media (max-width: 700px) {
  .hero-dots { bottom: 10px; }
  .hero-dot { width: 22px; }
  .hero-arrow { width: 34px; height: 34px; }
  .hero-arrow svg { width: 16px; height: 16px; }
  .hero-arrow--prev { inset-inline-start: 8px; }
  .hero-arrow--next { inset-inline-end: 8px; }
}
/* Reduced motion drops the MOVEMENT — the zoom — and keeps the dissolve. A
   cross-fade is a change in opacity with nothing travelling across the screen,
   which is what this preference is asking to be spared; cutting the fade as
   well just turned the hero into a hard flick between photos, which is both
   uglier and harder to notice. */
@media (prefers-reduced-motion: reduce) {
  .hero-slide,
  .hero-slide.is-active {
    transform: none;
    transition: opacity 1300ms ease-in-out;
  }
}

/* ---------- Split layout (text column + explicit <img> column) ----------
   Used on the HOMEPAGE hero so the product photo on the right is rendered
   as a real <img> instead of a background image. Background-image cover
   was cropping the rightmost product (the orange polka-dot lanyard) on
   narrow / mid-size viewports because the section was wider than the
   visible viewport area. With an explicit <img> + object-fit: contain,
   the photo always shows in full, with the cream texture matching the
   section background so it blends seamlessly.

   Apply with: <section class="category-banner ... category-banner--split">
                 <div class="container">
                   <div class="category-banner__inner">
                     <div class="category-banner__text">…</div>
                     <div class="category-banner__media"><img …></div>
                   </div>
                 </div>
               </section>
*/
.category-banner--split {
  background-image: none !important;    /* drop the background image — the <img> takes over */
  background-color: #faf4e8;             /* cream wash matches the photo paper background */
}
.category-banner--split .category-banner__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  align-items: center;
  column-gap: 32px;
}
.category-banner--split .category-banner__media {
  position: relative;
  width: 100%;
  /* Hard ceiling so the image never reports a height taller than the
     section minimum — keeps the products from pushing the hero too tall
     on ultrawide monitors. */
  max-height: 520px;
}
.category-banner--split .category-banner__media img {
  display: block;
  width: 100%;
  height: 100%;
  max-height: 520px;
  object-fit: contain;
  object-position: right center;
}

/* --- narrow laptop / tablet / mobile: products fade to the bottom; text
   sits on top with full width. Breakpoint bumped from 900px → 1080px so
   common 13" laptops (≈1024px viewport) and split-screen / DevTools-open
   layouts always get the stacked variant — the side-by-side desktop
   layout left the headline too close to the products on narrower
   windows, causing long CJK copy to look clipped. */
@media (max-width: 1080px) {
  .category-banner__inner { padding: 36px 0 280px; min-height: auto; }
  .category-banner__text { max-width: 100%; }
  /* Below this width the photo stops being a backdrop: it shrinks to `contain`
     and sits along the bottom, leaving the copy on plain cream. White outlined
     type belongs on a photo and would be unreadable there, so the dark-photo
     treatment is confined to the wide layout that actually puts text over the
     image. */
  .category-banner.is-photo-busy .category-banner__h1 {
    color: #1f2937;
    text-shadow: none;
  }
  .category-banner.is-photo-busy .category-banner__lead {
    color: #4b5563;
    text-shadow: none;
  }
  .category-banner.is-photo-busy .breadcrumb,
  .category-banner.is-photo-busy .breadcrumb a,
  .category-banner.is-photo-busy .breadcrumb .sep {
    color: var(--color-text-muted);
    text-shadow: none;
  }
  .category-banner.is-photo-busy .category-banner__eyebrow,
  .category-banner.is-photo-busy .category-banner__sub {
    color: var(--color-accent, #00b3a4);
    text-shadow: none;
  }
  .category-banner--wristbands {
    background-image:
      linear-gradient(180deg, rgba(250, 245, 233, 0.92) 0%,
                              rgba(250, 245, 233, 0.40) 50%,
                              rgba(250, 245, 233, 0) 80%),
      url('../images/hero/category-banner-wristbands.jpg?v=hero-9');
    background-position: center bottom;
    background-size: contain;
  }
  .category-banner--lanyards {
    background-image:
      linear-gradient(180deg, rgba(250, 245, 233, 0.92) 0%,
                              rgba(250, 245, 233, 0.40) 50%,
                              rgba(250, 245, 233, 0) 80%),
      url('../images/hero/category-banner-lanyards.jpg?v=hero-9');
    background-position: center bottom;
    background-size: contain;
  }
  .category-banner--idcards {
    background-image:
      linear-gradient(180deg, rgba(250, 245, 233, 0.92) 0%,
                              rgba(250, 245, 233, 0.40) 50%,
                              rgba(250, 245, 233, 0) 80%),
      url('../images/hero/category-banner-idcards.jpg?v=hero-9');
    background-position: center bottom;
    background-size: contain;
  }
  .category-banner--handsanitizers {
    background-image:
      linear-gradient(180deg, rgba(250, 245, 233, 0.92) 0%,
                              rgba(250, 245, 233, 0.40) 50%,
                              rgba(250, 245, 233, 0) 80%),
      url('../images/hero/category-banner-handsanitizers.jpg?v=hs-hero-3');
    background-position: center bottom;
    background-size: contain;
  }
  .category-banner--baghangers {
    background-image:
      linear-gradient(180deg, rgba(250, 245, 233, 0.92) 0%,
                              rgba(250, 245, 233, 0.40) 50%,
                              rgba(250, 245, 233, 0) 80%),
      url('../images/hero/category-banner-baghangers.jpg?v=bh-hero-2');
    background-position: center bottom;
    background-size: contain;
  }
  .category-banner--scarves {
    background-image:
      linear-gradient(180deg, rgba(250, 245, 233, 0.92) 0%,
                              rgba(250, 245, 233, 0.40) 50%,
                              rgba(250, 245, 233, 0) 80%),
      url('../images/hero/category-banner-scarves.jpg?v=sc-hero-2');
    background-position: center bottom;
    background-size: contain;
  }
  .category-banner--buttons {
    background-image:
      linear-gradient(180deg, rgba(250, 245, 233, 0.92) 0%,
                              rgba(250, 245, 233, 0.40) 50%,
                              rgba(250, 245, 233, 0) 80%),
      url('../images/hero/category-banner-buttons.jpg?v=bt-hero-1');
    background-position: center bottom;
    background-size: contain;
  }
  .category-banner--lapelpins {
    background-image:
      linear-gradient(180deg, rgba(250, 245, 233, 0.92) 0%,
                              rgba(250, 245, 233, 0.40) 50%,
                              rgba(250, 245, 233, 0) 80%),
      url('../images/hero/category-banner-lapelpins.jpg?v=lp-hero-5');
    background-position: center bottom;
    background-size: contain;
  }
  .category-banner--luggage-tags {
    background-image:
      linear-gradient(180deg, rgba(250, 245, 233, 0.92) 0%,
                              rgba(250, 245, 233, 0.40) 50%,
                              rgba(250, 245, 233, 0) 80%),
      url('../images/hero/category-banner-luggage-tags.jpg?v=lt-hero-1');
    background-position: center bottom;
    background-size: contain;
  }
  .category-banner--milk-frothers {
    background-image:
      linear-gradient(180deg, rgba(250, 245, 233, 0.92) 0%,
                              rgba(250, 245, 233, 0.40) 50%,
                              rgba(250, 245, 233, 0) 80%),
      url('../images/hero/category-banner-milk-frothers.jpg?v=mf-hero-8');
    background-position: center bottom;
    background-size: contain;
  }
  .category-banner--finder-tags {
    background-image:
      linear-gradient(180deg, rgba(250, 245, 233, 0.92) 0%,
                              rgba(250, 245, 233, 0.40) 50%,
                              rgba(250, 245, 233, 0) 80%),
      url('../images/hero/category-banner-finder-tags.jpg?v=fnd-banner-11');
    background-position: center bottom;
    background-size: contain;
  }
  .category-banner--all {
    background-image:
      linear-gradient(180deg, rgba(250, 245, 233, 0.92) 0%,
                              rgba(250, 245, 233, 0.40) 50%,
                              rgba(250, 245, 233, 0) 80%),
      url('../images/hero/category-banner-all.jpg?v=hero-25');
    background-position: center bottom;
    background-size: contain;
  }
  /* Carousel: same treatment as the static banners above — the photo drops
     below the copy, contained so nothing is cropped, and the scrim turns
     vertical so it washes the top of the banner instead of the left.
     The homepage hero needs the room reserved explicitly: its own
     .category-banner--lg padding outranks the one-class rule at the top of
     this block, so without this the photo band sits behind the CTA. */
  .category-banner--lg .category-banner__inner { padding: 36px 0 280px; min-height: auto; }
  .hero-slide,
  .hero-slide.is-active {
    background-position: center bottom;
    background-size: contain;
    /* No zoom down here: the photo is CONTAINED, so scaling it up crops the
       edges off instead of filling more of the banner. The dissolve carries
       the change on its own. */
    transform: none;
  }
  .hero-slides::after {
    /* Same fix as the desktop scrim, on the other axis: the copy occupies
       roughly the top half here (36px of padding, then the headline, with the
       photo band reserved by the 280px bottom padding), but the scrim used to
       be down to 0.40 by the 50% mark — right where the headline still is.
       Holding it near-opaque until 50% costs nothing, because a `contain`
       photo anchored to the bottom has little in the top half anyway. */
    background: linear-gradient(180deg, rgba(250, 245, 233, 0.96) 0%,
                                       rgba(250, 245, 233, 0.94) 50%,
                                       rgba(250, 245, 233, 0.62) 68%,
                                       rgba(250, 245, 233, 0) 88%);
  }
  /* Split layout collapses to a single column: text on top, photo below.
     The <img> takes the full width with a comfortable max-height so the
     hero doesn't grow taller than necessary on tablets. */
  .category-banner--split .category-banner__inner {
    grid-template-columns: minmax(0, 1fr);
    row-gap: 24px;
    padding: 36px 0 32px;
  }
  .category-banner--split .category-banner__media,
  .category-banner--split .category-banner__media img {
    max-height: 320px;
  }
  .category-banner--split .category-banner__media img {
    object-position: center center;
  }
}
@media (max-width: 520px) {
  .category-banner__inner { padding: 28px 0 220px; }
  .category-banner__h1 { font-size: 1.85rem; }
}

/* ============ ID Cards hero (richer hero with sample cards graphic) ============ */
.id-hero {
  background: linear-gradient(135deg, #fafaf7 0%, #f5f3ee 100%);
  padding: 40px 0 28px;
  border-bottom: 1px solid var(--color-border);
}
.id-hero .breadcrumb { margin-bottom: 18px; }
.id-hero-grid {
  display: grid;
  grid-template-columns: minmax(280px, 1fr) minmax(380px, 1.05fr);
  gap: 32px;
  align-items: center;
}
.id-hero-eyebrow {
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 2.5px;
  color: var(--color-text-muted);
  margin-bottom: 8px;
  text-transform: uppercase;
}
.id-hero-h1 {
  margin: 0 0 14px;
  font-size: clamp(2rem, 4.5vw, 3.4rem);
  line-height: 1.02;
  font-weight: 900;
  letter-spacing: -0.02em;
  color: #0f0f0e;
}
.id-hero-tagline {
  display: inline-block;
  background: #111;
  color: #fff;
  padding: 9px 16px;
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 1.2px;
  margin: 0 0 18px;
  border-radius: 4px;
  line-height: 1.3;
  text-transform: uppercase;
}
.id-hero .lead {
  color: var(--color-text-muted);
  font-size: 1rem;
  max-width: 560px;
  margin: 0;
  line-height: 1.55;
}
.id-hero .lead-sub {
  margin: 12px 0 0;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--color-primary);
  letter-spacing: 0.02em;
}
.id-hero-cards {
  width: 100%;
  max-width: 620px;
  justify-self: end;
}
.id-hero-cards svg {
  width: 100%;
  height: auto;
  display: block;
}
@media (max-width: 860px) {
  .id-hero-grid { grid-template-columns: 1fr; gap: 22px; }
  .id-hero-cards { max-width: 540px; justify-self: center; }
  .id-hero-tagline { font-size: 0.78rem; letter-spacing: 1px; padding: 8px 13px; }
}
@media (max-width: 520px) {
  .id-hero { padding: 28px 0 20px; }
  .id-hero-h1 { font-size: 1.85rem; }
}

/* ============ Lanyard hero (same vibe as .id-hero, but with real
   product photos fanned across the right column instead of SVG
   cards). The five photo tiles overlap and rotate progressively to
   create the same magazine-cover spread the PVC page uses. ============ */
.lan-hero {
  background: linear-gradient(135deg, #f7f6f1 0%, #efece5 100%);
  padding: 40px 0 36px;
  border-bottom: 1px solid var(--color-border);
  overflow: hidden;
}
.lan-hero .breadcrumb { margin-bottom: 18px; }
.lan-hero-grid {
  display: grid;
  grid-template-columns: minmax(280px, 1fr) minmax(420px, 1.05fr);
  gap: 32px;
  align-items: center;
}
.lan-hero-eyebrow {
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 2.5px;
  color: var(--color-text-muted);
  margin-bottom: 8px;
  text-transform: uppercase;
}
.lan-hero-h1 {
  margin: 0 0 14px;
  font-size: clamp(2rem, 4.5vw, 3.4rem);
  line-height: 1.02;
  font-weight: 900;
  letter-spacing: -0.02em;
  color: #0f0f0e;
}
.lan-hero-tagline {
  display: inline-block;
  background: #111;
  color: #fff;
  padding: 9px 16px;
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 1.2px;
  margin: 0 0 18px;
  border-radius: 4px;
  line-height: 1.3;
  text-transform: uppercase;
}
.lan-hero .lead {
  color: var(--color-text-muted);
  font-size: 1rem;
  max-width: 560px;
  margin: 0;
  line-height: 1.55;
}
.lan-hero .lead-sub {
  margin: 12px 0 0;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--color-primary);
  letter-spacing: 0.02em;
}

/* The fanned-photo strip.
   Layout: relative-positioned container, each card absolutely
   placed with a left%, rotation and z-index. The container height
   is set proportionally so we don't need width-tied media queries. */
.lan-hero-strip {
  position: relative;
  width: 100%;
  max-width: 620px;
  justify-self: end;
  aspect-ratio: 720 / 440;          /* matches the PVC hero canvas */
}
.lan-hero-card {
  position: absolute;
  top: 8%;
  width: 24%;
  aspect-ratio: 156 / 246;          /* same portrait shape as PVC cards */
  border-radius: 14px;
  overflow: hidden;
  background: #fff;
  box-shadow:
    0 1px 0 rgba(255,255,255,0.7) inset,
    0 14px 28px rgba(33, 25, 60, 0.20),
    0 4px 8px rgba(33, 25, 60, 0.10);
  transform-origin: 50% 80%;
  transition: transform 0.35s ease, box-shadow 0.35s ease;
}
.lan-hero-card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.lan-hero-card--1 { inset-inline-start:  1%;  z-index: 1; transform: rotate(-22deg); }
.lan-hero-card--2 { inset-inline-start: 19%;  z-index: 2; transform: rotate(-11deg); }
.lan-hero-card--3 { inset-inline-start: 38%;  z-index: 5; transform: rotate(  0deg) translateY(-6%); }
.lan-hero-card--4 { inset-inline-start: 57%;  z-index: 4; transform: rotate( 11deg); }
.lan-hero-card--5 { inset-inline-start: 75%;  z-index: 3; transform: rotate( 22deg); }
.lan-hero-card:hover {
  z-index: 9;
  transform: scale(1.06) rotate(0deg) translateY(-10px);
  box-shadow:
    0 1px 0 rgba(255,255,255,0.7) inset,
    0 22px 38px rgba(33, 25, 60, 0.32),
    0 6px 12px rgba(33, 25, 60, 0.14);
}

@media (max-width: 980px) {
  .lan-hero-grid { grid-template-columns: 1fr; gap: 24px; }
  .lan-hero-strip { max-width: 580px; justify-self: center; }
  .lan-hero-tagline { font-size: 0.78rem; letter-spacing: 1px; padding: 8px 13px; }
}
@media (max-width: 560px) {
  .lan-hero { padding: 28px 0 24px; }
  .lan-hero-h1 { font-size: 1.85rem; }
  /* Tighter fan + slightly larger cards so the 3 visible ones still
     read on a phone (#1 and #5 partly clip off-canvas — that's the
     intended magazine-edge feel). */
  .lan-hero-card { width: 30%; }
  .lan-hero-card--1 { inset-inline-start:  -4%; transform: rotate(-22deg); }
  .lan-hero-card--2 { inset-inline-start: 13%;  transform: rotate(-11deg); }
  .lan-hero-card--3 { inset-inline-start: 35%;  transform: rotate(  0deg) translateY(-6%); }
  .lan-hero-card--4 { inset-inline-start: 57%;  transform: rotate( 11deg); }
  .lan-hero-card--5 { inset-inline-start: 76%;  transform: rotate( 22deg); }
}
.breadcrumb {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.875rem;
  color: var(--color-text-muted);
  margin-bottom: 16px;
}
.breadcrumb a { color: var(--color-text-muted); }
.breadcrumb a:hover { color: var(--color-primary); }
.breadcrumb .sep { color: var(--color-text-light); }
.page-header h1 {
  margin: 0 0 12px;
  font-size: clamp(1.75rem, 3vw, 2.5rem);
}
.page-header .lead {
  color: var(--color-text-muted);
  font-size: 1.05rem;
  max-width: 720px;
  margin: 0;
}
.page-header .lead-sub {
  margin: 14px 0 0;
  font-size: 1rem;
  font-weight: 600;
  color: var(--color-primary);
  letter-spacing: 0.01em;
}

.product-section { padding: 48px 0; }
.product-section h2 {
  font-size: 1.5rem;
  margin-bottom: 24px;
  padding-bottom: 12px;
  border-bottom: 2px solid var(--color-border);
}

/* ----- 11. Steps (Customize in X Easy Steps) ----- */
.steps-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 24px;
  counter-reset: step;
}
.step-card {
  text-align: center;
  padding: 24px 16px;
  position: relative;
}
.step-card .step-icon {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-light) 100%);
  display: grid;
  place-items: center;
  margin: 0 auto 16px;
  color: #fff;
  font-weight: 800;
  font-size: 1.25rem;
}
.step-card h4 {
  font-size: 1rem;
  margin-bottom: 6px;
}
.step-card p {
  font-size: 0.875rem;
  color: var(--color-text-muted);
  margin: 0;
}

/* ----- 12. Brand strip ----- */
.brand-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 32px;
  align-items: center;
  opacity: 0.7;
}
.brand-strip > div {
  display: grid;
  place-items: center;
  /* Sector names, not short brand marks: German "Gastgewerbe" and French
     "Associations" are twice the width of the logotypes this strip was built
     for, so the row has to grow rather than clip them. */
  min-height: 60px;
  text-align: center;
  font-weight: 700;
  font-size: 1.15rem;
  color: var(--color-text-muted);
  letter-spacing: 0.02em;
}

/* ----- 13. Testimonials ----- */
.testimonial-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 24px;
}
.testimonial-card {
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: 24px;
  position: relative;
}
.testimonial-card .stars {
  color: var(--color-accent);
  letter-spacing: 2px;
  margin-bottom: 12px;
  /* Inter's Latin subset has no U+2605; without a symbol fallback the
     glyph becomes tofu / replacement chars on some Windows fonts. */
  font-family: "Segoe UI Symbol", "Apple Symbols", "Noto Sans Symbols", sans-serif;
}
.testimonial-card blockquote {
  margin: 0 0 16px;
  font-size: 0.95rem;
  color: var(--color-text);
  line-height: 1.6;
  font-style: italic;
}
.testimonial-card .author {
  display: flex;
  align-items: center;
  gap: 12px;
}
.testimonial-card .avatar {
  width: 40px; height: 40px;
  border-radius: 50%;
  background: linear-gradient(135deg, #1e3a8a 0%, #3b82f6 100%);
  display: grid; place-items: center;
  color: #fff;
  font-weight: 700;
  font-size: 0.9rem;
}
.testimonial-card .author-name {
  font-weight: 600;
  font-size: 0.875rem;
}
.testimonial-card .author-meta {
  font-size: 0.75rem;
  color: var(--color-text-muted);
}

/* ----- 14. Features (Why us) ----- */
.feature-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 24px;
}
.feature-card {
  text-align: center;
  padding: 24px;
}
.feature-card .feature-icon {
  width: 56px; height: 56px;
  border-radius: 14px;
  background: rgba(59, 130, 246, 0.1);
  color: var(--color-primary);
  display: grid; place-items: center;
  margin: 0 auto 16px;
}
.feature-card h4 {
  font-size: 1.05rem;
  margin-bottom: 8px;
}
.feature-card p {
  color: var(--color-text-muted);
  font-size: 0.9rem;
  margin: 0;
}

/* ----- 15. CTA banner ----- */
.cta-banner {
  background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-dark) 100%);
  color: #fff;
  padding: 56px 0;
  text-align: center;
  border-radius: var(--radius-lg);
  margin: 48px 0;
  position: relative;
  overflow: hidden;
}
.cta-banner::before {
  content: '';
  position: absolute;
  top: -50%; inset-inline-end: -10%;
  width: 60%; height: 200%;
  background: radial-gradient(circle, rgba(234, 88, 12, 0.18) 0%, transparent 60%);
  pointer-events: none;
}
.cta-banner h2 {
  color: #fff;
  margin-bottom: 12px;
  position: relative;
}
.cta-banner p {
  color: rgba(255, 255, 255, 0.85);
  max-width: 600px;
  margin: 0 auto 24px;
  position: relative;
}

/* ----- 16. Footer ----- */
.footer {
  background: var(--color-bg-dark);
  color: #cbd5e1;
  padding: 64px 0 0;
}
.footer-grid {
  display: grid;
  grid-template-columns: 1.5fr 1fr 1fr 1fr;
  gap: 48px;
  padding-bottom: 48px;
}
@media (max-width: 800px) {
  .footer-grid { grid-template-columns: 1fr 1fr; gap: 32px; }
}
@media (max-width: 480px) {
  .footer-grid { grid-template-columns: 1fr; }
}

.footer h4 {
  color: #fff;
  font-size: 0.9rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-bottom: 18px;
}
.footer ul {
  list-style: none;
  padding: 0;
  margin: 0;
}
.footer li { margin-bottom: 10px; }
.footer a {
  color: #94a3b8;
  font-size: 0.9rem;
}
.footer a:hover { color: #fff; }

.footer-brand .logo {
  color: #fff;
  margin-bottom: 16px;
}
.footer-brand p {
  font-size: 0.875rem;
  color: #94a3b8;
  margin-bottom: 16px;
  line-height: 1.6;
}
.footer-contact {
  font-size: 0.875rem;
  line-height: 1.8;
  color: #94a3b8;
}

/* WhatsApp row inside the footer-contact block — order-card.com
   ships the same kind of green call-to-action right under the
   support phone. Matches the topbar wa icon colour for visual
   continuity. */
.footer-wa {
  margin-top: 10px;
}
.footer-wa a {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 12px;
  border-radius: 999px;
  background: rgba(34, 197, 94, 0.12);
  color: #4ade80;
  font-size: 0.825rem;
  font-weight: 600;
  text-decoration: none;
  transition: background 0.15s ease, color 0.15s ease;
}
.footer-wa a:hover {
  background: rgba(34, 197, 94, 0.22);
  color: #86efac;
}
.footer-wa a svg {
  flex-shrink: 0;
}

.footer-bottom {
  border-top: 1px solid rgba(255, 255, 255, 0.08);
  padding: 24px 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  font-size: 0.85rem;
  color: #64748b;
}
.payment-methods {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}
.payment-methods .pm {
  background: rgba(255,255,255,0.08);
  border: 1px solid rgba(255,255,255,0.12);
  color: #cbd5e1;
  font-size: 0.7rem;
  padding: 4px 8px;
  border-radius: 4px;
  font-weight: 600;
  letter-spacing: 0.04em;
}

/* ----- 17. Cookie banner (GDPR) ----- */
.cookie-banner {
  position: fixed;
  bottom: 16px;
  inset-inline-start: 16px;
  inset-inline-end: 16px;
  max-width: 540px;
  margin: 0 auto;
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  padding: 20px;
  z-index: 100;
  display: none;
}
.cookie-banner.visible { display: block; }
.cookie-banner h4 {
  margin: 0 0 8px;
  font-size: 1rem;
}
.cookie-banner p {
  font-size: 0.875rem;
  color: var(--color-text-muted);
  margin: 0 0 16px;
  line-height: 1.5;
}
.cookie-banner p a {
  color: var(--color-primary);
  text-decoration: underline;
}
.cookie-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.cookie-actions .btn { padding: 8px 16px; font-size: 0.875rem; }

/* ----- 17b. "Read this page in your language?" offer -----
   Injected by main.js maybeOfferBrowserLang() only when the page is showing a
   language the visitor's browser didn't ask for. Sits under the sticky topbar +
   header and points at the switcher, so declining it still teaches where the
   control is. Fixed rather than in-flow: it appears after first paint and must
   not shove the page down. z-index below .cookie-banner (100) — consent is the
   more urgent of the two if they ever collide, and they cannot overlap
   visually since this one is anchored to the top. */
.lang-hint {
  position: fixed;
  top: calc(36px + var(--header-height) + 12px);
  inset-inline-end: 16px;
  z-index: 90;
  display: flex;
  align-items: center;
  gap: 12px;
  max-width: calc(100vw - 32px);
  padding: 10px 10px 10px 16px;
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  font-size: 0.875rem;
  animation: langHintIn 0.28s var(--ease-out) both;
}
@keyframes langHintIn {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .lang-hint { animation: none; }
}
.lang-hint-text {
  color: var(--color-text);
  font-weight: 500;
}
.lang-hint-go {
  flex: 0 0 auto;
  padding: 7px 14px;
  border: 0;
  border-radius: var(--radius-sm);
  background: var(--color-primary);
  color: #fff;
  font-size: 0.8125rem;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition: background var(--transition-fast);
}
.lang-hint-go:hover { background: var(--color-primary-dark); }
.lang-hint-close {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--color-text-muted);
  font-size: 1.15rem;
  line-height: 1;
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast);
}
.lang-hint-close:hover {
  background: var(--color-surface-alt);
  color: var(--color-text);
}
/* Narrow screens: full width under the header. Kept at the top rather than
   moved to the bottom so it never stacks with the cookie banner. */
@media (max-width: 700px) {
  .lang-hint {
    inset-inline-start: 12px;
    inset-inline-end: 12px;
    max-width: none;
    flex-wrap: wrap;
    padding: 12px;
  }
  .lang-hint-text { flex: 1 1 100%; }
  .lang-hint-go { flex: 1 1 auto; padding: 9px 14px; }
  .lang-hint-close { position: absolute; top: 6px; inset-inline-end: 6px; }
}

/* ----- 18. Generic content pages (about, faq, etc.) ----- */
.content-page {
  padding: 56px 0 80px;
  max-width: 800px;
  margin: 0 auto;
}
.content-page h1 {
  margin-bottom: 8px;
}
.content-page .updated {
  color: var(--color-text-muted);
  font-size: 0.875rem;
  margin-bottom: 32px;
}
.content-page h2 {
  font-size: 1.5rem;
  margin-top: 40px;
  margin-bottom: 16px;
}
.content-page h3 {
  font-size: 1.15rem;
  margin-top: 24px;
  margin-bottom: 8px;
}
.content-page p, .content-page li {
  color: var(--color-text);
  line-height: 1.7;
}

/* FAQ accordion */
.faq-list { display: flex; flex-direction: column; gap: 12px; }
.faq-item {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: #fff;
}
.faq-question {
  width: 100%;
  text-align: start;
  padding: 18px 20px;
  font-weight: 600;
  font-size: 1rem;
  color: var(--color-text);
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: #fff;
  transition: var(--transition);
}
.faq-question:hover { background: var(--color-bg-alt); }
.faq-question .icon {
  flex-shrink: 0;
  transition: transform 0.2s ease;
}
.faq-item.open .faq-question .icon { transform: rotate(180deg); }
.faq-answer {
  display: none;
  padding: 0 20px 18px;
  color: var(--color-text-muted);
  line-height: 1.7;
}
.faq-item.open .faq-answer { display: block; }

/* Contact form */
.form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}
@media (max-width: 600px) { .form-grid { grid-template-columns: 1fr; } }
.form-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.form-field.full { grid-column: 1 / -1; }
.form-field label {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--color-text);
}
.form-field input,
.form-field select,
.form-field textarea {
  padding: 10px 14px;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 0.95rem;
  color: var(--color-text);
  background: #fff;
  transition: var(--transition);
}
.form-field input:focus,
.form-field select:focus,
.form-field textarea:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px rgba(30, 58, 138, 0.1);
}
.form-field textarea { resize: vertical; min-height: 120px; }

/* Status banner shown under the contact-page submit button after
   the buyer's mail client has been opened. Only two states needed
   since mailto: dispatch is instant: success (mailto fired ok)
   and error (validation failure). order-card.com's contact form
   uses an equally lightweight inline notice. */
.contact-form-status {
  margin: 12px 0 0;
  padding: 10px 14px;
  border-radius: 8px;
  font-size: 14px;
  line-height: 1.45;
  border: 1px solid transparent;
}
.contact-form-status--success {
  background: #ecfdf5;
  border-color: #6ee7b7;
  color: #065f46;
}
.contact-form-status--error {
  background: #fef2f2;
  border-color: #fca5a5;
  color: #991b1b;
}

/* ----- 19. Utilities ----- */
.text-center { text-align: center; }
.text-muted { color: var(--color-text-muted); }
.mt-0 { margin-top: 0 !important; }
.mb-0 { margin-bottom: 0 !important; }
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border: 0;
}

/* ===========================================================================
   20. Product card "Customize" CTA
   =========================================================================== */
.product-card .card-cta {
  margin-top: 8px;
}
.btn-customize {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 10px 14px;
  background: var(--color-primary-dark);
  color: #fff;
  border-radius: var(--radius-sm);
  font-weight: 600;
  font-size: 0.875rem;
  border: none;
  cursor: pointer;
  transition: var(--transition);
  font-family: inherit;
}
.btn-customize:hover {
  background: var(--color-accent);
  color: #fff;
  transform: translateY(-1px);
}
.btn-customize svg { width: 14px; height: 14px; }

/* ===========================================================================
   21. Configurator modal (Step 1 → Step N wizard)
   =========================================================================== */
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 42, 0.55);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  display: none;
  align-items: center;
  justify-content: center;
  padding: 16px;
  z-index: 200;
  opacity: 0;
  transition: opacity 0.2s ease;
}
.modal-backdrop.open {
  display: flex;
  opacity: 1;
}
.modal {
  background: #fff;
  border-radius: var(--radius-lg);
  width: 100%;
  max-width: 980px;
  max-height: 92vh;
  display: grid;
  grid-template-rows: auto 1fr auto;
  overflow: hidden;
  box-shadow: 0 24px 64px rgba(0,0,0,0.25);
  transform: translateY(8px);
  transition: transform 0.2s ease;
}
.modal-backdrop.open .modal { transform: translateY(0); }

/* Modal header */
.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 18px 24px;
  border-bottom: 1px solid var(--color-border);
}
.modal-title {
  font-size: 1rem;
  font-weight: 700;
  margin: 0;
  color: var(--color-text);
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
}
.modal-title .modal-product {
  color: var(--color-accent-dark);
  font-weight: 700;
}
.modal-close {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: transparent;
  color: var(--color-text-muted);
  display: grid;
  place-items: center;
  cursor: pointer;
  border: none;
  transition: var(--transition);
}
.modal-close:hover { background: var(--color-bg-alt); color: var(--color-text); }

/* Step navigation */
.config-stepnav {
  display: flex;
  align-items: center;
  gap: 0;
  padding: 18px 24px 8px;
  overflow-x: auto;
  border-bottom: 1px solid var(--color-border);
  background: var(--color-bg-alt);
}
.config-stepnav::-webkit-scrollbar { height: 4px; }
.config-stepnav::-webkit-scrollbar-thumb { background: var(--color-border-strong); border-radius: 2px; }

.config-stepnav .step {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
  position: relative;
}
.config-stepnav .step + .step { padding-inline-start: 28px; }
.config-stepnav .step + .step::before {
  content: '';
  position: absolute;
  inset-inline-start: 12px; top: 50%;
  width: 12px;
  height: 1px;
  background: var(--color-border-strong);
}
.config-stepnav .step-circle {
  width: 28px; height: 28px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: #fff;
  border: 1.5px solid var(--color-border-strong);
  color: var(--color-text-muted);
  font-weight: 700;
  font-size: 0.78rem;
  flex-shrink: 0;
}
.config-stepnav .step-label {
  font-size: 0.82rem;
  color: var(--color-text-muted);
  font-weight: 600;
  white-space: nowrap;
}
.config-stepnav .step.is-active .step-circle {
  background: var(--color-primary-dark);
  border-color: var(--color-primary-dark);
  color: #fff;
}
.config-stepnav .step.is-active .step-label {
  color: var(--color-text);
}
.config-stepnav .step.is-done .step-circle {
  background: var(--color-success);
  border-color: var(--color-success);
  color: #fff;
}
.config-stepnav .step.is-done .step-circle::after {
  content: '\2713';
  font-weight: 700;
}
.config-stepnav .step.is-done .step-circle .step-num { display: none; }

/* Modal body / step panels */
.modal-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 280px;
  overflow: hidden;
  min-height: 380px;
}
@media (max-width: 800px) {
  .modal-body { grid-template-columns: 1fr; }
}
.config-main {
  padding: 28px 32px 16px;
  overflow-y: auto;
  min-width: 0;
}
/* Disable the browser's scroll-anchoring inside the configurator. When a
   style tile is clicked, applyVariantSteps() hides every step block and then
   re-inserts them in order; the browser would otherwise re-anchor the viewport
   onto a moved block and fling the page to a different step (most noticeable
   when the clicked tile was only half visible). With anchoring off, our manual
   scroll-restore (requestAnimationFrame + scrollTo) keeps the page steady. */
#configurator, #configurator * { overflow-anchor: none; }
/* Minimum-order-quantity notice under the cloth wristband Quantity input. */
.qty-moq-warn {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 6px 0 0;
  color: #dc2626;
  font-size: 0.82rem;
  font-weight: 600;
}
.qty-moq-warn[hidden] { display: none; }
.qty-moq-warn svg { flex: 0 0 auto; }
.wb-input.qty-invalid {
  border-color: #dc2626;
  box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.15);
}
.config-step-panel { display: none; }
.config-step-panel.is-active { display: block; }

.config-step-panel h3 {
  font-size: 1.25rem;
  margin: 0 0 6px;
  color: var(--color-text);
}
.config-step-panel .step-help {
  margin: 0 0 22px;
  color: var(--color-text-muted);
  font-size: 0.92rem;
}

/* Option cards (style/material/clip selection) */
.option-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 12px;
}
.option-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 14px 12px;
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: #fff;
  cursor: pointer;
  text-align: start;
  transition: var(--transition);
  font-family: inherit;
  position: relative;
}
.option-card:hover {
  border-color: var(--color-accent);
  background: var(--color-accent-soft);
}
.option-card.is-selected {
  border-color: var(--color-accent);
  background: var(--color-accent-soft);
  box-shadow: 0 0 0 3px rgba(234, 88, 12, 0.12);
}
.option-card.is-selected::after {
  content: '\2713';
  position: absolute;
  top: 6px; inset-inline-end: 8px;
  width: 18px; height: 18px;
  border-radius: 50%;
  background: var(--color-accent);
  color: #fff;
  font-size: 0.7rem;
  display: grid;
  place-items: center;
  font-weight: 700;
}
.option-card .opt-title {
  font-weight: 700;
  font-size: 0.92rem;
  color: var(--color-text);
}
.option-card .opt-meta,
.option-card .opt-sub {
  font-size: 0.78rem;
  color: var(--color-text-muted);
}

/* Force-column variants for the cloth-wristband configurator so the
   "Print Design" (4 cards) and "Weave Threads" (3 cards) steps line up
   neatly instead of falling into the auto-fill flow. */
.option-grid--3 { grid-template-columns: repeat(3, minmax(140px, 1fr)); }
.option-grid--4 { grid-template-columns: repeat(4, minmax(140px, 1fr)); }
@media (max-width: 720px) {
  .option-grid--3,
  .option-grid--4 { grid-template-columns: repeat(2, minmax(140px, 1fr)); }
}

/* ============================================================
   Hand-sanitizer multi-size capacity tiles
   ============================================================
   Used by hand-sanitizer-detail.html Step 2 (and any future
   product that mirrors lapelpinnow.com's per-capacity Qty inputs).

   Layout: 2 large tiles side by side, each containing a hero
   image of that capacity, its label, and its own Qty input. The
   buyer can fill one OR both — main.js's multi-size shim sums
   the qtys into selections.qty and writes a structured
   sizeQtys map for per-size pricing.
   ============================================================ */
.hs-cap-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(180px, 1fr));
  gap: 16px;
  margin-bottom: 8px;
}
/* An author `display` outranks the UA's [hidden] rule — and .lt-config
   .hs-cap-grid re-declares it further down at the same weight — so hiding the
   grid needs a selector at least as strong as that one. The made-to-size
   luggage tags hide it from JS and would otherwise leave an empty grid holding
   space beneath their dimension boxes. */
.hs-cap-grid[hidden],
.lt-config .hs-cap-grid[hidden] { display: none; }
@media (max-width: 480px) {
  /* Tablets / phones: stack the two tiles vertically so the
     image + qty input stay legible at narrow widths. */
  .hs-cap-grid { grid-template-columns: 1fr; }
}
.hs-cap-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 18px 14px 16px;
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-md, 10px);
  background: #fff;
  transition: var(--transition);
}
.hs-cap-tile.is-active {
  /* Active = at least 1 unit entered. Mirrors the orange highlight
     that .option-card.is-selected uses so the visual language of
     "this option is in the order" stays consistent across steps. */
  border-color: var(--color-accent);
  background: var(--color-accent-soft);
  box-shadow: 0 0 0 3px rgba(234, 88, 12, 0.12);
}
.hs-cap-img {
  width: 100%;
  /* Bumped to 460 px so on full-width desktop the bottle's "YOUR DESIGN
     HERE" label and 1 oz / 2 oz volume markings stay crisp. The tile
     itself is half the configurator column (~520 px on desktop), so
     this cap leaves a small breathing border without ever forcing the
     image to be smaller than the source can render. */
  max-width: 460px;
  aspect-ratio: 1 / 1;
  display: grid;
  place-items: center;
  border-radius: var(--radius-sm);
  background: #f8f8f8;
  overflow: hidden;
}
.hs-cap-img img {
  /* Was 75 % / 90 % — the bottle photos already include their own
     padding, so capping them at 75 % shrank the print area to ~½ of
     what the source asset provides. Letting them fill the tile (with
     a tiny safety margin) recovers all that resolution. */
  max-width: 98%;
  max-height: 98%;
  object-fit: contain;
}
.hs-cap-name {
  font-weight: 700;
  font-size: 0.98rem;
  color: var(--color-text);
  text-align: center;
  /* Absorb the tile's leftover height here rather than below the input.
     Grid rows already stretch every tile to the same height, but a size
     label that wraps to two lines ("6×109 cm (2.4×43 in)") used to push its
     own qty input 25 px lower than its neighbours' while the slack sat
     unused at the bottom of the shorter tiles. Pushing the slack above the
     input instead bottom-aligns every input in the row, whatever the label
     length or language. */
  margin-bottom: auto;
}
.hs-cap-qty-label {
  font-size: 0.78rem;
  color: var(--color-text-muted);
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.hs-cap-qty {
  width: 100%;
  max-width: 160px;
  padding: 10px 12px;
  font-size: 1.02rem;
  font-weight: 600;
  text-align: center;
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: #fff;
  font-family: inherit;
  color: var(--color-text);
  transition: var(--transition);
  /* Hide the up/down spinner — buyers typing big numbers (200,
     500, 1500) shouldn't accidentally click +1 fifty times. */
  -moz-appearance: textfield;
}
.hs-cap-qty::-webkit-outer-spin-button,
.hs-cap-qty::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.hs-cap-qty:focus {
  outline: none;
  border-color: var(--color-accent);
  box-shadow: 0 0 0 3px rgba(234, 88, 12, 0.18);
}
.hs-cap-qty.qty-invalid {
  border-color: #d12c1f;
  background: #fff5f5;
}
.hs-cap-hint {
  font-size: 0.82rem;
  color: var(--color-text-muted);
  margin: 10px 0 0;
  padding: 8px 12px;
  background: #fafafa;
  border-inline-start: 3px solid var(--color-accent);
  border-radius: 4px;
}

/* Cloth wristband colour / metallic-thread swatches.
   The swatches double as picker buttons: the coloured face IS the
   option, with a small label below for accessibility / clarity. */
.cloth-swatch-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.cloth-swatch {
  flex: 0 0 auto;
  width: 96px;
  min-height: 84px;
  border: 2px solid var(--color-border);
  border-radius: var(--radius-sm);
  cursor: pointer;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 8px;
  font-family: inherit;
  font-weight: 600;
  font-size: 0.78rem;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
  transition: var(--transition);
  position: relative;
  overflow: hidden;
}
.cloth-swatch:hover { transform: translateY(-1px); border-color: var(--color-accent); }
.cloth-swatch.is-selected {
  border-color: var(--color-accent);
  box-shadow: 0 0 0 3px rgba(234, 88, 12, 0.25);
}
.cloth-swatch.is-selected::after {
  content: '\2713';
  position: absolute;
  top: 4px; inset-inline-end: 4px;
  width: 18px; height: 18px;
  border-radius: 50%;
  background: var(--color-accent);
  color: #fff;
  font-size: 0.72rem;
  display: grid;
  place-items: center;
  font-weight: 700;
  text-shadow: none;
}
.cloth-swatch .cloth-swatch-label {
  background: rgba(15, 23, 42, 0.55);
  padding: 2px 6px;
  border-radius: 4px;
  color: #fff;
  font-size: 0.72rem;
  white-space: nowrap;
}
.cloth-swatch--none {
  background: repeating-linear-gradient(135deg, #f8fafc, #f8fafc 10px, #e2e8f0 10px, #e2e8f0 20px) !important;
  color: #475569 !important;
  text-shadow: none;
}
.cloth-swatch--none .cloth-swatch-label {
  background: rgba(255, 255, 255, 0.9);
  color: #0f172a;
}
.cloth-swatch--pms .cloth-swatch-label {
  background: rgba(15, 23, 42, 0.75);
}

/* Size buttons (compact pill style) */
.size-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.size-btn {
  padding: 10px 16px;
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: #fff;
  font-family: inherit;
  font-size: 0.88rem;
  cursor: pointer;
  transition: var(--transition);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  min-width: 96px;
}
.size-btn:hover {
  border-color: var(--color-accent);
  background: var(--color-accent-soft);
}
.size-btn.is-selected {
  border-color: var(--color-accent);
  background: var(--color-accent-soft);
  color: var(--color-accent-dark);
  font-weight: 700;
}
.size-btn .sz-name { font-weight: 700; }
.size-btn .sz-dim {
  font-size: 0.72rem;
  color: var(--color-text-muted);
  font-weight: 500;
}
.size-btn.is-selected .sz-dim { color: var(--color-accent-dark); }

/* Color swatches */
.swatch-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 14px 12px;
}
.swatch-grid > .swatch { flex: 0 0 100px; }
/* "Card-style" swatch — name on TOP, colour block below (matches the
   polyester-lanyard reference: each colour is a small white card with a
   name label, a colour rectangle and (later) a qty input). */
.swatch {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  transition: transform 180ms ease, box-shadow 180ms ease,
              border-color 180ms ease, background 180ms ease;
  padding: 10px 8px;
  background: #fff;
  border: 1.5px solid var(--color-border, #e5e7eb);
  border-radius: 10px;
  font-family: inherit;
  min-height: 92px;
}
.swatch-name {
  font-size: 0.78rem;
  color: var(--color-text);
  text-align: center;
  line-height: 1.2;
  word-break: break-word;
  max-width: 100%;
  order: 1;          /* name on top */
  font-weight: 500;
}
.swatch::before {
  content: '';
  width: 36px;
  height: 22px;
  border-radius: 4px;
  background: var(--swatch-bg, #ccc);
  border: 1px solid var(--color-border-strong, #d1d5db);
  order: 2;          /* colour block below */
  flex: 0 0 auto;
  transition: transform 180ms ease;
}
.swatch:hover {
  border-color: var(--color-accent);
  box-shadow: 0 4px 12px rgba(15,23,42,0.06);
  transform: translateY(-1px);
}
.swatch:hover .swatch-name { color: var(--color-accent-dark); }
.swatch:hover::before { transform: scale(1.08); }
.swatch.is-selected {
  border-color: var(--color-accent);
  background: var(--color-accent-soft, rgba(37,99,235,0.08));
  box-shadow: 0 0 0 2px var(--color-accent);
}
.swatch.is-selected .swatch-name { color: var(--color-accent-dark, var(--color-accent)); font-weight: 700; }
.swatch.is-selected::before {
  border-color: var(--color-accent-dark, var(--color-accent));
  border-width: 1.5px;
}
.swatch-info {
  margin-top: 12px;
  font-size: 0.82rem;
  color: var(--color-text-muted);
}
.swatch-info strong { color: var(--color-text); font-weight: 600; }

/* ============================================================
   "With Qty" swatch variant — horizontal card with a colour
   band on the left and a name + numeric Qty input on the right.
   Used in lanyard Step 3 where each colour is independently
   priced/orderable (matches the tablecoversnow reference layout).
   ============================================================ */
.swatch-grid--qty {
  gap: 14px 14px;
  justify-content: flex-start;
}
.swatch-grid--qty > .swatch--with-qty {
  flex: 0 0 168px;
}
.swatch--with-qty {
  /* Reset the column flex inherited from .swatch */
  display: grid;
  grid-template-columns: 56px 1fr;
  align-items: stretch;
  gap: 0;
  padding: 0;
  min-height: 70px;
  overflow: hidden;
  background: #fff;
  border: 1.5px solid var(--color-border, #e5e7eb);
  border-radius: 10px;
  transition: transform 180ms ease, box-shadow 180ms ease, border-color 180ms ease;
}
.swatch--with-qty::before { display: none; }
.swatch--with-qty .swatch-color {
  background: var(--swatch-bg, #ccc);
  display: block;
  width: 100%;
  height: 100%;
  border-inline-end: 1px solid var(--color-border-strong, #d1d5db);
  position: relative;
  overflow: hidden;
}
.swatch--with-qty.swatch-light .swatch-color {
  box-shadow: inset 0 0 0 1px rgba(0,0,0,0.06);
}
/* Photo-backed swatches (lanyard popular tab). The <img> fills the
   coloured tile and sits above the solid hex fallback. If the image
   fails to load, onerror hides it and the underlying CSS background
   becomes the swatch face again. */
.swatch--photo .swatch-color {
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}
.swatch--photo .swatch-color img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.swatch--with-qty .swatch-info-col {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 6px;
  padding: 7px 9px;
  min-width: 0;
}
.swatch--with-qty .swatch-name {
  text-align: start;
  font-size: 0.78rem;
  line-height: 1.15;
  margin: 0;
  order: 0;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--color-text);
}
.swatch--with-qty .swatch-qty {
  width: 100%;
  padding: 4px 8px;
  border: 1px solid var(--color-border, #d1d5db);
  border-radius: 6px;
  font-size: 0.78rem;
  font-family: inherit;
  color: var(--color-text);
  background: #fff;
  text-align: start;
  appearance: textfield;
  -moz-appearance: textfield;
  box-sizing: border-box;
}
.swatch--with-qty .swatch-qty::-webkit-outer-spin-button,
.swatch--with-qty .swatch-qty::-webkit-inner-spin-button {
  -webkit-appearance: none; margin: 0;
}
.swatch--with-qty .swatch-qty::placeholder {
  color: var(--color-text-muted, #94a3b8);
  font-weight: 400;
}
.swatch--with-qty .swatch-qty:focus {
  outline: none;
  border-color: var(--color-accent);
  box-shadow: 0 0 0 2px var(--color-accent-soft, rgba(37,99,235,0.18));
}
.swatch--with-qty .swatch-qty.qty-invalid,
.swatch--with-qty .swatch-qty.qty-invalid:focus {
  border-color: #ef4444;
  box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.18);
}
.color-qty-warn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin: 0 0 12px;
  padding: 10px 14px;
  border: 1px solid #fecaca;
  background: #fef2f2;
  color: #dc2626;
  border-radius: 8px;
  font-size: 0.85rem;
  font-weight: 500;
}
.color-qty-warn[hidden] { display: none; }
.color-qty-warn svg { flex: 0 0 auto; }
.swatch--with-qty:hover {
  border-color: var(--color-accent-soft, #cbd5e1);
  box-shadow: 0 4px 12px rgba(15,23,42,0.06);
}
.swatch--with-qty.has-qty,
.swatch--with-qty.is-selected {
  border-color: var(--color-accent);
  box-shadow: 0 0 0 2px var(--color-accent);
  background: #fff; /* override .swatch.is-selected accent-soft fill */
}
.swatch--with-qty.has-qty .swatch-name,
.swatch--with-qty.is-selected .swatch-name {
  color: var(--color-accent-dark, var(--color-accent));
  font-weight: 700;
}
/* When the PMS pane shows ~1,800 cards, tighten the card width so more
   fit per row (still keep the color band + qty layout). */
.pms-grid-inline.swatch-grid--qty > .swatch--with-qty {
  flex: 0 0 150px;
  min-height: 62px;
}
.pms-grid-inline.swatch-grid--qty .swatch-name { font-size: 0.72rem; }
.pms-grid-inline.swatch-grid--qty .swatch-qty { font-size: 0.72rem; padding: 3px 6px; }

/* ============================================================
   ATTACH-GRID — image-card grid for Step 5 (Attachment), Step 6
   (Stitch), Step 7/8/9 (Optional add-ons), Step 11/12 (Holders /
   Reels). Card layout: name on TOP, image on BOTTOM (matches
   polyester-lanyard reference).
   ============================================================ */
.attach-tabs {
  display: flex;
  gap: 12px;
  background: var(--color-surface-alt, #f1f5f9);
  border: 1px solid var(--color-border, #e2e8f0);
  border-radius: 12px;
  padding: 14px;
  margin-bottom: 14px;
  flex-wrap: wrap;
  justify-content: center;
}
.attach-tab {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 14px 22px;
  background: #fff;
  border: 2px solid transparent;
  border-radius: 12px;
  cursor: pointer;
  min-width: 200px;
  transition: var(--transition);
  font-family: inherit;
}
.attach-tab .at-img {
  width: 150px;
  height: 150px;
  background: var(--color-surface-alt, #f1f5f9);
  border-radius: 8px;
  display: grid;
  place-items: center;
  overflow: hidden;
}
.attach-tab .at-img img, .attach-tab .at-img svg { max-width: 100%; max-height: 100%; object-fit: contain; }
.attach-tab .at-name {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--color-text-muted);
}
.attach-tab:hover { border-color: var(--color-accent-soft, #cbd5e1); }
.attach-tab.is-active {
  border-color: var(--color-accent);
  background: var(--color-accent-soft, rgba(37,99,235,0.06));
}
.attach-tab.is-active .at-name { color: var(--color-accent-dark); font-weight: 600; }

.attach-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 16px;
  margin-top: 10px;
}
.attach-grid > .attach-card { flex: 0 0 230px; }

/* Open Ended / Dual Clips — two-column side-by-side layout
   (Left Attachment | Right Attachment). Use !important to win against
   the global ".step-block-body .attach-grid" flex override later in
   the file. Matches polyester-lanyard reference. */
.step-block-body .attach-side-grid {
  display: grid !important;
  grid-template-columns: 1fr 1fr !important;
  gap: 16px;
  margin: 12px auto 0;
  align-items: start;
  max-width: 100%;
}
.step-block-body .attach-side {
  display: flex;
  flex-direction: column;
  min-width: 0;
  border: 1px solid var(--color-border, #e5e7eb);
  border-radius: 12px;
  background: #fff;
  overflow: hidden;
}
.step-block-body .attach-side .holder-section-title {
  margin: 0;
  padding: 12px 16px;
  text-align: center;
  font-size: 1rem;
  font-weight: 700;
  color: var(--color-text);
  background: var(--color-surface-alt, #f9fafb);
  border-bottom: 1px solid var(--color-border, #e5e7eb);
}
/* No max-height + no overflow-y on the inner grid: a fixed-height nested
   scroll container creates a "scroll trap" — the user's wheel events get
   eaten by the hook grid and the page below (Holder, Reel, etc.) becomes
   unreachable. Hooks max out at ~6 colours, so the grid is short anyway
   and flows naturally with the page. */
.step-block-body .attach-side .attach-grid {
  margin: 0 !important;
  padding: 12px;
  display: grid !important;
  grid-template-columns: repeat(2, 1fr) !important;
  gap: 10px !important;
  justify-items: stretch !important;
  max-width: none !important;
}
.step-block-body .attach-side .attach-grid > .attach-card {
  flex: none !important;
  width: 100% !important;
  max-width: none !important;
  min-width: 0;
}
@media (max-width: 720px) {
  .step-block-body .attach-side-grid { grid-template-columns: 1fr !important; }
  .step-block-body .attach-side .attach-grid {
    grid-template-columns: repeat(2, 1fr) !important;
  }
}
.attach-card {
  display: flex;
  flex-direction: column;
  padding: 0;
  background: #fff;
  border: 1.5px solid var(--color-border, #e5e7eb);
  border-radius: 10px;
  cursor: pointer;
  overflow: hidden;
  transition: var(--transition);
  font-family: inherit;
}
.attach-card-name {
  font-size: 0.95rem;
  font-weight: 600;
  padding: 12px 14px;
  color: var(--color-text);
  background: var(--color-surface-alt, #f9fafb);
  border-bottom: 1px solid var(--color-border, #e5e7eb);
  text-align: center;
}

/* Small "i" info pill shown after a variant label. Pure CSS tooltip on
   hover / focus / tap (works without JS by leveraging data-tip + ::after).
   The serif + italic combo turns the plain "i" character into the
   familiar circled-italic-i info glyph without needing an icon font. */
.info-bang {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  margin-inline-start: 6px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--color-accent, #2563eb);
  color: #fff;
  font-size: 0.78rem;
  font-weight: 700;
  font-style: italic;
  font-family: "Times New Roman", Georgia, "DejaVu Serif", serif;
  cursor: help;
  line-height: 1;
  vertical-align: middle;
  position: relative;
  transition: transform 0.12s ease, background 0.12s ease;
}
.info-bang:hover,
.info-bang:focus-visible {
  background: var(--color-accent-dark, #1d4ed8);
  transform: scale(1.1);
  outline: none;
}
.info-bang::after {
  content: attr(data-tip);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  background: #0f172a;
  color: #fff;
  /* The button face uses italic Times New Roman to look like the "i"
     icon. Reset both here so the tooltip body reads in the normal sans
     body font, not in italic serif. */
  font-family: var(--font-sans);
  font-style: normal;
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  padding: 8px 12px;
  border-radius: 8px;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s ease;
  box-shadow: 0 6px 16px rgba(15, 23, 42, 0.18);
  z-index: 20;
}
.info-bang::before {
  /* Little arrow under the bubble */
  content: '';
  position: absolute;
  bottom: calc(100% + 2px);
  left: 50%;
  transform: translateX(-50%);
  border: 5px solid transparent;
  border-top-color: #0f172a;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s ease;
  z-index: 20;
}
.info-bang:hover::after,
.info-bang:hover::before,
.info-bang:focus-visible::after,
.info-bang:focus-visible::before {
  opacity: 1;
}
/* Long tooltips need to wrap, not bleed off the right edge.
   pre-line (not normal) so explicit "\n" characters in data-tip render
   as a real line break — used by LAN_HOLDER_TOOLTIPS to put "Holder
   size" and "Insert size" on separate lines. Consecutive spaces still
   collapse, so existing tooltips that don't carry "\n" look unchanged. */
.attach-card .info-bang::after { max-width: 280px; white-space: pre-line; text-align: center; }
/* Cloth closure tooltips: author them as fixed two-line blocks. Size the
   bubble to its longest line (max-content) so it is NOT crushed to one
   word per line by the tiny 18px info-bang containing block. The "\n" in
   each tip string (js/wristband-options.js) supplies the line break.
   The two-line bubble is wider than the ~230px card, so the card must be
   overflow:visible (it defaults to hidden) or the bubble gets clipped at
   the card edges; the grid/step containers don't clip. z-index keeps the
   bubble above neighbouring cards. */
[data-render="cloth-attach"] .attach-card { overflow: visible; }
[data-render="cloth-attach"] .info-bang::after {
  width: max-content;
  max-width: 92vw;
  white-space: pre-line;
  text-align: center;
  z-index: 100;
}
[data-render="cloth-attach"] .info-bang::before { z-index: 100; }
/* Hand-sanitizer print-method tooltips: same fix as cloth-attach — the
   info-bang is only 18px wide, so without width:max-content the bubble
   collapses to one word per line (see lapelpinnow reference for the
   intended ~2-line layout). max-width caps the bubble so longer tips
   (e.g. Silk Screen description) wrap at a readable width instead of
   stretching off-screen. z-index lets the bubble float over the
   neighbouring print-method card. */
[data-step-kind="print-method"] .attach-card { overflow: visible; }
[data-step-kind="print-method"] .info-bang::after {
  width: max-content;
  max-width: 240px;
  white-space: pre-line;
  text-align: center;
  line-height: 1.45;
  z-index: 100;
}
[data-step-kind="print-method"] .info-bang::before { z-index: 100; }
.attach-card-img {
  display: grid;
  place-items: center;
  padding: 14px 10px;
  background: #fff;
  min-height: 240px;
}
.attach-card-img svg { width: 96px; height: 96px; }
/* Source photos are 230x400 portrait — cap by height, let width auto-shrink */
.attach-card-img img.ac-photo {
  max-width: 100%;
  max-height: 230px;
  width: auto;
  height: auto;
  object-fit: contain;
  display: block;
}
.attach-card-img .ac-fallback { display: block; }

/* Photo variant: real PVC card photos (Step 2 size/orient, Step 3 finish, Step 4 hole).
   Image fills the entire card-img area (no swatch overlay). */
.attach-card-img--photo {
  padding: 0;
  background: #fff;
  min-height: 240px;
  overflow: hidden;
}
.attach-card-img--photo img {
  width: 100%;
  height: 100%;
  min-height: 240px;
  object-fit: cover;
  display: block;
}

/* Finder-tag print / case cards: the factory shots are white products on a
   white sweep. Cover-cropping them into a white well hides the product.
   Contain + a light well keeps both units visible. */
.fnd-config .attach-card-img.attach-card-img--photo {
  min-height: 0;
  aspect-ratio: 1 / 1;
  background: #eef1f4;
  padding: 10px;
}
.fnd-config .attach-card-img.attach-card-img--photo img.ac-photo {
  width: 100%;
  height: 100%;
  max-height: none;
  min-height: 0;
  object-fit: contain;
}

/* ===== Step 2 — Orientation tabs (Vertical / Horizontal) =====
   Pill-style toggle above the size grid. Mirrors the reference toast.com
   card-builder. */
.idc-orient-tabs {
  display: flex;
  justify-content: center;
  gap: 0;
  margin: 0 auto 24px;
  padding: 4px;
  background: #f1f5f9;
  border-radius: 999px;
  width: max-content;
}
.idc-orient-tab {
  appearance: none;
  -webkit-appearance: none;
  border: 0;
  background: transparent;
  color: #475569;
  font: 600 14px/1 Inter, system-ui, sans-serif;
  padding: 10px 22px;
  border-radius: 999px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  transition: background 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
}
.idc-orient-tab:hover { color: #0f172a; }
.idc-orient-tab.is-active {
  background: #ffffff;
  color: #0f172a;
  box-shadow: 0 1px 2px rgba(0,0,0,0.08), 0 0 0 1px rgba(15,23,42,0.06);
}
.idc-orient-tab .idc-orient-tab-ico { display: inline-flex; }

/* ===== Step 2 — Size cards with Qty input =====
   Card is laid out vertically: image · size label · qty input. */
.idc-orient-grid > .attach-card {
  flex: 0 0 220px;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  padding: 0;
  overflow: hidden;
}
.idc-orient-grid .attach-card-img--photo {
  min-height: 200px;
  height: 200px;
  background: #fff;
}
.idc-orient-grid .attach-card-img--photo img {
  min-height: 200px;
  height: 200px;
  object-fit: contain;
  padding: 10px;
}
.idc-orient-grid .attach-card-name {
  background: #f1f5f9;
  padding: 10px 12px;
  text-align: center;
  font: 600 14px/1.2 Inter, system-ui, sans-serif;
  color: #1f2937;
}
/* Two-line size label: inch dimensions on top (bold, same size as the
   default name) + mm dimensions underneath (smaller, muted). Layout uses
   flex column with a thin gap so vertical rhythm stays tight inside the
   card. The whole block sits in the same .attach-card-name slot the
   single-line version used, so we keep the inherited background, padding
   and "selected-state" highlight without writing them again. */
.idc-orient-grid .attach-card-name.idc-size-name {
  display: flex;
  flex-direction: column;
  gap: 2px;
  line-height: 1.25;
}
.idc-orient-grid .idc-size-in {
  font-weight: 600;
  color: #1f2937;
}
.idc-orient-grid .idc-size-mm {
  font-weight: 500;
  font-size: 12px;
  color: #64748b;
}
.idc-orient-grid .attach-card.is-selected .idc-size-in { color: inherit; }
.idc-orient-grid .attach-card.is-selected .idc-size-mm { color: inherit; opacity: 0.85; }
.idc-orient-grid .ac-qty-row {
  padding: 10px;
  background: #ffffff;
}
.idc-orient-grid .ac-qty {
  width: 100%;
  box-sizing: border-box;
  border: 1px solid #d1d5db;
  border-radius: 6px;
  padding: 8px 10px;
  font: 500 14px/1.2 Inter, system-ui, sans-serif;
  color: #111827;
  text-align: center;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.idc-orient-grid .ac-qty:focus {
  outline: none;
  border-color: var(--color-accent, #2563eb);
  box-shadow: 0 0 0 3px rgba(37,99,235,0.18);
}
.idc-orient-grid .ac-qty::-webkit-outer-spin-button,
.idc-orient-grid .ac-qty::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.idc-orient-grid .ac-qty[type=number] { -moz-appearance: textfield; }
.idc-orient-grid .attach-card.is-selected .attach-card-name {
  background: var(--color-accent-soft, rgba(37,99,235,0.12));
  color: var(--color-accent-dark, #1d4ed8);
}
.attach-card:hover {
  border-color: var(--color-accent);
  box-shadow: 0 4px 12px rgba(0,0,0,0.06);
}
.attach-card.is-selected {
  border-color: var(--color-accent);
  box-shadow: 0 0 0 2px var(--color-accent) inset;
}
.attach-card.is-selected .attach-card-name {
  background: var(--color-accent-soft, rgba(37,99,235,0.08));
  color: var(--color-accent-dark);
  font-weight: 600;
}

/* Upgrade cards gated behind a minimum order quantity (scarves: the custom
   hangtag needs 30 pcs). The card goes inert and the threshold is spelled out
   on it, so the buyer learns what to change instead of only discovering that
   it won't click. The note keeps its full contrast — greying the card must
   not grey the one line explaining why. */
.attach-card.is-locked { cursor: not-allowed; }
.attach-card.is-locked .attach-card-img img { filter: grayscale(1); opacity: 0.55; }
.attach-card.is-locked .attach-card-name { color: var(--color-text-muted, #6b7280); }
/* Beats the three .attach-card:hover rules further down the sheet. */
.attach-card.is-locked:hover {
  transform: none;
  border-color: var(--color-border, #e5e7eb);
  box-shadow: none;
}
.attach-card-note {
  padding: 8px 12px 10px;
  background: var(--color-surface-alt, #f9fafb);
  color: #b45309;
  font-size: 0.8rem;
  font-weight: 600;
  line-height: 1.3;
  text-align: center;
}
.attach-card-note[hidden] { display: none; }

/* Holder / Reel rows — outer style row, inner colour-variant row.
   Variant sections are kept fully collapsed until the corresponding main
   card is clicked. */
.holder-section { margin-top: 18px; }
.holder-section.is-collapsed,
.holder-section[hidden] { display: none !important; }
.holder-section .holder-section-title { display: none !important; }

/* ------ Step 11 main holder grid — flex-wrap centered (7 items: 4 + 3 centered) ------ */
.step-block-body .attach-grid[data-single="holder"],
.step-block-body .attach-grid[data-single="reel"] {
  display: flex !important;
  flex-wrap: wrap !important;
  justify-content: center !important;
  align-items: stretch !important;
  gap: 14px !important;
  max-width: 960px;
  margin: 0 auto !important;
  padding: 18px !important;
  background: #edf4fa;
  border-radius: 12px;
}
.step-block-body .attach-grid[data-single="holder"] > .attach-card,
.step-block-body .attach-grid[data-single="reel"]   > .attach-card {
  flex: 0 0 200px !important;
  width: 200px !important;
  max-width: 200px !important;
  background: #fff;
  border: 2px solid transparent;
  border-radius: 10px;
  box-shadow: 0 1px 3px rgba(15,23,42,0.04);
  flex-direction: column !important;
  align-items: stretch;
  padding: 14px 10px 10px;
  gap: 8px;
}
.step-block-body .attach-grid[data-single="holder"] > .attach-card.is-selected,
.step-block-body .attach-grid[data-single="reel"]   > .attach-card.is-selected {
  border-color: var(--color-accent, #2563eb);
  box-shadow: 0 3px 10px rgba(37,99,235,0.12);
}
.step-block-body .attach-grid[data-single="holder"] .attach-card-name,
.step-block-body .attach-grid[data-single="reel"]   .attach-card-name {
  background: transparent !important;
  border: 0 !important;
  font-size: 0.88rem;
  font-weight: 600;
  padding: 4px 0 0;
  color: var(--color-accent, #2563eb);
  text-align: center;
  line-height: 1.25;
}
.step-block-body .attach-grid[data-single="holder"] .attach-card-img,
.step-block-body .attach-grid[data-single="reel"]   .attach-card-img {
  padding: 0;
  min-height: 130px;
  display: grid;
  place-items: center;
}
.step-block-body .attach-grid[data-single="holder"] .attach-card-img img.ac-photo,
.step-block-body .attach-grid[data-single="reel"]   .attach-card-img img.ac-photo {
  max-height: 130px;
  max-width: 95%;
}
.step-block-body .attach-grid[data-single="holder"] .attach-card-img svg,
.step-block-body .attach-grid[data-single="reel"]   .attach-card-img svg {
  width: 80px; height: 80px;
}
@media (max-width: 760px) {
  .step-block-body .attach-grid[data-single="holder"],
  .step-block-body .attach-grid[data-single="reel"] {
    grid-template-columns: repeat(2, 1fr) !important;
  }
  .step-block-body .attach-grid[data-single="holder"] .attach-card-img,
  .step-block-body .attach-grid[data-single="reel"]   .attach-card-img {
    min-height: 100px;
  }
  .step-block-body .attach-grid[data-single="holder"] .attach-card-img img.ac-photo,
  .step-block-body .attach-grid[data-single="reel"]   .attach-card-img img.ac-photo {
    max-height: 100px;
  }
}

/* ------ Variant grid inside holder-section — flex-wrap centered + Qty input ------ */
.holder-section > .attach-grid {
  display: flex !important;
  flex-wrap: wrap !important;
  justify-content: center !important;
  align-items: stretch !important;
  gap: 16px !important;
  max-width: 980px;
  margin: 14px auto 0;
  padding: 0 4px !important;
}
.holder-section > .attach-grid > .attach-card {
  flex: 0 0 190px !important;
  width: 190px !important;
  max-width: 190px !important;
  flex-direction: column !important;
  border-radius: 10px;
  /* Override the .attach-card overflow:hidden default — the holder
     tooltip is wider than the 190px card, so we have to let it spill
     out. Pair with the info-bang::after z-index below so the bubble
     stacks on top of neighbouring cards instead of being clipped. */
  overflow: visible !important;
  background: #fff;
  border: 1px solid #e5e7eb;
  box-shadow: 0 1px 2px rgba(15,23,42,0.04);
  padding: 0 0 10px;
  overflow: hidden;
}
.holder-section > .attach-grid > .attach-card.is-selected {
  border-color: var(--color-accent, #2563eb);
  box-shadow: 0 0 0 1px var(--color-accent, #2563eb), 0 2px 8px rgba(37,99,235,0.12);
}
.holder-section .attach-card-img {
  order: 1;
  padding: 16px 12px 8px;
  min-height: 180px;
  background: transparent;
}
.holder-section .attach-card-img img.ac-photo {
  max-height: 170px;
  max-width: 92%;
}
.holder-section .attach-card-img svg { width: 78px; height: 78px; }
.holder-section .attach-card-name {
  order: 2;
  background: transparent !important;
  border: 0 !important;
  font-size: 0.88rem;
  font-weight: 500;
  padding: 6px 10px 8px;
  color: var(--color-text);
  text-align: center;
  line-height: 1.25;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
}
.holder-section .attach-card.is-selected .attach-card-name {
  color: var(--color-accent-dark, #1d4ed8);
  font-weight: 600;
}
/* The .holder-section used to shrink the info-bang to 14x14 and recolour
   it teal to fit a denser 4-up holder-picker grid. Removed so every
   info-bang on the site renders identically (18x18 italic blue "i",
   per the site-wide rule above). The TOOLTIP body override below stays,
   because holder tooltips are 4-line blocks (label / value x 2) and
   need a wider bubble than the default.

   Only structural tweak we keep: `.attach-card-name` inside
   .holder-section is a flex container with `gap: 5px`, so the badge's
   default `margin-left: 6px` would compound to ~11px between title
   and badge. Reset just the margin (NOT the size/colour) so the
   appearance stays consistent across the site while the flex gap
   takes care of spacing. */
.holder-section .attach-card-name .info-bang { margin-inline-start: 0; }
.holder-section .attach-card .info-bang::after {
  /* The holder tooltips are 4-line blocks (label / value × 2). Give
     them room: bigger padding so the bubble doesn't crush the text, a
     wider max-width so the (98×82mm) values stay on one line, and a
     comfier line-height + min-width so all four lines breathe.
     z-index is bumped above .info-bang's default so the bubble can
     overlap neighbouring cards (now that the card itself is
     overflow:visible) without being painted under them. */
  font-size: 0.78rem;
  padding: 10px 14px;
  min-width: 200px;
  max-width: 280px;
  line-height: 1.5;
  z-index: 100;
}
.holder-section .attach-card .info-bang::before { z-index: 100; }
/* Per-variant qty input, rendered by JS as <span class="variant-qty-row"> */
.variant-qty-row {
  display: flex;
  justify-content: center;
  padding: 4px 10px 2px;
  order: 3;
}
.variant-qty-input {
  width: 100%;
  max-width: 110px;
  height: 28px;
  padding: 2px 6px;
  border: 1px solid #d1d5db;
  border-radius: 4px;
  background: #fff;
  text-align: center;
  font-size: 0.78rem;
  color: var(--color-text);
  outline: none;
  transition: border-color 0.12s ease;
}
.variant-qty-input::placeholder { color: #9ca3af; font-weight: 400; }
.variant-qty-input:focus { border-color: var(--color-accent, #2563eb); }
/* Hide native number-spinner arrows for a cleaner look */
.variant-qty-input::-webkit-outer-spin-button,
.variant-qty-input::-webkit-inner-spin-button {
  -webkit-appearance: none; margin: 0;
}
.variant-qty-input[type=number] { -moz-appearance: textfield; }

@media (max-width: 760px) {
  .holder-section > .attach-grid {
    grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)) !important;
    gap: 8px !important;
  }
  .holder-section .attach-card-img img.ac-photo { max-height: 100px; }
  .holder-section .attach-card-img { min-height: 108px; padding: 10px 6px 4px; }
  .holder-section .attach-card-name { font-size: 0.72rem; }
  .variant-qty-input { height: 24px; font-size: 0.72rem; }
}
.holder-section-title {
  font-size: 0.95rem;
  font-weight: 600;
  margin: 14px 0 8px;
  padding-bottom: 6px;
  border-bottom: 1px dashed var(--color-border, #e5e7eb);
}

/* PMS picker — open-modal button */
.pms-open-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  background: var(--color-accent-soft, #eef2ff);
  color: var(--color-accent-dark);
  border: 1.5px solid var(--color-accent);
  border-radius: 8px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  margin: 8px 0 14px;
  transition: var(--transition);
}
.pms-open-btn:hover { background: var(--color-accent); color: #fff; }

/* Modal — full-screen search for all 1800+ PMS codes */
.pms-modal-backdrop {
  position: fixed; inset: 0;
  background: rgba(15, 23, 42, 0.55);
  display: none;
  align-items: center;
  justify-content: center;
  padding: 20px;
  z-index: 9999;
}
.pms-modal-backdrop.is-open { display: flex; }
/* Legacy backdrop dialog. Renamed from `.pms-modal` to `.pms-dialog` so it no
   longer collides with the current full-screen overlay `.pms-modal` (used by
   openPmsModal). The collision leaked max-width/width/flex-direction onto the
   overlay and pinned it to the top-left instead of centring it. */
.pms-dialog {
  background: #fff;
  border-radius: 14px;
  max-width: 900px;
  width: 100%;
  max-height: 85vh;
  display: flex;
  flex-direction: column;
  box-shadow: 0 20px 60px rgba(0,0,0,0.3);
}
.pms-modal-head {
  padding: 16px 20px;
  border-bottom: 1px solid var(--color-border, #e5e7eb);
  display: flex;
  align-items: center;
  gap: 12px;
}
.pms-modal-title { flex: 1; font-size: 1.1rem; font-weight: 700; }
.pms-modal-close {
  background: none;
  border: none;
  cursor: pointer;
  width: 36px; height: 36px;
  border-radius: 50%;
  display: grid; place-items: center;
  font-size: 1.5rem;
  color: var(--color-text-muted);
}
.pms-modal-close:hover { background: var(--color-surface-alt); color: var(--color-text); }
.pms-modal-search {
  padding: 12px 20px;
  border-bottom: 1px solid var(--color-border, #e5e7eb);
}
.pms-modal-search input {
  width: 100%;
  padding: 10px 14px;
  border: 1.5px solid var(--color-border-strong, #d1d5db);
  border-radius: 8px;
  font-size: 0.95rem;
}
.pms-modal-body {
  flex: 1;
  overflow-y: auto;
  padding: 16px 20px;
}
.pms-modal-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
  gap: 12px;
}

/* Text / message input */
.config-text-input {
  width: 100%;
  padding: 12px 14px;
  border: 1.5px solid var(--color-border-strong);
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 0.95rem;
  color: var(--color-text);
  background: #fff;
  transition: var(--transition);
}
.config-text-input:focus {
  outline: none;
  border-color: var(--color-accent);
  box-shadow: 0 0 0 3px rgba(234, 88, 12, 0.12);
}
.config-text-help {
  margin-top: 6px;
  font-size: 0.78rem;
  color: var(--color-text-muted);
}

/* Font sample picker */
.font-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 10px;
}
.font-card {
  padding: 18px 14px;
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: #fff;
  cursor: pointer;
  text-align: center;
  transition: var(--transition);
  font-family: inherit;
}
.font-card:hover {
  border-color: var(--color-accent);
  background: var(--color-accent-soft);
}
.font-card.is-selected {
  border-color: var(--color-accent);
  background: var(--color-accent-soft);
}
.font-card .font-sample {
  font-size: 1.4rem;
  font-weight: 600;
  color: var(--color-text);
  margin-bottom: 4px;
}
.font-card .font-name {
  font-size: 0.74rem;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* Multi-select add-ons (checkbox cards) */
.addon-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 10px;
}
.addon-card {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 12px 14px;
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: #fff;
  cursor: pointer;
  transition: var(--transition);
  font-family: inherit;
  text-align: start;
}
.addon-card:hover {
  border-color: var(--color-accent);
  background: var(--color-accent-soft);
}
.addon-card.is-selected {
  border-color: var(--color-accent);
  background: var(--color-accent-soft);
}
.addon-card .addon-check {
  width: 18px; height: 18px;
  flex-shrink: 0;
  margin-top: 2px;
  border-radius: 4px;
  border: 1.5px solid var(--color-border-strong);
  background: #fff;
  display: grid;
  place-items: center;
  color: #fff;
  font-size: 0.7rem;
  font-weight: 700;
}
.addon-card.is-selected .addon-check {
  background: var(--color-accent);
  border-color: var(--color-accent);
}
.addon-card.is-selected .addon-check::after { content: '\2713'; }
.addon-card .addon-info {
  flex: 1;
  min-width: 0;
}
.addon-card .addon-title {
  font-weight: 700;
  font-size: 0.88rem;
  color: var(--color-text);
  display: flex;
  justify-content: space-between;
  gap: 8px;
}
.addon-card .addon-meta {
  font-size: 0.76rem;
  color: var(--color-text-muted);
  margin-top: 2px;
}

/* Yes/No toggle group */
.toggle-group {
  display: inline-flex;
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.toggle-btn {
  padding: 10px 22px;
  background: #fff;
  border: none;
  font-family: inherit;
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--color-text-muted);
  cursor: pointer;
  transition: var(--transition);
}
.toggle-btn + .toggle-btn { border-inline-start: 1.5px solid var(--color-border); }
.toggle-btn.is-selected {
  background: var(--color-primary-dark);
  color: #fff;
}

/* Quantity tier picker */
.qty-tier-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
  gap: 8px;
  margin-bottom: 16px;
}
.qty-tier {
  padding: 12px 8px;
  text-align: center;
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: #fff;
  cursor: pointer;
  transition: var(--transition);
  font-family: inherit;
}
.qty-tier:hover { border-color: var(--color-accent); background: var(--color-accent-soft); }
.qty-tier.is-selected {
  border-color: var(--color-accent);
  background: var(--color-accent-soft);
}
.qty-tier .qty-num { font-weight: 800; font-size: 1.05rem; color: var(--color-text); }
.qty-tier .qty-price {
  font-size: 0.76rem;
  color: var(--color-text-muted);
  margin-top: 2px;
}
.qty-tier.is-selected .qty-price { color: var(--color-accent-dark); font-weight: 600; }

.qty-custom-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.qty-custom-row label {
  font-size: 0.88rem;
  color: var(--color-text-muted);
  font-weight: 600;
}
.qty-custom-row input {
  width: 110px;
  padding: 10px 12px;
  border: 1.5px solid var(--color-border-strong);
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 0.95rem;
  text-align: center;
}
.qty-custom-row input:focus {
  outline: none;
  border-color: var(--color-accent);
}

/* File upload field */
.file-upload {
  border: 2px dashed var(--color-border-strong);
  border-radius: var(--radius-md);
  padding: 24px 18px;
  text-align: center;
  cursor: pointer;
  transition: var(--transition);
  background: var(--color-bg-alt);
}
.file-upload:hover {
  border-color: var(--color-accent);
  background: var(--color-accent-soft);
}
.file-upload .file-icon {
  width: 36px; height: 36px;
  margin: 0 auto 8px;
  color: var(--color-text-muted);
}
.file-upload .file-text {
  font-weight: 600;
  font-size: 0.92rem;
  color: var(--color-text);
}
.file-upload .file-help {
  font-size: 0.78rem;
  color: var(--color-text-muted);
  margin-top: 4px;
}
.file-upload input[type="file"] { display: none; }
.file-upload.has-file {
  border-color: var(--color-success);
  background: rgba(5, 150, 105, 0.05);
}
.file-upload.has-file .file-icon { color: var(--color-success); }

/* Sidebar summary */
.config-summary {
  background: var(--color-bg-alt);
  border-inline-start: 1px solid var(--color-border);
  padding: 24px 22px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
}
@media (max-width: 800px) {
  .config-summary {
    border-inline-start: none;
    border-top: 1px solid var(--color-border);
  }
}
.config-summary h4 {
  margin: 0 0 14px;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-text-muted);
  font-weight: 700;
}
.summary-list {
  list-style: none;
  padding: 0;
  margin: 0 0 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.summary-list li {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  font-size: 0.86rem;
  color: var(--color-text);
  padding-bottom: 8px;
  border-bottom: 1px dashed var(--color-border);
}
/* The rule above sets `display`, which beats the UA's `[hidden] {display:none}`
   outright (author styles win over UA styles whatever the specificity), so the
   `hidden` attribute stopped working on summary rows. The pin, luggage-tag and
   scarf configurators all rely on it to drop steps their style doesn't offer,
   and without this a soft-enamel pin listed "成型方式: 无" — a PVC-only step —
   as if the buyer had chosen it. `!important` because `.config-allsteps
   .summary-list li` sets `display: grid` further down (~L6400). */
.summary-list li[hidden] { display: none !important; }
.summary-list li .sum-key {
  color: var(--color-text-muted);
  font-weight: 500;
}
.summary-list li .sum-val {
  font-weight: 600;
  text-align: end;
  max-width: 60%;
  word-break: break-word;
}
.summary-list li.empty .sum-val {
  color: var(--color-text-light);
  font-style: italic;
  font-weight: 500;
}
/* Multi-line summary value used by holder/reel rows: each selected parent
   holder/reel gets a small group with the parent name on top and one
   sub-line per (size × qty) underneath. */
.summary-list li .sum-val .sum-acc-group {
  display: block;
  margin-bottom: 6px;
}
.summary-list li .sum-val .sum-acc-group:last-child {
  margin-bottom: 0;
}
.summary-list li .sum-val .sum-acc-name {
  font-weight: 700;
  color: var(--color-text);
}
.summary-list li .sum-val .sum-sub {
  font-weight: 500;
  font-size: 0.85em;
  color: var(--color-text-muted);
  margin-top: 1px;
}
/* `sum-row-wide` — used when the value is too rich for the default
   two-column key/value layout (e.g. the wristband colour table with
   band-colour / size / qty / text-colour columns). Use `display:block`
   instead of flex-column so the key span and the .sum-val payload
   always stack vertically with a clear gap, regardless of any other
   `.summary-list li` rule downstream in the cascade.

   IMPORTANT: A later block at ~L4665 uses `.config-allsteps .summary-list
   li { display: grid; grid-template-columns: 110px 1fr; }`, which has
   the SAME specificity as the bare `.summary-list li.sum-row-wide`
   selector but wins by document order — that's why the label "颜色"
   was being pinned to the 110 px left column while the table got
   squeezed into the 1fr right column (same row). The fix is to MATCH
   that scope (`.config-allsteps`) so our rule sits at higher
   specificity (0,0,3,1) and `display: block` wins, putting the table
   on the line BELOW the label as the user requested. The plain
   `.summary-list li.sum-row-wide` rule remains as a fallback for any
   future mount-point that isn't inside `.config-allsteps`. */
.summary-list li.sum-row-wide,
.config-allsteps .summary-list li.sum-row-wide {
  display: block;
  grid-template-columns: none;
  padding-bottom: 10px;
}
.summary-list li.sum-row-wide .sum-key,
.config-allsteps .summary-list li.sum-row-wide .sum-key {
  display: block;
  margin-bottom: 8px;
  font-weight: 600;
  color: var(--color-text);
}
.summary-list li.sum-row-wide .sum-val,
.config-allsteps .summary-list li.sum-row-wide .sum-val {
  display: block;
  text-align: start;
  max-width: 100%;
  width: 100%;
  font-weight: 500;
}
.sum-color-table {
  width: 100%;
  border-collapse: collapse;
  table-layout: auto;
  font-size: 0.78rem;
  line-height: 1.25;
}
.sum-color-table th,
.sum-color-table td {
  padding: 4px 5px;
  border-bottom: 1px solid var(--color-border);
  text-align: start;
  vertical-align: middle;
  color: var(--color-text);
  font-weight: 500;
  /* Don't let the panel's narrow width chop "Adult" into "Adu/lt"
     vertically — keep tokens whole. Long colour names like
     "Black | Gold" can still wrap (they're space-separated), but
     atomic size labels stay on one line. */
  word-break: keep-all;
  overflow-wrap: anywhere;
}
.sum-color-table th {
  font-weight: 600;
  color: var(--color-text-muted);
  background: rgba(0, 0, 0, 0.025);
  white-space: nowrap;
  font-size: 0.75rem;
}
/* Size column (2nd) — short tokens like "成人 / 青少年" should never
   wrap, so keep them on one line. */
.sum-color-table th:nth-child(2),
.sum-color-table td:nth-child(2) {
  white-space: nowrap;
}
/* Qty column (3rd) — right-aligned monospace digits keep totals
   easy to scan vertically. */
.sum-color-table th:nth-child(3),
.sum-color-table td:nth-child(3) {
  text-align: end;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  font-weight: 600;
  color: var(--color-text);
}
.sum-color-table tbody tr:last-child td {
  border-bottom: none;
}

/* ------------------------------------------------------------------
   Cart line — wristband colour / size / qty / text-colour table.
   Used by both the slide-out cart drawer (cart.js → renderDrawer)
   and the full cart page (products/cart.html → render). Mirrors the
   summary panel's .sum-color-table so the buyer sees the same shape
   across configurator → drawer → cart page → checkout review.
   ------------------------------------------------------------------ */
.cart-line-desc li.cart-line-desc-wide {
  /* Promote a single bullet to full width so the inline table can
     breathe — without this it stays inside the standard text bullet
     and the table compresses to the bullet's content width. */
  list-style: none;
  margin-inline-start: -1.1rem;   /* compensate for the parent UL's default indent */
  padding: 4px 0 6px;
}
.cart-color-label {
  font-weight: 600;
  font-size: 0.78rem;
  margin: 0 0 4px;
  color: var(--color-text);
}
.cart-color-table {
  width: 100%;
  border-collapse: collapse;
  table-layout: auto;
  font-size: 0.72rem;
  line-height: 1.25;
  margin: 0;
}
.cart-color-table th,
.cart-color-table td {
  padding: 3px 5px;
  border-bottom: 1px solid var(--color-border);
  text-align: start;
  vertical-align: middle;
  color: var(--color-text);
  font-weight: 500;
  word-break: keep-all;
  overflow-wrap: anywhere;
}
.cart-color-table th {
  font-weight: 600;
  color: var(--color-text-muted);
  background: rgba(0, 0, 0, 0.025);
  white-space: nowrap;
  font-size: 0.68rem;
}
.cart-color-table th:nth-child(2),
.cart-color-table td:nth-child(2) { white-space: nowrap; }
.cart-color-table th:nth-child(3),
.cart-color-table td:nth-child(3) {
  text-align: end;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  font-weight: 600;
}
.cart-color-table tbody tr:last-child td { border-bottom: none; }

/* Editable variant — the qty column hosts a number input the buyer can
   tweak inline. The input ALWAYS shows a visible chrome (border + light
   fill) so it reads as "I am interactive" the moment the page loads,
   not just on hover — buyers were assuming the cell was static text
   and missing the feature entirely (user feedback). Focus deepens the
   accent ring to match .cart-acc-input so the two inline editors on
   the cart line stay in the same family. */
.cart-color-table--editable .cart-color-qty-cell {
  padding: 2px 4px;
}
.cart-color-qty-input {
  width: 72px;
  padding: 4px 8px;
  font: 700 0.82rem/1.2 Inter, sans-serif;
  font-variant-numeric: tabular-nums;
  text-align: end;
  color: var(--color-text);
  background: #fff;
  border: 1.5px solid #94a3b8;            /* slate-400 — clearly a field */
  border-radius: 6px;
  box-shadow: inset 0 1px 0 rgba(15, 23, 42, 0.04);
  transition: border-color 0.12s ease, background 0.12s ease, box-shadow 0.12s ease;
  -moz-appearance: textfield;
  cursor: text;
}
.cart-color-qty-input::-webkit-outer-spin-button,
.cart-color-qty-input::-webkit-inner-spin-button {
  -webkit-appearance: none; margin: 0;
}
.cart-color-qty-input:hover {
  border-color: var(--color-accent, #ea580c);
  background: #fff7ed;                    /* amber-50 hover tint */
}
.cart-color-qty-input:focus {
  outline: none;
  background: #fff;
  border-color: var(--color-accent, #ea580c);
  box-shadow: 0 0 0 3px var(--color-accent-soft, rgba(243, 123, 30, 0.22));
}

/* ------------------------------------------------------------------
   Step 13 — six delivery-date slots (business-day cadence, admin-
   configurable lead-time + optional holiday overrides).
   ------------------------------------------------------------------ */
/* Centred rows, not a fixed grid.

   How many cards there are is decided at runtime and is NOT the number of
   slots the family declares: pins declare seven, but any slot that would
   repeat an earlier date is dropped (the dearer of a same-date pair is an
   option nobody should buy), so the row is usually six. The tracks were nailed
   to seven regardless, so the cards hugged the left and the spare track sat
   there as 135px of dead space.

   A fixed grid can't fix that on its own: whenever the count doesn't divide by
   the column count, the closing row is short and grid leaves its slack on the
   right — six cards in four columns put 324px of it under the second row. A
   wrapping flex row centres a short row instead, which is even-handed at any
   count and needs nothing to know the count. */
.delivery-slots {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
}
/* Wide: one row, cards splitting it equally, so it is flush edge to edge
   whether there are six or seven. min-width overrides flex's `auto` default,
   which would refuse to shrink past the date text and wrap the row. */
.delivery-slot {
  flex: 1 1 0;
  min-width: 0;
}
/* Narrower: cards hold a width matching the column count of the band and wrap,
   rather than being squeezed. Basis subtracts the gutters the row will carry.
   [data-slots] survives as "this family declares that many slots" — pins wrap
   one step earlier than the rest — and no longer claims to be the rendered
   count. The [data-slots] selector is repeated in the narrower bands because
   it out-specifies the plain one and would otherwise keep winning. */
@media (max-width: 1200px) {
  .delivery-slots[data-slots="7"] > .delivery-slot {
    flex: 0 0 calc((100% - 30px) / 4);
  }
}
@media (max-width: 980px) {
  .delivery-slots > .delivery-slot,
  .delivery-slots[data-slots="7"] > .delivery-slot {
    flex: 0 0 calc((100% - 20px) / 3);
  }
}
@media (max-width: 560px) {
  .delivery-slots > .delivery-slot,
  .delivery-slots[data-slots="7"] > .delivery-slot {
    flex: 0 0 calc((100% - 10px) / 2);
  }
}
.delivery-slot {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 14px 10px;
  background: #fff;
  border: 1.5px solid var(--color-border, #e3e6eb);
  border-radius: 12px;
  cursor: pointer;
  text-align: center;
  font-family: inherit;
  font-size: 14px;
  color: var(--color-text);
  transition: border-color 0.18s ease, box-shadow 0.18s ease, transform 0.18s ease, background 0.18s ease;
}
.delivery-slot:hover {
  border-color: var(--color-primary, #2b6cb0);
  box-shadow: 0 4px 12px rgba(43, 108, 176, 0.10);
  transform: translateY(-1px);
}
.delivery-slot .slot-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  font-weight: 600;
  color: var(--color-text-light);
}
.delivery-slot .slot-date {
  font-size: 15px;
  font-weight: 700;
  color: var(--color-text);
  line-height: 1.25;
}
.delivery-slot[data-soonest="1"] .slot-label {
  color: #1b5e20;
}
.delivery-slot.is-selected {
  border-color: var(--color-primary, #2b6cb0);
  background: linear-gradient(135deg, #ebf4ff 0%, #d6e7ff 100%);
  box-shadow: 0 6px 18px rgba(43, 108, 176, 0.18);
}
.delivery-slot.is-selected .slot-date,
.delivery-slot.is-selected .slot-label {
  color: var(--color-primary, #1e4a80);
}
.delivery-slot.is-selected::after {
  content: "";
  position: absolute;
  top: 8px;
  inset-inline-end: 8px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--color-primary, #2b6cb0) url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><polyline points='20 6 9 17 4 12'/></svg>") center/12px no-repeat;
}
/* Per-slot price-delta pill: shown only when the merchant has configured a
   rule for that delivery slot (e.g. "+€2.50" rush fee on slot 1 or
   "−€1.00" patience discount on slot 6). Sits centred under the date so
   it doesn't fight the top-right "selected" checkmark for space. */
.delivery-slot .slot-delta {
  display: inline-block;
  margin-top: 4px;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 700;
  line-height: 1.4;
  background: #f1f4fa;
  color: var(--color-text-light, #5b6373);
}
.delivery-slot .slot-delta--up {
  background: #fff3e0;
  color: #b34d00;
}
.delivery-slot .slot-delta--down {
  background: #e6f5ea;
  color: #1b5e20;
}
/* Stronger green for the "no rush fee" standard-delivery slot —
   makes it visually pop the way tablecoversnow's "FREE" badge does. */
.delivery-slot .slot-delta--free {
  background: #1b5e20;
  color: #fff;
  font-weight: 700;
  letter-spacing: 0.4px;
  text-transform: uppercase;
}
.delivery-slot.is-selected .slot-delta { background: rgba(255,255,255,0.65); }
.delivery-slot.is-selected .slot-delta--up { color: #b34d00; }
.delivery-slot.is-selected .slot-delta--down { color: #1b5e20; }
.delivery-slot.is-selected .slot-delta--free {
  background: #1b5e20;
  color: #fff;
}

.summary-total {
  margin-top: auto;
  padding-top: 14px;
  border-top: 1px solid var(--color-border-strong);
}
.summary-total .label {
  font-size: 0.78rem;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-weight: 700;
}
.summary-total .price {
  font-size: 1.6rem;
  font-weight: 800;
  color: var(--color-text);
  margin: 4px 0 0;
  letter-spacing: -0.01em;
}
.summary-total .price-each {
  font-size: 0.8rem;
  color: var(--color-text-muted);
  font-weight: 500;
  margin-top: 2px;
}
/* One-time setup / plate fee row — sits below the per-each line and is
   shown only when any matched delta carries a `setup` amount. */
.summary-total .price-setup {
  font-size: 0.8rem;
  color: var(--color-accent, #b45309);
  font-weight: 600;
  margin-top: 2px;
}
.summary-total .price-setup[hidden] { display: none; }

/* Modal footer (prev / next / quote) */
.modal-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 16px 24px;
  border-top: 1px solid var(--color-border);
  background: #fff;
}
.modal-footer .step-counter {
  font-size: 0.85rem;
  color: var(--color-text-muted);
  font-weight: 600;
}
.modal-footer .footer-actions {
  display: flex;
  gap: 8px;
}
.btn-step-prev,
.btn-step-next {
  padding: 10px 18px;
  border-radius: var(--radius-sm);
  font-weight: 600;
  font-size: 0.9rem;
  border: none;
  cursor: pointer;
  font-family: inherit;
  transition: var(--transition);
}
.btn-step-prev {
  background: transparent;
  color: var(--color-text-muted);
}
.btn-step-prev:hover { background: var(--color-bg-alt); color: var(--color-text); }
.btn-step-prev:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.btn-step-next {
  background: var(--color-accent);
  color: #fff;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.btn-step-next:hover {
  background: var(--color-accent-dark);
  transform: translateY(-1px);
}
.btn-step-next svg { width: 14px; height: 14px; }

/* lock body scroll when modal open */
body.modal-open { overflow: hidden; }

/* ===========================================================================
   22. Product detail page (hero, tabs, pricing chart, inline configurator)
   =========================================================================== */

/* Breadcrumb on detail page */
.pd-breadcrumb {
  padding: 18px 0 8px;
  margin: 0;
}

/* Hero section */
.pd-hero {
  background: linear-gradient(135deg, #fafaf7 0%, #f5f3ee 100%);
  padding: 28px 0 56px;
  border-bottom: 1px solid var(--color-border);
}
.pd-hero-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 56px;
  align-items: center;
}
@media (max-width: 880px) {
  .pd-hero-grid { grid-template-columns: 1fr; gap: 32px; }
}
.pd-image-wrap {
  position: relative;
  background: #fff;
  border-radius: 20px;
  overflow: hidden;
  box-shadow: 0 24px 64px rgba(15, 23, 42, 0.10);
  border: 1px solid var(--color-border);
  aspect-ratio: 1 / 1;
  max-width: 540px;
  margin: 0 auto;
  width: 100%;
  padding: 32px;
  display: grid;
  place-items: center;
}
.pd-image-wrap::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 30% 30%, rgba(234, 88, 12, 0.06) 0%, transparent 60%);
  pointer-events: none;
}
.pd-image {
  width: 100%;
  height: 100%;
  max-width: 360px;
  max-height: 360px;
  object-fit: contain;
  position: relative;
  z-index: 1;
}

/* Hero carousel controls (e.g. Metallic Woven Gold/Silver colourways). */
.pd-carousel-btn {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: none;
  background: rgba(255, 255, 255, 0.92);
  box-shadow: 0 2px 10px rgba(15, 23, 42, 0.18);
  color: #111827;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 3;
  transition: background .15s, transform .15s;
}
.pd-carousel-btn:hover { background: #fff; }
.pd-carousel-btn--prev { inset-inline-start: 12px; }
.pd-carousel-btn--next { inset-inline-end: 12px; }
.pd-carousel-dots {
  position: absolute;
  bottom: 14px;
  inset-inline-start: 0; inset-inline-end: 0;
  display: flex;
  gap: 8px;
  justify-content: center;
  z-index: 3;
}
.pd-carousel-dot {
  width: 9px;
  height: 9px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(17, 24, 39, 0.28);
  cursor: pointer;
  transition: background .15s, transform .15s;
}
.pd-carousel-dot.is-active { background: var(--color-accent, #2563eb); transform: scale(1.2); }

.pd-info { min-width: 0; }
.pd-eyebrow {
  display: inline-block;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--color-accent-dark);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-bottom: 12px;
}
.pd-info h1 {
  font-size: clamp(1.75rem, 3.5vw, 2.5rem);
  margin: 0 0 14px;
  line-height: 1.15;
  font-weight: 800;
  color: var(--color-text);
}
.pd-meta-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
  margin-bottom: 16px;
  font-size: 0.88rem;
}
.pd-rating {
  color: var(--color-accent);
  letter-spacing: 1px;
  font-weight: 600;
}
.pd-rating span {
  color: var(--color-text-muted);
  margin-inline-start: 6px;
  font-weight: 500;
  letter-spacing: 0;
}
.pd-meta {
  background: #fff;
  border: 1px solid var(--color-border);
  padding: 4px 10px;
  border-radius: 999px;
  color: var(--color-text-muted);
  font-weight: 600;
  font-size: 0.78rem;
}
.pd-lead {
  color: var(--color-text-muted);
  margin: 0 0 22px;
  line-height: 1.6;
  font-size: 1rem;
}
.pd-price-block {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 4px;
}
.pd-price-label {
  font-size: 0.85rem;
  color: var(--color-text-muted);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.pd-price {
  font-size: 2rem;
  font-weight: 800;
  color: var(--color-text);
  letter-spacing: -0.02em;
}
.pd-price-each {
  font-size: 0.85rem;
  color: var(--color-text-muted);
  font-weight: 500;
}
.pd-cta-row {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 26px;
}
.pd-trust-row {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  font-size: 0.85rem;
  color: var(--color-text-muted);
}
.pd-trust-row > span {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.pd-trust-row svg { color: var(--color-success); flex-shrink: 0; }

/* Sticky tabs */
.pd-tabs-wrap {
  position: sticky;
  /* Clear of BOTH sticky bars. This used to be just the header height and so
     slid under it by the height of the topbar above it. */
  top: calc(36px + var(--header-height));
  z-index: 30;
  background: #fff;
  border-bottom: 1px solid var(--color-border);
  box-shadow: 0 1px 0 rgba(15, 23, 42, 0.04);
}
.pd-tabs {
  display: flex;
  gap: 4px;
  overflow-x: auto;
  scrollbar-width: thin;
}
.pd-tabs::-webkit-scrollbar { height: 0; }
.pd-tabs a {
  padding: 16px 18px;
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--color-text-muted);
  white-space: nowrap;
  border-bottom: 2px solid transparent;
  transition: var(--transition);
}
.pd-tabs a:hover { color: var(--color-text); }
.pd-tabs a.is-active {
  color: var(--color-accent-dark);
  border-bottom-color: var(--color-accent);
}

/* Inline configurator (no modal wrapper) */
.config-inline {
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: 18px;
  overflow: hidden;
  box-shadow: 0 12px 32px rgba(15, 23, 42, 0.06);
  display: grid;
  grid-template-rows: auto auto 1fr auto;
}
.config-inline-header {
  padding: 24px 28px 6px;
}
.config-inline-header h2 {
  font-size: 1.5rem;
  margin: 0 0 6px;
}
.config-inline-header p {
  margin: 0;
  color: var(--color-text-muted);
  font-size: 0.95rem;
}
.config-inline .config-stepnav {
  background: var(--color-bg-alt);
  border-bottom: 1px solid var(--color-border);
  border-top: 1px solid var(--color-border);
  margin-top: 18px;
}
.config-inline .modal-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  min-height: 460px;
}
@media (max-width: 880px) {
  .config-inline .modal-body { grid-template-columns: 1fr; }
}
.config-inline .config-summary {
  background: var(--color-bg-alt);
  border-inline-start: 1px solid var(--color-border);
  position: sticky;
  top: calc(var(--header-height) + 56px);
  align-self: start;
  max-height: calc(100vh - var(--header-height) - 80px);
}
@media (max-width: 880px) {
  .config-inline .config-summary { position: static; max-height: none; }
}
.config-inline .modal-footer {
  border-top: 1px solid var(--color-border);
  background: #fff;
}

/* Pricing table */
.pd-price-chart-size {
  font: 500 16px/1.2 Inter, sans-serif;
  color: var(--color-text-muted);
  margin-inline-start: 6px;
  letter-spacing: 0;
}
.pd-pricing-table-wrap {
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: 14px;
  overflow: hidden;
  box-shadow: var(--shadow-sm);
}
.pd-pricing-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.95rem;
}
.pd-pricing-table thead th {
  background: var(--color-bg-alt);
  text-align: start;
  padding: 14px 18px;
  font-weight: 700;
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-text-muted);
  border-bottom: 1px solid var(--color-border);
}
.pd-pricing-table tbody td {
  padding: 14px 18px;
  border-bottom: 1px solid var(--color-border);
  color: var(--color-text);
}
.pd-pricing-table tbody tr:last-child td { border-bottom: none; }
.pd-pricing-table tbody tr:hover { background: var(--color-bg-warm); }
.pd-pricing-table tbody td:first-child {
  font-weight: 700;
}
.pd-pricing-table tbody td:nth-child(2) {
  color: var(--color-accent-dark);
  font-weight: 700;
}
.pd-pricing-table tbody td:last-child {
  color: var(--color-success);
  font-weight: 600;
}

/* Toast utility */
.pe-toast {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translate(-50%, 16px);
  background: var(--color-text);
  color: #fff;
  padding: 12px 20px;
  border-radius: 999px;
  box-shadow: 0 12px 32px rgba(0,0,0,0.25);
  font-size: 0.9rem;
  font-weight: 600;
  opacity: 0;
  pointer-events: none;
  transition: all 0.25s ease;
  z-index: 300;
  max-width: 90%;
  text-align: center;
}
.pe-toast.show { opacity: 1; transform: translate(-50%, 0); }

/* Section spacing on detail page (tighter) */
.section[data-pd-section] {
  padding: 56px 0;
  scroll-margin-top: calc(var(--header-height) + 64px);
}

/* Beautification: refined card hover, smoother category cards */
.product-card {
  border-radius: 14px;
}
.product-card .card-body {
  padding: 18px 18px 16px;
}
.product-card h3 {
  font-size: 1.02rem;
  font-weight: 700;
  letter-spacing: -0.01em;
}
.product-card .price-current {
  font-size: 1.2rem;
  font-weight: 800;
  letter-spacing: -0.01em;
}
.product-card .badge-sale {
  background: var(--color-text);
  color: #fff;
  border-radius: 999px;
  padding: 4px 10px;
  font-size: 0.65rem;
  font-weight: 800;
  letter-spacing: 0.08em;
}
.category-card {
  border-radius: 18px;
}
.category-card:hover { border-color: var(--color-accent); }

/* Refined faq question styling */
.faq-question { font-size: 0.95rem; }
.faq-item { border-radius: 12px; }
.faq-item.open { border-color: var(--color-accent); }

/* Refined buttons */
.btn { border-radius: 10px; }
.btn-primary { box-shadow: 0 4px 12px rgba(234, 88, 12, 0.18); }
.btn-primary:hover { box-shadow: 0 6px 16px rgba(234, 88, 12, 0.28); }

/* ==========================================================================
   Multi-step ALL-VISIBLE configurator (replaces wizard nav)
   Each Step block stacks vertically; right column = sticky live summary.
   ========================================================================== */
.config-allsteps {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 280px;
  gap: 24px;
  align-items: start;
}
/* Configurator container — main column ≈ 2/3 of viewport on typical screens.
   Widened to 1600px so step bodies (especially wristband Step 3's colour
   cards) have room to show full-size product photos without cropping. */
[data-pd-section="configurator"] > .container {
  max-width: 1600px;
  padding: 0 16px;
}
@media (max-width: 980px) {
  .config-allsteps { grid-template-columns: 1fr; }
}

.config-allsteps .config-summary {
  background: #fff;
  border: 1.5px solid var(--color-border);
  border-radius: 16px;
  padding: 22px 22px 18px;
  position: sticky;
  top: calc(var(--header-height) + 72px);
  align-self: start;
  max-height: calc(100vh - var(--header-height) - 96px);
  /* Flex column so the summary list takes the flexible middle space
     (scrolls internally when long), and the total + CTA stay pinned at
     the bottom of the panel — always visible without inner scrolling.
     Removed the panel-level overflow:auto so the CTA never gets pushed
     below the viewport. */
  display: flex;
  flex-direction: column;
  box-shadow: 0 10px 28px rgba(15, 23, 42, 0.06);
}
.config-allsteps .config-summary > h4 { flex: 0 0 auto; }
.config-allsteps .config-summary .summary-list {
  flex: 1 1 auto;
  overflow-y: auto;
  min-height: 80px;
  /* Subtle scroll-track so customers know there's more below */
  scrollbar-width: thin;
  scrollbar-gutter: stable;
}
.config-allsteps .config-summary .summary-list::-webkit-scrollbar { width: 6px; }
.config-allsteps .config-summary .summary-list::-webkit-scrollbar-thumb {
  background: var(--color-border);
  border-radius: 3px;
}
.config-allsteps .config-summary .summary-total,
.config-allsteps .config-summary .summary-cta {
  flex: 0 0 auto;
}
.config-allsteps .config-summary .summary-cta .btn {
  width: 100%;
  font-size: 1.02rem;
  padding-block: 14px;
}
@media (max-width: 980px) {
  .config-allsteps .config-summary { position: static; max-height: none; display: block; }
  .config-allsteps .config-summary .summary-list { max-height: none; overflow-y: visible; }
}

.step-block {
  background: #fff;
  border: 1.5px solid var(--color-border);
  border-radius: 16px;
  padding: 24px 26px 26px;
  margin-bottom: 18px;
  box-shadow: 0 4px 14px rgba(15, 23, 42, 0.04);
  scroll-margin-top: calc(var(--header-height) + 80px);
  transition: border-color 0.18s ease, box-shadow 0.18s ease;
}
.step-block:hover { border-color: var(--color-border-strong); }
.step-block.is-touched { border-color: var(--color-accent); box-shadow: 0 8px 22px rgba(234, 88, 12, 0.10); }
/* Red glow + soft shake when the customer tries to add to cart without
   making a required selection (e.g. lanyard attachment in STEP 5). Cleared
   the moment they click any card in the step.
   The bag-hanger and beanie configurators build their own step containers
   (.bh-step / .bn-step) instead of .step-block, so they share this rule
   rather than each redefining the same red-glow treatment. */
.step-block.step-block--invalid,
.bh-step.bh-step--invalid,
.bn-step.bn-step--invalid {
  border-color: #dc2626 !important;
  box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.18) !important;
  animation: stepShake 0.4s ease;
}
/* Keep a flagged step clear of the sticky header when it's scrolled to. */
.bh-step, .bn-step { scroll-margin-top: calc(var(--header-height) + 80px); }
@keyframes stepShake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-6px); }
  40% { transform: translateX(6px); }
  60% { transform: translateX(-4px); }
  80% { transform: translateX(4px); }
}
/* Red inline-error bubble injected into the offending step-block when the
   customer clicks Add-to-Cart with a required field still empty. Replaces
   the old native alert() popup — non-blocking, in-flow, and scoped to the
   field the customer actually needs to fix. Auto-dismisses when they
   interact with the step. */
.inline-error {
  margin: 0 0 12px;
  padding: 10px 14px 10px 38px;
  position: relative;
  background: #fef2f2;
  border: 1px solid #f5c6c6;
  border-inline-start: 4px solid #dc2626;
  color: #b91c1c;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.5;
  border-radius: 6px;
  animation: inlineErrorIn 0.22s ease;
}
.inline-error::before {
  content: "!";
  position: absolute;
  inset-inline-start: 12px;
  top: 50%;
  transform: translateY(-50%);
  width: 18px;
  height: 18px;
  line-height: 18px;
  text-align: center;
  background: #dc2626;
  color: #fff;
  border-radius: 50%;
  font-weight: 800;
  font-size: 12px;
}
@keyframes inlineErrorIn {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Per-field validation message, sitting directly under the control it belongs
   to. The step-level .inline-error bubble above can only name one problem,
   which isn't enough for a step like "Customise Your Lanyard" where the
   message, the font and the imprint-colour count can all be missing at once —
   the buyer needs to see which fields, not just that something is wrong. */
.field-error {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  margin: 6px 0 0;
  color: #b91c1c;
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.4;
  animation: inlineErrorIn 0.22s ease;
}
.field-error::before {
  content: "!";
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  margin-top: 1px;
  line-height: 15px;
  text-align: center;
  background: #dc2626;
  color: #fff;
  border-radius: 50%;
  font-size: 11px;
  font-weight: 800;
}
/* Inside a configurator step the inputs are capped at 520px and centred, so the
   message has to be capped the same way — otherwise it starts at the edge of
   the step body, well to the left of the field it points at. Rows whose control
   spans the full width (file browse rows, the clipart pair) opt back out. */
.step-block-body .field-error {
  max-width: 520px;
  margin-inline-start: auto;
  margin-inline-end: auto;
  text-align: start;
}
.step-block-body .subrow:has(> .browse-row) .field-error,
.step-block-body .subrow:has(> .clipart-row) .field-error {
  max-width: none;
}
/* Cloth wristband rows lay their control out at the full width of the step
   body, so the capped-and-centred message would sit visibly inset from the
   field it belongs to. */
.step-block-body .wb-field > .field-error {
  max-width: none;
  margin-inline-start: 0;
  margin-inline-end: 0;
}

/* Red outline on the offending control itself. Covers the native inputs and
   selects as well as the custom swatch/clipart triggers that replace them. */
.field-invalid,
.field-invalid.text-field,
.field-invalid.select-field,
.field-invalid .swatch-select-trigger,
.field-invalid .clip-select-trigger,
.field-invalid .cloth-cselect__trigger,
.field-invalid.cloth-file,
.field-invalid.browse-row {
  border-color: #dc2626 !important;
  box-shadow: 0 0 0 2px rgba(220, 38, 38, 0.14);
}

.step-block-head {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  margin-bottom: 14px;
}
.step-num-big {
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  border-radius: 10px;
  background: var(--color-accent);
  color: #fff;
  display: grid;
  place-items: center;
  font-weight: 800;
  font-size: 0.95rem;
  letter-spacing: 0.02em;
  box-shadow: 0 4px 10px rgba(234, 88, 12, 0.25);
}
.step-block.is-touched .step-num-big { background: var(--color-success); box-shadow: 0 4px 10px rgba(22, 163, 74, 0.25); }
.step-num-big::before {
  content: 'STEP ';
  font-size: 0.5rem;
  letter-spacing: 0.1em;
  display: block;
  line-height: 1;
  opacity: 0.85;
  margin-bottom: 1px;
}
.step-block-title { flex: 1 1 auto; min-width: 0; }
.step-block-title h3 {
  margin: 0 0 4px;
  font-size: 1.18rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--color-text);
}
.step-block-title .step-help {
  margin: 0;
  font-size: 0.9rem;
  color: var(--color-text-muted);
  line-height: 1.5;
}
.step-block-optional {
  display: inline-block;
  margin-inline-start: 8px;
  padding: 2px 8px;
  background: var(--color-bg-alt);
  color: var(--color-text-muted);
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  vertical-align: middle;
}
.step-block-body { margin-top: 6px; }

/* Tile grid (used for Step 1: pick a style) — flex so leftover cards on
   the last row stay horizontally centred. The minimum card width was
   bumped from 140px to 210px so the product photo inside each tile is
   visibly larger and easier to identify (lanyard/wristband styles all
   look quite similar at thumbnail size). */
.tile-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 14px;
  max-width: 1200px;
  margin: 0 auto;
}
.tile-grid > .tile-card { flex: 0 0 210px; }
@media (max-width: 600px) {
  .tile-grid > .tile-card { flex: 0 0 160px; }
}
.tile-card {
  background: #fff;
  border: 1.5px solid var(--color-border);
  border-radius: 12px;
  padding: 12px 10px 10px;
  cursor: pointer;
  text-align: center;
  transition: var(--transition);
  display: flex;
  flex-direction: column;
  gap: 8px;
  position: relative;
  font-family: inherit;
}
.tile-card:hover { border-color: var(--color-accent); transform: translateY(-1px); }
.tile-card.is-selected {
  border-color: var(--color-accent);
  background: var(--color-accent-soft);
  box-shadow: 0 6px 16px rgba(234, 88, 12, 0.12);
}
.tile-card.is-selected::after {
  content: '';
  position: absolute;
  top: 8px;
  inset-inline-end: 8px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--color-accent) url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='20 6 9 17 4 12'/></svg>") no-repeat center / 12px 12px;
}
.tile-card .tile-img {
  width: 100%;
  aspect-ratio: 1.2 / 1;
  /* Always paint a clean white panel behind the product photo. The tubular
     lanyard hero is a transparent PNG (unlike the other styles' JPGs);
     if we kept the warm tint here, that single tile looked beige while
     the rest looked white. */
  background: #fff;
  border-radius: 8px;
  overflow: hidden;
  display: grid;
  place-items: center;
}
.tile-card .tile-img img { width: 92%; height: 92%; object-fit: contain; }
.tile-card .tile-name { font-weight: 700; font-size: 0.92rem; color: var(--color-text); line-height: 1.3; }
.tile-card .tile-meta { font-size: 0.78rem; color: var(--color-text-muted); }

/* Sub-row group (e.g. Step 2 has Type + Width + Length subgroups) */
.subrow { margin-top: 16px; }
.subrow:first-child { margin-top: 0; }
.subrow-label {
  display: block;
  font-weight: 700;
  font-size: 0.85rem;
  color: var(--color-text);
  margin-bottom: 8px;
}
.subrow-help {
  font-size: 0.78rem;
  color: var(--color-text-muted);
  margin: 0 0 8px;
}

/* Compact pill row (used for size/length) */
.pill-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.pill-btn {
  background: #fff;
  border: 1.5px solid var(--color-border);
  border-radius: 999px;
  padding: 8px 16px;
  font-weight: 600;
  font-size: 0.86rem;
  color: var(--color-text);
  cursor: pointer;
  transition: var(--transition);
  font-family: inherit;
}
.pill-btn:hover { border-color: var(--color-accent); color: var(--color-accent-dark); }
.pill-btn.is-selected {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: #fff;
}

/* ==========================================================================
   STEP 9 — "Different designs per card" variable-data table
   ------------------------------------------------------------
   Layout mirrors the tablecoversnow ID-card configurator: a centred
   pill toggle ("Enter Data" / "Upload File"), then either an editable
   per-card spreadsheet OR a drag-and-drop upload zone. The spreadsheet
   has a sticky header where the merchant types FIELD NAMES (Name, Title,
   ID#…) and a body whose row-count is locked to the live order qty.
   ========================================================================== */
.subrow--center { display: flex; justify-content: center; }
.subrow--center .pill-row { justify-content: center; }
/* Restore the browser default for `hidden` on flex/grid subrows —
   class selectors win the cascade against the UA `[hidden] { display:none }`
   rule unless we re-state it here, which is why the Step 9 "Enter Data /
   Upload File" toggle kept showing through even when designSplit was set
   to "Same Design on All Cards". */
.subrow--center[hidden] { display: none !important; }

/* Stacked variant of .subrow used by the Different-design note textarea:
   label sits on its own line, textarea stretches full-width below.
   The default .subrow is block-flow with inline label + control, which
   would crush a multi-line textarea down to one line. */
.subrow--col { display: block; }
.subrow--col[hidden] { display: none !important; }
.subrow--col .subrow-label {
  display: block;
  margin-bottom: 6px;
  font-weight: 700;
}
.cd-diff-note-input {
  width: 100%;
  box-sizing: border-box;
  padding: 10px 12px;
  border: 1px solid #d1d5db;
  border-radius: 8px;
  font: 500 13.5px/1.5 Inter, sans-serif;
  color: #374151;
  background: #fafafa;
  resize: vertical;
  min-height: 76px;
}
.cd-diff-note-input:focus {
  outline: none;
  border-color: #2563eb;
  background: #fff;
}

.cd-data-table-wrap { margin-top: 16px; }
.cd-data-toolbar {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 6px;
}
.cd-add-field-btn {
  background: transparent;
  border: none;
  color: var(--color-accent);
  font-weight: 700;
  font-size: 0.86rem;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 2px;
  font-family: inherit;
}
.cd-add-field-btn:hover { color: var(--color-accent-dark); text-decoration: underline; }
.cd-add-field-plus {
  display: inline-block;
  width: 16px;
  height: 16px;
  line-height: 14px;
  text-align: center;
  font-size: 1.1rem;
  font-weight: 700;
}

.cd-data-table-scroll {
  border: 1px solid var(--color-border);
  border-radius: 10px;
  max-height: 380px;
  overflow: auto;
  background: #fff;
}
.cd-data-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: 0.86rem;
}
.cd-data-table thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: #f6f8fa;
  border-bottom: 1px solid var(--color-border);
  padding: 8px 8px;
  font-weight: 700;
  color: var(--color-text);
  text-align: start;
  vertical-align: middle;
}
.cd-data-table th.cd-card-num-col {
  width: 80px;
  min-width: 80px;
  text-align: center;
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  background: #f0f3f6;
}
.cd-data-table .cd-field-cell {
  position: relative;
  padding: 4px 6px;
  min-width: 160px;
}
.cd-data-table .cd-field-name {
  width: 100%;
  border: 1px solid var(--color-border);
  border-radius: 6px;
  padding: 6px 10px;
  font: 600 0.86rem/1.2 inherit;
  color: var(--color-text);
  background: #fff;
  box-sizing: border-box;
}
.cd-data-table .cd-field-name::placeholder {
  font-weight: 400;
  color: #b5bcc4;
  font-style: italic;
}
.cd-data-table .cd-field-name:focus {
  outline: none;
  border-color: var(--color-accent);
  box-shadow: 0 0 0 2px rgba(0, 161, 178, 0.18);
}
.cd-field-remove {
  position: absolute;
  top: 0;
  inset-inline-end: 4px;
  border: none;
  background: transparent;
  color: #b5bcc4;
  font-size: 0.95rem;
  line-height: 1;
  cursor: pointer;
  padding: 2px 4px;
  border-radius: 3px;
  opacity: 0;
  transition: opacity 0.12s, color 0.12s, background 0.12s;
  font-family: inherit;
}
.cd-data-table .cd-field-cell:hover .cd-field-remove { opacity: 1; }
.cd-field-remove:hover { color: #d9534f; background: #fff0f0; }

.cd-data-table tbody tr td {
  padding: 4px 6px;
  border-bottom: 1px solid #eef1f4;
  background: #fff;
}
.cd-data-table tbody tr:last-child td { border-bottom: none; }
.cd-data-table tbody tr td.cd-card-num-cell {
  text-align: center;
  font-weight: 600;
  color: var(--color-text-muted);
  background: #fafbfc;
  width: 80px;
  min-width: 80px;
}
.cd-data-table .cd-value-input {
  width: 100%;
  border: 1px solid var(--color-border);
  border-radius: 6px;
  padding: 6px 10px;
  font: 400 0.85rem/1.2 inherit;
  color: var(--color-text);
  background: #fff;
  box-sizing: border-box;
}
.cd-data-table .cd-value-input::placeholder { color: #c2c8cf; }
.cd-data-table .cd-value-input:focus {
  outline: none;
  border-color: var(--color-accent);
  box-shadow: 0 0 0 2px rgba(0, 161, 178, 0.18);
}
.cd-table-empty {
  padding: 18px;
  text-align: center;
  color: var(--color-text-muted);
  font-size: 0.85rem;
}
.cd-row-cap-notice {
  margin-top: 8px;
  padding: 8px 12px;
  border-radius: 8px;
  background: #fff8e6;
  border: 1px solid #ffe5a8;
  color: #8a6d1a;
  font-size: 0.82rem;
}

/* --- Upload zone --------------------------------------------------------- */
.cd-upload-wrap { margin-top: 16px; }
.cd-upload-zone {
  display: block;
  border: 2px dashed var(--color-border);
  border-radius: 12px;
  padding: 28px 20px;
  text-align: center;
  cursor: pointer;
  background: #fafbfc;
  transition: background 0.15s, border-color 0.15s;
}
.cd-upload-zone:hover,
.cd-upload-zone.is-dragover {
  border-color: var(--color-accent);
  background: #f0fafc;
}
.cd-upload-input { display: none; }
.cd-upload-zone-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}
.cd-upload-icon {
  font-size: 1.6rem;
  width: 44px;
  height: 44px;
  line-height: 44px;
  border-radius: 50%;
  background: var(--color-accent);
  color: #fff;
  font-weight: 700;
}
.cd-upload-text strong {
  display: block;
  font-size: 0.95rem;
  color: var(--color-text);
  margin-bottom: 2px;
}
.cd-upload-text small {
  color: var(--color-text-muted);
  font-size: 0.78rem;
}
.cd-upload-list {
  margin-top: 10px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.cd-upload-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  border-radius: 8px;
  background: #f6f8fa;
  border: 1px solid var(--color-border);
  font-size: 0.85rem;
}
.cd-upload-item-name { flex: 1; color: var(--color-text); word-break: break-all; }
.cd-upload-item-size { color: var(--color-text-muted); font-size: 0.78rem; }
.cd-upload-item-remove {
  border: none;
  background: transparent;
  color: #b5bcc4;
  cursor: pointer;
  font-size: 1.1rem;
  line-height: 1;
  padding: 0 4px;
}
.cd-upload-item-remove:hover { color: #d9534f; }

@media (max-width: 700px) {
  .cd-data-table .cd-field-cell { min-width: 140px; }
  .cd-data-table { font-size: 0.82rem; }
}

/* Tabbed swatch list (Popular / All / PMS) */
.swatch-tabs {
  display: flex;
  gap: 4px;
  border-bottom: 1.5px solid var(--color-border);
  margin-bottom: 12px;
}
.swatch-tab {
  background: transparent;
  border: none;
  padding: 8px 14px;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--color-text-muted);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1.5px;
  transition: var(--transition);
  font-family: inherit;
}
.swatch-tab:hover { color: var(--color-text); }
.swatch-tab.is-active { color: var(--color-accent-dark); border-bottom-color: var(--color-accent); }
.swatch-pane { display: none; }
.swatch-pane.is-active { display: block; }

/* Side block (Front Side / Back Side groups inside Step 4) — flat layout */
.side-block {
  margin-top: 4px;
  padding: 0;
  background: transparent;
  border: none;
  border-radius: 0;
}
.side-block + .side-block {
  margin-top: 18px;
  padding-top: 18px;
  border-top: 1px dashed var(--color-border);
}
.side-block-title {
  display: none;  /* hidden by default — flat look like reference */
  font-size: 0.95rem;
  font-weight: 700;
  margin: 0 0 10px;
  color: var(--color-text);
}
/* When both sides are shown (back-side not hidden), reveal the titles
   so users can tell Front vs Back apart. */
.swatch-pane:has([data-side-back]:not([hidden])) .side-block-title {
  display: block;
}
.side-block-title {
  font-size: 1rem;
  font-weight: 700;
  margin: 0 0 10px;
  padding-bottom: 8px;
  border-bottom: 1px dashed var(--color-border, #e5e7eb);
  color: var(--color-accent-dark);
}

/* Final big CTA at end of all steps */
.config-final-cta {
  margin-top: 16px;
  padding: 22px 26px;
  background: linear-gradient(135deg, var(--color-accent-soft), #fff);
  border: 1.5px solid var(--color-accent);
  border-radius: 16px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  flex-wrap: wrap;
}
.config-final-cta h4 {
  margin: 0 0 4px;
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--color-text);
}
.config-final-cta p {
  margin: 0;
  font-size: 0.88rem;
  color: var(--color-text-muted);
}
.config-final-cta .btn { white-space: nowrap; }
.config-cta-actions {
  display: inline-flex;
  gap: 10px;
  flex-wrap: wrap;
  justify-content: center;
}
.config-cta-actions .btn { min-width: 160px; }

/* Checkout form layout */
.form-row { display: flex; flex-direction: column; gap: 4px; margin-bottom: 12px; }
.form-row label { font-size: 0.85rem; color: var(--color-text-muted); font-weight: 500; }
.form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 14px;
  margin-bottom: 8px;
}
.form-row-wide { grid-column: 1 / -1; }
@media (max-width: 600px) { .form-grid { grid-template-columns: 1fr; } }
.checkout-submit { display: block; width: 100%; margin-top: 16px; padding: 14px; }
.stripe-mount { padding: 12px 0 4px; }
.btn-ghost {
  background: transparent;
  color: var(--color-text);
  border: 1px solid var(--color-border);
}
.btn-ghost:hover { border-color: var(--color-accent); color: var(--color-accent); }

/* Sticky right summary - more compact */
.config-allsteps .summary-list { padding: 0; list-style: none; margin: 0 0 12px; }
.config-allsteps .summary-list li {
  padding: 8px 0;
  border-bottom: 1px dashed var(--color-border);
  display: grid;
  grid-template-columns: 110px 1fr;
  gap: 8px;
  font-size: 0.86rem;
}
.config-allsteps .summary-list li:last-child { border-bottom: none; }
.config-allsteps .summary-list li .sum-key { color: var(--color-text-muted); font-weight: 500; }
.config-allsteps .summary-list li .sum-val { color: var(--color-text); font-weight: 600; }
.config-allsteps .summary-list li.empty .sum-val { color: var(--color-border-strong); font-weight: 400; font-style: italic; }
.config-allsteps .summary-total { padding-top: 8px; border-top: 1.5px solid var(--color-border); }
.config-allsteps .summary-total .label {
  font-size: 0.78rem;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-weight: 600;
}
.config-allsteps .summary-total .price {
  font-size: 1.7rem;
  font-weight: 800;
  color: var(--color-text);
  letter-spacing: -0.02em;
  margin: 4px 0 2px;
}
.config-allsteps .summary-total .price-each {
  font-size: 0.8rem;
  color: var(--color-text-muted);
}
.config-allsteps .summary-cta {
  margin-top: 14px;
  display: grid;
  gap: 8px;
}

/* File upload for artwork */
.upload-zone {
  display: block;
  background: var(--color-bg-alt);
  border: 2px dashed var(--color-border-strong);
  border-radius: 12px;
  padding: 20px;
  text-align: center;
  cursor: pointer;
  transition: var(--transition);
}
.upload-zone:hover { border-color: var(--color-accent); background: var(--color-accent-soft); }
.upload-zone.has-file { border-color: var(--color-success); background: rgba(22, 163, 74, 0.06); border-style: solid; }
.upload-zone input[type="file"] { display: none; }
.upload-zone .upload-main { font-weight: 600; color: var(--color-text); margin-bottom: 4px; }
.upload-zone .upload-help { font-size: 0.76rem; color: var(--color-text-muted); }

/* ============================================================
   ID-card Step 7 (Customize Card) — pill-tab layout matching the
   tablecoversnow.com reference. Two tabs sit on a flat (non-panel)
   background and animate to a filled teal pill when active. Below
   the tabs the panes use a stacked label + control layout (vs the
   default subrow horizontal layout) so the Browse / radios / msg
   card line up edge-to-edge with the textarea in the shared foot.
   ============================================================ */
.cc-tabs {
  display: flex;
  justify-content: center;
  gap: 16px;
  background: transparent;
  border: 0;
  padding: 4px 0 18px;
  margin: 0 0 18px;
  border-bottom: 1px solid var(--color-border, #e2e8f0);
  flex-wrap: wrap;
}
.cc-tab {
  appearance: none;
  font: inherit;
  cursor: pointer;
  padding: 10px 26px;
  min-width: 180px;
  border-radius: 999px;
  border: 1.5px solid var(--color-accent, #38b6c8);
  background: #fff;
  color: var(--color-accent, #38b6c8);
  font-weight: 600;
  font-size: 0.95rem;
  transition: var(--transition, all .2s ease);
}
.cc-tab:hover { background: rgba(56, 182, 200, 0.08); }
.cc-tab.is-active {
  background: var(--color-accent, #38b6c8);
  color: #fff;
  box-shadow: 0 4px 12px rgba(56, 182, 200, 0.25);
}

.cc-pane { margin: 0; }
.cc-pane:not(.is-active) { display: none; }

.cc-field {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0 0 22px;
}
/* HTML's [hidden] sets display:none, but our .cc-field rule above
   defines display:flex with equal specificity and is loaded later,
   so it would otherwise win. Restore [hidden] semantics explicitly. */
.cc-field[hidden] { display: none; }
.msg-card[hidden] { display: none; }
.cc-label {
  font-weight: 700;
  font-size: 1rem;
  color: var(--color-text, #0f172a);
  line-height: 1.3;
}
.cc-label--caps {
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--color-text, #0f172a);
  text-transform: uppercase;
}
.cc-select.text-field,
.cc-textarea.text-field {
  width: 100%;
  padding: 12px 14px;
  font-size: 0.95rem;
  border: 1px solid var(--color-border, #e2e8f0);
  border-radius: 8px;
  background: #fff;
  color: var(--color-text, #0f172a);
}
.cc-select.text-field {
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'><path d='M1 1l5 5 5-5' stroke='%2364748b' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 14px center;
  padding-inline-end: 36px;
}
.cc-textarea.text-field { min-height: 110px; resize: vertical; line-height: 1.5; }

/* "Browse Files To Upload | Browse" — flat text-field look with a
   teal action label on the right. Whole row is the file <label>. */
.browse-zone {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 12px 16px;
  border: 1px solid var(--color-border, #e2e8f0);
  border-radius: 8px;
  background: #fff;
  cursor: pointer;
  transition: border-color .15s ease, background .15s ease;
}
.browse-zone:hover { border-color: var(--color-accent-soft, #cbd5e1); background: #fafcfd; }
.browse-zone.has-file { border-color: var(--color-success, #16a34a); background: rgba(22, 163, 74, 0.05); }
.browse-zone.browse-zone--invalid {
  border-color: #c92c2c;
  background: rgba(201, 44, 44, 0.05);
  animation: browseZoneShake 0.35s ease-in-out;
}
@keyframes browseZoneShake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-4px); }
  75% { transform: translateX(4px); }
}
/* Method-note banner that appears under the Artwork-Upload-Method dropdown
   when the customer chooses "Help with Artwork" or "Email Artwork Later". */
.cc-method-note {
  margin: -6px 0 14px;
  padding: 10px 14px;
  background: #eef6ff;
  border: 1px solid #b6d7f5;
  border-radius: 4px;
  color: #1c4e80;
  font-size: 0.9rem;
  line-height: 1.45;
}
.browse-zone input[type="file"] { display: none; }
.browse-zone-text {
  color: var(--color-text-muted, #64748b);
  font-size: 0.95rem;
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.browse-zone.has-file .browse-zone-text { color: var(--color-text, #0f172a); font-weight: 600; }
.browse-zone-btn {
  color: var(--color-accent, #38b6c8);
  font-weight: 600;
  font-size: 0.95rem;
  white-space: nowrap;
}

/* Print-position radios in the Easy Order Form pane. Stacked
   inline with a hollow circle that fills teal when selected. */
.cc-radios {
  display: flex;
  flex-wrap: wrap;
  gap: 18px 26px;
  padding-top: 4px;
}
.cc-radio {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font-size: 0.92rem;
  color: var(--color-text, #0f172a);
}
.cc-radio input[type="radio"] {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
  pointer-events: none;
}
.cc-radio-bullet {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 1.5px solid var(--color-border-strong, #94a3b8);
  background: #fff;
  display: inline-block;
  position: relative;
  flex: none;
  transition: border-color .15s ease;
}
.cc-radio input[type="radio"]:checked + .cc-radio-bullet {
  border-color: var(--color-accent, #38b6c8);
}
.cc-radio input[type="radio"]:checked + .cc-radio-bullet::after {
  content: "";
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  background: var(--color-accent, #38b6c8);
}
.cc-radio:hover .cc-radio-bullet { border-color: var(--color-accent, #38b6c8); }
.cc-radio.is-disabled {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}
.cc-radio.is-disabled .cc-radio-bullet { border-color: var(--color-border, #e2e8f0); background: #f1f5f9; }
.cc-select option:disabled { color: #94a3b8; }

/* "Easy Order Form" message card — 3-column row (Message Line /
   Font / Color) with column headers in small caps, followed by
   centered Add-Line / Upload-Logo pills. Extra lines append below
   the grid and use a slim 2-col row (label + input + remove). */
.msg-card {
  margin: 0 0 22px;
  padding: 18px 20px 20px;
  background: #fff;
  border: 1px solid var(--color-border, #e2e8f0);
  border-radius: 10px;
}
.msg-grid {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr;
  gap: 18px;
}
.msg-grid .text-field {
  width: 100%;
  padding: 11px 14px;
  font-size: 0.93rem;
  border: 1px solid var(--color-border, #e2e8f0);
  border-radius: 6px;
  background: #fff;
}
.msg-grid-cell select.text-field {
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'><path d='M1 1l5 5 5-5' stroke='%2364748b' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 12px center;
  padding-inline-end: 32px;
}
.msg-grid-cell .cc-label--caps { margin: 0 0 6px; }

.msg-extra-lines { display: flex; flex-direction: column; gap: 12px; margin-top: 12px; }

/* Each dynamically-added MESSAGE LINE row is a CLONE of the LINE 1
   header grid — same 3 columns (MESSAGE / FONT STYLE / IMPRINT COLOR),
   so every line gets its own independent font + colour picker. The ×
   remove button is tucked inside the message input's right padding so
   it doesn't push the FONT STYLE column out of pixel alignment with
   LINE 1. (The .msg-grid-cell--placeholder rule below is retained as
   a safety net for any in-flight HTML that pre-dates this change —
   new rows never use placeholder cells.) */
.msg-extra-line.msg-grid {
  align-items: end;
}
.msg-extra-line .msg-grid-cell--placeholder {
  /* Legacy invisible spacer cell from the previous LINE 2 layout.
     min-height keeps Safari from collapsing the row when all cells
     are empty. */
  min-height: 1px;
}
.msg-input-wrap {
  position: relative;
}
.msg-input-wrap .text-field {
  /* Reserve room on the right so the × icon never overlaps the
     buyer's text. */
  padding-inline-end: 40px;
}
.msg-input-wrap .msg-line-remove {
  position: absolute;
  top: 50%;
  inset-inline-end: 6px;
  transform: translateY(-50%);
  appearance: none;
  background: transparent;
  border: none;
  width: 28px;
  height: 28px;
  font-size: 1.25rem;
  line-height: 1;
  color: var(--color-text-muted, #94a3b8);
  cursor: pointer;
  border-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}
.msg-input-wrap .msg-line-remove:hover {
  color: #dc2626;
  background: rgba(220, 38, 38, 0.08);
}
.msg-input-wrap .msg-line-remove:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
}

/* On narrow screens the original .msg-grid collapses to one column, so
   the extra row's empty placeholder cells would create dead vertical
   space. Hide them and let the input cell take the full width. */
@media (max-width: 720px) {
  .msg-extra-line .msg-grid-cell--placeholder { display: none; }
}

.msg-actions {
  display: flex;
  justify-content: center;
  gap: 14px;
  margin-top: 18px;
  flex-wrap: wrap;
}
.btn-pill {
  appearance: none;
  font: inherit;
  cursor: pointer;
  padding: 10px 22px;
  border-radius: 999px;
  font-weight: 600;
  font-size: 0.92rem;
  transition: var(--transition, all .2s ease);
  display: inline-flex;
  align-items: center;
  gap: 8px;
  border: 1.5px solid var(--color-accent, #38b6c8);
  text-decoration: none;
}
.btn-pill input[type="file"] { display: none; }
.btn-pill--filled {
  background: var(--color-accent, #38b6c8);
  color: #fff;
}
.btn-pill--filled:hover { filter: brightness(1.05); }
.btn-pill--outline {
  background: #fff;
  color: var(--color-accent, #38b6c8);
}
.btn-pill--outline:hover { background: rgba(56, 182, 200, 0.08); }
.btn-pill.has-file {
  border-color: var(--color-success, #16a34a);
  color: var(--color-success, #16a34a);
  background: rgba(22, 163, 74, 0.06);
}

/* Shared bottom: Additional Instructions + notices. */
.cc-foot {
  margin-top: 8px;
  padding-top: 4px;
}
.cc-proof-notice {
  margin: 4px 0 6px;
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  color: var(--color-text, #0f172a);
  display: flex;
  align-items: center;
  gap: 8px;
  text-transform: uppercase;
}
.cc-proof-bullet {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #dc2626;
  color: #fff;
  font-weight: 700;
  font-size: 0.7rem;
  line-height: 1;
  flex: none;
}
.cc-fullcolor-notice {
  margin: 0;
  font-size: 0.88rem;
  color: var(--color-accent, #38b6c8);
}

@media (max-width: 720px) {
  .msg-grid { grid-template-columns: 1fr; gap: 14px; }
  .cc-radios { flex-direction: column; gap: 12px; }
  .cc-tab { min-width: 0; padding: 10px 18px; font-size: 0.9rem; }
}

/* Clipart row — two select+button pairs side by side (Start Clip / End Clip) */
.clipart-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr) auto;
  gap: 10px;
  align-items: center;
  max-width: 880px;
  margin: 0 auto;
}
@media (max-width: 720px) {
  .clipart-row { grid-template-columns: 1fr auto; row-gap: 12px; }
}
.clipart-row .select-field { max-width: none; margin: 0; }
.btn-upload-art {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 9px 16px;
  background: #fff;
  border: 1.5px solid var(--color-teal-line);
  border-radius: 999px;
  color: var(--color-teal);
  font-family: inherit;
  font-size: 0.86rem;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color 180ms ease, color 180ms ease, border-color 180ms ease;
}
.btn-upload-art:hover {
  background: var(--color-teal-soft);
  color: var(--color-teal-dark);
  border-color: var(--color-teal);
}
.btn-upload-art svg { flex-shrink: 0; }

/* Browse Files row — file picker styled as a wide field with Browse button on the right */
.browse-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  padding: 11px 14px;
  border: 1.5px solid var(--color-border);
  border-radius: 10px;
  background: #fff;
  cursor: pointer;
  transition: border-color 180ms ease, background-color 180ms ease;
}
.browse-row:hover { border-color: var(--color-border-strong); }
.browse-row.has-file { border-color: var(--color-success); background: rgba(22, 163, 74, 0.05); }
.browse-row-text {
  flex: 1;
  font-size: 0.92rem;
  color: var(--color-text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.browse-row.has-file .browse-row-text { color: var(--color-text); font-weight: 600; }
.browse-row-cta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--color-teal);
  font-weight: 700;
  font-size: 0.88rem;
  flex-shrink: 0;
}

.art-file-list {
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  width: 100%;
}
.art-file-list[hidden] { display: none; }
.art-file-list li {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border: 1px solid var(--color-border);
  border-radius: 10px;
  background: #fff;
  font-size: 0.9rem;
}
.art-file-list li.is-failed { border-color: #f59e0b; }
.art-file-list .art-file-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.art-file-list .art-file-remove {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  border: 0;
  border-radius: 50%;
  background: #f1f5f9;
  color: #475569;
  font-size: 1.15rem;
  line-height: 1;
  cursor: pointer;
  padding: 0;
}
.art-file-list .art-file-remove:hover {
  background: #fee2e2;
  color: #b91c1c;
}

/* ============================================================
   CART DRAWER + CART PAGE — added when migrating to Medusa backend.
   Drawer slides in from right; overlay dims rest of page.
   ============================================================ */
.cart-drawer-overlay {
  position: fixed; inset: 0;
  background: rgba(15,23,42,0.45);
  z-index: 70;
  animation: cartFadeIn .18s ease;
}
@keyframes cartFadeIn { from { opacity: 0; } to { opacity: 1; } }

.cart-drawer {
  position: fixed;
  top: 0; inset-inline-end: 0; bottom: 0;
  width: min(420px, 92vw);
  background: #fff;
  z-index: 71;
  display: flex; flex-direction: column;
  box-shadow: -16px 0 40px rgba(15,23,42,0.18);
  transform: translateX(105%);
  transition: transform .26s cubic-bezier(.4,0,.2,1);
}
.cart-drawer.is-open { transform: translateX(0); }
html.cart-open { overflow: hidden; }

.cart-drawer-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--color-border);
}
.cart-drawer-head h3 { margin: 0; font-size: 1.05rem; }
.cart-drawer-close {
  background: none; border: 0; font-size: 28px; line-height: 1;
  color: var(--color-text-muted); cursor: pointer; padding: 0 4px;
}
.cart-drawer-close:hover { color: var(--color-text); }

.cart-drawer-body { flex: 1; overflow-y: auto; padding: 12px 16px; }
.cart-empty { text-align: center; color: var(--color-text-muted); padding: 32px 0; }

.cart-line {
  display: grid;
  grid-template-columns: 56px 1fr;
  gap: 12px;
  padding: 12px 0;
  border-bottom: 1px solid var(--color-border);
}
.cart-line-img {
  width: 56px; height: 56px; object-fit: contain;
  background: var(--color-surface-alt);
  border-radius: 8px;
  display: grid; place-items: center;
}
.cart-line-img.is-blank::before { content: "📦"; font-size: 24px; opacity: 0.4; }
.cart-line-info { min-width: 0; }
.cart-line-title { font-weight: 600; font-size: 0.92rem; }
.cart-line-desc {
  font-size: 0.78rem; color: var(--color-text-muted);
  margin: 4px 0 0; padding: 0; list-style: none;
  display: flex; flex-direction: column; gap: 3px;
  overflow-wrap: anywhere;
}
.cart-line-desc li {
  position: relative;
  padding-inline-start: 10px;
  line-height: 1.35;
}
.cart-line-desc li::before {
  content: ""; position: absolute; inset-inline-start: 0; top: 0.55em;
  width: 4px; height: 4px; border-radius: 50%;
  background: currentColor; opacity: 0.55;
}
.cart-line-row {
  display: flex; justify-content: space-between; align-items: center;
  margin-top: 8px;
}
.cart-line-qty {
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid var(--color-border); border-radius: 999px;
  padding: 2px 8px;
}
.cart-line-qty button {
  background: none; border: 0; cursor: pointer;
  width: 22px; height: 22px; font-size: 16px; line-height: 1;
  color: var(--color-text-muted);
}
.cart-line-qty button:hover { color: var(--color-accent); }
.cart-line-qty span { min-width: 18px; text-align: center; font-size: 0.9rem; }
/* Locked variant — shown for configurator-built lines where the qty
   is the sum of multi-colour entries (e.g. Black × 1000 + Brown × 1000
   = 2000). +/- buttons removed because single-step increments would
   be both meaningless on bulk orders AND inconsistent with the colour
   breakdown above; the customer must use "Edit Order" instead. */
.cart-line-qty--locked {
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  border: 0;
  padding: 0;
}
.cart-line-qty--locked .cart-line-qty-num {
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--color-text);
  min-width: 0;
  text-align: start;
}
.cart-line-qty--locked .cart-line-qty-hint {
  font-size: 0.72rem;
  color: var(--color-text-muted);
  line-height: 1.2;
  min-width: 0;
  text-align: start;
}
.cart-line-price { font-weight: 700; font-size: 0.92rem; color: var(--color-text); }
/* In-cart accessory editor (holders / reels). Lives between the spec-row
   bullets and the main-qty row so the customer can tune badge holders and
   reels without re-opening the configurator. The "card" framing isolates
   it visually from the plain bullets above so it reads as an interactive
   table, not a static description. */
.cart-acc-section {
  margin-top: 10px;
  padding: 8px 10px 10px;
  border: 1px solid var(--color-border);
  border-radius: 8px;
  background: var(--color-bg-soft, #fafafa);
}
.cart-acc-section + .cart-acc-section { margin-top: 6px; }
.cart-acc-title {
  font-size: 0.78rem; font-weight: 700;
  color: var(--color-text);
  margin-bottom: 6px;
}
.cart-acc-group + .cart-acc-group { margin-top: 6px; }
.cart-acc-parent {
  font-size: 0.78rem; font-weight: 600;
  color: var(--color-text-muted);
  margin: 2px 0 4px;
}
.cart-acc-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px;
  padding: 4px 0;
  font-size: 0.78rem;
}
.cart-acc-row + .cart-acc-row {
  border-top: 1px dashed var(--color-border);
}
.cart-acc-row-label { color: var(--color-text); flex: 1 1 auto; }
.cart-acc-qty {
  display: inline-flex; align-items: center; gap: 4px;
  border: 1px solid var(--color-border); border-radius: 999px;
  padding: 1px 4px;
  background: #fff;
}
.cart-acc-btn {
  background: none; border: 0; cursor: pointer;
  width: 20px; height: 20px; font-size: 14px; line-height: 1;
  color: var(--color-text-muted);
  padding: 0;
}
.cart-acc-btn:hover { color: var(--color-accent); }
.cart-acc-input {
  width: 44px; text-align: center; font-size: 0.82rem;
  border: 0; background: transparent;
  -moz-appearance: textfield;
  padding: 0;
}
.cart-acc-input::-webkit-outer-spin-button,
.cart-acc-input::-webkit-inner-spin-button {
  -webkit-appearance: none; margin: 0;
}
.cart-acc-input:focus { outline: 0; box-shadow: 0 0 0 2px var(--color-accent-soft, rgba(243,123,30,0.18)); border-radius: 4px; }
.cart-line-actions {
  display: flex; align-items: center; gap: 8px; margin-top: 10px;
}
/* Edit Order + Remove rendered as compact pill buttons with a leading icon,
   so they read as real actions instead of bare underlined text. */
.cart-line-edit,
.cart-line-rm {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 0.76rem; font-weight: 600; line-height: 1;
  padding: 6px 11px; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--color-border, #e5e7eb);
  background: #fff; text-decoration: none;
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.cart-line-edit svg,
.cart-line-rm svg { flex: none; }
.cart-line-edit {
  color: var(--color-accent, #2563eb);
  border-color: color-mix(in srgb, var(--color-accent, #2563eb) 35%, #e5e7eb);
}
.cart-line-edit:hover {
  background: color-mix(in srgb, var(--color-accent, #2563eb) 10%, #fff);
  border-color: var(--color-accent, #2563eb);
  color: var(--color-primary, #1d4ed8);
}
.cart-line-rm {
  color: var(--color-text-muted, #6b7280);
}
.cart-line-rm:hover {
  background: #fef2f2; border-color: #fca5a5; color: #dc2626;
}

.cart-drawer-foot {
  padding: 16px 20px;
  border-top: 1px solid var(--color-border);
  background: var(--color-surface-alt, #f9fafb);
}
.cart-drawer-subtotal {
  display: flex; justify-content: space-between; align-items: center;
  margin-bottom: 12px;
  font-size: 0.95rem;
}
.cart-drawer-subtotal strong { font-size: 1.15rem; }
.cart-drawer-checkout {
  display: block; text-align: center; padding: 12px 16px;
  font-weight: 600;
}

/* Cart count badge in header.
   NOTE: the `.cart-count-badge[hidden]` rule below is REQUIRED. cart.js
   sets `el.hidden = (count === 0)` to hide the badge on empty carts,
   but the UA's `[hidden] { display: none }` rule loses to our explicit
   `display: inline-grid` (any author `display` overrides the UA rule).
   Without the override the badge would always render "0" next to the
   cart icon, which is exactly what was visible in the empty-cart UI. */
.cart-count-badge {
  position: absolute;
  top: 0; inset-inline-end: 0;
  transform: translate(30%, -30%);
  display: inline-grid; place-items: center;
  min-width: 18px; height: 18px;
  background: #dc2626; color: #fff;
  border-radius: 999px; font-size: 11px;
  line-height: 1; padding: 0 5px;
  pointer-events: none;
}
.cart-count-badge[hidden] { display: none !important; }
.cart-icon-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  padding: 4px 8px;
  color: var(--color-text);
  background: none; border: 0; font-size: 0.88rem;
}
.cart-icon-wrap:hover { color: var(--color-accent); }
.cart-icon-wrap svg { width: 20px; height: 20px; }

/* Cart page (products/cart.html) */
.cart-page { padding: 32px 0 64px; }
.cart-page .container { max-width: 1080px; }
.cart-page-grid {
  display: grid;
  /* Right column is 420px because the payment selector lives inside
     the order-summary aside — it needs the extra width so the split
     Card Number / Expiry / CVC fields don't wrap to 3 lines. */
  grid-template-columns: 1fr 420px;
  gap: 28px;
  margin-top: 24px;
  align-items: start;
}
.cart-list {
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: 12px;
  padding: 8px 20px;
}
.cart-summary {
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: 12px;
  padding: 20px;
  align-self: start;
  /* The payment method card lives INSIDE this aside, directly under
     the Order Summary rows (matches order-card.com's right-rail
     payment layout). That makes the column easily taller than the
     viewport on common 14"/15" laptops, so position: sticky would
     silently fall back to static (sticky elements taller than their
     scroll container can't actually stick) and just introduce
     layout jank. Plain static positioning is the predictable
     choice here. */
}
.cart-summary h4 { margin: 0 0 12px; font-size: 1.05rem; }
.cart-summary-row {
  display: flex; justify-content: space-between;
  padding: 8px 0;
  font-size: 0.92rem;
}
.cart-summary-row.is-total {
  border-top: 1px solid var(--color-border);
  margin-top: 8px; padding-top: 12px;
  font-weight: 700; font-size: 1.05rem;
}

/* Import-charges disclosure under the Total (cart + checkout summaries).
   Small print by design, but deliberately NOT behind a tooltip or accordion:
   it only does its job if the buyer can read it without interacting. */
.customs-note {
  margin: 12px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--color-border);
  font-size: 0.76rem;
  line-height: 1.5;
  color: #4b5563;
}
.customs-note__title {
  display: block;
  margin-bottom: 3px;
  font-weight: 700;
  color: var(--color-text);
}
.cart-summary .btn {
  display: block; width: 100%; margin-top: 16px;
  text-align: center;
}

/* ── Coupon / discount code ───────────────────────────────────────── */
.cart-summary-row--discount,
.cart-summary-row--discount strong { color: #1a7f37; }

/* Sits BELOW the Total. Scoped !important so an applied chip / hidden row
   actually collapses (the row uses display:flex, which otherwise beats the
   [hidden] attribute's UA display:none). */
.cart-coupon { margin-top: 14px; }
.cart-coupon [hidden] { display: none !important; }

/* The applied code sits above the box, which stays open so a second code can
   be tried (only one discount counts — the cart keeps the better of the two). */
.cart-coupon__applied + .cart-coupon__row { margin-top: 10px; }
.cart-coupon__row { display: flex; gap: 8px; }
.cart-coupon__input {
  flex: 1 1 auto; min-width: 0;
  height: 44px; padding: 0 14px;
  border: 1px solid var(--color-border); border-radius: 10px;
  font-size: 0.92rem; letter-spacing: 0.05em; text-transform: uppercase;
  background: #fff; color: #1f2733;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.cart-coupon__input::placeholder {
  letter-spacing: 0; text-transform: none; color: #9aa1ab;
}
.cart-coupon__input:focus {
  outline: none; border-color: var(--color-primary, #ea5a1e);
  box-shadow: 0 0 0 3px rgba(234, 90, 30, 0.14);
}
/* Compact secondary button — deliberately NOT .btn so the
   `.cart-summary .btn { width:100% }` rule can't stretch it. */
.cart-coupon__apply {
  flex: 0 0 auto; height: 44px; padding: 0 22px;
  border: 1px solid var(--color-border); border-radius: 10px;
  background: #f5f6f8; color: #1f2733;
  font-weight: 600; font-size: 0.9rem; white-space: nowrap; cursor: pointer;
  transition: background .15s ease, border-color .15s ease;
}
.cart-coupon__apply:hover { background: #eceef1; border-color: #cdd2d9; }
.cart-coupon__apply:active { transform: translateY(1px); }
.cart-coupon__apply:disabled { opacity: .55; cursor: default; }

/* The live campaign restated next to the box where the code is typed. */
.cart-coupon__promo {
  margin-top: 8px;
  font-size: 0.78rem;
  line-height: 1.45;
  color: var(--color-accent-dark);
}
.cart-coupon__msg { margin-top: 8px; font-size: 0.82rem; line-height: 1.45; }
.cart-coupon__msg.is-error { color: #b42318; }
.cart-coupon__msg.is-ok { color: #1a7f37; }

.cart-coupon__applied {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  background: #ecfdf3; border: 1px solid #abefc6; border-radius: 10px;
  padding: 11px 14px;
}
.cart-coupon__code {
  display: inline-flex; align-items: center; gap: 6px;
  font-weight: 700; letter-spacing: 0.03em; color: #1a7f37;
}
.cart-coupon__code::before {
  content: "\2713"; font-weight: 700; font-size: 0.95em;
}
.cart-coupon__remove {
  background: none; border: none; cursor: pointer;
  color: #6b7280; font-size: 0.82rem; text-decoration: underline;
  padding: 2px 4px;
}
.cart-coupon__remove:hover { color: #b42318; }
.cart-summary__couponcode { color: #1a7f37; font-weight: 600; }

@media (max-width: 880px) {
  .cart-page-grid { grid-template-columns: 1fr; }
}

/* =====================================================================
   Checkout — Order-Card style address form
   Layout: white cards with a red-titled section header.
   Stacked single-column inputs (First/Last Name as a 2-col row);
   ALL-CAPS gray labels, red asterisks for required, red italic helpers.
   ===================================================================== */
.ck-back-link {
  display: inline-flex; align-items: center; gap: 4px;
  margin: 4px 0 18px;
  font-size: 1rem; font-weight: 700;
  color: #222; text-decoration: none;
}
.ck-back-link:hover { color: var(--color-primary, #c92c2c); }
.ck-back-chev { font-size: 1.3rem; line-height: 1; transform: translateY(-1px); }

.ck-form { display: flex; flex-direction: column; gap: 22px; }
.ck-card {
  background: #fff;
  border: 1px solid #e5e7eb;
  border-radius: 6px;
  padding: 24px 28px 28px;
  box-shadow: 0 1px 2px rgba(0,0,0,0.03);
}
.ck-card-title {
  margin: 0 0 18px;
  padding: 0 0 12px;
  font-size: 1.15rem; font-weight: 700;
  color: #c92c2c;
  border-bottom: 1px solid #e5e7eb;
}

/* Header row that pairs a card title with its "saved address" picker
   (one for shipping, one for billing). The picker sits inline at the
   right edge on wide screens and stacks below the title on narrow
   screens. When the picker has no addresses to show, .ck-card-head-picker
   is set [hidden] by bindSavedAddresses() and only the title remains. */
.ck-card-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin: 0 0 18px;
  padding: 0 0 12px;
  border-bottom: 1px solid #e5e7eb;
}
.ck-card-head .ck-card-title {
  margin: 0;
  padding: 0;
  border-bottom: 0;
  flex: 1 1 auto;
}
.ck-card-head-picker {
  flex: 0 1 280px;
  min-width: 200px;
}
.ck-card-head-picker .ck-saved-pick {
  width: 100%;
  height: 36px;
  padding: 6px 28px 6px 10px;
  font-size: 13px;
  border-radius: 4px;
}
@media (max-width: 520px) {
  .ck-card-head-picker { flex: 1 1 100%; }
}

.ck-row { margin-bottom: 14px; }
.ck-row:last-child { margin-bottom: 0; }
.ck-row-2col {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px 18px;
}
.ck-row-2col > div { display: flex; flex-direction: column; }

.ck-label {
  display: block;
  margin: 0 0 6px;
  font-size: 0.72rem; font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #5a6068;
}
.ck-label-with-tip {
  display: flex;
  align-items: center;
  margin: 0 0 6px;
}
.ck-label-with-tip .ck-label { margin: 0; }
.ck-label-with-tip .info-bang::after {
  width: max-content;
  max-width: min(320px, 80vw);
  white-space: normal;
}
/* Keep the same tooltip visible while the buyer is filling the associated
   email/phone field, not only while hovering the small info badge. */
.ck-row:focus-within > .ck-label-with-tip .info-bang::after,
.ck-row:focus-within > .ck-label-with-tip .info-bang::before {
  opacity: 1;
}
.ck-req { color: #c92c2c; margin-inline-start: 2px; }

.ck-input {
  display: block; width: 100%;
  padding: 11px 14px;
  font-size: 0.95rem;
  font-family: inherit;
  color: #1f2937;
  background: #fafafa;
  border: 1px solid #d8dcdf;
  border-radius: 4px;
  transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
  box-sizing: border-box;
}
.ck-input:focus {
  outline: none;
  background: #fff;
  border-color: #c92c2c;
  box-shadow: 0 0 0 3px rgba(201, 44, 44, 0.10);
}
.ck-input::placeholder { color: #b0b6bd; }
.ck-input--mt { margin-top: 8px; }

.ck-select {
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'><path d='M1 1l5 5 5-5' fill='none' stroke='%235a6068' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 12px 8px;
  padding-inline-end: 38px;
}

.ck-help-text {
  margin: 6px 0 0;
  font-size: 0.8rem;
  font-style: italic;
  color: #c92c2c;
}

/* ----- Address checks (see wireAddressChecks() in products/checkout.html) -----
   Two ranks of message under an address field, and the visual difference is
   load-bearing: red is a verdict that blocks the order, amber is advice the
   buyer is free to ignore. Never style them alike — a buyer who cannot tell
   which one is stopping them will abandon the checkout. */
.ck-field-error,
.ck-field-hint {
  margin: 6px 0 0;
  font-size: 0.8rem;
  line-height: 1.45;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
}
.ck-field-error {
  color: #c92c2c;
  font-weight: 500;
}
.ck-field-hint {
  color: #92400e;
}
.ck-input--invalid {
  border-color: #c92c2c !important;
  background: #fff7f7;
}
/* Brief highlight when we fill a field on the buyer's behalf, so a value
   appearing without them typing it doesn't go unnoticed. */
.ck-input--filled {
  animation: ckFilled 1.2s var(--ease-soft) both;
}
@keyframes ckFilled {
  0%   { background: #ecfdf5; border-color: #10b981; }
  100% { background: #fff;    border-color: var(--color-border); }
}
@media (prefers-reduced-motion: reduce) {
  .ck-input--filled { animation: none; }
}

/* "Same as shipping address" toggle row + the new "save this address
   to my account" toggles. We pin `display:none` for the [hidden] state
   because `display:flex` would otherwise win the cascade over the
   default UA `[hidden]{display:none}` rule and the row would still
   show for guests. */
.ck-check-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 14px;
  font-size: 0.95rem;
  color: #1f2937;
  cursor: pointer;
  user-select: none;
}
.ck-check-row[hidden] { display: none !important; }
.ck-check-row input[type="checkbox"] {
  width: 18px; height: 18px;
  accent-color: #c92c2c;
  cursor: pointer;
  flex-shrink: 0;
}
.ck-billing-fields[hidden] { display: none !important; }
.ck-billing-fields {
  padding-top: 8px;
  border-top: 1px dashed #e5e7eb;
}

/* ============================================================
   PEUI — Site-wide prompt system (replaces native alert/confirm)
   ------------------------------------------------------------
   Three primitives, all wired up in js/main.js → window.PEUI:

     PEUI.toast(msg, opts)   → non-blocking corner notification
                                 (opts.type = info | success | warning | error)
     PEUI.alert(msg, opts)   → blocking modal with a single OK button
                                 (returns Promise<void>)
     PEUI.confirm(msg, opts) → blocking modal with OK + Cancel
                                 (returns Promise<boolean>)

   Replaces window.alert() / window.confirm() throughout the
   storefront so we never show the browser's native chrome-style
   dialog (which is jarring next to our branded UI and on mobile
   loses translations + truncates long strings). The same red
   accent + rounded-card visual language used by .btn-primary
   keeps this in-keeping with the rest of the site.
============================================================ */
.peui-toast-stack {
  position: fixed;
  bottom: 24px;
  inset-inline-end: 24px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  z-index: 10000;
  pointer-events: none;
  max-width: calc(100vw - 48px);
}
.peui-toast {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  min-width: 240px;
  max-width: 380px;
  padding: 12px 16px;
  border-radius: 10px;
  font-size: 0.92rem;
  line-height: 1.45;
  color: #fff;
  background: #0f172a;
  box-shadow: 0 12px 32px rgba(15, 23, 42, 0.18);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.18s ease, transform 0.18s ease;
  pointer-events: auto;
  word-break: break-word;
}
.peui-toast.is-visible { opacity: 1; transform: translateY(0); }
.peui-toast--info    { background: #0f172a; }
.peui-toast--success { background: #16a34a; }
.peui-toast--warning { background: #d97706; }
.peui-toast--error   { background: #c92c2c; }
.peui-toast__icon {
  flex-shrink: 0;
  font-weight: 700;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.18);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.78rem;
  margin-top: 1px;
}
.peui-toast__msg { flex: 1; }
.peui-toast__close {
  margin-inline-start: 4px;
  background: none;
  border: 0;
  color: inherit;
  opacity: 0.7;
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
  padding: 0 2px;
}
.peui-toast__close:hover { opacity: 1; }

.peui-modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 42, 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10001;
  opacity: 0;
  transition: opacity 0.16s ease;
  padding: 24px;
}
.peui-modal-overlay.is-visible { opacity: 1; }
.peui-modal {
  background: #fff;
  border-radius: 12px;
  padding: 24px 24px 20px;
  width: 100%;
  max-width: 420px;
  box-shadow: 0 24px 48px rgba(15, 23, 42, 0.22);
  transform: translateY(6px) scale(0.98);
  transition: transform 0.16s ease;
}
.peui-modal-overlay.is-visible .peui-modal { transform: translateY(0) scale(1); }
.peui-modal__title {
  margin: 0 0 8px;
  font-size: 1.05rem;
  font-weight: 600;
  color: #1f2937;
}
.peui-modal__body {
  margin: 0 0 18px;
  font-size: 0.95rem;
  line-height: 1.5;
  color: #374151;
  white-space: pre-line;
  word-break: break-word;
}
.peui-modal__actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}
.peui-modal__btn {
  appearance: none;
  border: 1px solid transparent;
  padding: 8px 18px;
  border-radius: 6px;
  font-size: 0.92rem;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
.peui-modal__btn--cancel {
  background: #fff;
  border-color: #d1d5db;
  color: #374151;
}
.peui-modal__btn--cancel:hover { background: #f3f4f6; }
.peui-modal__btn--ok {
  background: #c92c2c;
  color: #fff;
}
.peui-modal__btn--ok:hover { background: #b22424; }
.peui-modal__btn--ok.peui-modal__btn--neutral {
  background: #0f172a;
}
.peui-modal__btn--ok.peui-modal__btn--neutral:hover { background: #1e293b; }
body.peui-modal-open { overflow: hidden; }

.ck-test-note {
  margin: 0 0 18px;
  padding: 14px 16px;
  background: #fff8e1;
  border: 1px solid #f3d27a;
  border-radius: 4px;
  font-size: 0.9rem;
  color: #6b5400;
}
.ck-test-note p { margin: 0; }
.ck-test-note strong { color: #8a4a00; }

.ck-submit {
  display: block; width: 100%;
  padding: 14px 24px;
  font-size: 1rem; font-weight: 700;
  letter-spacing: 0.02em;
}

/* ═════════════════════════════════════════════════════════════════════
   Payment selector — Stripe + PayPal radio cards + provider UIs
   ═════════════════════════════════════════════════════════════════════
   Visual model copied from Stripe Checkout's "split" layout: a stack
   of radio rows where each selected row "owns" the mount slot below
   it. The unselected provider's mount slot stays hidden so the page
   doesn't have to reflow when the buyer switches between methods. */
.pe-pay { padding: 22px 24px; }
.pe-pay .ck-card-head { margin-bottom: 18px; }
.pe-pay .ck-card-head h3 { margin: 0 0 4px; font-size: 1.05rem; }
.pe-pay .ck-card-sub {
  margin: 0; font-size: 0.82rem; color: #64748b; line-height: 1.5;
}

.pe-pay__row {
  display: grid;
  grid-template-columns: 22px 1fr;
  gap: 12px;
  padding: 14px 16px;
  margin-bottom: 10px;
  border: 1.5px solid #e2e8f0;
  border-radius: 10px;
  cursor: pointer;
  transition: border-color .15s ease, background .15s ease;
  background: #fff;
}
.pe-pay__row:hover { border-color: #cbd5e1; }
.pe-pay__row input[type="radio"] {
  margin: 4px 0 0; accent-color: #1d4ed8; cursor: pointer;
  width: 18px; height: 18px;
}
.pe-pay__row:has(input:checked) {
  border-color: #1d4ed8;
  background: #f5f9ff;
  box-shadow: 0 0 0 3px rgba(29, 78, 216, 0.08);
}
.pe-pay__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
}
.pe-pay__title { font-weight: 700; font-size: 0.95rem; color: #0f172a; }
.pe-pay__hint  {
  /* The label uses a 2-col grid (22px radio | 1fr body).
     Without an explicit grid-column the hint falls back into
     the next available cell — column 1 row 2 — which is only
     22px wide and forces every Chinese character onto its own
     line, blowing the row up to 400-500 px tall. Pinning the
     hint to column 2 keeps it aligned under the head text
     where it visually belongs. */
  grid-column: 2 / -1;
  margin: 4px 0 0;
  font-size: 0.8rem;
  color: #64748b;
  line-height: 1.45;
}

/* Card-brand chips on the right side of each row header.
   ─────────────────────────────────────────────────────────
   Slightly upsized from the original 0.65rem/3px to feel
   premium next to the radio label. Soft shadow gives them
   "embossed credit-card sticker" depth without being noisy.
   --pp-badge is the slate-coloured generic chip used by
   PayPal's row (e.g. "Buyer Protection") — neutral so the
   row reads as text, not as a logo collage. */
.pe-pay__brands { display: inline-flex; gap: 5px; flex-wrap: wrap; align-items: center; }
.pe-pay__brand {
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.03em;
  padding: 4px 8px; border-radius: 5px;
  background: #f1f5f9; color: #475569; line-height: 1;
  box-shadow: 0 1px 0 rgba(15, 23, 42, 0.04);
  white-space: nowrap;
}
.pe-pay__brand--visa     { background: #1a1f71; color: #fff; }
.pe-pay__brand--mc       { background: #eb001b; color: #fff; }
.pe-pay__brand--amex     { background: #006fcf; color: #fff; }
.pe-pay__brand--pp-badge {
  background: #eef2ff; color: #1e3a8a;
  border: 1px solid #c7d2fe;
  text-transform: none; letter-spacing: 0;
  font-weight: 600;
}
/* "See Design - Pay Later" badge — amber chip signals "not paid yet"
   without looking alarming. Matches the same neutral pp-badge shape
   so the three rows visually balance. */
.pe-pay__brand--later {
  background: #fef3c7; color: #92400e;
  border: 1px solid #fcd34d;
  text-transform: none; letter-spacing: 0;
  font-weight: 600;
}

/* Compact variant — used when the payment card lives inside the
   narrow (~420px) right-column order summary. Tightens horizontal
   padding so the split Expiration/Security row still fits two
   columns instead of wrapping, and reduces the row gaps so the
   whole card doesn't dominate the viewport when "card" is selected. */
.pe-pay--compact { padding: 16px 18px; }
.pe-pay--compact .ck-card-head { margin-bottom: 14px; }
.pe-pay--compact .pe-pay__row { padding: 12px 14px; }
.pe-pay--compact .pe-pay__mount { padding: 12px 14px; }


/* Provider mount slot — sits below the row that owns it. */
.pe-pay__mount {
  margin: -2px 0 14px;
  padding: 14px 16px;
  background: #fafbfc;
  border: 1px solid #e2e8f0;
  border-top: none;
  border-radius: 0 0 10px 10px;
}
.pe-pay__mount[hidden] { display: none; }

/* Inline error banner under the place-order button. */
.pe-pay__error {
  margin: 12px 0 0;
  padding: 10px 14px;
  background: #fef2f2;
  border: 1px solid #fca5a5;
  border-radius: 8px;
  color: #991b1b;
  font-size: 0.875rem;
}

/* ───── Stripe Elements look-alike (mock + real wrapper) ───── */
.pe-pay-card { display: flex; flex-direction: column; gap: 12px; }
.pe-pay-card__row {
  display: flex; flex-direction: column; gap: 6px;
}
.pe-pay-card__row--split {
  display: grid; grid-template-columns: 1fr 1fr; gap: 10px;
}
@media (max-width: 540px) {
  .pe-pay-card__row--split { grid-template-columns: 1fr; }
}
/* Stripe Element mount points — Stripe injects an <iframe> here
   whose own input field handles styling internally. We just need
   the wrapper to look like our plain <input> (border, padding,
   height) so the Card Number / Expiration / CVC fields visually
   match the Card Holder Name field above. The .StripeElement* hooks
   are Stripe's documented stable class names. */
.pe-pay-card__stripe-mount {
  height: 42px;
  padding: 12px;
  border: 1px solid #cbd5e1;
  border-radius: 8px;
  background: #fff;
  box-sizing: border-box;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.pe-pay-card__stripe-mount.StripeElement--focus {
  border-color: #635bff;
  box-shadow: 0 0 0 3px rgba(99, 91, 255, 0.18);
}
.pe-pay-card__stripe-mount.StripeElement--invalid {
  border-color: #dc2626;
  box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.12);
}
.pe-pay-card__stripe-mount.StripeElement--complete {
  border-color: #16a34a;
}

/* ── Stripe wallet (Apple Pay / Google Pay / Link) ────────────────
   The Stripe-rendered button comes with its own black/white pill
   styling; we just give the mount node room + a divider beneath. */
.pe-pay-card__wallet {
  min-height: 0;
  margin-bottom: 0;
}
.pe-pay-card__wallet:not(:empty) {
  margin-bottom: 14px;
}
.pe-pay-card__divider {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 16px;
  color: #94a3b8;
  font-size: 0.78rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.pe-pay-card__divider::before,
.pe-pay-card__divider::after {
  content: '';
  flex: 1 1 auto;
  height: 1px;
  background: #e2e8f0;
}
.pe-pay-card__divider[hidden] { display: none; }

.pe-pay-card__label {
  display: flex; flex-direction: column; gap: 6px;
  font-size: 0.78rem; color: #475569; font-weight: 500;
}
.pe-pay-card__label > span { font-size: 0.78rem; color: #475569; }
.pe-pay-card__label input {
  height: 42px; padding: 0 12px;
  border: 1px solid #cbd5e1; border-radius: 8px;
  background: #fff; font-size: 0.95rem; color: #0f172a;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.pe-pay-card__label input:focus {
  outline: none; border-color: #635bff;
  box-shadow: 0 0 0 3px rgba(99, 91, 255, 0.18);
}
.pe-pay-card__num {
  position: relative;
}
.pe-pay-card__num input { width: 100%; padding-inline-end: 72px; }
.pe-pay-card__brand {
  position: absolute; inset-inline-end: 12px; top: 50%; transform: translateY(-50%);
  font-size: 0.7rem; font-weight: 700; color: #635bff;
  letter-spacing: 0.04em;
}
.pe-pay-card__note {
  margin: 0; font-size: 0.75rem; color: #64748b; line-height: 1.5;
}
/* Real-mode wrapper inherits all styles from .pe-pay-card above —
   the per-field Stripe mounts get their borders from
   .pe-pay-card__stripe-mount, so no extra rules needed here. */

/* See Design - Pay Later explainer panel. Sits where the card form
   or PayPal buttons would, but renders only an information block
   (no input controls) since the buyer commits to the order without
   paying yet. Soft amber tint cues "pending — action required later"
   without being alarming. */
.pe-pay-later {
  display: flex; flex-direction: column; gap: 8px;
}
.pe-pay-later__note {
  margin: 0;
  padding: 12px 14px;
  background: #fffbeb;
  border: 1px solid #fcd34d;
  border-radius: 8px;
  font-size: 0.85rem;
  color: #78350f;
  line-height: 1.5;
}

/* ───── PayPal Smart-Buttons look-alike (mock + real wrapper) ───── */
.pe-pay-paypal {
  display: flex; flex-direction: column; gap: 10px;
}
.pe-pay-paypal__btn {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  height: 45px; border: none; border-radius: 999px;
  cursor: pointer; font-size: 0.95rem; font-weight: 700;
  letter-spacing: 0.01em;
  transition: filter .15s ease, transform .1s ease;
}
.pe-pay-paypal__btn--gold {
  background: #ffc439; color: #003087;
}
.pe-pay-paypal__btn--black {
  background: #2c2e2f; color: #ffc439;
}
.pe-pay-paypal__btn:hover  { filter: brightness(1.05); }
.pe-pay-paypal__btn:active { transform: scale(0.99); }
.pe-pay-paypal__btn--done {
  opacity: 0.6; pointer-events: none;
}
.pe-pay-paypal__logo { font-family: 'Inter', sans-serif; font-weight: 800; }
.pe-pay-paypal__logo b { font-style: italic; font-weight: 800; }
.pe-pay-paypal__logo i {
  font-style: italic; font-weight: 800; color: #009cde;
  margin-inline-start: 1px;
}
.pe-pay-paypal__logo--inv i { color: #ffc439; }
.pe-pay-paypal__btn--black small {
  font-size: 0.7rem; font-weight: 600; color: #fff;
  text-transform: uppercase; letter-spacing: 0.05em;
}
.pe-pay-paypal__status {
  padding: 10px 14px; border-radius: 8px;
  background: #ecfdf5; border: 1px solid #6ee7b7;
  color: #065f46; font-size: 0.85rem;
}
.pe-pay-paypal--real #pe-paypal-buttons {
  min-height: 100px;
}

@media (max-width: 560px) {
  .ck-card { padding: 18px 16px 22px; }
  .ck-row-2col { grid-template-columns: 1fr; }
}

/* Imprint method banner — info card at top of Step 4 showing what
   printing technique applies to the current Step 1 selection */
.imprint-method-banner {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 0 auto 14px;
  padding: 8px 16px;
  background: var(--color-teal-soft);
  border: 1px solid var(--color-teal-line);
  border-radius: 999px;
  color: var(--color-teal-dark);
  font-size: 0.86rem;
  align-self: center;
}
.imprint-method-banner svg { color: var(--color-teal); flex-shrink: 0; }
.imprint-method-banner strong { font-weight: 700; margin-inline-end: 4px; }

/* Upload notes box (Preferred / Accepted / Guide link) */
.upload-note {
  margin: 10px auto 0;
  max-width: 880px;
  padding: 12px 16px;
  background: var(--color-teal-soft);
  border: 1px solid var(--color-teal-line);
  border-radius: 10px;
  font-size: 0.84rem;
  color: var(--color-text);
}
.upload-note p { margin: 4px 0; line-height: 1.5; }
.upload-note p:first-child { margin-top: 0; }
.upload-note p:last-child { margin-bottom: 0; }
.upload-note strong { font-weight: 700; }
.upload-note a {
  color: var(--color-teal-dark);
  font-weight: 700;
  text-decoration: none;
  border-bottom: 1px solid currentColor;
}
.upload-note a:hover { color: var(--color-teal); }

/* Compact input for text */
.text-field {
  display: block;
  width: 100%;
  padding: 11px 14px;
  border: 1.5px solid var(--color-border);
  border-radius: 10px;
  font-family: inherit;
  font-size: 0.95rem;
  color: var(--color-text);
  background: #fff;
  transition: var(--transition);
}
.text-field:focus { outline: none; border-color: var(--color-accent); box-shadow: 0 0 0 3px var(--color-accent-soft); }

/* Select field */
.select-field {
  display: block;
  width: 100%;
  padding: 10px 14px;
  border: 1.5px solid var(--color-border);
  border-radius: 10px;
  font-family: inherit;
  font-size: 0.92rem;
  color: var(--color-text);
  background: #fff;
  cursor: pointer;
}
.select-field:focus { outline: none; border-color: var(--color-accent); }

/* ============================================================
   SWATCH-AWARE <SELECT> (Step 4 "Select Imprint Color 1/2/3")
   Replaces native <select> with a coloured-tile grid dropdown
   matching the internal Wristband configurator UX (reference
   image 1). "Custom Color" opens a PMS modal (image 2).
   ============================================================ */
.swatch-select { position: relative; width: 100%; max-width: 640px; margin: 0 auto; text-align: start; }
.swatch-select > select[data-swatch-select] { position: absolute; opacity: 0; pointer-events: none; height: 0; width: 0; }
.swatch-trigger {
  display: flex; align-items: center; gap: 10px;
  width: 100%;
  padding: 10px 14px;
  border: 1.5px solid var(--color-border);
  border-radius: 10px;
  background: #fff;
  font: inherit;
  color: var(--color-text);
  cursor: pointer;
  text-align: start;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.swatch-trigger:hover { border-color: var(--color-accent); }
.swatch-trigger:focus-visible { outline: none; border-color: var(--color-accent); box-shadow: 0 0 0 3px var(--color-accent-soft); }
.swatch-select.is-open .swatch-trigger { border-color: var(--color-accent); box-shadow: 0 0 0 3px var(--color-accent-soft); }
.swatch-trigger .swatch-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.swatch-trigger .swatch-name.is-placeholder { color: var(--color-text-soft, #9ca3af); }
.swatch-trigger .swatch-caret { opacity: 0.55; font-size: 0.85em; transition: transform .15s ease; margin-inline-start: auto; }
.swatch-select.is-open .swatch-trigger .swatch-caret { transform: rotate(180deg); }
.swatch-menu {
  position: absolute;
  inset-inline-start: 0; inset-inline-end: 0; top: calc(100% + 6px);
  z-index: 80;
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: 12px;
  box-shadow: 0 14px 36px rgba(15, 23, 42, 0.18);
  padding: 12px;
  max-height: 460px;
  overflow-y: auto;
}
.swatch-menu::-webkit-scrollbar { width: 8px; }
.swatch-menu::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 4px; }
.swatch-menu__virt { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.swatch-virt-btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 12px;
  background: #f8fafc;
  border: 1px solid #e2e8f0;
  border-radius: 999px;
  font: inherit;
  color: #1f2937;
  cursor: pointer;
}
.swatch-virt-btn:hover { background: var(--color-accent-soft, #e6fffb); border-color: var(--color-accent); }
.swatch-virt-btn.is-selected { background: var(--color-accent-soft, #e6fffb); border-color: var(--color-accent); font-weight: 600; }
.swatch-chip {
  width: 18px; height: 18px;
  flex-shrink: 0;
  border-radius: 4px;
  border: 1px solid rgba(15, 23, 42, 0.18);
  display: inline-block;
}
.swatch-chip.is-light { border-color: rgba(15, 23, 42, 0.32); }
.swatch-chip.is-virtual {
  background: repeating-linear-gradient(45deg, #f1f5f9 0 4px, #e2e8f0 4px 8px);
  border-color: rgba(15, 23, 42, 0.16);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 11px; color: #475569;
}
.swatch-chip.is-virtual[data-glyph]::after { content: attr(data-glyph); line-height: 1; }
.swatch-tiles {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
}
@media (max-width: 700px) { .swatch-tiles { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 480px) { .swatch-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.swatch-tile {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  min-height: 64px;
  padding: 10px 6px;
  border: 1px solid rgba(15, 23, 42, 0.12);
  border-radius: 8px;
  cursor: pointer;
  font: inherit;
  line-height: 1.15;
  text-align: center;
  transition: transform .12s ease, box-shadow .12s ease, outline-color .12s ease;
  outline: 2px solid transparent;
}
.swatch-tile.is-light { border-color: rgba(15, 23, 42, 0.32); }
.swatch-tile:hover { transform: translateY(-1px); box-shadow: 0 8px 18px rgba(15, 23, 42, 0.18); }
.swatch-tile.is-selected { outline-color: var(--color-accent, #00b3a4); outline-offset: 2px; box-shadow: 0 8px 18px rgba(15, 23, 42, 0.18); }
.swatch-tile__label { font-size: 11.5px; font-weight: 700; letter-spacing: 0.04em; }
.swatch-tile__pms   { font-size: 10.5px; opacity: 0.85; }

/* ============================================================
   PMS modal — opens when "Custom Color" tile is clicked.
   Mirrors reference image 2: searchable PMS code grid.
   ============================================================ */
body.has-pms-modal { overflow: hidden; }
.pms-modal {
  position: fixed; inset: 0;
  z-index: 9999;
  background: rgba(15, 23, 42, 0.55);
  display: flex; align-items: center; justify-content: center;
  padding: 16px;
}
.pms-modal[hidden] { display: none !important; }
.pms-modal__panel {
  width: min(880px, 100%);
  max-height: 85vh;
  display: flex; flex-direction: column;
  background: #fff;
  border-radius: 14px;
  box-shadow: 0 24px 64px rgba(15, 23, 42, 0.3);
  overflow: hidden;
}
.pms-modal__head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 18px;
  border-bottom: 1px solid #e2e8f0;
}
.pms-modal__title { margin: 0; font-size: 1rem; font-weight: 600; color: #0f172a; }
.pms-modal__close, .pms-modal__close-text {
  background: none; border: 0; cursor: pointer; color: #475569; font: inherit;
}
.pms-modal__close { font-size: 1.4rem; line-height: 1; padding: 4px 10px; }
.pms-modal__close:hover, .pms-modal__close-text:hover { color: #0f172a; }
.pms-modal__search {
  padding: 12px 18px;
  border-bottom: 1px solid #e2e8f0;
}
.pms-modal__search input {
  width: 100%;
  padding: 9px 14px;
  border: 1px solid #cbd5e1;
  border-radius: 999px;
  background: #f8fafc;
  font: inherit;
  color: #0f172a;
}
.pms-modal__search input:focus { outline: none; border-color: var(--color-accent); background: #fff; box-shadow: 0 0 0 3px var(--color-accent-soft); }
.pms-modal__body { flex: 1; overflow-y: auto; padding: 14px 18px; }
.pms-modal__loading, .pms-modal__empty { color: #64748b; padding: 24px; text-align: center; font-size: 0.92rem; }
/* Load failure, kept visually distinct from "nothing matched your search" so
   the two are never mistaken for each other. */
.pms-modal__error { color: #b91c1c; padding: 24px; text-align: center; font-size: 0.92rem; }
.pms-modal__grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 8px;
}
@media (max-width: 700px) { .pms-modal__grid { grid-template-columns: repeat(5, minmax(0, 1fr)); } }
@media (max-width: 480px) { .pms-modal__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.pms-tile {
  display: flex; align-items: flex-end; justify-content: center;
  min-height: 72px;
  padding: 6px;
  border: 1px solid rgba(15, 23, 42, 0.12);
  border-radius: 6px;
  cursor: pointer;
  color: #0f172a;
  font: inherit;
  text-align: center;
  transition: transform .1s ease, box-shadow .1s ease;
}
.pms-tile:hover { transform: translateY(-1px); box-shadow: 0 6px 14px rgba(15, 23, 42, 0.2); }
.pms-tile__label {
  background: rgba(255, 255, 255, 0.92);
  color: #0f172a;
  font-size: 11px;
  font-weight: 600;
  padding: 2px 6px;
  border-radius: 4px;
  line-height: 1.2;
}
.pms-modal__foot {
  padding: 12px 18px;
  border-top: 1px solid #e2e8f0;
  text-align: start;
}

/* ============================================================
   STEP-BLOCK BEAUTIFY + CENTER-ALIGN ALL OPTIONS
   (Override layer — applies on top of the base layouts above.)
   ============================================================ */

/* Inner panel — softer card with gradient bg and bigger breathing room */
.step-block {
  background: linear-gradient(180deg, #ffffff 0%, #fafbff 100%);
  border: 1px solid var(--color-border, #e5e7eb);
  border-radius: 16px;
  padding: 26px 28px;
  margin-bottom: 18px;
  box-shadow: 0 1px 2px rgba(15,23,42,0.04), 0 2px 12px rgba(15,23,42,0.02);
  transition: box-shadow 240ms ease, border-color 240ms ease, transform 240ms ease;
}
.step-block:hover {
  box-shadow: 0 4px 16px rgba(15,23,42,0.06), 0 2px 4px rgba(15,23,42,0.04);
  border-color: var(--color-border-strong, #d1d5db);
}
.step-block.is-touched {
  border-color: var(--color-accent);
  box-shadow: 0 0 0 1px var(--color-accent-soft, rgba(37,99,235,0.15)),
              0 4px 20px rgba(37,99,235,0.08);
}

/* Step header — centered title block with the number badge above */
/* Step header — full-width cyan strip with horizontal navy pill badge.
   The head bleeds to the step-block edges by using negative margins. */
.step-block-head {
  display: flex;
  flex-direction: row;
  align-items: center;
  text-align: start;
  margin: -26px -28px 20px;
  padding: 14px 22px;
  background: linear-gradient(180deg, var(--color-step-strip-top) 0%, var(--color-step-strip-bot) 100%);
  border-radius: 14px 14px 0 0;
  border-bottom: 1px solid rgba(15, 23, 42, 0.07);
  gap: 14px;
}
.step-num-big {
  width: auto;
  height: 32px;
  min-width: 78px;
  padding: 0 12px;
  border-radius: 6px;
  background: linear-gradient(180deg, #1e3a8a 0%, #172554 100%);
  color: #fff;
  display: inline-flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 6px;
  font-weight: 900;
  font-size: 1.05rem;
  line-height: 1;
  letter-spacing: 0;
  transform: none;
  position: relative;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.14) inset,
    0 -1px 0 rgba(0, 0, 0, 0.35) inset,
    0 2px 6px rgba(15, 23, 42, 0.25);
  font-variant-numeric: tabular-nums;
}
.step-num-big::before {
  content: 'STEP';
  display: inline;
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  font-weight: 800;
  opacity: 1;
  line-height: 1;
  margin-bottom: 0;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.35);
}
/* Touched (selected) state — keep navy family, shift to lighter blue */
.step-block.is-touched .step-num-big {
  background: linear-gradient(180deg, #2563eb 0%, #1e40af 100%);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.18) inset,
    0 -1px 0 rgba(0, 0, 0, 0.35) inset,
    0 2px 6px rgba(30, 64, 175, 0.30);
}
.step-block-title { flex: 1 1 auto; text-align: start; min-width: 0; }
.step-block-title h3 {
  font-size: 1.05rem;
  font-weight: 700;
  margin: 0;
  letter-spacing: 0;
  color: var(--color-text);
}
.step-block-title .step-help {
  margin: 4px 0 0;
  max-width: none;
  color: var(--color-text-muted);
  font-size: 0.82rem;
  line-height: 1.45;
  text-align: start;
}
.step-block-optional {
  display: inline-block;
  margin-inline-start: 10px;
  padding: 2px 10px;
  background: var(--color-surface-alt, #f1f5f9);
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  vertical-align: middle;
}

/* Body — center every option grid / row / list */
.step-block-body { display: flex; flex-direction: column; align-items: stretch; }
.step-block-body .subrow { text-align: center; }
.step-block-body .subrow-label {
  display: block;
  text-align: center;
  font-weight: 600;
  letter-spacing: 0.01em;
  margin-bottom: 10px;
}
.step-block-body .subrow-help {
  text-align: center;
  margin-inline-start: auto;
  margin-inline-end: auto;
  max-width: 640px;
}
/* The rules above declare `display`, which outranks the UA's
   `[hidden] { display: none }`, so a configurator that hides a heading per
   style (see NO_PRINTING_HEADING in luggage-tags.config.js) would set the
   attribute and see the text stay put. */
.step-block-body .subrow-label[hidden],
.step-block-body .subrow-help[hidden] { display: none !important; }

/* All grids inside Step bodies — switch to FLEX so short rows actually
   center (CSS grid auto-fill keeps phantom empty columns that left-align
   the items). Each child gets a fixed width so cards stay uniform. */
.step-block-body .tile-grid,
.step-block-body .option-grid,
.step-block-body .size-grid,
.step-block-body .qty-tiers,
.step-block-body .font-list,
.step-block-body .attach-grid,
.step-block-body .swatch-grid,
.step-block-body .pill-row,
.step-block-body .toggle-group,
.step-block-body .swatch-tabs {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: stretch;
  gap: 12px;
  margin-inline-start: auto;
  margin-inline-end: auto;
}
.step-block-body .tile-grid       > * { flex: 0 0 230px; }
.step-block-body .option-grid     > * { flex: 0 0 200px; }
.step-block-body .size-grid       > * { flex: 0 0 120px; }
.step-block-body .qty-tiers       > * { flex: 0 0 130px; }
.step-block-body .font-list       > * { flex: 0 0 200px; }
.step-block-body .attach-grid     > * { flex: 0 0 230px; }
.step-block-body .swatch-grid     > .swatch { flex: 0 0 100px; }
/* Override for the "with-qty" variant which uses a wider horizontal card. */
.step-block-body .swatch-grid--qty > .swatch--with-qty { flex: 0 0 168px; }
.step-block-body .pms-grid-inline.swatch-grid--qty > .swatch--with-qty { flex: 0 0 150px; }
.step-block-body .pill-row        > * { flex: 0 0 auto; }
.step-block-body .toggle-group    > * { flex: 0 0 auto; }
.step-block-body .swatch-tabs     > * { flex: 0 0 auto; }

.step-block-body .swatch-grid { max-width: 1100px; gap: 14px 12px; }
.step-block-body .attach-grid { max-width: 1100px; }

/* Text inputs and selects — give them a sensible max-width so they don't
   stretch edge-to-edge on wide screens, and center them */
.step-block-body .text-field,
.step-block-body .select-field {
  max-width: 520px;
  margin-inline-start: auto;
  margin-inline-end: auto;
}

/* Upload zone */
.step-block-body .upload-zone {
  max-width: 640px;
  margin-inline-start: auto;
  margin-inline-end: auto;
}

/* Side blocks inside Step 4 (Front Side / Back Side) */
.side-block {
  max-width: 720px;
  margin-inline-start: auto;
  margin-inline-end: auto;
}
.side-block .subrow-label { text-align: center; }

/* Tile cards / option cards — center inner content */
.tile-card, .option-card {
  text-align: center;
  align-items: center;
}

/* Slightly nicer hover transition for pill buttons */
.pill-btn {
  transition: transform 180ms ease, background 180ms ease,
              border-color 180ms ease, color 180ms ease,
              box-shadow 180ms ease;
}
.pill-btn:hover { transform: translateY(-1px); box-shadow: 0 4px 10px rgba(15,23,42,0.06); }
.pill-btn.is-selected {
  box-shadow: 0 6px 14px rgba(37,99,235,0.22);
}

/* Tile cards get a subtle lift on hover */
.tile-card { transition: var(--transition); }
.tile-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 24px rgba(15,23,42,0.08);
  border-color: var(--color-accent);
}
.tile-card.is-selected {
  box-shadow: 0 8px 24px rgba(37,99,235,0.18);
}

/* Refine the option-grid card visuals */
.option-card { text-align: center; }
.option-card .opt-title { display: block; font-weight: 600; }
.option-card .opt-meta {
  display: block;
  margin-top: 4px;
  font-size: 0.8rem;
  color: var(--color-text-muted);
}
.option-card:hover {
  border-color: var(--color-accent);
  background: var(--color-accent-soft, rgba(37,99,235,0.05));
}
.option-card.is-selected {
  border-color: var(--color-accent);
  background: var(--color-accent-soft, rgba(37,99,235,0.1));
  color: var(--color-accent-dark);
}

/* PMS open button — center it */
.pms-open-btn { margin-inline-start: auto; margin-inline-end: auto; display: flex; }

/* Improve attach-card hover/selected */
.attach-card {
  transition: transform 200ms ease, border-color 200ms ease, box-shadow 200ms ease;
}
.attach-card:hover { transform: translateY(-2px); }

/* Polish step-block on touch */
.step-block.is-touched .step-num-big {
  background: linear-gradient(180deg, #2563eb 0%, #1e40af 100%);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.18) inset,
    0 -1px 0 rgba(0, 0, 0, 0.35) inset,
    0 2px 6px rgba(30, 64, 175, 0.30);
}

/* Tab swatch (Popular / Custom) — make it look like proper tab bar */
/* Tab toggle (Customize Message / Upload Artwork, Popular / Custom)
   — pill-style toggle with teal accent matching the reference site. */
.swatch-tabs {
  display: flex;
  justify-content: center;
  gap: 14px;
  margin: 4px auto 18px;
  border-bottom: none !important;
  padding-bottom: 0;
  flex-wrap: wrap;
}
.swatch-tab {
  position: relative;
  padding: 9px 28px;
  background: #fff;
  border: 1.5px solid var(--color-teal, #4ea4ad);
  border-radius: 999px;
  margin: 0;
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--color-teal, #4ea4ad);
  cursor: pointer;
  font-family: inherit;
  min-width: 200px;
  transition: background-color 180ms ease, color 180ms ease, box-shadow 180ms ease, transform 180ms ease;
}
.swatch-tab:hover {
  background: #f0f8fa;
  color: var(--color-teal-dark, #3b8a93);
}
.swatch-tab.is-active {
  background: var(--color-teal, #4ea4ad);
  color: #fff;
  border-color: var(--color-teal, #4ea4ad);
  box-shadow: 0 4px 12px rgba(78, 164, 173, 0.30);
}

/* Increase the PMS modal swatch grid spacing for breathing room */
.pms-modal-grid { gap: 14px; }
.pms-modal-grid .swatch { font-size: 0.74rem; }
.pms-modal-grid .swatch-name { font-size: 0.72rem; }

/* Step nav (the floating step-jump rail at the top) — center its content */
.config-stepnav { justify-content: center; }
.config-stepnav .step-nav-item {
  flex: 0 0 auto;
  min-width: 64px;
}

/* Help text inside swatch panes — softer, italic */
.swatch-pane .subrow-help {
  font-style: italic;
  color: var(--color-text-muted);
  margin: 0 auto 14px;
  text-align: center;
}

/* ============================================================
   PMS inline search + scrollable swatch grid (Custom pane)
   ============================================================ */
.pms-inline-search {
  display: flex;
  align-items: center;
  gap: 12px;
  max-width: 560px;
  margin: 0 auto 16px;
}
.pms-inline-search input[type="search"] {
  flex: 1;
  padding: 11px 16px;
  font-size: 0.95rem;
  border: 1.5px solid var(--color-border-strong, #d1d5db);
  border-radius: 10px;
  background: #fff;
  font-family: inherit;
  transition: border-color 200ms ease, box-shadow 200ms ease;
}
.pms-inline-search input[type="search"]:focus {
  outline: none;
  border-color: var(--color-accent);
  box-shadow: 0 0 0 3px var(--color-accent-soft, rgba(37,99,235,0.15));
}
.pms-inline-count {
  flex: 0 0 auto;
  font-size: 0.85rem;
  color: var(--color-text-muted);
  font-weight: 600;
  white-space: nowrap;
}

/* Scroll wrapper so the 1,814-swatch grid doesn't dominate the page */
.pms-scroll-wrap {
  max-width: 1100px;
  margin: 0 auto;
  max-height: 640px;
  overflow-y: auto;
  background: var(--color-surface-alt, #f8f9fb);
  border: 1px solid var(--color-border, #e5e7eb);
  border-radius: 14px;
  padding: 16px 12px;
  /* Custom thin scrollbar */
  scrollbar-width: thin;
  scrollbar-color: var(--color-border-strong, #cbd5e1) transparent;
}
.pms-scroll-wrap::-webkit-scrollbar { width: 8px; }
.pms-scroll-wrap::-webkit-scrollbar-thumb {
  background: var(--color-border-strong, #cbd5e1);
  border-radius: 4px;
}
.pms-scroll-wrap::-webkit-scrollbar-thumb:hover {
  background: var(--color-text-muted);
}

/* Compact swatches inside the inline PMS grid so more fit per row */
.pms-grid-inline > .swatch { flex: 0 0 92px; min-height: 78px; padding: 8px 6px; }
.pms-grid-inline .swatch::before { width: 60px; height: 20px; }
.pms-grid-inline .swatch-name { font-size: 0.7rem; }
/* Restore horizontal qty-card layout inside the PMS grid (the rules
   above target the legacy single-pick swatch). */
.pms-grid-inline.swatch-grid--qty > .swatch--with-qty {
  min-height: 62px;
  padding: 0;
}
.pms-grid-inline.swatch-grid--qty .swatch--with-qty::before { display: none; }

/* ============================================================
   Clipart picker — image dropdown for the "Add Clipart" sub-rows
   ============================================================ */
.clipart-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 12px;
}

/* Wrapper that hosts the native <select> (hidden) + trigger + panel */
.clip-select {
  position: relative;
  display: inline-block;
  width: 250px;
}

/* Trigger looks like a select field but shows a thumbnail */
.clip-select-trigger {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 6px 36px 6px 8px;
  background: var(--color-surface, #fff);
  border: 1px solid var(--color-border, #e5e7eb);
  border-radius: 8px;
  font-size: 0.9rem;
  text-align: start;
  cursor: pointer;
  position: relative;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
  min-height: 44px;
}
.clip-select-trigger:hover { border-color: var(--color-border-strong, #cbd5e1); }
.clip-select.is-open .clip-select-trigger {
  border-color: var(--color-primary, #2563eb);
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.14);
}

/* Caret (small chevron, like a select) */
.clip-select-trigger .ct-caret {
  position: absolute;
  inset-inline-end: 12px;
  top: 50%;
  width: 8px;
  height: 8px;
  transform: translateY(-70%) rotate(45deg);
  border-inline-end: 2px solid var(--color-text-muted, #94a3b8);
  border-bottom: 2px solid var(--color-text-muted, #94a3b8);
  pointer-events: none;
}

/* Thumbnail tile on the left of the trigger */
.clip-select-trigger .ct-thumb {
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  border-radius: 4px;
  border: 1px solid var(--color-border, #e5e7eb);
  background: #fafbfd center / 80% no-repeat;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.clip-select-trigger .ct-thumb.is-placeholder {
  background: var(--color-surface-alt, #f8f9fb);
  border-style: dashed;
}
.clip-select-trigger .ct-thumb.is-placeholder::after {
  content: '+';
  font-size: 1rem;
  color: var(--color-text-muted, #94a3b8);
  line-height: 1;
}

/* Uploaded-file chip inside the trigger thumb (vector / non-preview files) */
.clip-select-trigger .ct-thumb.is-file {
  background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);
  border-color: var(--color-primary, #2563eb);
}
.clip-select-trigger .ct-ext {
  font-size: 0.6rem;
  font-weight: 700;
  color: var(--color-primary, #2563eb);
  letter-spacing: 0.04em;
  line-height: 1;
}

/* Label text on the right of the thumbnail */
.clip-select-trigger .ct-label {
  flex: 1;
  color: var(--color-text, #0f172a);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.clip-select-trigger .ct-label.is-placeholder {
  color: var(--color-text-muted, #94a3b8);
}

/* Dropdown panel (the image grid) */
.clip-select-panel {
  position: absolute;
  top: calc(100% + 4px);
  inset-inline-start: 0;
  z-index: 30;
  width: 380px;
  max-width: calc(100vw - 32px);
  background: #fff;
  border: 1px solid var(--color-border-strong, #cbd5e1);
  border-radius: 12px;
  box-shadow: 0 12px 32px rgba(15, 23, 42, 0.18);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: clipPanelIn 0.16s ease both;
}
.clip-select-panel[hidden] { display: none; }
@keyframes clipPanelIn {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}

.clip-select-search {
  padding: 8px;
  border-bottom: 1px solid var(--color-border, #e5e7eb);
  background: #fbfcfe;
}
.clip-select-search input {
  width: 100%;
  padding: 6px 10px;
  border: 1px solid var(--color-border, #e5e7eb);
  border-radius: 6px;
  font-size: 0.85rem;
  outline: none;
  transition: border-color 0.15s ease;
}
.clip-select-search input:focus { border-color: var(--color-primary, #2563eb); }

.clip-select-grid {
  flex: 1;
  overflow-y: auto;
  padding: 8px;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 4px;
  max-height: 320px;
  scrollbar-width: thin;
  scrollbar-color: #cbd5e1 transparent;
}
.clip-select-grid::-webkit-scrollbar { width: 8px; }
.clip-select-grid::-webkit-scrollbar-thumb {
  background: #cbd5e1;
  border-radius: 4px;
}

/* Individual thumbnail cells */
.clip-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 6px 4px;
  background: #fff;
  border: 1px solid transparent;
  border-radius: 8px;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease;
}
.clip-cell:hover {
  background: #eff6ff;
  border-color: var(--color-primary, #2563eb);
}
.clip-cell.is-active {
  background: #dbeafe;
  border-color: var(--color-primary, #2563eb);
}
.clip-cell img {
  width: 48px;
  height: 48px;
  object-fit: contain;
  background: #fafbfd;
  border-radius: 4px;
  padding: 2px;
}
.clip-cell-label {
  font-size: 0.65rem;
  color: var(--color-text-muted, #64748b);
  letter-spacing: 0.02em;
}

/* Full-row cells: "Clear selection" + "Upload Clipart" */
.clip-cell.is-clear,
.clip-cell.is-upload {
  grid-column: 1 / -1;
  flex-direction: row;
  justify-content: center;
  gap: 6px;
  padding: 8px 12px;
  font-size: 0.82rem;
  color: var(--color-text-muted, #64748b);
  border: 1px dashed var(--color-border, #e5e7eb);
}
.clip-cell.is-clear:hover {
  background: #fef2f2;
  border-color: #fca5a5;
  color: #dc2626;
}
.clip-cell.is-upload:hover {
  background: #ecfeff;
  border-color: #67e8f9;
  color: #0e7490;
}
.clip-cell.is-clear svg,
.clip-cell.is-upload svg { flex-shrink: 0; }

/* File-type hint shown at the bottom of the panel */
.clip-select-hint {
  padding: 8px 12px;
  border-top: 1px solid var(--color-border, #e5e7eb);
  background: #fbfcfe;
  font-size: 0.7rem;
  line-height: 1.5;
  color: var(--color-text-muted, #64748b);
}
.clip-select-hint strong {
  color: var(--color-text, #0f172a);
  font-weight: 600;
}
.clip-select-hint .hint-pref {
  margin-top: 2px;
  color: var(--color-primary, #2563eb);
}
.clip-select-hint .hint-pref strong { color: var(--color-primary, #2563eb); }

/* ============================================================================
 *  ADMIN PRICE-EDITOR OVERLAY
 *  Activated only when the storefront page is loaded with ?adminMode=1.
 *  Hides buyer-facing chrome and turns option buttons / text fields into
 *  clickable price-editor targets.
 * ========================================================================== */

/* Hard hide every buyer-facing chrome element. Selectors target the actual
   class names used in products/lanyard-detail.html / id-card-detail.html /
   wristband-detail.html (.topbar / .promo-bar / .header / .footer /
   .pd-breadcrumb / .pd-hero / .pd-tabs-wrap / .pd-tabs and the description /
   pricing / reviews / faqs sections marked with data-pd-section). */
html.admin-mode .topbar,
html.admin-mode .header,
html.admin-mode .footer,
html.admin-mode .pd-breadcrumb,
html.admin-mode .breadcrumb,
html.admin-mode .pd-hero,
html.admin-mode .pd-tabs-wrap,
html.admin-mode .pd-tabs,
html.admin-mode .page-header,
html.admin-mode .product-hero,
html.admin-mode .id-hero,
html.admin-mode .lan-hero,
html.admin-mode .cart-cta,
html.admin-mode .config-aside-cta,
html.admin-mode .summary-card .summary-cta,
html.admin-mode .summary-card [data-add-to-cart],
html.admin-mode section[data-pd-section="description"],
html.admin-mode section[data-pd-section="pricing"],
html.admin-mode section[data-pd-section="reviews"],
html.admin-mode section[data-pd-section="faqs"],
html.admin-mode section[data-pd-section="related"] {
  display: none !important;
}
/* Keep only the configurator section visible. Anything else inside main that
   wasn't matched above is forced hidden as a safety net. */
html.admin-mode body > section:not([data-pd-section="configurator"]):not(:has(#configurator)) {
  display: none !important;
}
html.admin-mode .step-block[data-step-kind="style"] { display: none !important; }
html.admin-mode body { padding-top: 36px !important; background: #f7f8fa !important; margin: 0 !important; }
/* Trim the configurator section's own padding so the iframe content sits
   tight against the banner. */
html.admin-mode section[data-pd-section="configurator"],
html.admin-mode section:has(#configurator) {
  padding-top: 12px !important;
  padding-bottom: 12px !important;
  background: #f7f8fa !important;
}
/* Hide the summary side card (price preview, add-to-cart) — merchant doesn't
   need it for pricing setup. */
html.admin-mode .config-aside,
html.admin-mode .summary-card {
  display: none !important;
}
/* Force the config-main column to fill the available width once the side card
   is gone. */
html.admin-mode .config-main,
html.admin-mode .config-allsteps {
  width: 100% !important;
  max-width: none !important;
  flex: 1 1 100% !important;
}

.admin-banner {
  position: fixed; top: 0; inset-inline-start: 0; inset-inline-end: 0; z-index: 50000;
  display: flex; align-items: center; gap: 12px;
  padding: 6px 14px; font: 500 12px/1.4 system-ui, -apple-system, sans-serif;
  color: #fff;
  background: linear-gradient(90deg, #1d4ed8, #2563eb);
  box-shadow: 0 2px 10px rgba(15, 23, 42, 0.12);
}
.admin-banner strong { font-weight: 700; font-size: 12px; letter-spacing: 0.02em; }
.admin-banner-slug {
  padding: 2px 8px; border-radius: 6px;
  background: rgba(255, 255, 255, 0.18);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
}
.admin-banner-hint { margin-inline-start: auto; opacity: 0.85; font-size: 11px; }

/* Subtle hint that an element is priceable. Cursor stays as default so the
   element behaves exactly like in normal browsing (clicking selects /
   switches tab); the price affordance is the badge or hover-icon. */
html.admin-mode .step-block [data-value],
html.admin-mode .swatch-tab[data-swatch-pane] {
  outline: 1px dashed transparent;
  outline-offset: 2px;
  transition: outline-color 0.12s ease, box-shadow 0.12s ease;
}
html.admin-mode .step-block [data-value]:hover,
html.admin-mode .swatch-tab[data-swatch-pane]:hover {
  outline-color: rgba(37, 99, 235, 0.5) !important;
  box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.10) !important;
}

/* Delta badge — itself an edit button (role=button, clickable). Default
   sizing is tuned for small option pills; large-card overrides below. */
.admin-badge {
  position: absolute;
  top: -10px; inset-inline-end: -10px;
  z-index: 4;
  min-width: 28px;
  padding: 3px 9px;
  border-radius: 999px;
  font: 700 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: 0.02em;
  color: #fff;
  background: #2563eb;
  border: 2px solid #fff;
  box-shadow: 0 2px 6px rgba(15, 23, 42, 0.25);
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  text-align: center;
  transition: transform 0.1s ease, box-shadow 0.1s ease, filter 0.1s ease;
}
.admin-badge:hover {
  transform: scale(1.08);
  filter: brightness(1.08);
  box-shadow: 0 3px 10px rgba(15, 23, 42, 0.32);
}
.admin-badge:focus-visible {
  outline: 2px solid #1d4ed8;
  outline-offset: 2px;
}

/* Inherited badge — painted on child options (e.g. individual PMS swatches)
   when they don't have their own price rule but their parent group (e.g.
   the "Custom (PMS)" tab) does. Visually a ghost / outlined chip so the
   merchant can tell at a glance that the price is COMING FROM the parent,
   not set directly on this child. Clicking it opens this child's own
   popover so a single click lets the merchant override the inheritance. */
.admin-badge--inherited {
  background: #f8fafc !important;
  color: #1f2937 !important;
  border: 1.5px dashed #94a3b8 !important;
  box-shadow: 0 1px 3px rgba(15, 23, 42, 0.10) !important;
  font-weight: 600 !important;
  letter-spacing: 0 !important;
  padding-inline-start: 7px !important;
  padding-inline-end: 9px !important;
}
.admin-badge--inherited:hover {
  background: #eff6ff !important;
  border-color: #2563eb !important;
  color: #1d4ed8 !important;
  filter: none;
  box-shadow: 0 2px 6px rgba(37, 99, 235, 0.20) !important;
}

/* Popover ghost-mode — when the child has no own rule and we've pre-filled
   the inputs with the PARENT's values, render them in muted/italic style
   so the merchant immediately reads "this is what's inherited, not what
   I typed". On first input event the .admin-pop--inheriting class is
   removed and the inputs snap back to the normal solid look. */
.admin-pop--inheriting .admin-pop-value,
.admin-pop--inheriting .admin-pop-setup,
.admin-pop--inheriting .admin-pop-unit {
  color: #6b7280;
  font-style: italic;
  background: #f8fafc;
  border-color: #cbd5e1;
}
.admin-pop--inheriting .admin-pop-value:focus,
.admin-pop--inheriting .admin-pop-setup:focus,
.admin-pop--inheriting .admin-pop-unit:focus {
  color: #111827;
  font-style: normal;
  background: #ffffff;
  border-color: #2563eb;
}
/* The same hide-edit-icon rule applies when a child shows an inherited
   badge — it's not "rule-less" from the merchant's perspective, so don't
   stack the hover € icon on top. */
html.admin-mode .step-block [data-value].inherits-rule > .admin-edit-icon,
html.admin-mode .step-block .swatch[data-name-key].inherits-rule > .admin-edit-icon,
html.admin-mode .swatch-tab[data-swatch-pane].inherits-rule > .admin-edit-icon {
  display: none !important;
}

/* Hover-revealed edit icon for priceable elements without a rule. */
.admin-edit-icon {
  position: absolute;
  top: -10px; inset-inline-end: -10px;
  z-index: 3;
  width: 28px; height: 28px;
  display: none;
  align-items: center;
  justify-content: center;
  color: #fff;
  background: #2563eb;
  border: 2px solid #fff;
  border-radius: 50%;
  font: 700 13px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  box-shadow: 0 2px 6px rgba(15, 23, 42, 0.28);
  cursor: pointer;
  user-select: none;
  transition: transform 0.1s ease, background 0.1s ease, box-shadow 0.1s ease;
}
.admin-edit-icon:hover { transform: scale(1.12); background: #1d4ed8; box-shadow: 0 3px 10px rgba(15, 23, 42, 0.35); }
.admin-edit-icon:focus-visible {
  display: inline-flex;
  outline: 2px solid #1d4ed8;
  outline-offset: 2px;
}
html.admin-mode .step-block [data-value]:hover > .admin-edit-icon,
html.admin-mode .step-block .swatch[data-name-key]:hover > .admin-edit-icon,
html.admin-mode .swatch-tab[data-swatch-pane]:hover > .admin-edit-icon {
  display: inline-flex;
}
/* When a rule already exists the badge owns the corner — hide the hover
   icon to avoid two stacked pills. */
html.admin-mode .step-block [data-value].has-rule > .admin-edit-icon,
html.admin-mode .step-block .swatch[data-name-key].has-rule > .admin-edit-icon,
html.admin-mode .swatch-tab[data-swatch-pane].has-rule > .admin-edit-icon {
  display: none !important;
}

/* === Size up the affordance on large product cards (attach-card,
   tile-card, option-card, holder/reel main cards) === */
html.admin-mode .attach-card > .admin-badge,
html.admin-mode .attach-card > .admin-edit-icon,
html.admin-mode .tile-card  > .admin-badge,
html.admin-mode .tile-card  > .admin-edit-icon,
html.admin-mode .option-card > .admin-badge,
html.admin-mode .option-card > .admin-edit-icon,
html.admin-mode .wb-extra-card > .admin-badge,
html.admin-mode .wb-extra-card > .admin-edit-icon {
  top: -12px; inset-inline-end: -12px;
}
html.admin-mode .attach-card > .admin-edit-icon,
html.admin-mode .tile-card  > .admin-edit-icon,
html.admin-mode .option-card > .admin-edit-icon,
html.admin-mode .wb-extra-card > .admin-edit-icon {
  width: 34px; height: 34px;
  font-size: 16px;
}
html.admin-mode .attach-card > .admin-badge,
html.admin-mode .tile-card  > .admin-badge,
html.admin-mode .option-card > .admin-badge,
html.admin-mode .wb-extra-card > .admin-badge {
  font-size: 12px;
  padding: 4px 10px;
  min-width: 34px;
}
/* Make sure cards can show the badge/icon poking out of their corners.
   .wb-extra-card / .wb-extras-grid clip their corners (overflow:hidden) on
   the storefront, which cropped the Step-5 price affordances in the editor —
   so the blue "set price" button was invisible. Let them overflow here. */
html.admin-mode .attach-card,
html.admin-mode .tile-card,
html.admin-mode .option-card,
html.admin-mode .wb-extra-card,
html.admin-mode .attach-grid,
html.admin-mode .option-grid,
html.admin-mode .tile-grid,
html.admin-mode .wb-extras-grid {
  overflow: visible !important;
}
/* Also a touch larger for swatches in the colour grid. */
html.admin-mode .swatch[data-name-key] > .admin-edit-icon {
  width: 26px; height: 26px;
  top: -9px; inset-inline-end: -9px;
  font-size: 12px;
}
html.admin-mode .swatch[data-name-key] {
  overflow: visible !important;
}

/* Bulk-apply checkbox row inside the popover. */
.admin-pop-bulk {
  display: inline-flex; align-items: center; gap: 8px;
  margin-top: 4px;
  padding: 8px 10px;
  border-radius: 8px;
  background: #eff6ff;
  border: 1px solid #dbeafe;
  color: #1e3a8a;
  font-size: 12px; line-height: 1.4;
  cursor: pointer;
  user-select: none;
}
.admin-pop-bulk input[type="checkbox"] {
  width: 14px; height: 14px;
  accent-color: #2563eb;
  margin: 0;
}
.admin-pop-bulk strong { color: #1d4ed8; font-weight: 700; }
.admin-pop-bulk:hover { background: #dbeafe; }

/* Scope notice — surfaces "此价格只对宽度 15 mm 生效" in the popover
   head so the merchant can't miss that the row they're about to save
   only applies to the current scope (width). Visually warm/amber so it
   reads as a CONTEXTUAL constraint, distinct from the cool-blue
   bulk-apply checkbox which is an OPT-IN expansion. */
.admin-pop-scope {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px;
  margin: 8px 0 4px;
  padding: 10px 12px;
  border-radius: 8px;
  background: #fff7ed;
  border: 1px solid #fed7aa;
  color: #9a3412;
  font-size: 12px; line-height: 1.5;
}
.admin-pop-scope > svg { flex: 0 0 auto; color: #c2410c; }
.admin-pop-scope strong { color: #7c2d12; font-weight: 700; }
.admin-pop-scope-all {
  display: inline-flex; align-items: center; gap: 6px;
  margin-inline-start: auto;
  padding: 4px 8px;
  border-radius: 6px;
  background: #fff;
  border: 1px solid #fed7aa;
  color: #9a3412;
  cursor: pointer;
  user-select: none;
}
.admin-pop-scope-all input[type="checkbox"] {
  width: 13px; height: 13px;
  accent-color: #ea580c;
  margin: 0;
}
.admin-pop-scope-all:hover { background: #ffedd5; }
.admin-pop-scope-fallback {
  flex-basis: 100%;
  margin-top: 4px;
  padding-top: 6px;
  border-top: 1px dashed #fed7aa;
  color: #7c2d12;
  font-size: 11px;
}
.admin-pop-scope-fallback strong { color: #7c2d12; }

/* "梯度价格表" entry button inside the single-rule popover. Visually
   sits between the bulk-apply checkbox and the helper hint. Distinct
   accent (amber) keeps it from looking like a save/cancel action — it
   opens a different popover, it doesn't commit a change. */
.admin-pop-tier-btn {
  display: inline-flex; align-items: center; gap: 8px;
  margin-top: 6px;
  padding: 8px 12px;
  border-radius: 8px;
  background: #fffbeb;
  border: 1px solid #fde68a;
  color: #92400e;
  font-size: 12px; line-height: 1.3; font-weight: 600;
  cursor: pointer;
  text-align: start;
  width: 100%;
  justify-content: center;
}
.admin-pop-tier-btn:hover {
  background: #fef3c7;
  border-color: #fcd34d;
}
.admin-pop-tier-btn svg {
  flex: 0 0 14px;
  color: #b45309;
}

/* ---- Toolbar (download / import / paste) inside qty-tier popover ---
   A horizontal pill-row mirroring the base-price grid affordances so
   merchants get the same bulk-edit workflow at the per-rule level. */
.admin-pop-toolbar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  margin: 6px 0 8px;
  padding: 8px 10px;
  border-radius: 8px;
  background: #f8fafc;
  border: 1px solid #e2e8f0;
}
.admin-pop-toolbar-btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 10px;
  border-radius: 6px;
  background: #fff;
  border: 1px solid #cbd5e1;
  color: #334155;
  font: 600 12px/1.3 inherit;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.admin-pop-toolbar-btn:hover {
  background: #eff6ff;
  border-color: #93c5fd;
  color: #1d4ed8;
}
.admin-pop-toolbar-btn svg {
  flex: 0 0 13px;
}
.admin-pop-toolbar-hint {
  flex: 1 1 100%;
  margin: 2px 2px 0;
  color: #64748b;
  font-size: 11px;
  line-height: 1.35;
}

/* ---- Quantity-tier popover ----------------------------------------
   Wider variant used by the qty-tier editor: a baseline "default" row
   on top, then one editable row per merchant-defined volume break.
   Amber accent on the unit-badge ties it visually to the trigger
   button so the merchant feels they're inside the same feature. */
.admin-pop-tier-table .tier-cell-label {
  color: #475569; vertical-align: middle;
  padding: 8px 10px;
}
.admin-pop-tier-table .tier-cell-label strong {
  color: #0f172a; font-weight: 600;
}
.admin-pop-tier-table .tier-cell-label .tier-sub {
  margin-top: 2px; font-size: 11px; color: #94a3b8; font-weight: 400;
}
.admin-pop-tier-table tr[data-default="1"] {
  background: #fffdf5;
}
.admin-pop-tier-table tr[data-default="1"] td {
  border-bottom: 2px solid #fde68a;
}
.admin-pop-tier-table .tier-qty-fixed {
  display: inline-block; color: #94a3b8; text-align: center; width: 100%;
}
.admin-pop-tier-table .tier-minqty,
.admin-pop-tier-table .tier-value {
  width: 100%; padding: 4px 6px;
  border: 1px solid #cbd5f5; border-radius: 4px;
  text-align: end; font-variant-numeric: tabular-nums;
}
.admin-pop-tier-table .tier-minqty:focus,
.admin-pop-tier-table .tier-value:focus {
  outline: 2px solid #6366f1; outline-offset: 0; border-color: #6366f1;
}
.admin-pop-tier-table .tier-del {
  width: 22px; height: 22px;
  border: 1px solid #fecaca; background: #fef2f2; color: #dc2626;
  border-radius: 4px; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 11px;
}
.admin-pop-tier-table .tier-del:hover { background: #fee2e2; }
.admin-pop-tier-table .tier-unit-badge {
  display: inline-block;
  min-width: 18px; padding: 0 5px; margin-inline-start: 4px;
  background: #fef3c7; color: #92400e;
  border-radius: 4px; font-size: 11px; font-weight: 700;
}

/* Unit selector row above the table */
.admin-pop .tier-unit-hint {
  color: #94a3b8; font-size: 11px; margin-inline-start: 8px;
}

/* "+ Add tier" pill underneath the table */
.admin-pop-add-tier {
  display: inline-flex; align-items: center; gap: 6px;
  margin-top: 8px;
  padding: 6px 12px;
  border: 1px dashed #cbd5f5; border-radius: 6px;
  background: #f8fafc; color: #4338ca;
  font-size: 12px; font-weight: 600; cursor: pointer;
}
.admin-pop-add-tier:hover {
  background: #eef2ff; border-color: #6366f1; border-style: solid;
}
.admin-pop-add-tier svg { color: #4338ca; }

/* Footer setup-fee summary (read-only in the tier popover — the user
   edits setup back on the single-rule popover, we just surface it
   here so they remember it'll still be charged). */
.admin-pop-tier-setup {
  display: inline-flex; flex-direction: column;
  font-size: 12px; color: #92400e;
}
.admin-pop-tier-setup small {
  font-size: 10.5px; color: #b45309; font-weight: 400; margin-top: 1px;
}

.admin-money-btn {
  display: inline-flex; align-items: center; justify-content: center;
  margin: 4px 0 0 8px;
  width: 28px; height: 28px;
  vertical-align: middle;
  border-radius: 8px;
  border: 1px solid #cbd5f5;
  background: #eef4ff;
  color: #1d4ed8;
  cursor: pointer;
  font: 700 13px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  position: relative;
  transition: background 0.12s ease, transform 0.06s ease, box-shadow 0.12s ease;
}
.admin-money-btn:hover { background: #dde8ff; box-shadow: 0 2px 6px rgba(37, 99, 235, 0.16); }
.admin-money-btn:active { transform: translateY(1px); }
.admin-money-btn.has-rule {
  background: #2563eb; color: #fff; border-color: #2563eb;
}
.admin-money-btn.has-rule::after {
  content: attr(data-delta-label);
  position: absolute; bottom: 100%; left: 50%; transform: translate(-50%, -4px);
  padding: 2px 6px; border-radius: 6px;
  background: #0f172a; color: #fff;
  font: 700 10px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  white-space: nowrap;
  opacity: 0; pointer-events: none;
  transition: opacity 0.12s ease;
}
.admin-money-btn.has-rule:hover::after { opacity: 1; }

.admin-pop {
  position: absolute;
  z-index: 60000;
  width: 360px;
  background: #fff;
  border: 1px solid #e2e8f0;
  border-radius: 12px;
  box-shadow: 0 10px 30px rgba(15, 23, 42, 0.18);
  font: 13px/1.45 system-ui, -apple-system, sans-serif;
  color: #0f172a;
  overflow: hidden;
}
.admin-pop.is-saving { opacity: 0.5; pointer-events: none; }
.admin-pop-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 14px;
  border-bottom: 1px solid #f1f5f9;
  background: #f8fafc;
}
.admin-pop-title { font-weight: 600; font-size: 13px; color: #0f172a; }
.admin-pop-close {
  width: 24px; height: 24px;
  border: none; background: transparent;
  font-size: 20px; line-height: 1; color: #64748b;
  cursor: pointer; border-radius: 6px;
}
.admin-pop-close:hover { background: #e2e8f0; color: #0f172a; }
.admin-pop-body { padding: 12px 14px; display: flex; flex-direction: column; gap: 10px; }
.admin-pop-target {
  display: flex; align-items: center; gap: 6px;
  padding: 8px 10px; border-radius: 8px;
  background: #eff6ff; border: 1px solid #dbeafe;
  font-size: 12px; line-height: 1.3;
}
.admin-pop-step { color: #1d4ed8; font-weight: 600; }
.admin-pop-arrow { color: #94a3b8; }
.admin-pop-option { color: #0f172a; font-weight: 500; }
.admin-pop-row { display: grid; grid-template-columns: minmax(96px, max-content) 1fr; align-items: center; gap: 10px; }
.admin-pop-row label { font-size: 12px; color: #475569; font-weight: 500; white-space: nowrap; }
.admin-pop-row select,
.admin-pop-row input {
  width: 100%; padding: 6px 8px;
  border: 1px solid #cbd5f5; border-radius: 6px;
  font: 13px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: #0f172a; background: #fff;
  outline: none;
}
.admin-pop-row select:focus,
.admin-pop-row input:focus { border-color: #2563eb; box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.18); }
.admin-pop-hint { font-size: 11px; color: #64748b; line-height: 1.5; }
.admin-pop-foot {
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 14px;
  border-top: 1px solid #f1f5f9;
  background: #fafbff;
}
.admin-pop-actions { display: inline-flex; gap: 8px; }
.admin-pop-foot button {
  padding: 6px 12px;
  border: 1px solid #cbd5f5; background: #fff; color: #1d4ed8;
  border-radius: 8px; cursor: pointer;
  font: 600 12px/1 system-ui, -apple-system, sans-serif;
}
.admin-pop-foot button:hover { background: #eff6ff; }
.admin-pop-del { color: #dc2626 !important; border-color: #fecaca !important; }
.admin-pop-del:hover { background: #fef2f2 !important; }
.admin-pop-primary {
  background: #2563eb !important;
  color: #fff !important;
  border-color: #2563eb !important;
}
.admin-pop-primary:hover { background: #1d4ed8 !important; }

.admin-pop-wide { width: 480px; max-width: 92vw; }
.admin-pop-inherit {
  padding: 8px 10px;
  background: #fef9c3; border: 1px solid #fde68a; color: #78350f;
  border-radius: 8px;
  font-size: 12px; line-height: 1.45;
}
.admin-pop-inherit strong { color: #92400e; }
.admin-pop-table-wrap { max-height: 320px; overflow-y: auto; }
.admin-pop-table {
  width: 100%; border-collapse: collapse;
  font-size: 12.5px; line-height: 1.3;
}
.admin-pop-table th,
.admin-pop-table td {
  border: 1px solid #e2e8f0; padding: 4px 6px;
}
.admin-pop-table th { background: #f8fafc; font-weight: 600; color: #475569; text-align: start; }
.admin-pop-table .opt-label { font-weight: 500; color: #0f172a; max-width: 200px; word-break: break-word; }
.admin-pop-table .opt-unit { width: 64px; padding: 2px 4px; border: 1px solid #cbd5f5; border-radius: 4px; }
.admin-pop-table .opt-value { width: 78px; padding: 2px 6px; border: 1px solid #cbd5f5; border-radius: 4px; text-align: end; }
.admin-pop-table .opt-setup {
  width: 78px; padding: 2px 6px;
  border: 1px solid #fde68a; background: #fffbeb;
  border-radius: 4px; text-align: end;
  color: #92400e;
}
.admin-pop-table .opt-setup:focus { outline: 2px solid #f59e0b; outline-offset: 0; background: #fff; }

/* Two-line legend above the per-option table, explains what "Each" vs
   "Setup" mean so the merchant doesn't have to hover the headers. */
.admin-pop-legend {
  display: flex; flex-direction: column; gap: 3px;
  margin: 4px 0 8px;
  padding: 8px 10px;
  background: #f1f5f9; border: 1px solid #e2e8f0;
  border-radius: 8px;
  font-size: 11.5px; line-height: 1.4; color: #475569;
}
.admin-pop-legend strong { color: #0f172a; font-weight: 700; }

/* Setup-fee row in the single-rule popover gets the same amber accent
   as the per-option setup column, so the merchant immediately recognises
   it as a one-time fee rather than a per-piece markup. */
.admin-pop-row-setup label { color: #92400e; }
.admin-pop-row-setup .admin-pop-setup {
  border-color: #fde68a; background: #fffbeb; color: #92400e;
}
.admin-pop-row-setup .admin-pop-setup:focus {
  outline: 2px solid #f59e0b; outline-offset: 0; background: #fff;
}
.admin-pop-table .opt-del {
  width: 22px; height: 22px;
  border: 1px solid #fecaca; background: #fef2f2; color: #dc2626;
  border-radius: 4px; cursor: pointer; font: 600 11px/1 system-ui;
}
.admin-pop-table .opt-del:hover { background: #fee2e2; }
.admin-pop-table .opt-actions { display: flex; align-items: center; gap: 6px; justify-content: flex-end; }
.admin-pop-table .opt-tier {
  display: inline-flex; align-items: center; gap: 4px;
  height: 22px; padding: 0 6px;
  border: 1px solid #bfdbfe; background: #eff6ff; color: #1d4ed8;
  border-radius: 4px; cursor: pointer; font: 600 11px/1 system-ui;
}
.admin-pop-table .opt-tier:hover { background: #dbeafe; }
.admin-pop-table .opt-tier .opt-tier-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 14px; height: 14px; padding: 0 3px;
  border-radius: 7px; background: #1d4ed8; color: #fff; font-size: 9px;
}

/* € button anchored on the subrow-label looks tighter when the label
   text wraps; keep it tidy. */
.subrow-label .admin-money-btn { margin-top: 0; margin-inline-start: 6px; width: 22px; height: 22px; font-size: 12px; }

/* When several priceable fields share one .subrow, each button carries a
   short text tag so the admin can tell them apart (e.g. "Start" / "End"). */
.admin-money-btn-tag {
  margin-inline-start: 4px;
  padding: 1px 5px;
  border-radius: 4px;
  background: rgba(255,255,255,0.18);
  color: inherit;
  font: 600 10px/1.1 system-ui, -apple-system, sans-serif;
  letter-spacing: 0.01em;
  white-space: nowrap;
  max-width: 90px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.admin-money-btn.has-rule .admin-money-btn-tag { background: rgba(255,255,255,0.25); }
.admin-money-btn:not(.has-rule) .admin-money-btn-tag {
  background: rgba(29,78,216,0.10); color: #1d4ed8;
}
/* Auto-width when carrying a tag */
.admin-money-btn-field:has(.admin-money-btn-tag) { width: auto; padding: 0 8px; gap: 0; }

/* € anchored on a clipart-picker wrapper sits on its top-right corner. */
.clip-select { position: relative; }
.admin-money-btn.admin-money-btn-anchor {
  position: absolute;
  top: -10px; inset-inline-end: -10px;
  z-index: 5;
  margin: 0;
  width: 28px; height: 28px;
}
.admin-money-btn.admin-money-btn-anchor.has-rule { width: auto; min-width: 28px; padding: 0 8px; }

.admin-toast {
  position: fixed; bottom: 24px; inset-inline-end: 24px;
  z-index: 70000;
  padding: 10px 14px;
  border-radius: 8px;
  font: 600 13px/1.3 system-ui, -apple-system, sans-serif;
  color: #fff;
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.24);
  transform: translateY(0);
  transition: transform 0.18s ease, opacity 0.18s ease;
}
.admin-toast-ok { background: #16a34a; }
.admin-toast-error { background: #dc2626; }
.admin-toast.is-out { opacity: 0; transform: translateY(8px); }

/* ==========================================================================
   Wristband configurator — new step layouts (migrated from D:\Wristband)
   Step 2 size cards · Step 3 colour-category tabs · Step 4 message wizard
   · Step 5 extras grid. Re-uses existing primitives where possible.
   ========================================================================== */

/* ----- Step 2 — width cards (image + name + meta) -----
   Flex (not grid) so leftover items on the last row stay horizontally
   centred inside the step body — important for our six-card list, where
   a 5-column row would otherwise leave the 6th card alone on the left
   of a second row. */
.wb-size-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-content: center;
  gap: 24px;
  max-width: 1320px;
  margin: 0 auto;
}
/* Step 2 width tiles are intentionally large — the wristband photo
   carries the 1/2 inch / 12 mm caption baked into the band itself, so
   the buyer has to be able to read it without leaning in. Two cards
   per row at the standard 792 px configurator column; auto-flows down
   on narrower screens via the media query at the end. */
.wb-size-grid .wb-size-card { flex: 0 0 360px; }
.wb-size-card {
  appearance: none;
  background: #fff;
  border: 2px solid #e5e7eb;
  border-radius: 18px;
  padding: 26px 20px 22px;
  text-align: center;
  cursor: pointer;
  transition: border-color .15s ease, transform .15s ease, box-shadow .15s ease;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  position: relative;
  min-height: 430px;
}
.wb-size-card:hover { border-color: #f87171; transform: translateY(-2px); box-shadow: 0 6px 18px rgba(15,23,42,.08); }
.wb-size-card.is-selected { border-color: #dc2626; background: #fff7f7; box-shadow: 0 8px 22px rgba(220,38,38,.18); }
.wb-size-card.is-selected::after {
  content: "\2713";
  position: absolute;
  top: 12px; inset-inline-end: 14px;
  width: 30px; height: 30px;
  background: #dc2626; color: #fff;
  border-radius: 50%;
  font: 700 16px/30px system-ui, sans-serif;
}
.wb-size-img {
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 300px;
}
.wb-size-img img { max-width: 100%; max-height: 100%; object-fit: contain; }
.wb-size-name { font: 700 19px/1.25 Inter, system-ui, sans-serif; color: #111827; }
.wb-size-meta { font: 500 14px/1.45 Inter, system-ui, sans-serif; color: #6b7280; }

@media (max-width: 820px) {
  .wb-size-grid .wb-size-card { flex: 0 0 90%; min-height: 380px; }
  .wb-size-img { height: 250px; }
}

/* ----- Step 1 (pick a style) cards across ALL products ---------------
   Per user direction: enlarge the Step 1 style picker on every product
   detail page (PVC cards, lanyards, wristbands) to match the visual
   weight of the wristband Step 2 width cards above. Scope is restricted
   to [data-step-kind="style"] so other in-page tile-grids (e.g. lanyard
   holder variants, reel styles) keep their compact size. */
.step-block[data-step-kind="style"] .tile-grid {
  max-width: 1320px;
  gap: 24px;
}
.step-block[data-step-kind="style"] .step-block-body .tile-grid > *,
.step-block[data-step-kind="style"] .tile-grid > .tile-card {
  flex: 0 0 360px;
}
.step-block[data-step-kind="style"] .tile-card {
  padding: 26px 20px 22px;
  border-radius: 18px;
  gap: 14px;
  min-height: 430px;
}
.step-block[data-step-kind="style"] .tile-card .tile-img {
  width: 100%;
  height: 300px;
  aspect-ratio: auto;          /* override the 1.2/1 aspect from base */
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.step-block[data-step-kind="style"] .tile-card .tile-img img {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}
.step-block[data-step-kind="style"] .tile-card .tile-name {
  font: 700 19px/1.25 Inter, system-ui, sans-serif;
  color: #111827;
}
.step-block[data-step-kind="style"] .tile-card .tile-meta {
  font: 500 14px/1.45 Inter, system-ui, sans-serif;
  color: #6b7280;
}

@media (max-width: 820px) {
  .step-block[data-step-kind="style"] .step-block-body .tile-grid > *,
  .step-block[data-step-kind="style"] .tile-grid > .tile-card {
    flex: 0 0 90%;
    min-height: 380px;
  }
  .step-block[data-step-kind="style"] .tile-card .tile-img { height: 250px; }
}

/* ----- Step 3 — colour cards with size table ----------------------------
   The wristband Step 3 layout mirrors tablecoversnow.com/product/wristband:
   each colour is a card that combines the silicone photo with a four-row
   sizing table (Adult / Youth / Adult XL / Toddler), each row offering
   its own Qty input and Text Color dropdown so customers can mix sizes
   within a single colour. */
.wb-color-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-content: flex-start;
  gap: 18px;
  max-width: 1280px;
  margin: 0 auto;
}
.wb-color-grid .wb-color-card { flex: 0 0 320px; }
@media (max-width: 900px) {
  .wb-color-grid .wb-color-card { flex: 0 0 280px; }
}
@media (max-width: 600px) {
  .wb-color-grid .wb-color-card { flex: 0 0 220px; }
}
.wb-color-card {
  background: #fff;
  border: 2px solid #e5e7eb;
  border-radius: 14px;
  padding: 14px 14px 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  position: relative;
  transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
.wb-color-card:hover { border-color: #f87171; transform: translateY(-2px); box-shadow: 0 6px 18px rgba(15,23,42,.08); }
.wb-color-card.is-selected { border-color: #dc2626; background: #fff7f7; box-shadow: 0 6px 18px rgba(220,38,38,.18); }
.wb-color-card.is-selected::after {
  content: "\2713";
  position: absolute;
  top: 6px; inset-inline-end: 8px;
  width: 22px; height: 22px;
  background: #dc2626; color: #fff;
  border-radius: 50%;
  font: 700 13px/22px system-ui, sans-serif;
  text-align: center;
}
.wb-color-card-head {
  font: 700 15px/1.25 Inter, system-ui, sans-serif;
  color: #111827;
  text-align: center;
  padding: 4px 6px 2px;
}
.wb-color-card-img {
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 160px;
  border-radius: 10px;
  overflow: hidden;
  /* Force a fixed neutral background here so the leftover area around
     a `contain`-fitted photo never visually merges with the band (the
     earlier Camouflage card vanished against its own green tint). The
     inline `style="background:#hex"` on .wb-color-card-img added by JS
     is overridden by !important below. */
  background: #f3f4f6 !important;
  padding: 8px;
}
/* `contain` shows the whole silicone band photo with no cropping
   (user explicitly asked for the image to be smaller and complete).
   The card-img container gives it a soft neutral mat so leftover
   space reads as deliberate framing, not a layout bug. */
.wb-color-card-img img {
  max-width: 100%; max-height: 100%;
  width: auto; height: auto;
  object-fit: contain;
  display: block;
}
/* Glow variants get a tinted halo so the card hints at the effect. */
.wb-color-card--glow .wb-color-card-img {
  box-shadow: inset 0 0 0 2px rgba(34,197,94,.30), 0 0 14px rgba(132,204,22,.18);
}
.wb-color-card--light .wb-color-card-img {
  box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08);
}

/* The pricing table */
.wb-color-card-table {
  width: 100%;
  border-collapse: collapse;
  font: 500 13px/1.2 Inter, system-ui, sans-serif;
  color: #374151;
  table-layout: fixed;
}
.wb-color-card-table thead th {
  font: 600 11px/1.2 Inter, system-ui, sans-serif;
  text-transform: uppercase;
  color: #6b7280;
  letter-spacing: .03em;
  text-align: start;
  padding: 5px 4px;
  border-bottom: 1px solid #e5e7eb;
}
/* Size narrower, Qty wider, Text Color wider — keeps inputs readable
   on the bigger card. */
/* Column ratios re-balanced: size labels (Adult / Youth / Toddler)
   are short, so size can shrink in favour of a roomier Qty input —
   buyers typing 4-digit counts like "1250" should not feel cramped.
   TC dropdown is slightly trimmed but still wide enough for "White",
   "Black", "Yellow" etc. */
.wb-color-card-table .wb-color-th-size { width: 22%; }
.wb-color-card-table .wb-color-th-qty  { width: 42%; }
.wb-color-card-table .wb-color-th-tc   { width: 36%; }

/* Variant for styles that don't have a per-band Text Color choice
   (debossed / embossed / blank-wristbands).
   The TC <th>/<td> are STILL in the DOM so the buyer's per-cell state
   is preserved across Step-1 style switches; we just hide the column
   via CSS when the active product doesn't need it, and stretch the
   Size + Qty columns to fill the freed-up space. The `.wb-no-tc`
   class lives on the panes container (.wb-color-panes / data-render
   ="wb-color-panes") and is toggled by syncTcVisibility() in main.js
   whenever switchProduct() runs. */
.wb-color-card-table--no-tc .wb-color-th-size,
.wb-no-tc .wb-color-card-table .wb-color-th-size { width: 30%; }
.wb-color-card-table--no-tc .wb-color-th-qty,
.wb-no-tc .wb-color-card-table .wb-color-th-qty  { width: 70%; }
.wb-no-tc .wb-color-card-table .wb-color-th-tc,
.wb-no-tc .wb-color-card-table .wb-color-td-tc {
  display: none;
}
/* Required-text-colour validation feedback. Applied by main.js when the
   buyer enters a quantity on a wristband colour row but doesn't pick a
   text colour for that row. Cleared as soon as a value is selected, or
   automatically when the offending card no longer has any invalid rows. */
.wb-color-tc.wb-color-tc--invalid {
  border-color: #dc2626 !important;
  box-shadow: 0 0 0 3px rgba(220, 38, 38, .18);
  background: #fef2f2;
}
.wb-color-card.wb-color-card--invalid {
  outline: 2px solid #dc2626;
  outline-offset: 2px;
  border-radius: 14px;
  animation: wb-tc-required-shake .35s ease;
}
@keyframes wb-tc-required-shake {
  0%, 100% { transform: translateX(0); }
  20%      { transform: translateX(-4px); }
  40%      { transform: translateX(4px); }
  60%      { transform: translateX(-3px); }
  80%      { transform: translateX(3px); }
}
.wb-color-card-table td {
  padding: 6px 4px;
  vertical-align: middle;
}
.wb-color-card-table .wb-color-size {
  font: 600 13px/1.2 Inter, system-ui, sans-serif;
  color: #111827;
}
.wb-color-qty {
  width: 100%;
  height: 36px;
  border: 1px solid #d1d5db;
  border-radius: 7px;
  padding: 4px 8px;
  font: 600 14px/1 Inter, system-ui, sans-serif;
  color: #111827;
  text-align: center;
  background: #fff;
}
.wb-color-qty:focus { outline: none; border-color: #3b82f6; box-shadow: 0 0 0 3px rgba(59,130,246,.18); }
.wb-color-tc {
  width: 100%;
  height: 36px;
  border: 1px solid #d1d5db;
  border-radius: 7px;
  padding: 4px 8px;
  font: 500 13px/1 Inter, system-ui, sans-serif;
  color: #111827;
  background: #fff;
}
.wb-color-tc:focus { outline: none; border-color: #3b82f6; box-shadow: 0 0 0 3px rgba(59,130,246,.18); }

@media (max-width: 600px) {
  .wb-color-grid { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }
  .wb-color-card-img { height: 130px; }
}

/* Shared custom-colour section sits ABOVE the per-tab pane content
   so user-added PMS colours persist across Popular / Solid / Segmented
   / Swirl / Glow tab switches. */
.wb-color-custom-section {
  margin: 0 auto 22px;
}
.wb-color-grid--custom {
  margin-bottom: 6px;
}

/* ========================================================================
   Wristband Step 3 — "Custom Color" placeholder + Pantone picker modal
   ======================================================================== */

/* The action bar that sits above each pane's colour grid. Holds the
   "Add more custom color" button centered on its own row. */
.wb-pane-actions {
  display: flex;
  justify-content: center;
  margin: 4px 0 14px;
}
.wb-add-custom-btn {
  appearance: none;
  border: 0;
  background: #2dd4bf;
  color: #fff;
  font: 700 13px/1 Inter, system-ui, sans-serif;
  padding: 10px 22px;
  border-radius: 999px;
  cursor: pointer;
  letter-spacing: .01em;
  box-shadow: 0 4px 12px rgba(45,212,191,.32);
  transition: background .15s ease, transform .12s ease, box-shadow .15s ease;
}
.wb-add-custom-btn:hover {
  background: #14b8a6;
  box-shadow: 0 6px 16px rgba(20,184,166,.36);
  transform: translateY(-1px);
}
.wb-add-custom-btn:active { transform: translateY(0); }

/* The "Custom Color" placeholder card. Same outline as a real card so
   it slots cleanly into the grid; rainbow gradient + "Select Color"
   overlay button replaces the photo area. Cursor is only a pointer in
   the EMPTY state; once filled (data-wb-filled="1") it behaves like a
   normal colour card. */
.wb-color-card--custom:not([data-wb-filled]) {
  cursor: pointer;
}
.wb-color-card--custom:not([data-wb-filled]) .wb-color-card-head {
  background: #fff;
  color: #111827;
}
.wb-color-card-img--rainbow {
  background: linear-gradient(
    135deg,
    #ff5a5a 0%, #ffbd2e 20%, #f9f871 40%,
    #5dd39e 60%, #4cc9f0 80%, #6c63ff 100%
  ) !important;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}
.wb-custom-pick {
  appearance: none;
  border: 1px solid rgba(17,24,39,.18);
  background: rgba(255,255,255,.92);
  color: #111827;
  font: 700 12px/1 Inter, system-ui, sans-serif;
  padding: 9px 16px;
  border-radius: 8px;
  cursor: pointer;
  letter-spacing: .02em;
  box-shadow: 0 4px 10px rgba(15,23,42,.18);
  transition: background .12s ease, transform .12s ease;
}
.wb-custom-pick:hover { background: #fff; transform: translateY(-1px); }
.wb-color-card-table--placeholder td {
  color: #9ca3af;
  font: 500 12px/1.2 Inter, sans-serif;
}
.wb-color-card-table--placeholder .wb-color-card-placeholder {
  text-align: end;
  padding-inline-end: 4px;
}

/* User-added custom colour card (after Add). Same footprint as a stock
   card but with a teal accent border + ×-remove in the top-right of the
   photo area. */
.wb-color-card--user {
  border-color: #99f6e4;
  box-shadow: 0 4px 14px rgba(20,184,166,.14);
}
.wb-color-card--user .wb-color-card-img { position: relative; }
.wb-color-card-remove {
  position: absolute;
  top: 6px; inset-inline-end: 6px;
  width: 22px; height: 22px;
  border-radius: 50%;
  border: 1px solid rgba(15,23,42,.18);
  background: rgba(255,255,255,.92);
  color: #374151;
  font: 700 14px/18px Inter, system-ui, sans-serif;
  text-align: center;
  cursor: pointer;
  padding: 0;
  transition: background .12s, color .12s;
}
.wb-color-card-remove:hover { background: #ef4444; color: #fff; border-color: #ef4444; }

/* Brief highlight when the user tries to add a duplicate colour. */
.wb-color-card.is-highlighted {
  animation: wb-card-flash .9s ease;
}
@keyframes wb-card-flash {
  0%, 100% { box-shadow: 0 4px 14px rgba(20,184,166,.14); }
  30%, 60% { box-shadow: 0 0 0 4px rgba(45,212,191,.65); }
}

/* ----- Pantone picker modal ----- */
.wb-pms-modal-backdrop {
  position: fixed; inset: 0;
  background: rgba(15,23,42,.55);
  z-index: 9999;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.wb-pms-modal-backdrop.is-open { display: flex; }

/* SINGLE-PICK mode: triggered when the Step-3 "Text Color" → "Other"
   option opens the same Pantone picker. The user is just choosing ONE
   PMS shade for the text colour dropdown, so the type-select, the slot
   row, the helper text and the Add button are all irrelevant. We keep
   the title, search and PMS grid. */
.wb-pms-modal-backdrop--single .wb-pms-type,
.wb-pms-modal-backdrop--single .wb-pms-section-label,
.wb-pms-modal-backdrop--single .wb-pms-slots,
.wb-pms-modal-backdrop--single .wb-pms-helper,
.wb-pms-modal-backdrop--single .wb-pms-add {
  display: none !important;
}

.wb-pms-modal {
  width: min(720px, 100%);
  max-height: min(86vh, 760px);
  background: #fff;
  border-radius: 14px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-shadow: 0 24px 60px rgba(15,23,42,.32);
}
.wb-pms-modal-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 18px;
  border-bottom: 1px solid #e5e7eb;
}
.wb-pms-modal-title {
  font: 700 16px/1.2 Inter, system-ui, sans-serif;
  color: #111827;
}
.wb-pms-modal-close {
  appearance: none;
  background: transparent;
  border: 0;
  font: 700 22px/1 Inter, system-ui, sans-serif;
  color: #6b7280;
  cursor: pointer;
  padding: 4px 8px;
}
.wb-pms-modal-close:hover { color: #111827; }

.wb-pms-modal-body {
  padding: 14px 18px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  overflow: hidden;
}
.wb-pms-type {
  display: flex; align-items: center; gap: 10px;
  font: 600 13px/1 Inter, system-ui, sans-serif;
  color: #374151;
}
.wb-pms-type-select {
  flex: 1;
  height: 36px;
  border: 1px solid #d1d5db;
  border-radius: 8px;
  padding: 0 10px;
  font: 500 13px/1 Inter, system-ui, sans-serif;
  background: #fff;
  color: #111827;
}
.wb-pms-helper {
  font: 500 12.5px/1.4 Inter, sans-serif;
  color: #6b7280;
}
.wb-pms-search input {
  width: 100%;
  height: 36px;
  border: 1px solid #d1d5db;
  border-radius: 8px;
  padding: 0 12px;
  font: 500 13px/1 Inter, sans-serif;
  background: #fff;
  color: #111827;
}
.wb-pms-search input:focus {
  outline: none; border-color: #3b82f6; box-shadow: 0 0 0 3px rgba(59,130,246,.18);
}

/* Section label above the slot row ("Select Color") */
.wb-pms-section-label {
  font: 600 13px/1 Inter, system-ui, sans-serif;
  color: #374151;
  margin: 2px 0 -2px;
}

/* Fixed-count slot row. data-cols controls the column count
   (1 for Solid, 3 for Swirl, 3-wide × 2 rows for Segmented). */
.wb-pms-slots {
  display: grid;
  gap: 10px;
  padding: 4px 0;
}
.wb-pms-slots[data-cols="1"] { grid-template-columns: 1fr; }
.wb-pms-slots[data-cols="3"] { grid-template-columns: repeat(3, 1fr); }

.wb-pms-slot {
  appearance: none;
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  height: 40px;
  border: 1px solid #d1d5db;
  border-radius: 8px;
  background: #fff;
  font: 600 12.5px/1 'JetBrains Mono', Menlo, Consolas, monospace;
  color: #9ca3af;
  cursor: pointer;
  transition: border-color .12s ease, box-shadow .12s ease;
}
.wb-pms-slot:hover { border-color: #94a3b8; }
.wb-pms-slot.is-active {
  border-color: #2563eb;
  box-shadow: 0 0 0 3px rgba(37,99,235,.18);
}
.wb-pms-slot.is-filled .wb-pms-slot-hex { color: #111827; }
.wb-pms-slot-dot {
  display: inline-block;
  width: 18px; height: 18px;
  border-radius: 50%;
  border: 1.5px solid #d1d5db;
  background: #fff;
  flex: 0 0 auto;
}
.wb-pms-slot.is-filled .wb-pms-slot-dot {
  border-color: rgba(15,23,42,.18);
}
.wb-pms-slot-hex {
  letter-spacing: .04em;
  text-transform: uppercase;
}
.wb-pms-slot-x {
  position: absolute;
  top: -8px; inset-inline-end: -8px;
  width: 18px; height: 18px;
  border-radius: 50%;
  background: #ef4444;
  color: #fff;
  font: 700 12px/16px Inter, system-ui, sans-serif;
  text-align: center;
  border: 2px solid #fff;
  cursor: pointer;
  box-shadow: 0 1px 3px rgba(15,23,42,.2);
}
.wb-pms-slot-x:hover { background: #dc2626; }
@media (max-width: 540px) {
  .wb-pms-slots[data-cols="3"] { grid-template-columns: repeat(2, 1fr); }
}

.wb-pms-grid {
  flex: 1;
  overflow: auto;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: 8px;
  padding: 6px 4px 4px;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  min-height: 220px;
}
.wb-pms-card {
  appearance: none;
  border: 1px solid #e5e7eb;
  background: #fff;
  border-radius: 8px;
  padding: 6px;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  transition: border-color .12s ease, transform .1s ease, box-shadow .12s ease;
}
.wb-pms-card:hover {
  border-color: #fb7185;
  transform: translateY(-1px);
  box-shadow: 0 4px 10px rgba(15,23,42,.08);
}
.wb-pms-card.is-selected {
  border-color: #dc2626;
  box-shadow: 0 0 0 2px rgba(220,38,38,.18);
}
.wb-pms-card-chip {
  display: block;
  height: 38px;
  border-radius: 6px;
  border: 1px solid rgba(15,23,42,.08);
}
.wb-pms-card--light .wb-pms-card-chip { border-color: rgba(15,23,42,.18); }
.wb-pms-card-name {
  font: 600 11px/1.2 Inter, sans-serif;
  color: #1f2937;
  text-align: center;
  word-break: break-word;
}

.wb-pms-modal-foot {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding: 12px 18px;
  border-top: 1px solid #e5e7eb;
  background: #f9fafb;
}
.wb-pms-add[disabled] {
  opacity: .5;
  cursor: not-allowed;
}

@media (max-width: 600px) {
  .wb-pms-modal { max-height: 92vh; }
  .wb-pms-grid { grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); }
}

/* ==========================================================================
   Step 4 — Customise Your Wristband (rewritten to mirror the reference
   site's debossed-colorfilled Step 6). Pill tab pair, three-column
   message+clipart grid, full-width Internal/Font/Proof rows, native
   selects styled like the reference site, and a red-dot bottom note.
   ========================================================================== */
.wb-msg-tabs {
  display: flex; align-items: center; justify-content: center; gap: 0;
  margin: 4px auto 22px;
}
.wb-msg-tab {
  appearance: none;
  border: 2px solid #3aa9b3;
  background: #fff;
  color: #3aa9b3;
  padding: 10px 26px;
  font: 700 13px/1 Inter, system-ui, sans-serif;
  cursor: pointer;
  transition: background .15s ease, color .15s ease;
}
/* Rounded on the leading edge of the group and the trailing edge respectively,
   so the pill follows the writing direction instead of always bulging left. */
.wb-msg-tab:first-of-type { border-start-start-radius: 999px; border-end-start-radius: 999px; border-inline-end-width: 1px; }
.wb-msg-tab:last-of-type  { border-start-end-radius: 999px; border-end-end-radius: 999px; border-inline-start-width: 1px; }
.wb-msg-tab.is-active { background: #3aa9b3; color: #fff; border-color: #3aa9b3; }

.wb-msg-pane { display: none; }
.wb-msg-pane.is-active { display: block; }

/* Generic field container — label sitting above the input/select/textarea. */
.wb-field { display: flex; flex-direction: column; gap: 6px; margin: 0 0 14px; }
/* The class rule above sets `display:flex`, which would otherwise override
   the [hidden] attribute's implicit `display:none` (a common CSS gotcha).
   The cloth wristband configurator toggles per-variant fields via [hidden]
   (see [data-cloth-only] + applyClothVariantRules), so we must let hidden
   win — e.g. the Satin page must NOT show "Select Wristband Color" or the
   2nd/3rd imprint slots. Higher specificity (0,2,0) beats `.wb-field`. */
.wb-field[hidden] { display: none !important; }
.wb-field-label {
  font: 600 13px/1.2 Inter, system-ui, sans-serif;
  color: #111827;
  display: flex; align-items: center; gap: 8px;
}

/* Two- and three-column grids for the front/back rows. */
.wb-fields-grid {
  display: grid;
  gap: 14px 18px;
  margin-bottom: 14px;
}
.wb-fields-grid--3col { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 820px) {
  .wb-fields-grid--3col { grid-template-columns: 1fr; }
}
.wb-fields-grid .wb-field { margin: 0; }

/* Inside-text ("Internal Message") gating: hint shown below the input when
   the order qty is at/under the 100-piece threshold, plus the revealed
   method sub-field. */
.wb-field-note {
  margin: 2px 0 0;
  font: 500 12px/1.4 Inter, system-ui, sans-serif;
  color: #b45309;
}
.wb-field--sub { margin: 12px 0 0; }
.wb-field.is-locked .wb-input { background: #f3f4f6; color: #9ca3af; cursor: not-allowed; }

/* Standard input + textarea + select skin. Matches the reference site —
   thin neutral border, soft rounded corners, generous touch target. */
.wb-input,
.wb-select {
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  height: 44px;
  border: 1px solid #d1d5db;
  border-radius: 6px;
  padding: 0 14px;
  background: #fff;
  color: #111827;
  font: 500 14px/1 Inter, system-ui, sans-serif;
  transition: border-color .15s, box-shadow .15s;
}
.wb-input::placeholder { color: #9ca3af; font-weight: 500; }
.wb-input:focus,
.wb-select:focus,
.wb-textarea:focus {
  outline: none;
  border-color: #3aa9b3;
  box-shadow: 0 0 0 3px rgba(58,169,179,.18);
}
.wb-textarea {
  height: auto;
  min-height: 110px;
  padding: 12px 14px;
  line-height: 1.5;
  resize: vertical;
  font-family: Inter, system-ui, sans-serif;
}

/* Native chevron via background image keeps the markup simple. */
.wb-select {
  padding-inline-end: 38px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none' stroke='%236b7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
}
/* Big font select — taller, larger type so the picked font face shows
   off (e.g. "Arial bold" displays in actual Arial Bold). */
.wb-select--big {
  height: 56px;
  font-size: 20px;
  font-weight: 700;
  padding-inline-end: 44px;
  background-position: right 16px center;
}

/* ============================================================
   Cloth wristband colour dropdown — custom widget that shows a
   colour square in front of every option (native <select> can't
   render swatches reliably). The native <select> is hidden but
   kept as the value source; .cloth-cselect__trigger + __menu are
   the visible UI. Mirrors the reference site's colour picker.
   ============================================================ */
.cloth-cselect { position: relative; width: 100%; }
.cloth-cselect__native {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0);
  border: 0;
}
.cloth-cselect__trigger {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  height: 48px;
  padding: 0 14px;
  background: #fff;
  border: 1px solid #d1d5db;
  border-radius: 6px;
  font: 500 15px/1.2 Inter, system-ui, sans-serif;
  color: #111827;
  cursor: pointer;
  text-align: start;
  transition: border-color .15s, box-shadow .15s;
}
.cloth-cselect.is-open .cloth-cselect__trigger,
.cloth-cselect__trigger:hover {
  border-color: var(--color-accent, #2563eb);
}
.cloth-cselect.is-open .cloth-cselect__trigger {
  box-shadow: 0 0 0 3px var(--color-accent-soft, rgba(37, 99, 235, 0.12));
}
.cloth-cselect__label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cloth-cselect__caret { color: #6b7280; font-size: 11px; flex: 0 0 auto; }
.cloth-cselect__sq {
  flex: 0 0 auto;
  width: 22px; height: 22px;
  border-radius: 4px;
  background: #fff;
  border: 1px solid rgba(0, 0, 0, 0.18);
}
.cloth-cselect__sq.is-light { border-color: rgba(0, 0, 0, 0.28); }
.cloth-cselect__sq.is-empty {
  background:
    linear-gradient(45deg, #e5e7eb 25%, transparent 25%, transparent 75%, #e5e7eb 75%) 0 0/10px 10px,
    #fff;
}
.cloth-cselect__sq.is-custom {
  background: conic-gradient(#ef4444, #f59e0b, #10b981, #3b82f6, #a855f7, #ef4444);
}
.cloth-cselect__menu {
  position: absolute;
  z-index: 200;
  top: calc(100% + 6px);
  inset-inline-start: 0; inset-inline-end: 0;
  max-height: 320px;
  overflow-y: auto;
  background: #fff;
  border: 1px solid #d1d5db;
  border-radius: 8px;
  box-shadow: 0 12px 32px rgba(15, 23, 42, 0.16);
  padding: 6px;
}
.cloth-cselect__row {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 9px 10px;
  background: none;
  border: 0;
  border-radius: 6px;
  font: 500 14px/1.2 Inter, system-ui, sans-serif;
  color: #111827;
  cursor: pointer;
  text-align: start;
}
.cloth-cselect__row:hover { background: #f1f5f9; }
.cloth-cselect__row.is-selected { background: var(--color-accent-soft, rgba(37, 99, 235, 0.1)); font-weight: 700; }
.cloth-cselect__rowname { flex: 1; }

/* ============================================================
   Cloth wristband artwork file picker — tidy bordered box with a
   "Browse" button (reference image 2), replacing the raw native
   <input type=file>.
   ============================================================ */
.cloth-file {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 48px;
  padding: 0 6px 0 14px;
  background: #fff;
  border: 1px solid #d1d5db;
  border-radius: 6px;
  cursor: pointer;
  transition: border-color .15s, box-shadow .15s;
}
.cloth-file:hover { border-color: var(--color-accent, #2563eb); }
.cloth-file:focus-visible {
  outline: none;
  border-color: var(--color-accent, #2563eb);
  box-shadow: 0 0 0 3px var(--color-accent-soft, rgba(37, 99, 235, 0.12));
}
.cloth-file__input {
  position: absolute;
  width: 1px; height: 1px;
  opacity: 0; pointer-events: none;
}
.cloth-file__name {
  flex: 1;
  color: #9ca3af;
  font: 500 14px/1.2 Inter, system-ui, sans-serif;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cloth-file.has-file .cloth-file__name { color: #111827; }
.cloth-file__btn {
  flex: 0 0 auto;
  padding: 8px 18px;
  border-radius: 5px;
  background: var(--color-accent, #2563eb);
  color: #fff;
  font: 600 13px/1 Inter, system-ui, sans-serif;
}
.cloth-file:hover .cloth-file__btn { filter: brightness(0.95); }
.cloth-file__help {
  margin: 8px 0 0;
  font-size: 12px;
  line-height: 1.5;
  color: #6b7280;
}
.cloth-file__pref { font-weight: 600; color: #4b5563; }

/* Small circular info dot used next to "digital proof" / "browse". */
.wb-info-tip {
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px;
  border-radius: 50%;
  background: #3aa9b3;
  color: #fff;
  font: 700 10px/1 Inter, sans-serif;
  cursor: help;
  user-select: none;
}
.wb-info-tip:focus { outline: 2px solid #93c5fd; outline-offset: 1px; }
.wb-info-tip-inline {
  display: inline-flex; align-items: center; justify-content: center;
  width: 14px; height: 14px;
  border-radius: 50%;
  background: #2563eb;
  color: #fff;
  font: 700 9px/1 Inter, sans-serif;
  margin-inline-end: 6px;
}

/* Browse-file row: read-only mirror + Browse button label + info dot. */
.wb-file-row {
  display: flex; align-items: center; gap: 0;
  border: 1px solid #d1d5db;
  border-radius: 6px;
  background: #fff;
  overflow: hidden;
}
.wb-file-row:focus-within {
  border-color: #3aa9b3;
  box-shadow: 0 0 0 3px rgba(58,169,179,.18);
}
.wb-file-display {
  flex: 1;
  border: 0;
  border-radius: 0;
  background: transparent;
  cursor: default;
  height: 44px;
}
.wb-file-display:focus { box-shadow: none; border: 0; }
.wb-file-browse {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  height: 44px;
  padding: 0 18px;
  border-inline-start: 1px solid #e5e7eb;
  color: #3aa9b3;
  background: #fff;
  font: 600 13px/1 Inter, sans-serif;
  cursor: pointer;
  white-space: nowrap;
}
.wb-file-browse:hover { background: #f0fbfc; }
.wb-file-input {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  opacity: 0;
  cursor: pointer;
}
/* Browse-file row layout: the info dot (now .info-bang per the
   site-wide tooltip convention) sits to the right of the Browse
   button. .info-bang's default margin-left is 6px; we override here
   to give it a touch more breathing room within the file-row's
   flex container, plus a right margin so it doesn't kiss the
   container's rounded edge. The legacy .wb-info-tip selector is
   kept as a fallback in case any other page still uses that class. */
.wb-file-row .info-bang,
.wb-file-row .wb-info-tip {
  margin: 0 12px 0 8px;
  flex: 0 0 auto;
}

/* Small grey hint lines + link below the browse row. */
.wb-file-hint {
  margin: 6px 0 0;
  font: 500 12px/1.5 Inter, system-ui, sans-serif;
  color: #6b7280;
}
.wb-file-hint a {
  color: #2563eb;
  text-decoration: underline;
  font-weight: 600;
}

/* Red-dot bottom note ("BEFORE WE START YOUR ORDER..."). */
.wb-bottom-note {
  display: flex; align-items: center; gap: 10px;
  margin: 18px 0 0;
  padding: 0;
  font: 700 11.5px/1.4 Inter, system-ui, sans-serif;
  letter-spacing: .03em;
  color: #6b7280;
  text-transform: uppercase;
}
.wb-bottom-note-dot {
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px;
  border-radius: 50%;
  background: #dc2626;
  color: #fff;
  font: 700 11px/1 Inter, sans-serif;
  flex: 0 0 auto;
}

/* Backwards-compat shims so any pages still referencing the old
   classes don't break visually. They render exactly as before, but
   the new wristband Step 4 markup doesn't use them. */
.wb-msg-tab-or { color: #9ca3af; font: 700 12px/1 Inter, sans-serif; }
.wb-msg-mode-row {
  display: flex; align-items: center; justify-content: center; gap: 14px;
  flex-wrap: wrap; padding: 6px 0 14px;
}
.wb-msg-mode {
  appearance: none; background: none; border: 0; color: #9ca3af;
  font: 700 13px/1 Inter, system-ui, sans-serif; cursor: pointer; padding: 6px 8px;
}
.wb-msg-mode.is-active { color: #3aa9b3; }
.wb-msg-mode-or {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 50%; background: #f3f4f6;
  font: 700 11px/1 Inter, sans-serif; color: #9ca3af;
}
.wb-msg-grid {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 22px; margin-bottom: 18px;
}
.wb-msg-col { display: flex; flex-direction: column; gap: 12px; }
.wb-msg-extras-row { display: flex; gap: 16px; align-items: flex-end; }
.wb-msg-extra { flex: 0 0 260px; }
.wb-msg-proof-note {
  text-align: center; background: #f9fafb; border: 1px solid #e5e7eb;
  border-radius: 10px; padding: 10px 14px;
  font: 700 11.5px/1.5 Inter, sans-serif; letter-spacing: .04em;
  color: #4b5563; text-transform: uppercase;
}
@media (max-width: 820px) { .wb-msg-grid { grid-template-columns: 1fr; } }
.wb-upload-dz {
  border: 2px dashed #d1d5db; border-radius: 14px;
  padding: 28px 18px; text-align: center; background: #f9fafb;
  margin-bottom: 18px; cursor: pointer; position: relative;
}
.wb-upload-icon { color: #9ca3af; margin: 0 auto 10px; display: block; }
.wb-upload-title { font: 700 15px/1.3 Inter, sans-serif; color: #111827; }
.wb-upload-meta { font: 500 12px/1.4 Inter, sans-serif; color: #6b7280; margin-top: 6px; }
.wb-upload-dz input[type="file"] {
  position: absolute; inset: 0; opacity: 0; cursor: pointer;
}

/* When clipart-picker.js upgrades the <select> inside a .wb-field, make
   the resulting trigger full-width and match the new field skin. */
.wb-field .clip-select { width: 100%; display: block; }
.wb-field .clip-select-trigger {
  width: 100%;
  min-height: 44px;
  height: 44px;
  border-radius: 6px;
  padding: 0 36px 0 12px;
  background: #fff;
  border: 1px solid #d1d5db;
  font: 500 14px/1 Inter, system-ui, sans-serif;
  color: #111827;
}
.wb-field .clip-select-trigger .ct-thumb { width: 24px; height: 24px; }
.wb-field .clip-select-trigger .ct-label { font-weight: 500; color: #111827; }
.wb-field .clip-select-trigger .ct-label.is-placeholder { color: #9ca3af; }
.wb-field .clip-select.is-open .clip-select-trigger {
  border-color: #3aa9b3;
  box-shadow: 0 0 0 3px rgba(58,169,179,.18);
}

/* ----- Step 5 — extras grid -----
   Layout requirement (from the live site reference):
     - As many cards per row as fit, capped at ~340px each
     - The LAST row (typically an orphan when card_count % cols != 0)
       must be visually centred, not left-anchored.

   CSS Grid `repeat(auto-fit, minmax(...))` can't honour the second
   requirement on its own — the orphan card always lands in column 1
   and the remaining columns stay reserved as empty tracks, leaving
   wasted whitespace to the right. Flexbox with `flex-wrap: wrap` +
   `justify-content: center` is the idiomatic fix: each row centres
   its own cards, so the orphan in the last row is centred horizontally.
   We pin each card's flex basis to 340px (with a 280px minimum) so the
   per-row card count matches what the grid version produced. */
.wb-extras-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  justify-content: center;
  align-items: stretch;
}
.wb-extras-grid > .wb-extra-card {
  flex: 0 1 340px;
  min-width: 280px;
  max-width: 340px;
}
.wb-extra-card {
  appearance: none;
  background: #fff;
  border: 3px solid #e5e7eb;
  border-radius: 14px;
  padding: 0;
  /* Centre every text node inside the card. Originally left-aligned, but
     since these cards stack as a single column on narrow viewports (~556px
     container width — only one minmax(280, 340) track fits) the left
     alignment made the contents look pushed against an arbitrary edge.
     Centring matches the visual balance of the surrounding Step blocks. */
  text-align: center;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transition: border-color .15s ease, transform .15s ease, box-shadow .15s ease;
  position: relative;
}
.wb-extra-card:hover { border-color: #f87171; transform: translateY(-2px); box-shadow: 0 8px 22px rgba(15,23,42,.10); }
.wb-extra-card.is-selected { border-color: #dc2626; background: #fff8f8; }
.wb-extra-card.is-selected::after {
  content: "\2713";
  position: absolute;
  top: 10px; inset-inline-end: 12px;
  width: 24px; height: 24px;
  background: #dc2626; color: #fff;
  border-radius: 50%;
  font: 700 14px/24px system-ui, sans-serif;
  text-align: center;
  z-index: 2;
}
.wb-extra-card.is-locked {
  opacity: .55;
  cursor: not-allowed;
  filter: grayscale(.35);
}
.wb-extra-card.is-locked:hover {
  border-color: #e2e8f0; transform: none; box-shadow: none;
}
.wb-extra-card.is-locked .wb-extra-choices,
.wb-extra-card.is-locked .wb-extra-cta { pointer-events: none; }
.wb-extra-lock {
  margin-top: 8px;
  font-size: 12px;
  line-height: 1.4;
  color: #b91c1c;
  background: #fef2f2;
  border: 1px solid #fecaca;
  border-radius: 6px;
  padding: 6px 8px;
  /* Match the centred card content. The lock pill stretches to fill the
     card width (block default) and the text inside is centred by the
     ancestor .wb-extra-card's text-align: center. */
  width: 100%;
  box-sizing: border-box;
}
.wb-extra-img {
  background: #f8fafc;
  display: flex; align-items: center; justify-content: center;
  height: 150px;
  border-bottom: 1px solid #f1f5f9;
}
.wb-extra-img img { max-width: 80%; max-height: 100%; object-fit: contain; }
/* The body is a flex column; align-items: center centres each child
   (title, list, choices, lock, CTA) on the cross axis so they stay
   visually grouped under the centred title. */
.wb-extra-body { padding: 14px 16px 16px; display: flex; flex-direction: column; gap: 10px; align-items: center; }
.wb-extra-name {
  font: 700 15px/1.3 Inter, system-ui, sans-serif;
  color: #111827;
}
.wb-extra-price {
  display: inline-block;
  margin-inline-start: 6px;
  font: 700 12px/1 Inter, sans-serif;
  background: #f97316;
  color: #fff;
  padding: 4px 8px;
  border-radius: 999px;
}
/* Hide the static "+ €X / pc" badge on Step-5 extra cards. Several of these
   add-ons (Custom Branded Bagging, Keychain) are quantity-tiered, so a single
   flat per-piece figure is misleading; the real surcharge is reflected in the
   live order summary instead — matching the reference site, whose Step-4
   option cards carry no per-piece price label. */
.wb-extra-card .wb-extra-price { display: none; }
.wb-extra-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; align-items: center; }
.wb-extra-list li { font: 500 13px/1.45 Inter, sans-serif; color: #4b5563; }
.wb-extra-dash { color: #f97316; margin-inline-end: 4px; font-weight: 700; }

/* Choice-based extras (Keychain): two selectable radio options + Qty. */
.wb-extra-note { margin: 0; font: 600 12px/1.4 Inter, sans-serif; color: #dc2626; }
.wb-extra-choices { display: flex; flex-direction: column; gap: 8px; align-items: center; }
/* Keep radio + label horizontally aligned (radio left of text), but use
   inline-flex so the row itself can be horizontally centred by the
   parent's `align-items: center`. */
.wb-extra-choice {
  display: inline-flex; align-items: center; gap: 8px;
  font: 500 13px/1.4 Inter, sans-serif; color: #374151;
  cursor: pointer;
  text-align: start;
}
.wb-extra-choice input[type="radio"] { margin-top: 0; accent-color: #dc2626; cursor: pointer; flex: none; }
.wb-extra-qty {
  width: 120px; margin: 2px auto 4px; padding: 8px 10px;
  border: 1px solid #d1d5db; border-radius: 8px;
  font: 500 13px/1 Inter, sans-serif;
}
/* Free-form notes textarea revealed alongside the "Add Additional
   Keychains" radio. Width matches the card's body padding so it sits
   flush with the qty input above it; min-height of two rows keeps it
   visually distinct from the qty field while still being compact
   inside the card. The text-area travels through to the cart line and
   the admin order detail, so it MUST resize cleanly without forcing
   the parent card to grow unpredictably (resize: vertical). */
.wb-extra-note-input {
  width: 100%;
  box-sizing: border-box;
  margin: 6px 0 4px;
  padding: 8px 10px;
  border: 1px solid #d1d5db;
  border-radius: 8px;
  font: 500 12.5px/1.4 Inter, sans-serif;
  color: #374151;
  resize: vertical;
  min-height: 52px;
  background: #fafafa;
}
.wb-extra-note-input:focus {
  outline: none;
  border-color: #2563eb;
  background: #fff;
}
.wb-extra-cta {
  display: inline-block;
  /* Centre the CTA pill inside the column instead of pushing it to the
     left edge. The parent .wb-extra-body sets align-items: center, but
     repeat here for clarity and to override any inherited start-align. */
  align-self: center;
  margin-top: 4px;
  padding: 8px 14px;
  background: #f3f4f6;
  border-radius: 8px;
  font: 700 12.5px/1 Inter, sans-serif;
  color: #374151;
}
.wb-extra-cta--remove { display: none; background: #fee2e2; color: #b91c1c; }
.wb-extra-card.is-selected .wb-extra-cta { display: none; }
.wb-extra-card.is-selected .wb-extra-cta--remove { display: inline-block; }

/* Glow swatches get a subtle phosphorescent dot in the top-right */
.swatch.swatch-glow::before {
  content: "";
  position: absolute;
  top: 6px; inset-inline-end: 6px;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: radial-gradient(circle, #d1fae5 0%, #6ee7b7 60%, transparent 100%);
  box-shadow: 0 0 6px rgba(110, 231, 183, .8);
  z-index: 2;
}

/* PMS launcher inside Step 3 — centered button */
.pms-launcher { display: flex; justify-content: center; padding: 24px 0; }

/* ==========================================================================
   ★ Visual refresh v1 — Home page + global polish
   Loaded LAST so it overrides earlier rules without fighting specificity.
   ========================================================================== */

/* ----- 1. Refined buttons (used everywhere) ----- */
.btn {
  border-radius: var(--radius-md);
  padding: 13px 26px;
  font-weight: 600;
  transition: transform var(--transition-fast),
              box-shadow var(--transition-fast),
              background var(--transition-fast),
              color var(--transition-fast);
}
.btn-primary {
  background: linear-gradient(180deg, #f97316 0%, var(--color-accent) 50%, var(--color-accent-dark) 100%);
  color: #fff;
  box-shadow: var(--shadow-glow-accent);
  border: none;
}
.btn-primary:hover {
  transform: translateY(-2px);
  box-shadow: 0 12px 28px -8px rgba(234, 88, 12, 0.55), var(--shadow-md);
}
.btn-primary:active { transform: translateY(0); }

.btn-outline {
  background: #fff;
  color: var(--color-primary);
  border: 1.5px solid var(--color-primary);
  box-shadow: var(--shadow-xs);
}
.btn-outline:hover {
  background: var(--color-primary);
  color: #fff;
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}

/* ----- 2. Topbar + header polish ----- */
.topbar {
  background: linear-gradient(180deg, #0b1120 0%, #0f172a 100%);
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.header {
  background: rgba(255, 255, 255, 0.85);
  backdrop-filter: saturate(180%) blur(12px);
  -webkit-backdrop-filter: saturate(180%) blur(12px);
  box-shadow: var(--shadow-xs);
  border-bottom: 1px solid var(--color-border);
}
/* No frosted glass on phones, because it silently broke the whole mobile menu.
   A backdrop-filter makes its element the containing block for fixed-position
   descendants, and the drawer is a fixed child of .header. So its
   `inset: var(--header-height) 0 0 0` resolved against the 72px-tall header
   instead of the viewport: top 72px, bottom 0 left it 32px tall, and
   `overflow-y: auto` clipped all eleven links into that sliver. The menu
   appeared to do nothing.

   Overridden here rather than in the max-width block above because this rule
   comes later in the file and would otherwise win. Opaque white replaces the
   85% translucency the blur was there to support. */
@media (max-width: 960px) {
  .header {
    background: #fff;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  /* An open drawer has to out-rank the cookie banner (z 100) and the language
     offer (z 90), which are fixed and cover it, intercepting taps on the links
     underneath. Raising .header is the only lever available: the drawer lives
     inside its stacking context, so no z-index on the drawer itself can climb
     past .header's own 50. */
  body.pe-nav-open .header { z-index: 120; }
}

/* ----- 3. Hero ----- */
.hero {
  background:
    radial-gradient(circle at 18% 12%, rgba(234, 88, 12, 0.10) 0%, transparent 45%),
    radial-gradient(circle at 88% 88%, rgba(30, 58, 138, 0.08) 0%, transparent 50%),
    linear-gradient(180deg, #fdfaf5 0%, #f8f4ec 100%);
  border-bottom: 1px solid transparent;
  padding: clamp(56px, 9vw, 96px) 0 clamp(72px, 11vw, 120px);
}
.hero-eyebrow {
  background: rgba(255, 255, 255, 0.85);
  border: 1px solid rgba(234, 88, 12, 0.22);
  color: var(--color-accent-dark);
  padding: 8px 16px;
  box-shadow: var(--shadow-xs);
  backdrop-filter: blur(8px);
}
.hero-eyebrow svg { color: var(--color-accent); }

.hero h1 { letter-spacing: var(--letter-tight); }
.hero p.lead { max-width: 540px; }

.hero-visual .visual-card {
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-2xl);
  border: 1px solid rgba(255, 255, 255, 0.6);
}
.hero-visual .vc-1 { animation: heroFloat 7s var(--ease-soft) infinite alternate; }
.hero-visual .vc-2 { animation: heroFloat 9s var(--ease-soft) infinite alternate-reverse; }
.hero-visual .vc-3 {
  box-shadow: var(--shadow-xl), 0 0 0 6px rgba(255, 255, 255, 0.9);
  animation: heroFloat 6s var(--ease-soft) infinite alternate;
}
@keyframes heroFloat {
  from { transform: translateY(0); }
  to   { transform: translateY(-12px); }
}
@media (prefers-reduced-motion: reduce) {
  .hero-visual .vc-1,
  .hero-visual .vc-2,
  .hero-visual .vc-3 { animation: none; }
}

/* ----- 4. Section headers ----- */
.section-header h2 {
  background: linear-gradient(180deg, #0f172a 0%, #1e293b 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* ----- 5. Category cards (the product lines on the home page) ----- */
.category-card {
  border-radius: var(--radius-xl);
  border-color: var(--color-border);
  box-shadow: var(--shadow-sm);
  transition: transform var(--transition-slow),
              box-shadow var(--transition-slow),
              border-color var(--transition-fast);
}
.category-card:hover {
  transform: translateY(-6px);
  box-shadow: var(--shadow-xl);
  border-color: var(--color-accent);
}
.category-card .card-image {
  background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
  /* Taller than the 3:2 source: the card carries a title and a price and
     nothing else, so the picture is what sells the category and gets the room
     the blurb used to take. `object-fit: cover` trims the sides, which these
     covers can spare — the products sit in the middle of the frame. */
  aspect-ratio: 4/3;
}
.category-card .card-body { padding: 22px 24px 20px; gap: 4px; }
/* The blurb used to be the flex child that grew, which is what kept the price
   line on the bottom edge of every card in a row. With it gone the meta row has
   to hold that floor itself, or a two-line title would push its neighbour's
   price out of line. */
.category-card .card-meta { margin-top: auto; }
.category-card h3 {
  font-size: 1.5rem;
  font-weight: 700;
  margin-bottom: 10px;
  color: var(--color-text);
}
.category-card .price-from {
  background: var(--color-accent-soft);
  color: var(--color-accent-dark);
  padding: 4px 10px;
  border-radius: var(--radius-sm);
  font-size: 0.95rem;
}
.category-card .card-meta span:last-child {
  color: var(--color-primary);
  font-weight: 600;
  transition: var(--transition-fast);
}
.category-card:hover .card-meta span:last-child {
  color: var(--color-accent);
  transform: translateX(4px);
  display: inline-block;
}

/* ----- 6. Feature cards (Why us) ----- */
.feature-card {
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xs);
  padding: 32px 24px;
  transition: transform var(--transition-fast), box-shadow var(--transition-fast);
}
.feature-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-md);
}
.feature-card .feature-icon {
  width: 64px;
  height: 64px;
  border-radius: 18px;
  background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-light) 100%);
  color: #fff;
  box-shadow: var(--shadow-md);
}
.feature-card .feature-icon svg { color: #fff; stroke: currentColor; }

/* Dark sections must NOT use the dark-on-dark gradient title */
.section-dark .section-header h2,
.cta-banner h2 {
  background: none;
  -webkit-text-fill-color: #fff;
  color: #fff;
}

/* ----- 7. Steps grid (Order in 4 simple steps) ----- */
.step-card .step-icon {
  width: 72px;
  height: 72px;
  font-size: 1.4rem;
  font-weight: 800;
  box-shadow: var(--shadow-md);
  background: linear-gradient(135deg, var(--color-accent) 0%, var(--color-accent-dark) 100%);
}
.steps-grid {
  position: relative;
}
@media (min-width: 900px) {
  /* Subtle dashed connector line between step circles */
  .steps-grid::before {
    content: "";
    position: absolute;
    top: 36px;
    inset-inline-start: 12%;
    inset-inline-end: 12%;
    height: 2px;
    background-image: linear-gradient(90deg, var(--color-border-strong) 50%, transparent 50%);
    background-size: 12px 2px;
    background-repeat: repeat-x;
    z-index: 0;
  }
  .step-card { position: relative; z-index: 1; background: var(--color-bg-alt); }
}

/* ----- 8. Brand strip ----- */
.brand-strip {
  opacity: 1;
  padding: 16px 0;
}
.brand-strip > div {
  color: var(--color-text-light);
  font-weight: 800;
  letter-spacing: 0.08em;
  transition: color var(--transition-fast);
  font-size: 1.05rem;
}
.brand-strip > div:hover { color: var(--color-text-muted); }

/* ----- 9. Testimonials ----- */
.testimonial-card {
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xs);
  padding: 28px;
  transition: transform var(--transition-fast), box-shadow var(--transition-fast);
}
.testimonial-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-md);
}
.testimonial-card .stars { font-size: 1.05rem; letter-spacing: 3px; }
.testimonial-card blockquote {
  font-size: 1rem;
  font-style: normal;
  line-height: 1.65;
  color: var(--color-text);
}
.testimonial-card .author-name { font-size: 0.95rem; }

/* ----- 10. CTA banner ----- */
.cta-banner {
  background:
    radial-gradient(circle at 80% 30%, rgba(234, 88, 12, 0.22) 0%, transparent 55%),
    linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-dark) 100%);
  border-radius: var(--radius-xl);
  padding: clamp(48px, 8vw, 72px) clamp(24px, 6vw, 56px);
  box-shadow: var(--shadow-xl);
}
.cta-banner h2 {
  color: #fff;
  -webkit-background-clip: initial;
  background-clip: initial;
  background: none;
}
.cta-banner .btn-primary {
  background: #fff;
  color: var(--color-accent-dark);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
}
.cta-banner .btn-primary:hover {
  background: #fff;
  color: var(--color-accent);
  transform: translateY(-3px);
  box-shadow: 0 14px 32px rgba(0, 0, 0, 0.35);
}

/* ----- 11. Footer ----- */
.footer {
  background:
    radial-gradient(circle at 0% 0%, rgba(30, 58, 138, 0.25) 0%, transparent 40%),
    linear-gradient(180deg, var(--color-bg-darker) 0%, #060914 100%);
  border-top: 4px solid var(--color-accent);
}
.footer h4 {
  font-size: 0.78rem;
  color: #fff;
}
.footer a { transition: color var(--transition-fast), padding-inline-start var(--transition-fast); }
.footer a:hover { color: var(--color-accent-light); padding-inline-start: 4px; }

/* The mark is navy-on-white artwork, so on the near-black footer it needs its
   own white tile or half of it disappears. */
.footer-brand .logo .logo-img {
  background: #fff;
  border-radius: 8px;
  padding: 3px 5px;
  box-sizing: content-box;
  height: 30px;
}
.payment-methods .pm {
  display: inline-block;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  padding: 4px 8px;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.08);
  color: #cbd5e1;
  margin-inline-end: 6px;
}

/* ----- 13. Product listing pages (id-cards / lanyards / wristbands) ----- */
.page-header {
  background:
    radial-gradient(circle at 22% 18%, rgba(234, 88, 12, 0.10) 0%, transparent 50%),
    radial-gradient(circle at 80% 82%, rgba(30, 58, 138, 0.10) 0%, transparent 55%),
    linear-gradient(180deg, #fdfaf5 0%, #f6f2eb 100%);
  padding: clamp(48px, 7vw, 80px) 0 clamp(36px, 5vw, 56px);
  border-bottom: 1px solid var(--color-border);
}

.product-section { padding: clamp(48px, 7vw, 72px) 0; }
.product-section h2 {
  font-size: clamp(1.4rem, 2.4vw, 1.7rem);
  font-weight: 700;
  padding-bottom: 14px;
  margin-bottom: 32px;
  position: relative;
  border-bottom: none;
}
.product-section h2::after {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  bottom: 0;
  width: 56px;
  height: 3px;
  background: linear-gradient(90deg, var(--color-accent) 0%, var(--color-accent-dark) 100%);
  border-radius: var(--radius-pill);
}

.product-card {
  border-radius: var(--radius-lg);
  border-color: var(--color-border);
  box-shadow: var(--shadow-xs);
  transition: transform var(--transition-fast),
              box-shadow var(--transition-fast),
              border-color var(--transition-fast);
}
.product-card:hover {
  border-color: var(--color-accent);
  box-shadow: var(--shadow-lg);
  transform: translateY(-5px);
}
.product-card .card-image {
  background: linear-gradient(135deg, #fafaf7 0%, #f1f5f9 100%);
}
.product-card .card-image img { padding: 6px; }
.product-card:hover .card-image img { transform: scale(1.08); }
.product-card h3 { font-size: 1.05rem; min-height: 2.6em; }
.product-card .price-current {
  font-size: 1.25rem;
  color: var(--color-accent-dark);
}
.product-card .badge-sale {
  background: linear-gradient(135deg, var(--color-accent) 0%, var(--color-accent-dark) 100%);
  color: #fff;
  font-weight: 800;
  border-radius: var(--radius-pill);
  padding: 5px 11px;
  font-size: 0.65rem;
  letter-spacing: 0.1em;
  box-shadow: 0 4px 12px rgba(234, 88, 12, 0.35);
}
.product-card .rating .stars { letter-spacing: 2px; font-size: 0.9rem; }
.btn-customize {
  border-radius: var(--radius-md);
  padding: 11px 14px;
  letter-spacing: 0.02em;
  background: var(--color-primary);
  transition: var(--transition-fast);
}
.btn-customize:hover {
  background: var(--color-accent);
  box-shadow: var(--shadow-glow-accent);
  transform: translateY(-2px);
}

/* Page-header SVG decoration (the swirling card stack on listing pages) */
.page-header svg { filter: drop-shadow(0 14px 24px rgba(15, 23, 42, 0.18)); }
.page-header h1 {
  font-size: clamp(2rem, 4vw, 3rem);
  letter-spacing: var(--letter-tight);
}
.page-header p {
  font-size: clamp(1rem, 1.5vw, 1.15rem);
  color: var(--color-text-muted);
}

/* ----- 14. Configurator (product detail page wizard) polish ----- */

/* Step block container — crisper card, softer shadow */
.step-block {
  border-radius: var(--radius-xl);
  border: 1px solid var(--color-border);
  box-shadow: var(--shadow-sm);
  padding: 28px 30px 30px;
  margin-bottom: 22px;
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.step-block:hover {
  border-color: var(--color-border-strong);
  box-shadow: var(--shadow-md);
}
.step-block.is-touched {
  border-color: var(--color-accent);
  box-shadow: 0 0 0 1px var(--color-accent), var(--shadow-md);
}

/* Step header strip — give the gradient a touch more depth */
.step-header {
  background: linear-gradient(180deg, var(--color-step-strip-top) 0%, var(--color-step-strip-bot) 100%);
  border-radius: var(--radius-md);
  padding: 12px 16px;
  margin-bottom: 18px;
  display: flex;
  align-items: center;
  gap: 14px;
}

/* Selectable cards (attach-card) — refined shadow + ring + ✓ corner ribbon */
.attach-card {
  border-radius: var(--radius-md);
  transition: border-color var(--transition-fast),
              box-shadow var(--transition-fast),
              transform var(--transition-fast);
}
.attach-card:hover {
  border-color: var(--color-accent);
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}
.attach-card.is-selected {
  border-color: var(--color-accent);
  box-shadow: 0 0 0 2px var(--color-accent), var(--shadow-md);
  position: relative;
}
.attach-card.is-selected::after {
  content: "✓";
  position: absolute;
  top: 8px;
  inset-inline-end: 8px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--color-accent);
  color: #fff;
  font-size: 0.78rem;
  font-weight: 800;
  display: grid;
  place-items: center;
  box-shadow: 0 2px 6px rgba(234, 88, 12, 0.45);
  z-index: 3;
}

/* Live summary (sticky right column) — premium ticket-style */
.config-allsteps .config-summary {
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-md);
  border: 1px solid var(--color-border);
  background: linear-gradient(180deg, #ffffff 0%, #fafbfc 100%);
}
.config-allsteps .config-summary > h4 {
  font-size: 0.78rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin-bottom: 14px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--color-border);
}
.summary-list li {
  border-bottom: 1px dashed var(--color-border) !important;
  padding: 10px 0;
  font-size: 0.88rem;
}
.summary-list li:last-child { border-bottom: none !important; }
/* The separator belongs BETWEEN rows, so the closing one must not draw it.
   `:last-child` is the wrong test once rows can be hidden — the configurators
   keep the unused ones in the DOM, so the real last row on screen kept its
   dashed underline and the list looked cut off. This matches a visible row
   with no visible row after it. */
.summary-list li:not([hidden]):not(:has(~ li:not([hidden]))) {
  border-bottom: none !important;
}
.summary-list li .sum-val { color: var(--color-text); font-weight: 700; }
.config-allsteps .config-summary .summary-cta .btn {
  background: linear-gradient(180deg, #f97316 0%, var(--color-accent) 50%, var(--color-accent-dark) 100%);
  color: #fff;
  box-shadow: var(--shadow-glow-accent);
}
.config-allsteps .config-summary .summary-cta .btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 14px 32px -8px rgba(234, 88, 12, 0.55);
}

/* "View Cart" secondary action inside the summary panel — needs to
   OPT OUT of the orange-gradient default above (otherwise the outline
   button gets painted as a second primary, defeating the visual
   hierarchy where Add-to-Cart is THE call to action). Also shaves
   1.5px off vertical padding to cancel out the extra border so the
   two buttons read as the same height. */
.config-allsteps .config-summary .summary-cta .btn.btn-view-cart {
  background: transparent;
  color: var(--color-primary);
  border: 1.5px solid var(--color-primary);
  box-shadow: none;
  /* Cancel the 1.5px border on each axis so the secondary outline button
     matches the borderless primary button pixel-for-pixel (52 px tall vs
     the previously-observed 58 px). The .summary-cta is `display: grid;
     gap: 8px` so vertical stacking + spacing is already handled by the
     parent — no margin needed here. */
  padding-block: calc(14px - 1.5px);
}
.config-allsteps .config-summary .summary-cta .btn.btn-view-cart:hover {
  background: var(--color-primary);
  color: #fff;
  transform: translateY(-1px);
  box-shadow: 0 6px 16px -6px rgba(0, 0, 0, 0.2);
}

/* Tab pills (attach-tabs and cc-tabs) — keep brand colours consistent */
.cc-tabs .cc-tab,
.attach-tab {
  border-radius: var(--radius-md);
  transition: var(--transition-fast);
}
.attach-tab.is-active {
  background: var(--color-accent-soft);
  border-color: var(--color-accent);
}

/* Pricing chart row — make the price strip pop */
.pricing-table tr:hover { background: var(--color-accent-soft); }
.pricing-table th { background: var(--color-bg-alt); color: var(--color-text-muted); font-weight: 700; }

/* Pill buttons used inside customize-card */
.btn-pill {
  box-shadow: var(--shadow-xs);
  transition: var(--transition-fast);
}
.btn-pill:hover { transform: translateY(-1px); box-shadow: var(--shadow-sm); }
.btn-pill--filled { background: linear-gradient(135deg, var(--color-teal) 0%, var(--color-teal-dark) 100%); }
.btn-pill--filled:hover { box-shadow: var(--shadow-glow-teal); }

/* ----- 15. Static pages (About / FAQ / Contact / Terms / Privacy / Returns / Shipping) ----- */

/* FAQ — refined accordion */
.faq-item {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: #fff;
  box-shadow: var(--shadow-xs);
  margin-bottom: 12px;
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.faq-item:hover { border-color: var(--color-border-strong); }
.faq-item.open {
  border-color: var(--color-accent);
  box-shadow: var(--shadow-md);
}
.faq-question {
  padding: 20px 24px;
  font-size: 1rem;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  background: transparent;
  cursor: pointer;
}
.faq-question:hover { background: var(--color-accent-soft); }
.faq-item.open .faq-question { background: var(--color-accent-soft); color: var(--color-accent-dark); }
.faq-question .icon {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--color-bg-alt);
  display: grid;
  place-items: center;
  color: var(--color-text-muted);
  transition: transform 0.2s var(--ease-pop), background 0.2s, color 0.2s;
}
.faq-item.open .faq-question .icon {
  transform: rotate(180deg);
  background: var(--color-accent);
  color: #fff;
}
.faq-answer {
  padding: 0 24px 20px;
  color: var(--color-text-muted);
  line-height: 1.75;
}

/* Contact form — clean professional inputs */
.contact-form input,
.contact-form textarea,
.contact-form select,
.form-grid input,
.form-grid textarea,
.form-grid select {
  width: 100%;
  padding: 12px 14px;
  font: inherit;
  color: var(--color-text);
  background: #fff;
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-md);
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.contact-form input:focus,
.contact-form textarea:focus,
.contact-form select:focus,
.form-grid input:focus,
.form-grid textarea:focus,
.form-grid select:focus {
  border-color: var(--color-accent);
  box-shadow: 0 0 0 3px rgba(234, 88, 12, 0.12);
  outline: none;
}
.contact-form textarea, .form-grid textarea { min-height: 140px; resize: vertical; }
.contact-form label, .form-grid label {
  display: block;
  font-weight: 600;
  font-size: 0.88rem;
  color: var(--color-text);
  margin-bottom: 6px;
}

/* Static / legal pages — rendered content readability */
.static-content,
.legal-content {
  max-width: 760px;
  margin: 0 auto;
  font-size: 1.02rem;
  line-height: 1.8;
  color: var(--color-text);
}
.static-content h2,
.legal-content h2 {
  margin-top: 2.2em;
  font-size: 1.5rem;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--color-border);
}
.static-content h3,
.legal-content h3 {
  margin-top: 1.6em;
  font-size: 1.15rem;
  color: var(--color-text);
}
.static-content p,
.legal-content p { margin: 0 0 1em; color: var(--color-text-muted); }
.static-content ul,
.legal-content ul {
  padding-inline-start: 24px;
  color: var(--color-text-muted);
}
.static-content li,
.legal-content li { margin-bottom: 6px; }
.static-content a,
.legal-content a {
  color: var(--color-accent-dark);
  font-weight: 600;
  border-bottom: 1px dashed var(--color-accent-soft);
}
.static-content a:hover,
.legal-content a:hover { color: var(--color-accent); border-bottom-color: var(--color-accent); }

/* ==========================================================================
   Related Items rail (bottom of every product detail page)
   --------------------------------------------------------------------------
   Layout mirrors the tablecoversnow.com "Related Items" rail: a compact
   responsive grid of square photos with the product name centred below
   each tile. No price, no rating — this is a discovery / cross-sell
   element, not a buyable card. Cards link to the matching detail page.
   ========================================================================== */
.related-items {
  background: var(--color-bg-alt);
}
.related-items__title {
  margin: 0 0 28px;
  font-size: 1.5rem;
  font-weight: 800;
  color: var(--color-primary, #14304a);
  text-align: center;
  letter-spacing: -0.01em;
}
.related-items__grid {
  /* Flexbox (not grid) so the LAST row of cards is also centred when
     the card count doesn't fill a full row evenly — grid would left-
     align orphan items into tracks 1..n while leaving the right side
     empty. Cards keep a fixed flex-basis of 170px so they line up
     vertically and don't stretch on wide viewports. */
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-start;
  gap: 32px 24px;
}
.related-card {
  flex: 0 0 170px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-decoration: none;
  color: inherit;
  padding: 8px 4px 4px;
  border-radius: 10px;
  transition: transform .18s ease, box-shadow .18s ease, background .18s ease;
}
.related-card:hover {
  transform: translateY(-3px);
  background: #fff;
  box-shadow: 0 12px 28px -16px rgba(15, 38, 70, 0.18);
}
.related-card__img {
  width: 100%;
  aspect-ratio: 1 / 1;
  background: #ffffff;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  padding: 6px;
}
.related-card__img img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  display: block;
  transition: transform .25s ease;
}
.related-card:hover .related-card__img img {
  transform: scale(1.04);
}
.related-card__name {
  margin-top: 14px;
  font-size: 0.92rem;
  line-height: 1.35;
  font-weight: 600;
  color: var(--color-text, #1f2937);
  text-align: center;
}
.related-card:hover .related-card__name {
  color: var(--color-accent-dark, #0d9488);
}
@media (max-width: 640px) {
  .related-items__grid {
    gap: 18px 14px;
  }
  .related-card {
    flex: 0 0 calc(50% - 7px);
  }
  .related-card__name {
    font-size: 0.82rem;
    margin-top: 10px;
  }
}

/* ==========================================================================
   Customer account system (header control, auth modal, account pages)
   Injected/managed by js/account.js
   ========================================================================== */
.acct-ctl { position: relative; display: inline-flex; align-items: center; }
.acct-btn {
  display: inline-flex; align-items: center; gap: 8px;
  background: none; border: 0; cursor: pointer; padding: 4px 6px;
  color: var(--color-primary); border-radius: 8px;
}
.acct-btn:hover { background: var(--color-primary-soft); }
.acct-ico { display: inline-flex; }
.acct-text { display: flex; flex-direction: column; line-height: 1.05; text-align: start; }
.acct-l1 { font: 600 11px/1.1 Inter, sans-serif; color: var(--color-text-muted); }
.acct-l2 { font: 700 13px/1.15 Inter, sans-serif; color: var(--color-primary); max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acct-caret { font-size: 11px; color: var(--color-text-muted); transition: transform .18s ease; }
.acct-btn[aria-expanded="true"] .acct-caret { transform: rotate(180deg); }
@media (max-width: 720px) { .acct-text { display: none; } }

.acct-menu {
  position: absolute; top: calc(100% + 10px); inset-inline-end: 0; z-index: 1200;
  min-width: 248px; background: #fff; border: 1px solid var(--color-border, #e5e7eb);
  border-radius: 14px; box-shadow: var(--shadow-lg); padding: 8px;
  display: flex; flex-direction: column; gap: 2px;
  transform-origin: top right; animation: acctMenuIn .14s ease;
}
@keyframes acctMenuIn { from { opacity: 0; transform: translateY(-6px) scale(.98); } to { opacity: 1; transform: none; } }
.acct-menu[hidden] { display: none !important; }
.acct-menu-head {
  display: flex; align-items: center; gap: 11px;
  padding: 8px 10px 12px; margin-bottom: 4px; border-bottom: 1px solid #f1f5f9;
}
.acct-menu-avatar {
  width: 40px; height: 40px; border-radius: 50%; flex: none;
  display: flex; align-items: center; justify-content: center;
  background: var(--color-primary-soft); color: var(--color-primary);
}
.acct-menu-avatar svg { width: 20px; height: 20px; }
.acct-menu-id { display: flex; flex-direction: column; min-width: 0; }
.acct-menu-name { font: 800 13.5px/1.2 Inter, sans-serif; color: var(--color-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acct-menu-email { font: 500 11.5px/1.3 Inter, sans-serif; color: var(--color-text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acct-menu a, .acct-menu button {
  display: flex; align-items: center; gap: 11px; width: 100%; text-align: start;
  background: none; border: 0; cursor: pointer;
  padding: 10px 12px; border-radius: 9px; font: 600 13px/1.2 Inter, sans-serif;
  color: var(--color-text); text-decoration: none;
}
.acct-menu-ico { display: inline-flex; flex: none; color: var(--color-text-muted); transition: color .12s ease; }
.acct-menu-ico svg { width: 18px; height: 18px; }
.acct-menu a:hover, .acct-menu button:hover { background: var(--color-primary-soft); color: var(--color-primary); }
.acct-menu a:hover .acct-menu-ico, .acct-menu button:hover .acct-menu-ico { color: var(--color-primary); }
.acct-menu [data-acct-logout] { color: #b91c1c; border-top: 1px solid #f1f5f9; margin-top: 6px; padding-top: 12px; }
.acct-menu [data-acct-logout] .acct-menu-ico { color: #ef4444; }
.acct-menu [data-acct-logout]:hover { background: #fef2f2; color: #b91c1c; }
.acct-menu [data-acct-logout]:hover .acct-menu-ico { color: #b91c1c; }

/* ---- Auth modal ---- */
.acct-modal-backdrop {
  position: fixed; inset: 0; z-index: 5000; background: rgba(15, 23, 42, 0.55);
  display: flex; align-items: center; justify-content: center; padding: 20px;
}
/* The class above sets display:flex, which (being equal specificity but later
   in source order) overrides the UA [hidden]{display:none} rule. Without this
   explicit reset, toggling modalEl.hidden=true never actually hides the modal,
   so the × / backdrop close buttons appeared to "do nothing". */
.acct-modal-backdrop[hidden] { display: none !important; }
.acct-modal-open { overflow: hidden; }
.acct-modal {
  position: relative; width: 100%; max-width: 420px; background: #fff;
  border-radius: 16px; box-shadow: var(--shadow-2xl); padding: 30px 28px 28px;
  max-height: 90vh; overflow-y: auto;
}
.acct-addr-modal { max-width: 460px; }
.acct-modify-modal { max-width: 760px; }
.acct-request-modal { max-width: 520px; }
.acct-request-modal h3 {
  margin: 0 0 4px;
  font: 700 18px/1.3 Inter, sans-serif;
  color: var(--color-text);
}
.acct-request-modal .acct-modal-row {
  display: flex; flex-direction: column; gap: 6px;
  margin-bottom: 14px;
}
.acct-request-modal .acct-modal-row label {
  font: 600 12px/1.2 Inter, sans-serif;
  color: var(--color-text-muted);
}
.acct-request-modal .acct-modal-row input,
.acct-request-modal .acct-modal-row textarea {
  width: 100%;
  border: 1px solid #cbd5e1;
  border-radius: 8px;
  padding: 9px 11px;
  font: 500 13px/1.4 Inter, sans-serif;
  color: var(--color-text);
  background: #fff;
  box-sizing: border-box;
}
.acct-request-modal .acct-modal-row textarea {
  resize: vertical;
  min-height: 110px;
}
.acct-request-modal .acct-modal-row input:focus,
.acct-request-modal .acct-modal-row textarea:focus {
  border-color: var(--color-primary); outline: none;
  box-shadow: 0 0 0 3px rgba(201,44,44,.15);
}
.acct-request-modal .acct-modal-actions {
  display: flex; justify-content: flex-end; gap: 10px;
  margin-top: 8px;
}
.acct-modify-modal h4 {
  margin: 18px 0 10px; font: 700 13px/1.2 Inter, sans-serif;
  color: var(--color-text-muted); text-transform: uppercase; letter-spacing: .04em;
}
.acct-modify-modal h4:first-of-type { margin-top: 4px; }
.acct-modify-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 10px 16px;
}
.acct-modify-grid label {
  font: 600 12px/1.2 Inter, sans-serif; color: var(--color-text-muted);
  margin-top: 6px;
}
.acct-modify-grid input {
  border: 1px solid #cbd5e1; border-radius: 8px; padding: 9px 11px;
  font: 500 13px/1.3 Inter, sans-serif; color: var(--color-text); background: #fff;
}
.acct-modify-grid input:focus { border-color: var(--color-primary); outline: none; box-shadow: 0 0 0 3px rgba(2,132,199,.15); }

.acct-modify-items {
  display: flex; flex-direction: column; gap: 8px;
  border: 1px solid #e2e8f0; border-radius: 10px; padding: 8px; background: #f8fafc;
}
.acct-modify-item {
  display: grid; grid-template-columns: 1fr auto auto;
  align-items: center; gap: 10px;
  padding: 10px 12px; background: #fff; border-radius: 8px;
  border: 1px solid #eef2f7;
}
.acct-modify-item-meta {
  display: flex; flex-direction: column; gap: 4px; font: 500 12px/1.3 Inter, sans-serif;
}
.acct-modify-item-meta strong { font: 700 13px/1.3 Inter, sans-serif; color: var(--color-text); }
.acct-modify-item-price { color: var(--color-text-muted); font-size: 11px; }
.acct-modify-qty-lbl {
  font: 600 11px/1.2 Inter, sans-serif; color: var(--color-text-muted);
}
.acct-modify-qty {
  width: 84px; border: 1px solid #cbd5e1; border-radius: 8px;
  padding: 8px 10px; font: 600 13px/1.2 Inter, sans-serif; text-align: center;
}
.acct-modify-actions {
  display: flex; justify-content: flex-end; gap: 10px; margin-top: 18px;
}
@media (max-width: 640px) {
  .acct-modify-grid { grid-template-columns: 1fr; }
  .acct-modify-item { grid-template-columns: 1fr auto; }
  .acct-modify-qty-lbl { grid-column: 1 / -1; }
}
.acct-modal-x {
  position: absolute; top: 12px; inset-inline-end: 14px; background: none; border: 0;
  font-size: 26px; line-height: 1; color: #94a3b8; cursor: pointer;
}
.acct-modal-x:hover { color: #334155; }
.acct-modal h3 { margin: 0 0 16px; font: 800 20px/1.2 Inter, sans-serif; color: var(--color-primary); }
.acct-tabs { display: flex; gap: 6px; margin-bottom: 18px; background: #f1f5f9; padding: 5px; border-radius: 10px; }
.acct-tab {
  flex: 1; background: none; border: 0; cursor: pointer; padding: 9px 10px; border-radius: 8px;
  font: 700 13px/1.2 Inter, sans-serif; color: var(--color-text-muted);
}
.acct-tab.is-active { background: #fff; color: var(--color-primary); box-shadow: var(--shadow-sm); }
.acct-form { display: flex; flex-direction: column; }
/* Inactive auth form carries the [hidden] attribute; the .acct-form rule's
   display:flex would otherwise win over the UA [hidden]{display:none}, leaving
   BOTH Sign In and Register forms stacked together. Force the reset so only the
   active tab's form is visible. */
.acct-form[hidden] { display: none !important; }
.acct-form label { font: 600 12.5px/1.2 Inter, sans-serif; color: var(--color-text); margin: 12px 0 6px; }
.acct-form input {
  padding: 11px 12px; border: 1px solid #d1d5db; border-radius: 9px;
  font: 500 14px/1.2 Inter, sans-serif;
}
.acct-form input:focus { outline: none; border-color: var(--color-primary-light); box-shadow: 0 0 0 3px var(--color-primary-soft); }
.acct-submit { margin-top: 18px; }
.acct-err {
  background: #fef2f2; color: #b91c1c; border: 1px solid #fecaca; border-radius: 9px;
  padding: 9px 12px; font: 600 12.5px/1.4 Inter, sans-serif; margin-bottom: 6px;
}
.acct-err.is-success {
  background: #ecfdf5; color: #047857; border-color: #a7f3d0;
}
.acct-submit.is-busy { cursor: wait; }

/* ---- Auth modal: header + social sign-in (reference-aligned) ---- */
.acct-auth-head { text-align: center; margin-bottom: 18px; }
.acct-auth-logo {
  width: 52px; height: 52px; margin: 0 auto 12px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--color-primary-soft); color: var(--color-primary);
}
.acct-auth-logo svg { width: 26px; height: 26px; }
.acct-auth-head h3 { margin: 0 0 6px; font: 800 21px/1.2 Inter, sans-serif; color: var(--color-primary); }
.acct-auth-sub { margin: 0; font: 500 12.5px/1.5 Inter, sans-serif; color: var(--color-text-muted); }
.acct-forgot-link {
  align-self: flex-end; margin-top: 8px; background: none; border: 0; cursor: pointer; padding: 0;
  font: 600 12px/1.2 Inter, sans-serif; color: var(--color-primary); text-decoration: none;
}
.acct-forgot-link:hover { text-decoration: underline; }
.acct-or {
  display: flex; align-items: center; gap: 12px; margin: 20px 0 14px;
  color: var(--color-text-muted); font: 600 11.5px/1 Inter, sans-serif; text-transform: uppercase; letter-spacing: .06em;
}
.acct-or::before, .acct-or::after { content: ""; flex: 1; height: 1px; background: #e5e7eb; }
.acct-social { display: flex; flex-direction: column; gap: 10px; }
.acct-social-btn {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; padding: 11px 14px; cursor: pointer;
  background: #fff; border: 1px solid #d1d5db; border-radius: 9px;
  font: 700 13px/1.2 Inter, sans-serif; color: #1f2937;
  transition: background .12s ease, border-color .12s ease, box-shadow .12s ease;
}
.acct-social-btn:hover { background: #f8fafc; border-color: #94a3b8; box-shadow: var(--shadow-sm); }
.acct-social-btn svg { flex: none; }

/* ---- Account pages ---- */
.acct-hero { background: var(--color-primary); color: #fff; padding: 28px 0; }
.acct-hero h1 { margin: 0; font: 800 30px/1.2 Inter, sans-serif; text-align: center; color: #fff; }
.acct-body { padding: 28px 0 60px; }
.acct-profilebar { display: flex; align-items: center; gap: 18px; margin: 6px 0 26px; }
.acct-avatar {
  width: 64px; height: 64px; border-radius: 50%; background: #f1f5f9; color: #64748b;
  display: flex; align-items: center; justify-content: center; flex: none;
}
.acct-avatar svg { width: 30px; height: 30px; }
.acct-profile-name { font: 800 17px/1.2 Inter, sans-serif; color: var(--color-primary); }
.acct-profile-email { font: 500 13px/1.3 Inter, sans-serif; color: var(--color-text-muted); margin: 2px 0 8px; }
.acct-profile-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.acct-pill { border: 0; cursor: pointer; padding: 7px 12px; border-radius: 7px; font: 700 12px/1 Inter, sans-serif; color: #fff; background: #475569; }
.acct-pill--reset { background: #16a34a; }
.acct-pill--logout { background: #1e293b; }
.acct-pill--primary { background: var(--color-primary); }
.acct-pill--danger  { background: #dc2626; }
.acct-pill:hover { filter: brightness(1.06); }

/* Action column on the orders table: stack each button on its own row
   so the cell stays compact and readable at narrow widths. */
.acct-action-stack {
  display: inline-flex;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  min-width: 120px;
}
.acct-action-stack .acct-pill { text-align: center; white-space: nowrap; }

/* Inline "Service request pending" pill stacked under the action
   buttons. Non-clickable amber chip, so the buyer can see at a glance
   that customer service is already handling something for this order
   without confusing it with an action button. */
.acct-pill--badge {
  background: #fff7ed !important;
  color: #b45309 !important;
  border: 1px solid #fed7aa;
  cursor: default;
  font-weight: 600;
  text-align: center;
  padding: 6px 10px;
}
.acct-pill--badge:hover { filter: none; }

/* Service-request log shown inside the expanded order detail row.
   Mirrors the address card layout: a small heading + a stacked list of
   request rows colour-coded by status. */
.acct-order-requests {
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px dashed #e5e7eb;
}
.acct-order-requests h5 {
  margin: 0 0 8px;
  font: 700 12px/1.2 Inter, sans-serif;
  color: var(--color-text);
  letter-spacing: .02em;
  text-transform: uppercase;
}
.acct-req-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 8px; }
.acct-req-row {
  background: #f8fafc;
  border: 1px solid #e5e7eb;
  border-inline-start-width: 3px;
  border-radius: 8px;
  padding: 10px 12px;
}
.acct-req-row--pending  { border-inline-start-color: #f59e0b; }
.acct-req-row--resolved { border-inline-start-color: #16a34a; }
.acct-req-row--rejected { border-inline-start-color: #dc2626; }
.acct-req-head {
  display: flex; flex-wrap: wrap; gap: 8px;
  font: 700 11px/1.2 Inter, sans-serif; color: #475569;
  align-items: center;
}
.acct-req-type {
  background: #e2e8f0; color: #334155;
  padding: 2px 8px; border-radius: 999px;
}
.acct-req-status {
  padding: 2px 8px; border-radius: 999px;
  text-transform: capitalize;
}
.acct-req-status--pending  { background: #fff7ed; color: #b45309; }
.acct-req-status--resolved { background: #ecfdf5; color: #047857; }
.acct-req-status--rejected { background: #fef2f2; color: #b91c1c; }
.acct-req-time { color: #94a3b8; font-weight: 500; margin-inline-start: auto; }
.acct-req-msg {
  margin-top: 8px;
  font: 500 13px/1.45 Inter, sans-serif;
  color: var(--color-text);
  white-space: pre-line;
}
.acct-req-note {
  margin-top: 6px;
  padding: 8px 10px;
  background: #ecfeff;
  border-radius: 6px;
  font: 500 12px/1.45 Inter, sans-serif;
  color: #155e75;
}
.acct-req-note span[data-i18n] { font-weight: 700; margin-inline-end: 4px; }

/* Service-request modal — same chrome as the modify-order modal but
   with a textarea hero. Intro paragraph above the form is muted so
   the textarea remains the primary call-to-action. */
.acct-modal-intro {
  margin: 0 0 14px;
  font: 500 13px/1.5 Inter, sans-serif;
  color: #475569;
}
.acct-modal-row textarea {
  width: 100%;
  min-height: 110px;
  padding: 10px 12px;
  border: 1px solid #d1d5db;
  border-radius: 6px;
  font: 500 13px/1.5 Inter, sans-serif;
  color: var(--color-text);
  resize: vertical;
  box-sizing: border-box;
}
.acct-modal-row textarea:focus { outline: 2px solid var(--color-primary); outline-offset: -1px; }

.acct-section-head { display: flex; align-items: center; gap: 12px; margin: 18px 0 14px; }
.acct-section-head h2 { margin: 0; font: 800 18px/1.2 Inter, sans-serif; color: var(--color-primary); }
.acct-dash-btn, .acct-addr-add {
  background: #0ea5b7; color: #fff; border: 0; cursor: pointer; text-decoration: none;
  padding: 6px 14px; border-radius: 999px; font: 700 12px/1.3 Inter, sans-serif;
}
.acct-addr-add { background: var(--color-primary); }
.acct-addr-head { margin: 14px 0; }

.acct-table-wrap { overflow-x: auto; border: 1px solid #eef2f7; border-radius: 12px; }
.acct-table { width: 100%; border-collapse: collapse; min-width: 640px; background: #fff; }
.acct-table th {
  text-align: start; font: 700 11px/1.3 Inter, sans-serif; color: #64748b; letter-spacing: .03em;
  padding: 14px 16px; background: #f8fafc; border-bottom: 1px solid #eef2f7; white-space: nowrap;
}
.acct-table td { padding: 16px; font: 500 13px/1.4 Inter, sans-serif; color: var(--color-text); }
.acct-empty-row td { text-align: center; color: var(--color-text-muted); padding: 28px 16px; }
/* Dual-currency cell (used in the TOTAL column whenever the buyer paid
   in a non-EUR display currency). The charged amount is the bold primary
   line so it matches the buyer's bank statement at a glance; the EUR
   equivalent sits underneath in muted small type for product-price
   reference. Both spans force a block layout so they stack vertically
   inside the narrow TOTAL cell. */
.acct-money-charged { display: block; font-weight: 600; }
.acct-money-sub {
  display: block;
  margin-top: 2px;
  font: 400 11px/1.2 Inter, sans-serif;
  color: var(--color-text-muted, #64748b);
  letter-spacing: 0.01em;
}
/* Caption naming the coupon that pulled the TOTAL cell below unit × qty.
   Green so it reads as a saving rather than an error, but small enough to
   stay subordinate to the amount above it. */
.acct-money-save {
  display: block;
  margin-top: 3px;
  font: 600 11px/1.25 Inter, sans-serif;
  color: #047857;
  letter-spacing: 0.01em;
}
/* "You paid CHF X (≈ €Y · products priced in EUR)" banner inside the
   expandable order detail row. Quiet styling — small, soft background —
   because it's reference info, not a call-to-action. */
.acct-order-charged-note {
  margin: 4px 0 12px;
  padding: 8px 12px;
  background: #f0f9ff;
  border: 1px solid #bae6fd;
  border-radius: 6px;
  font: 500 12px/1.4 Inter, sans-serif;
  color: #0c4a6e;
}
.acct-order-charged-note__pri { font-weight: 600; }
.acct-order-charged-note__sub { color: #475569; font-weight: 400; }
.acct-status-pill {
  display: inline-block; padding: 4px 10px; border-radius: 999px;
  font: 700 11px/1 Inter, sans-serif; text-transform: capitalize;
  background: #fff7ed; color: #c2410c; border: 1px solid #fed7aa;
}
/* Order-status pills — colors mirror the 6-state workflow the
   merchant uses in the Medusa admin
   (待处理 → 生产中 → 生产完成 → 已寄出 → 已完成, with 已取消
   as a terminal early-exit). Keeping admin and self-service
   palettes identical so a CS agent can verbally describe a status
   ("blue pill") and the customer sees the same thing in their
   account. Legacy values (processing / delivered) kept for any
   historical row still carrying the old enum names. */
.acct-status-pill--pending        { background: #fff7ed; color: #c2410c; border-color: #fed7aa; }
/* Awaiting payment — distinct yellow tone so the buyer doesn't confuse
   "we need money from you" with "we're working on your already-paid
   order" (which is what plain 待处理 / pending implies). */
.acct-status-pill--awaiting_payment { background: #fefce8; color: #a16207; border-color: #fef08a; }
/* Paid — money has cleared (Stripe/PayPal webhook flipped it) but
   CS hasn't moved the line into production yet. Green tone signals
   "good — done on your end" to reassure the buyer immediately. */
.acct-status-pill--paid           { background: #ecfdf5; color: #047857; border-color: #a7f3d0; }
.acct-status-pill--in_production  { background: #eff6ff; color: #1d4ed8; border-color: #bfdbfe; }
.acct-status-pill--produced       { background: #faf5ff; color: #6b21a8; border-color: #ddd6fe; }
.acct-status-pill--shipped        { background: #eff6ff; color: #1d4ed8; border-color: #bfdbfe; }
.acct-status-pill--completed      { background: #f0fdf4; color: #15803d; border-color: #bbf7d0; }
.acct-status-pill--cancelled      { background: #fef2f2; color: #b91c1c; border-color: #fecaca; }
.acct-status-pill--processing     { background: #eff6ff; color: #1d4ed8; border-color: #bfdbfe; }
.acct-status-pill--delivered      { background: #ecfdf5; color: #047857; border-color: #a7f3d0; }
.acct-table td .acct-pill { text-decoration: none; display: inline-block; }

/* Flat per-line-item rendering of the orders table — mirrors the
   Medusa admin /orders-sim layout so the buyer and a CS agent
   reading the same screen share the same column model. Each cart
   line becomes one `<tr>`; order-level cells (id / date / name /
   total / status / action) render only on the first line of each
   order. A thicker top border draws the visual group line so the
   eye still reads several lines as "one order". */
.acct-order-row--first td { border-top: 2px solid #cbd5e1; }
.acct-order-row--cont  td { border-top: 1px dashed #e2e8f0; color: inherit; }
.acct-cont-mark { color: #94a3b8; font-size: 14px; }
.acct-cell-order-id { font-weight: 600; word-break: break-all; }
.acct-cell-sub-id   { white-space: nowrap; }
.acct-cell-product  { min-width: 180px; }
.acct-line-product  { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.acct-line-title    { font-weight: 500; color: #0f172a; }
.acct-line-qty      { color: #64748b; font-size: 12px; }
.acct-line-empty    { color: #94a3b8; font-size: 12px; }
/* Sub-order id chip. Sits in the "子单号" column right next to the
   master order id — has to read as "child/companion id" without
   competing visually with the master, so we use a calm slate hue
   (same family as the order-id text, just lighter) and a soft tinted
   surface instead of the loud bright pill we shipped first. Slightly
   larger font + letter-spacing makes the 7-digit number easy to scan
   when CS is reading the same code over the phone. */
.acct-suborder-code {
  display: inline-block;
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.04em;
  line-height: 1;
  color: #475569;
  background: #f8fafc;
  border: 1px solid #e2e8f0;
  border-radius: 999px;
  padding: 4px 10px;
  white-space: nowrap;
  transition: background-color .15s ease, color .15s ease, border-color .15s ease;
}
/* Continuation rows (subsequent 子单 of the same order) sit on a
   slightly different baseline — keep the chip consistent so the
   eye reads it as "same kind of thing as the first row's chip". */
.acct-order-row--cont .acct-suborder-code {
  color: #64748b;
  background: #f1f5f9;
}
/* Hover state — purely cosmetic today, but the chip is the natural
   place to surface a "click to copy" or "open sub-order detail"
   affordance in the future. */
.acct-suborder-code:hover {
  background: #eef2ff;
  color: #3730a3;
  border-color: #c7d2fe;
}

/* === Expandable order detail row ============================================
   Triggered by the "View" pill in the orders table — toggles `[hidden]` on
   the detail row beneath the matching order. */
.acct-order-row--open > td:first-child {
  border-inline-start: 3px solid var(--color-primary);
  background: #f8fafc;
}
.acct-order-detail > td {
  background: #f8fafc;
  border-top: 1px dashed #cbd5e1;
  padding: 18px 20px 22px;
}
.acct-order-detail-grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr;
  gap: 24px;
}
@media (max-width: 720px) {
  .acct-order-detail-grid { grid-template-columns: 1fr; }
}
.acct-order-items h5,
.acct-order-addr h5 {
  font: 800 12px/1.2 Inter, sans-serif;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: #475569;
  margin: 0 0 10px;
}
.acct-order-items ul {
  list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: 8px;
}
.acct-order-items li {
  background: #fff;
  border: 1px solid #e2e8f0;
  border-radius: 10px;
  padding: 10px 12px;
  font: 500 13px/1.45 Inter, sans-serif;
  color: var(--color-text);
}
.acct-order-item-desc {
  margin-top: 4px;
  font: 500 11px/1.4 Inter, sans-serif;
  color: var(--color-text-muted);
}
.acct-order-item-id {
  display: inline-block;
  font: 600 11px/1.2 "JetBrains Mono", ui-monospace, monospace;
  color: #475569;
  background: #f1f5f9;
  border: 1px solid #e2e8f0;
  border-radius: 999px;
  padding: 2px 8px;
  margin-inline-end: 8px;
  vertical-align: 1px;
}
/* Key/value layout used by both the order item meta block and the
   address block on the customer account orders detail row. */
.acct-kv-list {
  display: grid;
  grid-template-columns: max-content 1fr;
  column-gap: 12px;
  row-gap: 4px;
  margin: 0;
}
.acct-kv-row {
  display: contents;
}
.acct-kv-row--full {
  grid-column: 1 / -1;
  display: block;
}
.acct-kv-label {
  font: 600 12px/1.45 Inter, sans-serif;
  color: #64748b;
  white-space: nowrap;
}
.acct-kv-label::after { content: ":"; margin-inline-start: 2px; }
.acct-kv-value {
  font: 500 12px/1.45 Inter, sans-serif;
  color: var(--color-text);
  word-break: break-word;
}
.acct-order-item-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 10px;
  margin-bottom: 8px;
}
.acct-order-item-title {
  font: 700 13px/1.3 Inter, sans-serif;
  color: var(--color-text);
}
.acct-order-item-meta {
  padding: 6px 10px;
  background: #f8fafc;
  border: 1px solid #e2e8f0;
  border-radius: 8px;
  margin-bottom: 6px;
}
.acct-order-item-spec {
  padding-top: 4px;
  border-top: 1px dashed #e2e8f0;
  margin-top: 6px;
}
/* Wristband colour-breakdown table on the customer account order detail.
   Mirrors the cart-page / cart-drawer / configurator summary tables so
   the buyer sees the same shape across every order touch-point. The
   block sits inside .acct-kv-list and spans the full container width. */
.acct-color-block {
  grid-column: 1 / -1;            /* break out of the kv 2-column grid */
  margin: 4px 0 6px;
}
.acct-color-label {
  font-weight: 600;
  font-size: 0.78rem;
  color: #475569;
  margin-bottom: 4px;
}
.acct-color-table {
  width: 100%;
  border-collapse: collapse;
  table-layout: auto;
  font-size: 0.74rem;
  line-height: 1.25;
}
.acct-color-table th,
.acct-color-table td {
  padding: 3px 6px;
  border-bottom: 1px solid #e2e8f0;
  text-align: start;
  vertical-align: middle;
  color: #1e293b;
  font-weight: 500;
  word-break: keep-all;
  overflow-wrap: anywhere;
}
.acct-color-table th {
  font-weight: 600;
  color: #64748b;
  background: rgba(0, 0, 0, 0.025);
  white-space: nowrap;
  font-size: 0.70rem;
}
.acct-color-table th:nth-child(2),
.acct-color-table td:nth-child(2) { white-space: nowrap; }
.acct-color-table th:nth-child(3),
.acct-color-table td:nth-child(3) {
  text-align: end;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  font-weight: 600;
}
.acct-color-table tbody tr:last-child td { border-bottom: none; }

/* CS-authored amendment log under each line item. The block is rendered
   AFTER the original spec_rows so the customer can compare "what I
   originally ordered" against "what CS has agreed to change" without
   either piece overwriting the other. The purple accent keeps it
   visually distinct from the configurator spec without screaming. */
.acct-order-item-adjustments {
  margin-top: 8px;
  padding: 8px 10px;
  background: #faf5ff;
  border: 1px solid #e9d5ff;
  border-radius: 8px;
}
.acct-order-item-adjustments-title {
  font: 600 12px/1.3 Inter, sans-serif;
  color: #6b21a8;
  margin-bottom: 6px;
}
.acct-order-item-adjustments-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.acct-adj-item {
  font: 400 12px/1.45 Inter, sans-serif;
  color: #1f2937;
}
.acct-adj-fromto {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 2px;
}
.acct-adj-from {
  color: #64748b;
  text-decoration: line-through;
  text-decoration-color: #cbd5e1;
}
.acct-adj-arrow {
  color: #7c3aed;
  font-weight: 600;
}
.acct-adj-to {
  color: #6b21a8;
  font-weight: 600;
}
.acct-adj-note {
  white-space: pre-wrap;
  word-break: break-word;
}
.acct-adj-meta {
  margin-top: 2px;
  color: #94a3b8;
  font-size: 11px;
}
.acct-order-addrs {
  display: flex; flex-direction: column; gap: 14px;
}
.acct-order-addr {
  background: #fff;
  border: 1px solid #e2e8f0;
  border-radius: 10px;
  padding: 12px 14px;
  font: 500 12px/1.55 Inter, sans-serif;
  color: var(--color-text);
}
.acct-order-addr div { color: #475569; }

.acct-tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; }
@media (max-width: 860px) { .acct-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .acct-tiles { grid-template-columns: 1fr; } }
.acct-tile {
  position: relative; display: flex; align-items: center; gap: 14px; text-decoration: none;
  background: #fff; border: 1px solid #eef2f7; border-radius: 14px; padding: 20px 22px;
  box-shadow: var(--shadow-sm); transition: transform .12s ease, box-shadow .12s ease, border-color .12s ease;
}
.acct-tile:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); border-color: var(--color-primary-light); }
.acct-tile-ico { color: var(--color-primary); flex: none; }
.acct-tile-title { display: block; font: 800 15px/1.2 Inter, sans-serif; color: var(--color-primary); }
.acct-tile-desc { display: block; font: 500 12px/1.4 Inter, sans-serif; color: var(--color-text-muted); margin-top: 3px; }
.acct-tile-badge { margin-inline-start: auto; font: 800 16px/1 Inter, sans-serif; color: #16a34a; }

/* ── Agent portal (代理商订单, pages/account-agent.html) ──────────────────
   A partner-facing report, so it sits inside the account shell but has its own
   table: the columns are money an agent reconciles a commission against, not
   the buyer's own order history. */
.agent-head { margin-bottom: 16px; }
.agent-intro { margin: 0; font: 500 13px/1.55 Inter, sans-serif; color: var(--color-text-muted); }
.agent-who { margin: 8px 0 0; font: 500 13.5px/1.3 Inter, sans-serif; color: var(--color-text); }
.agent-code {
  margin-inline-start: 8px; padding: 2px 8px; border-radius: 999px; background: #f1f5f9;
  font: 700 11px/1.5 Inter, sans-serif; color: #475569; letter-spacing: .04em;
}
.agent-warn {
  margin: 10px 0 0; padding: 10px 14px; border-radius: 10px;
  background: #fffbeb; border: 1px solid #fde68a;
  font: 600 12.5px/1.5 Inter, sans-serif; color: #92400e;
}

.agent-toolbar {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px;
  padding: 14px 16px; border: 1px solid #eef2f7; border-radius: 12px; background: #fff;
}
.agent-field { display: flex; flex-direction: column; gap: 5px; min-width: 140px; }
.agent-field--grow { flex: 1 1 200px; }
.agent-field > span { font: 700 11px/1.2 Inter, sans-serif; color: #64748b; letter-spacing: .03em; }
.agent-field select, .agent-field input {
  padding: 9px 11px; border: 1px solid #d1d5db; border-radius: 8px;
  font: 500 13px/1.2 Inter, sans-serif; background: #fff; color: var(--color-text);
}
.agent-toolbar-actions { display: flex; gap: 8px; margin-inline-start: auto; }

.agent-summary {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px;
  margin: 16px 0 6px;
}
@media (max-width: 900px) { .agent-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.agent-sum-cell {
  display: flex; flex-direction: column; gap: 4px; padding: 14px 16px;
  background: #fff; border: 1px solid #eef2f7; border-radius: 12px;
}
.agent-sum-label { font: 700 10.5px/1.3 Inter, sans-serif; color: #64748b; letter-spacing: .03em; }
.agent-sum-value { font: 800 17px/1.2 Inter, sans-serif; color: var(--color-text); }
.agent-sum-value--strong { color: var(--color-primary); }
.agent-sum-value--pending { color: #b45309; }
.agent-pending-count { font: 700 12px/1 Inter, sans-serif; }

.agent-note { margin: 8px 0 16px; font: 500 11.5px/1.5 Inter, sans-serif; color: var(--color-text-muted); }
.agent-note--privacy { margin-top: 14px; }
.agent-hidden-test { font-weight: 700; }
.agent-error p { margin: 0; font: 600 13.5px/1.6 Inter, sans-serif; color: #b45309; }
.agent-loading { padding: 10px 0; font: 600 12px/1.2 Inter, sans-serif; color: var(--color-text-muted); }
.agent-empty {
  padding: 40px 20px; text-align: center; background: #fff;
  border: 1px solid #eef2f7; border-radius: 12px;
  font: 600 13px/1.4 Inter, sans-serif; color: var(--color-text-muted);
}

.agent-table-wrap { overflow-x: auto; border: 1px solid #eef2f7; border-radius: 12px; }
.agent-table { width: 100%; border-collapse: collapse; min-width: 860px; background: #fff; }
.agent-table th {
  text-align: start; font: 700 11px/1.3 Inter, sans-serif; color: #64748b; letter-spacing: .03em;
  padding: 13px 14px; background: #f8fafc; border-bottom: 1px solid #eef2f7; white-space: nowrap;
}
.agent-table td {
  padding: 13px 14px; font: 500 13px/1.45 Inter, sans-serif; color: var(--color-text);
  border-bottom: 1px solid #f1f5f9; vertical-align: top;
}
.agent-table tbody tr:last-child td { border-bottom: 0; }
.agent-row { cursor: pointer; }
.agent-row:hover { background: #f8fafc; }
.agent-row.is-open { background: #f1f5f9; }
.agent-num { text-align: end; white-space: nowrap; }
.agent-orderno { font: 700 13px/1.3 Inter, sans-serif; white-space: nowrap; }
.agent-toggle { width: 34px; }
.agent-toggle-btn {
  width: 22px; height: 22px; border: 1px solid #d1d5db; border-radius: 6px; background: #fff;
  cursor: pointer; font: 700 13px/1 Inter, sans-serif; color: #475569;
}
.agent-coupon, .agent-cust-mail {
  display: block; margin-top: 3px; font: 600 11px/1.3 Inter, sans-serif; color: var(--color-text-muted);
}
.agent-pay {
  display: inline-block; padding: 3px 9px; border-radius: 999px;
  font: 700 10.5px/1.5 Inter, sans-serif; white-space: nowrap;
  background: #f1f5f9; color: #475569;
}
.agent-pay--paid { background: #dcfce7; color: #166534; }
.agent-pay--pending { background: #fef3c7; color: #92400e; }
.agent-pay--failed { background: #fee2e2; color: #991b1b; }
.agent-pay--refunded { background: #e0e7ff; color: #3730a3; }

.agent-detail-row > td { padding: 0 14px 14px 48px; background: #f8fafc; }
.agent-detail { width: 100%; border-collapse: collapse; background: #fff; border: 1px solid #eef2f7; border-radius: 10px; }
.agent-detail th {
  font: 700 10.5px/1.3 Inter, sans-serif; padding: 10px 12px; background: #fff;
  border-bottom: 1px solid #eef2f7;
}
.agent-detail td { padding: 10px 12px; font: 500 12.5px/1.4 Inter, sans-serif; border-bottom: 1px solid #f8fafc; }
.agent-detail tbody tr:last-child td { border-bottom: 0; }
.agent-spec { color: var(--color-text-muted); font-size: 12px; }

.acct-card { background: #fff; border: 1px solid #eef2f7; border-radius: 12px; padding: 22px; }
.acct-empty { text-align: center; color: var(--color-text-muted); font: 600 13px/1.4 Inter, sans-serif; }
.acct-signin-required { text-align: center; }
.acct-signin-required p { margin: 0 0 16px; color: var(--color-text-muted); }
.acct-promo-total { font: 700 14px/1.3 Inter, sans-serif; color: var(--color-text); margin: 4px 0 12px; }

.acct-settings-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0; border: 1px solid #eef2f7; border-radius: 12px; overflow: hidden; background: #fff; }
@media (max-width: 680px) { .acct-settings-grid { grid-template-columns: 1fr; } }
.acct-set-row { display: flex; flex-direction: column; gap: 8px; padding: 18px 20px; border-bottom: 1px solid #f1f5f9; }
.acct-set-label { font: 700 12.5px/1.2 Inter, sans-serif; color: var(--color-text); }
.acct-set-val { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.acct-set-text { font: 500 14px/1.3 Inter, sans-serif; color: var(--color-text-muted); }
.acct-edit-btn {
  background: #f1f5f9; border: 0; cursor: pointer; padding: 6px 14px; border-radius: 999px;
  font: 700 11.5px/1.2 Inter, sans-serif; color: #334155; margin-inline-start: auto;
}
.acct-edit-btn:hover { background: #e2e8f0; }
.acct-save-btn { background: var(--color-primary); color: #fff; margin-inline-start: 0; }
.acct-cancel-btn { margin-inline-start: 0; }
.acct-inline-input { flex: 1 1 auto; min-width: 120px; padding: 8px 10px; border: 1px solid #d1d5db; border-radius: 8px; font: 500 13px/1.2 Inter, sans-serif; }
.acct-set-actions { gap: 10px; }
.btn-warn { background: #f59e0b; color: #fff; border: 0; cursor: pointer; padding: 8px 14px; border-radius: 8px; font: 700 12px/1.2 Inter, sans-serif; }
.btn-danger { background: #dc2626; color: #fff; border: 0; cursor: pointer; padding: 8px 14px; border-radius: 8px; font: 700 12px/1.2 Inter, sans-serif; }

.acct-switch { position: relative; display: inline-block; width: 46px; height: 26px; }
.acct-switch input { opacity: 0; width: 0; height: 0; }
.acct-switch-track { position: absolute; inset: 0; background: #cbd5e1; border-radius: 999px; transition: background .15s ease; }
.acct-switch-track::before { content: ""; position: absolute; top: 3px; inset-inline-start: 3px; width: 20px; height: 20px; background: #fff; border-radius: 50%; transition: transform .15s ease; box-shadow: var(--shadow-sm); }
.acct-switch input:checked + .acct-switch-track { background: #16a34a; }
.acct-switch input:checked + .acct-switch-track::before { transform: translateX(20px); }

.acct-addr-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 14px; }
.acct-addr-card { position: relative; background: #fff; border: 1px solid #eef2f7; border-radius: 12px; padding: 16px 18px; font: 500 13px/1.5 Inter, sans-serif; color: var(--color-text-muted); }
.acct-addr-card strong { display: block; color: var(--color-text); margin-bottom: 4px; }
.acct-addr-del { position: absolute; top: 8px; inset-inline-end: 10px; background: none; border: 0; cursor: pointer; font-size: 18px; color: #94a3b8; }
.acct-addr-del:hover { color: #dc2626; }

/* Section grouping for the address book — one panel for shipping,
   one for billing, plus a fallback "other" section for legacy entries
   that have no kind tag. Each section gets its own heading so the
   buyer can tell at a glance which group an address belongs to,
   matching the two pickers in checkout. */
.acct-addr-section { margin-bottom: 22px; }
.acct-addr-section:last-child { margin-bottom: 0; }
.acct-addr-section-title {
  margin: 0 0 10px;
  font: 700 13px/1.2 Inter, sans-serif;
  color: var(--color-text);
  letter-spacing: .02em;
  text-transform: uppercase;
}
/* Small badge in the card top-left that names the address kind
   ("收货" / "账单" / "Shipping" / "Billing"). Sits opposite the
   delete × so the two never overlap. */
.acct-addr-kind {
  display: inline-block;
  background: #eef2ff;
  color: #4338ca;
  font: 700 10px/1.2 Inter, sans-serif;
  text-transform: uppercase;
  letter-spacing: .04em;
  padding: 3px 7px;
  border-radius: 999px;
  margin-bottom: 6px;
}
.acct-addr-section:nth-of-type(2) .acct-addr-kind {
  /* Billing section variant — amber tint to differentiate from the
     shipping section's indigo. */
  background: #fef3c7;
  color: #92400e;
}

.acct-kv-link {
  color: var(--color-primary);
  text-decoration: underline;
  text-underline-offset: 2px;
  word-break: break-all;
}
.acct-kv-link:hover { color: var(--color-accent); }

/* ==========================================================================
   17b. Artwork upload progress states (applied by main.js to .file-upload,
        .browse-zone and similar wrappers while a file is being POSTed to
        /store/uploads). Subtle visuals so the existing layouts don't jump.
   ========================================================================== */
.file-upload.is-uploading,
.browse-zone.is-uploading,
.upload-zone.is-uploading {
  border-color: var(--color-primary);
  background: rgba(2, 132, 199, 0.05);
}
.file-upload.is-uploaded,
.browse-zone.is-uploaded,
.upload-zone.is-uploaded {
  border-color: var(--color-success);
  background: rgba(22, 163, 74, 0.06);
}
.file-upload.upload-failed,
.browse-zone.upload-failed,
.upload-zone.upload-failed {
  border-color: #dc2626;
  background: rgba(220, 38, 38, 0.06);
}
.file-upload.is-uploading [data-file-text],
.browse-zone.is-uploading [data-file-text] {
  color: var(--color-primary);
}
.file-upload.upload-failed [data-file-text],
.browse-zone.upload-failed [data-file-text] {
  color: #b91c1c;
}

/* ==========================================================================
   18. Site search (pages/search.html)
   Renders a centred search bar, category chips and a responsive grid of
   product cards. Styled to match the listing/related-items look so it
   slots in without feeling like a separate app.
   ========================================================================== */
.search-page { padding: 36px 0 80px; }
.search-page h1 {
  margin: 0 0 18px; font: 800 28px/1.2 Inter, sans-serif;
  color: var(--color-primary); text-align: center;
}
.search-form { max-width: 720px; margin: 0 auto 16px; }
.search-input-wrap { position: relative; }
.search-input-ico {
  position: absolute; top: 50%; inset-inline-start: 14px; transform: translateY(-50%);
  color: #94a3b8; pointer-events: none;
}
#search-input {
  width: 100%; padding: 14px 44px 14px 44px; border: 1.5px solid #cbd5e1;
  border-radius: 999px; font: 500 15px/1.2 Inter, sans-serif; color: var(--color-text);
  background: #fff; box-shadow: var(--shadow-sm); transition: border-color .15s ease, box-shadow .15s ease;
}
#search-input:focus {
  border-color: var(--color-primary); outline: none;
  box-shadow: 0 0 0 4px rgba(2, 132, 199, .18);
}
.search-clear {
  position: absolute; top: 50%; inset-inline-end: 12px; transform: translateY(-50%);
  width: 28px; height: 28px; border: 0; border-radius: 50%; background: #e2e8f0; color: #475569;
  font-size: 18px; line-height: 1; cursor: pointer; display: inline-flex; align-items: center; justify-content: center;
}
.search-clear:hover { background: #cbd5e1; }
.search-filters {
  display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin-top: 14px;
}
.search-chip {
  background: #f1f5f9; border: 1px solid #e2e8f0; color: #475569; cursor: pointer;
  padding: 7px 16px; border-radius: 999px; font: 700 12.5px/1.2 Inter, sans-serif;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.search-chip:hover { background: #e2e8f0; }
.search-chip.is-active {
  background: var(--color-primary); color: #fff; border-color: var(--color-primary);
}
.search-meta {
  text-align: center; color: var(--color-text-muted); font: 500 13.5px/1.4 Inter, sans-serif;
  margin: 12px 0 22px;
}
.search-results {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 18px;
}
.search-card {
  display: flex; flex-direction: column;
  background: #fff; border: 1px solid #eef2f7; border-radius: 12px; overflow: hidden;
  text-decoration: none; color: inherit;
  transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
}
.search-card:hover {
  transform: translateY(-2px); box-shadow: var(--shadow-md);
  border-color: var(--color-primary);
}
.search-card-img {
  background: #f8fafc; aspect-ratio: 4 / 3; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
}
.search-card-img img { width: 100%; height: 100%; object-fit: cover; }
.search-card-body {
  padding: 12px 14px 14px; display: flex; flex-direction: column; gap: 4px;
}
.search-card-cat {
  font: 700 10px/1.2 Inter, sans-serif; color: var(--color-accent);
  text-transform: uppercase; letter-spacing: .08em;
}
.search-card-name { font: 700 14.5px/1.3 Inter, sans-serif; color: var(--color-text); }
.search-card-meta { font: 500 12.5px/1.3 Inter, sans-serif; color: var(--color-text-muted); min-height: 32px; }
.search-card-price {
  margin-top: 6px; font: 800 14px/1.2 Inter, sans-serif; color: var(--color-primary);
}
@media (max-width: 540px) {
  .search-results { grid-template-columns: repeat(2, 1fr); gap: 12px; }
  .search-card-name { font-size: 13px; }
}

/* ==========================================================================
   Customer reviews  (js/reviews.js)
   --------------------------------------------------------------------------
   Two things share this block: the review section on a product page, and the
   form used by pages/review.html and the confirmation-page survey.

   There is no "empty" state to style. js/reviews.js renders nothing at all
   until a product has approved reviews, so every rule below assumes real
   content — no zero-state stars, no grey "no reviews yet" panel.
   ========================================================================== */
.pe-reviews {
  max-width: 1180px;
  margin: 0 auto;
  padding: 48px 20px 8px;
}
.rev-title {
  margin: 0 0 22px;
  font-size: 1.5rem;
  font-weight: 800;
  color: var(--color-primary, #14304a);
  letter-spacing: -0.01em;
}

/* ── summary ── */
.rev-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 28px 44px;
  padding: 20px 22px;
  border: 1px solid var(--color-border, #e6e8ec);
  border-radius: 12px;
  background: var(--color-bg-alt, #f8fafc);
}
.rev-summary__score {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
}
.rev-score {
  font: 800 2.35rem/1 Inter, sans-serif;
  color: var(--color-primary, #14304a);
  letter-spacing: -0.02em;
}
.rev-count {
  font: 600 0.85rem/1.4 Inter, sans-serif;
  color: var(--color-text-muted, #6b7280);
}
.rev-stars { display: inline-flex; gap: 1px; line-height: 1; }
.rev-star { color: #d5d9e0; font-size: 0.95rem; }
.rev-star.is-on { color: #f5a623; }
.rev-stars--lg .rev-star { font-size: 1.15rem; }

/* Bars are laid out on a fixed grid rather than flex so all five rows line up
   on the digit and the count, whatever the numbers are. */
.rev-bars { flex: 1 1 240px; min-width: 220px; display: grid; gap: 5px; }
.rev-bar {
  display: grid;
  grid-template-columns: 12px 1fr 28px;
  align-items: center;
  gap: 10px;
  font: 600 0.8rem/1 Inter, sans-serif;
  color: var(--color-text-muted, #6b7280);
}
.rev-bar__track {
  height: 7px;
  border-radius: 4px;
  background: #e6e8ec;
  overflow: hidden;
}
.rev-bar__fill { display: block; height: 100%; background: #f5a623; }
.rev-bar__n { text-align: end; }

/* The verification sentence is a legal statement (§5b(3) UWG), so it sits
   directly under the score where it is read, not in a footnote. */
.rev-verify,
.rev-family {
  margin: 12px 0 0;
  font: 500 0.83rem/1.6 Inter, sans-serif;
  color: var(--color-text-muted, #6b7280);
}
.rev-family { font-weight: 600; }

/* ── one review ── */
.rev-list { margin-top: 26px; display: grid; gap: 18px; }
.rev-card {
  padding: 18px 20px;
  border: 1px solid var(--color-border, #e6e8ec);
  border-radius: 12px;
  background: #fff;
}
.rev-card__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 12px;
  margin-bottom: 8px;
}
.rev-card__author { font: 700 0.92rem/1.3 Inter, sans-serif; color: var(--color-text, #1f2937); }
.rev-card__badge {
  font: 700 0.68rem/1 Inter, sans-serif;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #047857;
  background: #ecfdf5;
  border: 1px solid #a7f3d0;
  border-radius: 999px;
  padding: 4px 8px;
}
.rev-card__date {
  margin-inline-start: auto;
  font: 500 0.78rem/1 Inter, sans-serif;
  color: var(--color-text-muted, #6b7280);
}
.rev-card__body {
  margin: 0;
  font: 400 0.94rem/1.65 Inter, sans-serif;
  color: #374151;
  /* Buyer-authored text: keep their line breaks, and never let a pasted URL
     or a long unbroken word widen the card past the page. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.rev-card__photos { margin-top: 12px; display: flex; flex-wrap: wrap; gap: 8px; }
.rev-card__photos img {
  width: 72px;
  height: 72px;
  object-fit: cover;
  border-radius: 8px;
  border: 1px solid var(--color-border, #e6e8ec);
  display: block;
}
.rev-card__reply {
  margin: 14px 0 0;
  padding: 12px 14px;
  border-inline-start: 3px solid var(--color-accent, #0a84ff);
  background: #f8fafc;
  /* Square against the accent bar, rounded away from it — which side that is
     depends on the writing direction. */
  border-start-end-radius: 8px;
  border-end-end-radius: 8px;
}
.rev-card__reply-who {
  display: block;
  margin-bottom: 4px;
  font: 700 0.8rem/1.3 Inter, sans-serif;
  color: var(--color-primary, #14304a);
}
.rev-card__reply p {
  margin: 0;
  font: 400 0.88rem/1.6 Inter, sans-serif;
  color: #4b5563;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.rev-more {
  margin: 20px auto 0;
  display: block;
  padding: 11px 22px;
  background: #fff;
  color: #334155;
  border: 1px solid #cbd5e1;
  border-radius: 9px;
  font: 700 0.9rem/1 Inter, sans-serif;
  cursor: pointer;
}
.rev-more:hover { background: #f1f5f9; }

/* ── the form ── */
.rev-form { display: grid; gap: 12px; }
.rev-form--compact { gap: 10px; }
.rev-form__title { margin: 0; font: 700 1.02rem/1.35 Inter, sans-serif; color: var(--color-text, #1f2937); }
.rev-form__rating { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.rev-form__label { font: 600 0.85rem/1.3 Inter, sans-serif; color: var(--color-text-muted, #6b7280); }
.rev-form__field { display: grid; gap: 5px; }
.rev-pick { display: inline-flex; gap: 2px; }
/* Big enough to hit with a thumb: this is the one control the whole form
   depends on, and it is mostly used on a phone from an email. */
.rev-pick__star {
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  font-size: 1.7rem;
  line-height: 1;
  color: #d5d9e0;
  transition: color 0.12s, transform 0.12s;
}
.rev-pick__star:hover { transform: scale(1.08); }
.rev-pick__star.is-on { color: #f5a623; }
.rev-pick__star:focus-visible { outline: 2px solid var(--color-accent, #0a84ff); outline-offset: 2px; border-radius: 6px; }
.rev-form__body,
.rev-form__name {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--color-border, #e6e8ec);
  border-radius: 9px;
  font: 400 0.93rem/1.55 Inter, sans-serif;
  color: #1f2937;
  background: #fff;
  resize: vertical;
}
.rev-form__body:focus,
.rev-form__name:focus { outline: 2px solid var(--color-accent, #0a84ff); outline-offset: 1px; border-color: transparent; }
.rev-form__photos { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.rev-form__addphoto {
  padding: 8px 14px;
  background: #fff;
  border: 1px dashed #cbd5e1;
  border-radius: 8px;
  font: 600 0.83rem/1 Inter, sans-serif;
  color: #475569;
  cursor: pointer;
}
.rev-form__thumbs { display: flex; gap: 6px; flex-wrap: wrap; }
.rev-form__thumb {
  width: 46px;
  height: 46px;
  object-fit: cover;
  border-radius: 6px;
  border: 1px solid var(--color-border, #e6e8ec);
}
/* What we do with the review, said before the submit button rather than after
   it — consent has to be informed to be worth anything. */
.rev-form__policy {
  margin: 0;
  font: 400 0.78rem/1.55 Inter, sans-serif;
  color: var(--color-text-muted, #6b7280);
}
.rev-form__submit {
  justify-self: start;
  padding: 12px 24px;
  background: #0a84ff;
  color: #fff;
  border: 0;
  border-radius: 9px;
  font: 700 0.93rem/1 Inter, sans-serif;
  cursor: pointer;
}
.rev-form__submit:hover { background: #0066cc; }
.rev-form__submit:disabled { background: #93c5fd; cursor: default; }
.rev-form__msg { margin: 0; font: 600 0.83rem/1.5 Inter, sans-serif; }
.rev-form__msg--info { color: var(--color-text-muted, #6b7280); }
.rev-form__msg--err { color: #b91c1c; }

.rev-thanks {
  padding: 16px 18px;
  border: 1px solid #a7f3d0;
  background: #ecfdf5;
  border-radius: 10px;
}
.rev-thanks__title { margin: 0 0 4px; font: 700 0.98rem/1.4 Inter, sans-serif; color: #065f46; }
.rev-thanks__body { margin: 0; font: 400 0.87rem/1.6 Inter, sans-serif; color: #047857; }

/* ── pages/review.html ── */
.rev-page__lead { margin: 0 0 20px; font: 400 0.95rem/1.6 Inter, sans-serif; color: #374151; }
.rev-page__note,
.rev-page__done { margin: 18px 0 0; font: 500 0.85rem/1.6 Inter, sans-serif; color: var(--color-text-muted, #6b7280); }
.rev-page__err { margin: 0; font: 500 0.92rem/1.6 Inter, sans-serif; color: #b91c1c; }

/* Order history, when the eligibility lookup was rate-limited. The "write a
   review" links are then absent for a reason the buyer can act on (reload in a
   few minutes) rather than because their orders cannot be reviewed, so this
   says which — advisory amber, not an error. */
.rev-busy-note {
  margin: 0 0 16px;
  padding: 12px 14px;
  border: 1px solid #fde68a;
  border-radius: 10px;
  background: #fffbeb;
  font: 500 0.85rem/1.6 Inter, sans-serif;
  color: #92400e;
}
.rev-page__item {
  padding: 18px 0 0;
  margin-top: 18px;
  border-top: 1px solid #f1f3f5;
}
.rev-page__item:first-of-type { border-top: 0; margin-top: 0; }
/* A signed-in buyer can have several reviewable orders on this page, so each
   one is separated hard enough that its forms can't be read as belonging to
   the order above. */
.rev-page__order + .rev-page__order {
  margin-top: 30px;
  padding-top: 26px;
  border-top: 2px solid var(--color-border, #e6e8ec);
}

@media (max-width: 640px) {
  .pe-reviews { padding: 34px 16px 4px; }
  .rev-summary { gap: 18px 24px; padding: 16px; }
  .rev-score { font-size: 2rem; }
  .rev-card { padding: 15px 16px; }
  .rev-card__date { margin-inline-start: 0; }
}

/* ============================================================
   LUGGAGE TAGS — MADE-TO-SIZE STEP
   The creative-shape acrylic and soft PVC tags are die cut to the buyer's own
   outline, so the supplier asks for a width and a height instead of a size off
   a list, and prices the average of the two. The grid above the inputs is the
   supplier's own price table, kept scrollable because the acrylic tag carries
   13 size rows against 8 quantity tiers.
   ============================================================ */
.lt-cs { display: flex; flex-direction: column; gap: 14px; }
.lt-cs[hidden] { display: none; }

.lt-cs-table-wrap {
  max-height: 268px;
  overflow: auto;
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-md);
  background: #fff;
  -webkit-overflow-scrolling: touch;
}
.lt-cs-table {
  width: 100%;
  /* Sticky cells need their own borders: with border-collapse the shared
     edge belongs to the table, and it scrolls away from the frozen cell. */
  border-collapse: separate;
  border-spacing: 0;
  font-size: 0.82rem;
}
.lt-cs-table th,
.lt-cs-table td {
  padding: 7px 11px;
  text-align: end;
  white-space: nowrap;
  border-bottom: 1px solid #eef1f4;
}
.lt-cs-table thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: #f8fafc;
  font-weight: 700;
  color: var(--color-text-muted);
}
/* The size column holds still while the quantity tiers scroll sideways —
   otherwise a price in the middle of the grid belongs to no visible size. */
.lt-cs-table tbody th {
  position: sticky;
  inset-inline-start: 0;
  z-index: 1;
  background: #fff;
  text-align: start;
  font-weight: 700;
  color: var(--color-text);
}
.lt-cs-table thead th:first-child { inset-inline-start: 0; z-index: 3; }
.lt-cs-table tbody tr:last-child th,
.lt-cs-table tbody tr:last-child td { border-bottom: 0; }
/* The row the buyer's own dimensions land on, and the tier their quantity
   buys — so the number being charged is findable in the grid. */
.lt-cs-table tbody tr.is-active th,
.lt-cs-table tbody tr.is-active td { background: var(--color-accent-soft); }
.lt-cs-table .is-tier { color: var(--color-accent-dark); font-weight: 700; }

.lt-cs-lead,
.lt-cs-note { margin: 0; font-size: 0.86rem; color: var(--color-text-muted); }
.lt-cs-note { font-weight: 600; color: var(--color-text); }

.lt-cs-dims {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px;
}
@media (max-width: 480px) { .lt-cs-dims { grid-template-columns: 1fr; } }
.lt-cs-label {
  display: block;
  margin-bottom: 6px;
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--color-text);
}
.lt-cs-range { margin: 6px 0 0; font-size: 0.78rem; font-weight: 600; color: var(--color-accent); }
/* Shown while a side is outside the range the style allows. The buyer's own
   number stays in the box — snapping it to the nearest legal value taught them
   nothing and lost what they meant to type. */
.lt-cs-error {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 6px 0 0;
  padding: 7px 10px;
  border-radius: 6px;
  background: #fef2f2;
  font-size: 0.8rem;
  font-weight: 700;
  color: #b91c1c;
}
.lt-cs-error::before { content: "\26a0"; font-size: 0.9rem; }
.lt-cs-error[hidden] { display: none; }
.lt-cs .text-field.lt-cs-invalid {
  border-color: #dc2626;
  box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.15);
}
.lt-cs .text-field.qty-invalid {
  border-color: #dc2626;
  box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.15);
}

.lt-cs-result { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.lt-cs-result-label { font-size: 1.05rem; font-weight: 700; }
.lt-cs-result-value { font-size: 1.3rem; font-weight: 800; color: var(--color-accent); }
/* Shown only where the average falls between this style's price rows. */
.lt-cs-priced-as { font-size: 0.82rem; font-weight: 600; color: var(--color-text-muted); }
.lt-cs-priced-as[hidden] { display: none; }
.lt-cs-field--qty { max-width: 220px; }

/* ==================================================================
   RTL exceptions — the only rules that need a direction-specific copy.

   Everything else in this sheet mirrors on its own, because the physical
   left/right properties were replaced with logical ones (margin-inline-start,
   inset-inline-end, text-align: start, border-start-end-radius…) which resolve
   against <html dir>. That is deliberate: a full [dir="rtl"] override sheet
   would mean two copies of every rule to keep in step, and copies drift.

   What lands here is only what CSS gives no logical form to:

     · transform — translateX has no writing-mode-aware equivalent, so a
       panel that slides in from the trailing edge needs its sign flipped.
     · background-position — no logical keywords, so a caret pinned 14px from
       the right has to be re-pinned 14px from the left.
     · box-shadow — the horizontal offset is a physical length.

   Horizontal CENTRING (`left: 50%` + `translateX(-50%)`) is deliberately NOT
   here and deliberately still physical: centring does not depend on direction,
   and making the `left` half logical is what would break it.
   ================================================================== */

/* Mobile nav drawer: sits on the leading edge, so it hides off the leading
   side — which is the right-hand side once the page is mirrored. */
/* Breakpoint must match the rule it overrides (line ~1131) or the drawer stays
   unmirrored in the gap between the two widths. */
@media (max-width: 960px) {
  [dir="rtl"] .main-nav { transform: translateX(100%); }
  [dir="rtl"] .main-nav.open { transform: translateX(0); }
}

/* Cart drawer: pinned to the trailing edge (inset-inline-end), which mirrors
   to the left, so it must hide off the left and cast its shadow rightwards. */
[dir="rtl"] .cart-drawer {
  transform: translateX(-105%);
  box-shadow: 16px 0 40px rgba(15, 23, 42, 0.18);
}
[dir="rtl"] .cart-drawer.is-open { transform: translateX(0); }

/* Hover nudge on a category card's "see all" arrow — it should move the way
   the reader is heading, not always to the right. */
[dir="rtl"] .category-card:hover .card-meta span:last-child {
  transform: translateX(-4px);
}

/* Toggle knob travels from the leading edge to the trailing one. */
[dir="rtl"] .acct-switch input:checked + .acct-switch-track::before {
  transform: translateX(-20px);
}

/* <select> carets are background images pinned to the trailing edge. The
   padding that reserves room for them is already logical, so only the image
   position needs mirroring — otherwise the caret sits on top of the text. */
[dir="rtl"] .cc-select.text-field,
[dir="rtl"] .ck-select,
[dir="rtl"] .wb-select { background-position: left 14px center; }
[dir="rtl"] .msg-grid-cell select.text-field { background-position: left 12px center; }
[dir="rtl"] .wb-select--big { background-position: left 16px center; }

/* Category banners and hero slides DO NOT mirror, and that is deliberate.

   The photographs are composed for this layout: the left ~40% is empty
   backdrop and the products sit on the right (the same fact the hero-scrim
   comment above depends on). The cream scrim is a 90deg gradient that fades
   out by 36%, so the only readable zone in the frame is the left edge —
   a property of the JPEG, not of the CSS.

   Mirroring here therefore breaks two ways at once: swapping the photo anchor
   crops the products off, and letting the copy follow the text direction to
   the right drops the headline straight on top of the lanyards, over the
   transparent tail of the scrim. So the photo stays anchored right, and the
   copy column is pinned back to the physical left with justify-self: end
   (in RTL, inline `end` IS the left edge). The Arabic inside it still reads
   right-to-left and sits flush right within its own column, which is what a
   reader expects; only the column's side of the frame is held still.

   Flipping the art with scaleX(-1) is not an option: these shots contain
   legible print ("SHAW ELEMENTARY" on the lanyards, card faces on the ID
   banner) and mirrored text reads as a mistake. A properly mirrored banner
   needs new photography with the empty side on the right — an asset job, and
   the only thing that would let this block be deleted.

   --split banners are excluded on purpose: they have a real two-column grid
   with an <img> media column, so they mirror correctly on their own. */
[dir="rtl"] .category-banner:not(.category-banner--split) .category-banner__text {
  justify-self: end;
}

/* ---- Directional glyphs -------------------------------------------

   Chevrons and arrows carry meaning through the way they POINT, and the bidi
   algorithm does not mirror them: U+203A stays "›" no matter the direction.
   The buttons themselves already swapped sides (their insets are logical), so
   without this the "next" control sits on the left pointing right, i.e. back
   at the slide you just came from — worse than not mirroring at all.

   scaleX(-1) rather than swapping the characters, because the same rule then
   covers the inline SVG chevron in the hero, the text glyph in the product
   carousel and the "→" in the account adjustment row alike. Where the element
   already carries a transform it has to be restated in full, or the existing
   one is simply dropped. */
[dir="rtl"] .hero-arrow svg,
[dir="rtl"] .hero-cta .btn > svg,
[dir="rtl"] .btn-customize > svg,
[dir="rtl"] .acct-adj-arrow,
[dir="rtl"] .breadcrumb .sep { transform: scaleX(-1); display: inline-block; }
[dir="rtl"] .pd-carousel-btn { transform: translateY(-50%) scaleX(-1); }

/* ---- Bidi isolation for figures that are never Arabic ----------------

   Money, percentages and dimensions are Latin runs no matter the language, and
   inside an RTL paragraph the bidi algorithm hands their leading sign to the
   paragraph's direction: "−12%" comes out as "12%−", and "€1,880.00" can lose
   its symbol to the far side. `direction: ltr` on the element gives the run its
   own paragraph so the sign stays put, and `unicode-bidi: isolate` stops it
   from reordering the Arabic around it.

   Deliberately a short list rather than every class with "price" in the name.
   These elements only ever hold digits, a currency symbol and punctuation, so
   forcing LTR inside them cannot mangle Arabic text — a wider net would, and
   the mistake would be invisible until someone reads the page.

   NOT the whole job: quantity + unit ("100 pcs"), sizes ("3.15\"") and part
   numbers need the same treatment, but which of them actually break can only
   be judged against real Arabic copy, so that pass belongs with the
   translations rather than ahead of them. */
[dir="rtl"] .price,
[dir="rtl"] .price-each,
[dir="rtl"] .pd-price,
[dir="rtl"] .pd-price-each,
[dir="rtl"] .summary-total,
[dir="rtl"] .cart-line-price,
[dir="rtl"] .cart-drawer-subtotal,
[dir="rtl"] .acct-money-charged,
[dir="rtl"] .acct-money-save,
[dir="rtl"] .acct-money-sub,
[dir="rtl"] .acct-promo-total,
[dir="rtl"] .lt-cs-result-value {
  direction: ltr;
  unicode-bidi: isolate;
}

/* Emails, phones, passwords and mailto: text are Latin runs. Without
   isolation an RTL paragraph can visually flip 123@qq.com. Surrounding
   Arabic stays RTL. Do not put names or body copy on this list. */
[dir="rtl"] input[type="email"],
[dir="rtl"] input[type="tel"],
[dir="rtl"] input[type="url"],
[dir="rtl"] input[type="password"] {
  direction: ltr;
  unicode-bidi: isolate;
  text-align: left;
}
[dir="rtl"] a[href^="mailto:"],
[dir="rtl"] .acct-menu-email,
[dir="rtl"] .acct-profile-email,
[dir="rtl"] .acct-set-row[data-field="email"] .acct-set-text,
[dir="rtl"] .acct-kv-value[dir="ltr"],
[dir="rtl"] .agent-cust-mail {
  direction: ltr;
  unicode-bidi: isolate;
}
