/* ============================================================
   Premium nightlife referral engine — design system
   - Dark base, vibrant gradient accents (configurable per campaign)
   - Geist (Vercel/Stripe-style clean grotesque) — same family used at all sizes
   - Festival energy from oversized type, gradients, noise — not from a serif
   - Mobile-first, generous whitespace
   ============================================================ */

@import url('https://fonts.googleapis.com/css2?family=Geist:wght@300;400;500;600;700;800;900&family=Geist+Mono:wght@500;700&display=swap');

/* ============================================================
   v9.34 phase A — accessibility baseline (WCAG 2.1 AA)
   ============================================================ */

/* Visually-hidden text that screen readers still read.
   Pattern: pair an icon glyph (decorative, aria-hidden) with an
   sr-only label so SR users hear "Share" instead of "↗︎". */
.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;
}

/* Skip-to-content link. Hidden until focused. Lets keyboard users
   bypass the topbar nav and jump straight to <main>. WCAG 2.4.1. */
.skip-link {
  position: absolute;
  top: -100px;
  left: 8px;
  background: var(--accent, #ff6b35);
  color: #fff;
  padding: 12px 16px;
  border-radius: 8px;
  font-weight: 600;
  text-decoration: none;
  z-index: 1000;
  transition: top 0.15s;
}
.skip-link:focus {
  top: 8px;
  outline: 2px solid #fff;
  outline-offset: 2px;
}

/* Focus indicators for buttons and links. Browsers' default focus
   rings are inconsistent and often invisible against a dark theme.
   :focus-visible only fires on keyboard focus — mouse clicks don't
   trigger it. So this doesn't ugly up click interactions. */
button:focus-visible,
a:focus-visible,
[role="button"]:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--accent, #ff6b35);
  outline-offset: 2px;
  border-radius: 4px;
}

/* prefers-reduced-motion — vestibular accessibility. Users who set
   this OS-level preference get instant transitions and no scroll-
   triggered reveals. We don't kill ALL animation (toast slide is
   functional feedback), but we shorten everything to ≤0.01s and
   neutralize keyframe animations. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ============================================================
   v9.34 phase B — empty / error / loading state primitives
   ============================================================ */

/* Error banner — appears when a fetch fails or a write operation
   errors. Shape: small icon, message, retry button. Tone is
   restrained (not alarming) because most failures are recoverable
   network blips, not system catastrophes. */
.state-error {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  background: rgba(239, 68, 68, 0.08);
  border: 1px solid rgba(239, 68, 68, 0.25);
  border-radius: 10px;
  color: var(--text);
  font-size: 14px;
  line-height: 1.5;
}
.state-error .state-error-msg { flex: 1; }
.state-error .state-error-detail {
  display: block;
  font-size: 12px;
  color: var(--text-muted);
  margin-top: 2px;
}
.state-error .btn {
  flex: 0 0 auto;
}

/* Empty-state block — for lists/tables/cards that have no data yet.
   Contains a title, optional subtitle, optional CTA. The point is to
   tell the user WHY it's empty and WHAT to do, not just show blank
   space. Centered and lightly framed so it reads as intentional
   rather than broken. */
.state-empty {
  text-align: center;
  padding: 40px 20px;
  background: var(--surface);
  border: 1px dashed var(--border-2);
  border-radius: 12px;
  color: var(--text-muted);
}
.state-empty .state-empty-icon {
  font-size: 28px;
  line-height: 1;
  margin-bottom: 12px;
  opacity: 0.5;
}
.state-empty .state-empty-title {
  color: var(--text);
  font-weight: 600;
  font-size: 16px;
  margin-bottom: 6px;
}
.state-empty .state-empty-body {
  font-size: 14px;
  margin-bottom: 16px;
}
.state-empty .state-empty-body:last-child { margin-bottom: 0; }

/* Loading skeleton — animated shimmer bars that approximate the
   shape of the loading content. Better than a "Loading…" text
   placeholder because the perceived layout doesn't shift when
   data arrives. */
.skeleton {
  background: linear-gradient(
    90deg,
    var(--surface) 0%,
    var(--surface-2) 50%,
    var(--surface) 100%
  );
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.4s ease-in-out infinite;
  border-radius: 6px;
  display: block;
  width: 100%;
  height: 14px;
  margin-bottom: 8px;
}
.skeleton-line       { height: 14px; }
.skeleton-line-lg    { height: 24px; }
.skeleton-card       { height: 80px; border-radius: 12px; }
.skeleton-row > * + * { margin-left: 12px; }
.skeleton-row {
  display: flex;
  align-items: center;
  margin-bottom: 12px;
}
@keyframes skeleton-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .skeleton { animation: none; opacity: 0.7; }
}


:root {
  /* Surface */
  --bg:           #0a0a10;
  --bg-1:         #0f1218;  /* v9.262 — was undefined; widely used between --bg and --bg-2 */
  --bg-2:         #0f0f17;
  --bg-3:         #1a1d24;  /* v9.262 — was undefined; card surface */
  --bg-4:         #232732;  /* v9.262 — was undefined; elevated card */
  --bg-elev:      #1a1d24;  /* v9.262 — was undefined; tooltip / popover */
  --card:         #0f0f17;  /* v9.262 — was undefined; card variant alias */
  --surface:      #16161f;
  --surface-2:    #1d1d28;
  --surface-3:    #25252f;
  --border:       rgba(255,255,255,0.08);
  --border-2:     rgba(255,255,255,0.14);

  /* Text */
  --text:         #f5f5f0;
  --text-muted:   rgba(245,245,240,0.62);
  --text-dim:     rgba(245,245,240,0.42);
  --muted:        rgba(245,245,240,0.62);  /* v9.262 — was undefined; alias for --text-muted */
  --muted-text:   rgba(245,245,240,0.62);  /* v9.262 — was undefined; alias for --text-muted */

  /* Semantic colors. v9.262 — all were undefined; used by status callouts
     and 2026-strategy alerts (money-on-table, under-priced flags, etc) */
  --primary:        #3b82f6;
  --primary-text:   #ffffff;
  --secondary:      #6b7280;
  --secondary-text: #ffffff;
  --success:        #10b981;
  --success-bg:     rgba(16,185,129,0.10);
  --success-text:   #10b981;
  --danger:         #ef4444;
  --danger-bg:      rgba(239,68,68,0.10);
  --danger-text:    #ef4444;
  --warning:        #f59e0b;
  --warning-bg:     rgba(245,158,11,0.10);
  --warning-text:   #f59e0b;
  --info:           #3b82f6;
  --info-bg:        rgba(59,130,246,0.10);
  --info-text:      #3b82f6;

  /* Layout. v9.262 — all were undefined; used by form controls + modal sizing */
  --button-height:    36px;
  --input-height:     38px;
  --card-padding:     16px;
  --modal-max-width:  720px;
  --max:              1200px;
  --radius-pill:      9999px;

  /* Typography. v9.262 — all were undefined; previously fell back per-usage */
  --font-family:           'Geist', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  --font-display:          'Geist', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  --font-weight-bold:      700;
  --font-weight-semibold:  600;

  /* Shadows. v9.262 — all were undefined; used by elevated modals */
  --shadow-sm:    0 1px 2px rgba(0,0,0,0.30);
  --shadow-md:    0 4px 12px rgba(0,0,0,0.40);
  --shadow-lg:    0 12px 40px rgba(0,0,0,0.50);

  /* Brand accents — overridden by /api/campaign at runtime */
  --accent:       #ff6b35;
  --accent-2:     #e83e8c;
  --accent-3:     #6f42c1;
  --gradient:     linear-gradient(135deg, #ff6b35 0%, #e83e8c 50%, #6f42c1 100%);
  --gradient-soft: linear-gradient(135deg, rgba(255,107,53,0.15) 0%, rgba(232,62,140,0.15) 50%, rgba(111,66,193,0.15) 100%);

  /* Status */
  --green:        #34d399;
  --yellow:       #fbbf24;
  --red:          #f87171;

  /* Geometry */
  --radius-sm:    10px;
  --radius:       16px;
  --radius-lg:    22px;

  /* Easing */
  --ease:         cubic-bezier(.2,.8,.2,1);

  /* Type — single Geist family, weights for hierarchy. NO serifs. */
  --display:      'Geist', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  --body:         'Geist', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  --mono:         'Geist Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  /* ========================================================================
     v9.60 — SEMANTIC SURFACE TOKENS
     ========================================================================
     The tokens above (--bg, --text, --text-muted) are the dark-app-shell
     defaults. The tokens below are surface-aware and exist to fix one
     specific class of bug:
       light/muted text rendered against a light surface (white card,
       warning card) becomes unreadable — light-on-light.
     The fix: every "non-default" surface variant gets its OWN explicit
     foreground colors. Components must reference these tokens (not
     inherit --text-muted via opacity blending) so the contrast is
     correct on every surface.
     Color choices target WCAG AA: body text ≥ 4.5:1, large text ≥ 3:1.
     The contrast checker test (test-runtime-design-system-contrast.js)
     enforces this on every commit.
     -------------------------------------------------------------------- */

  /* WHITE CARD — used on surfaces that explicitly set a white background
     (e.g. printable receipts, exports, light-themed dashboard widgets) */
  --surface-white-bg:           #ffffff;
  --surface-white-border:       #d4d4d4;        /* solid grey, NOT rgba(0,0,0,0.x) */
  --surface-white-text:         #18181b;        /* near-black; 16.5:1 vs white  */
  --surface-white-text-muted:   #525252;        /* zinc-600;   7.7:1 vs white   */

  /* WARNING CARD — pale-yellow background. Pre-v9.60 the .muted class
     applied the dark-shell --text-muted (off-white at 62% opacity) on
     this surface, producing white-on-pale-yellow → unreadable. */
  --surface-warning-bg:         #fef3c7;        /* amber-100 */
  --surface-warning-border:     #f59e0b;        /* amber-500 */
  --surface-warning-text:       #78350f;        /* amber-900;  9.2:1 vs amber-100 */
  --surface-warning-text-muted: #92400e;        /* amber-800;  6.8:1 vs amber-100 */

  /* DANGER CARD — pale-red background (NOT to be confused with the
     dark-shell --red status color which is light pink on dark). */
  --surface-danger-bg:          #fee2e2;        /* red-100 */
  --surface-danger-border:      #dc2626;        /* red-600 */
  --surface-danger-text:        #7f1d1d;        /* red-900;   8.7:1 vs red-100 */
  --surface-danger-text-muted:  #991b1b;        /* red-800;   7.0:1 vs red-100 */

  /* SUCCESS CARD — pale-green background */
  --surface-success-bg:         #d1fae5;        /* emerald-100 */
  --surface-success-border:     #059669;        /* emerald-600 */
  --surface-success-text:       #064e3b;        /* emerald-900; 9.5:1 vs emerald-100 */
  --surface-success-text-muted: #065f46;        /* emerald-800; 7.8:1 vs emerald-100 */

  /* INFO CARD — pale-blue background */
  --surface-info-bg:            #dbeafe;        /* blue-100 */
  --surface-info-border:        #2563eb;        /* blue-600 */
  --surface-info-text:          #1e3a8a;        /* blue-900;  9.0:1 vs blue-100 */
  --surface-info-text-muted:    #1e40af;        /* blue-800;  7.2:1 vs blue-100 */

  /* CRITICAL CARD — solid dark-red background (for must-not-miss
     blockers like "DO NOT LAUNCH"). White text on dark red. */
  --surface-critical-bg:        #991b1b;        /* red-800 */
  --surface-critical-border:    #b91c1c;        /* red-700 */
  --surface-critical-text:      #ffffff;        /* white;     7.0:1 vs red-800 */
  --surface-critical-text-muted: #fecaca;       /* red-200;   4.7:1 vs red-800 */

  /* DARK SURFACE (default app shell) — explicit aliases so components
     can opt into the same token names regardless of surface. The
     muted color here is the existing --text-muted but expressed as
     a SOLID color (not opacity) so it stays readable on bg-2/surface
     and doesn't blend with surrounding pixels. */
  --surface-default-bg:         var(--surface);
  --surface-default-border:     var(--border);
  --surface-default-text:       #f5f5f0;        /* matches --text */
  --surface-default-text-muted: #a3a3a3;        /* zinc-400;  7.5:1 vs --surface */
}

/* ============================================================== */
/* v9.262.0 — Modal transparency safety net                       */
/* ============================================================== */
/*
   Recurring bug: certain popups rendered transparent because they
   used inline styles like `background: var(--bg-1)` where the var
   was UNDEFINED and the fallback was missing — so `background` became
   `unset`, inheriting transparency from the body.
 
   v9.262 fixed this at the source by defining every previously-
   undefined variable in :root above. But because the codebase has
   many dynamically-built modals (HTML appended via JS, often by
   contributors who forget the fallback pattern), this safety net
   guarantees ALL future modal content will have an opaque background
   even if a developer slips up.
 
   Three layers of defense:
 
   1. Direct children of any [role="dialog"] or .modal-content node
      that aren't backdrops get an opaque bg if not explicitly styled.
   2. Common modal IDs we use (#evt-refs-info-modal, #tier-edit-modal,
      etc.) all have their inner content guaranteed opaque.
   3. Any element with class containing "modal" gets the opaque
      treatment.
 
   The rules use `background-color` (not `background`) so we don't
   stomp on legitimate gradient backgrounds. They also use a SOLID
   fallback chain so even if --bg-2 is also somehow unset, the
   #0f0f17 hex literal wins.
*/

/* Any modal *backdrop* (the full-screen overlay) is allowed to be
   semi-transparent — it's the whole point. But its DIRECT CHILD
   (the content panel) must be opaque. This rule sets that default
   without requiring any class. */
[role="dialog"] > div:not([role]):not(.modal-backdrop):empty,
[role="dialog"] > div:not([role]):not(.modal-backdrop):not(:empty) {
  /* If inline style explicitly sets a background, it wins (CSS
     specificity: inline > class). This rule provides the fallback. */
  background-color: #0f0f17;
}

/* Belt-and-suspenders for our specific modals. Setting background-color
   on the inner content div by ID directly. Each ID is a known modal
   content panel. */
#evt-refs-info-modal > div,
#tier-edit-modal > .modal-content,
#automation-edit-modal > .modal-content,
#order-detail-modal > div,
#buyer-detail-modal > div,
#help-modal > div,
#payout-modal > .modal-content,
#order-modal > .modal-content {
  background-color: #0f0f17;
}

/* For any DYNAMICALLY-created confirm modal whose inner content
   uses an inline style. These typically have inline background already
   so this just guarantees the fallback. */
[aria-modal="true"] > div:first-child:not(.modal-backdrop) {
  background-color: #0f0f17;
}

/* Make sure html/body themselves are never accidentally transparent
   (would expose the page-default white). */
html { background-color: #0a0a10; }

/* ============== Reset / base ============== */
*, *::before, *::after { box-sizing: border-box; }
html, body {
  margin: 0; padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--body);
  font-size: 16px;
  line-height: 1.55;
  font-feature-settings: 'ss01', 'ss02', 'cv11';  /* Geist alternates for cleaner glyphs */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  /* v9.39 phase G1 — mobile horizontal overflow safety net.
     If any single descendant overflows by a few pixels (a long URL,
     a slightly oversized button, a positioned glow) the whole page
     should NOT become horizontally scrollable on a phone. The product
     is mobile-first; whole-page horizontal scroll is never acceptable.
     Component-level overflow (admin tables in their wrappers) still
     scroll inside their container — only the viewport is capped. */
  width: 100%;
  max-width: 100%;
  overflow-x: hidden;
}
body { min-height: 100vh; }
/* v9.39 phase G1 — media safety. height:auto preserves aspect ratio
   when max-width clamps. object-fit:contain on <img> keeps logos
   inside their cells without stretching. SVG uses its own viewBox and
   doesn't need object-fit; canvas is bitmap. */
img, video, svg, canvas {
  max-width: 100%;
  height: auto;
}
img { object-fit: contain; }
img, video { display: block; }
a { color: inherit; text-decoration: none; }
button { font-family: inherit; }
input, textarea, select { font-family: inherit; font-size: 16px; }

/* ============== Type ==============
   Festival energy comes from SCALE + WEIGHT + TRACKING, not from a serif.
   Big bold display, tight tracking on huge sizes, mono for codes.
*/
.display {
  font-family: var(--display);
  font-weight: 800;
  letter-spacing: -0.035em;
  line-height: 0.98;
}
.display-italic {
  /* Class name retained for backward compat. Geist has no italic variant —
     rendered here as a heavy bold for emphasis. */
  font-family: var(--display);
  font-weight: 800;
  letter-spacing: -0.03em;
}
.eyebrow {
  font-family: var(--body);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* v9.39 phase F3 — campaign logo (Doc spec).
 *
 * Premium UX rule: the logo should NOT push the hero CTA below the fold,
 * even on small phones. We cap by both width AND height; object-fit:
 * contain preserves transparent PNG quality without forcing the operator
 * to upload at exact dimensions.
 *
 * The three size classes give admins control over visual prominence
 * without giving them raw pixel inputs (which could be set so large
 * they break mobile layout). The CSS enum is the only knob.
 */
.campaign-logo {
  max-width: 180px;
  max-height: 72px;
  object-fit: contain;
  display: block;
  margin: 0 auto 18px;
}
.campaign-logo.small  { max-width: 120px; max-height: 52px; }
.campaign-logo.medium { max-width: 180px; max-height: 72px; }
.campaign-logo.large  { max-width: 240px; max-height: 96px; }

/* v9.39 phase F4 — dashboard topbar logo. The topbar has a fixed
   ~56px row height, so the logo must be capped much smaller than the
   hero variant. Sizes scale proportionally with the same enum. */
.campaign-logo-topbar {
  object-fit: contain;
  height: 28px;
  max-height: 28px;
  max-width: 140px;
  margin-right: 10px;
  vertical-align: middle;
}
.campaign-logo-topbar.small  { height: 22px; max-height: 22px; max-width: 100px; }
.campaign-logo-topbar.medium { height: 28px; max-height: 28px; max-width: 140px; }
.campaign-logo-topbar.large  { height: 34px; max-height: 34px; max-width: 180px; }

.muted { color: var(--text-muted); }
.dim { color: var(--text-dim); }

h1, h2, h3 { margin: 0; font-family: var(--display); font-weight: 800; letter-spacing: -0.03em; }
h1 { font-size: clamp(44px, 8vw, 88px); line-height: 0.96; letter-spacing: -0.04em; }
h2 { font-size: clamp(28px, 4vw, 44px); line-height: 1.05; letter-spacing: -0.025em; }
h3 { font-size: 18px; line-height: 1.3; font-weight: 700; letter-spacing: -0.015em; }
p  { margin: 0 0 12px; }

code, .mono { font-family: var(--mono); font-feature-settings: 'ss01'; }

/* ============== Layout ============== */
.shell {
  max-width: 1080px;
  margin: 0 auto;
  padding: 24px;
}
.shell-narrow {
  max-width: 640px;
  margin: 0 auto;
  padding: 24px;
}
.spacer-sm { height: 16px; }
.spacer    { height: 32px; }
.spacer-lg { height: 56px; }
.spacer-xl { height: 88px; }

/* ============== Hero ============== */
.hero {
  position: relative;
  padding: 88px 24px 64px;
  overflow: hidden;
  isolation: isolate;
}
.hero::before {
  content: '';
  position: absolute; inset: 0;
  background: var(--gradient);
  opacity: 0.18;
  z-index: -2;
}
.hero::after {
  content: '';
  position: absolute; inset: 0;
  background-image:
    radial-gradient(circle at 20% 10%, rgba(255,107,53,0.35), transparent 40%),
    radial-gradient(circle at 80% 30%, rgba(232,62,140,0.25), transparent 45%),
    radial-gradient(circle at 50% 90%, rgba(111,66,193,0.30), transparent 50%);
  filter: blur(40px);
  opacity: 0.7;
  z-index: -1;
}
.hero-noise {
  position: absolute; inset: 0; pointer-events: none;
  z-index: -1; mix-blend-mode: overlay; opacity: 0.4;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1, 0 0 0 0 1, 0 0 0 0 1, 0 0 0 0.16 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
.hero-inner { max-width: 720px; margin: 0 auto; text-align: center; }

.hero h1 .accent {

  background: var(--gradient);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.hero p.lead {
  font-size: clamp(17px, 2vw, 21px);
  line-height: 1.5;
  color: var(--text-muted);
  margin: 24px auto 0;
  max-width: 540px;
}

/* ============== Cards ============== */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 28px;
}
.card + .card { margin-top: 16px; }
.card-elevated {
  background: linear-gradient(180deg, var(--surface-2) 0%, var(--surface) 100%);
  border: 1px solid var(--border-2);
  box-shadow: 0 12px 40px rgba(0,0,0,0.4);
}
.card-highlight {
  background:
    linear-gradient(var(--surface), var(--surface)) padding-box,
    var(--gradient) border-box;
  border: 1px solid transparent;
}

.card-row { display: flex; gap: 16px; flex-wrap: wrap; }
.card-row > * { flex: 1; min-width: 220px; }

/* v9.39 phase G2 — admin table overflow.
   Admin has 41 tables across the various tabs; many have 6+ columns
   (e.g. orders table, payouts table, code pool). On a 320–430px
   viewport these tables would force the whole page horizontally
   scrollable. Wrap rule: if a <table> sits inside a .card or .panel,
   the card itself becomes the scroll container — the table can be
   any width, but the page never overflows. -webkit-overflow-scrolling
   gives mobile Safari its momentum scroll inside the card.
   The spec is explicit on this distinction:
   "Horizontal scrolling inside a table wrapper is acceptable for
    complex admin tables. Horizontal scrolling of the whole page is
    not acceptable." */
.card:has(> table),
.card:has(> form > table),
.panel .card:has(table) {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
/* Fallback for browsers that don't support :has() yet — explicit
   .table-scroll wrapper class that admin templates can use directly.
   Going forward, new admin tables can be wrapped in <div class="table-scroll">
   for the same effect on legacy browsers. */
.table-scroll {
  max-width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

/* v9.39 phase G2 — modal content base.
   Five admin modals had inline `max-width: 640/720/760/900px;
   margin: 0 auto;` styles. At a 320px viewport with a 20px backdrop
   padding × 2, the modal content area is ~280px. The fixed max-widths
   are correct ceilings (modal won't go wider than 900px on desktop)
   but the inline styles never said `width: min(100%, 900px)`, so any
   internal element with explicit width could push the content past
   viewport. This class formalizes the responsive shape so future
   modals just add `class="modal-content"` and skip the inline.
   `width: min(100%, var(--max))` guarantees: at desktop, modal is 900px;
   at mobile, modal is 100% of available width. Either way, never wider. */
.modal-content {
  width: min(100%, var(--modal-max-width, 720px));
  margin: 0 auto;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 24px;
  position: relative;
  /* v9.39 phase G2 — modals can have wide internal tables (payout
     detail, order detail). Same overflow-inside-not-out rule applies. */
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

/* ============== v9.60 — Surface-aware card variants ==================
   Each variant binds its own background, border, color, AND its own
   .muted descendant rule so children don't inherit the dark-shell
   --text-muted (which would render light-on-light on these surfaces).
   Use these instead of inline `style="background:#fef3c7"` + class="muted".

   Pattern for descendants:
     <div class="card-warning">
       <strong>Heading on warning surface</strong>
       <div class="muted">Secondary text — uses surface-warning-text-muted</div>
     </div>

   The .muted rule is scoped by descendant selector so the SAME .muted
   class works correctly on every surface — no "muted-warning" variant
   needed. The token system handles it.

   Code-blocks inside variant cards inherit the surface's text color
   too, NOT the dark-shell mono token (which would be black on warning).
   ===================================================================== */

.card-warning,
.card-danger,
.card-success,
.card-info,
.card-critical {
  display: block;
  padding: 14px 16px;
  border-radius: var(--radius-sm);
  border-width: 1px;
  border-style: solid;
  margin: 14px 0;
  /* The body text color cascades to ALL descendants UNLESS something
     more specific (button, link) overrides. Note we use color (not
     a custom property) here — descendants get this color via inherit. */
}

/* WARNING — pale yellow */
.card-warning {
  background: var(--surface-warning-bg);
  border-color: var(--surface-warning-border);
  color: var(--surface-warning-text);
}
.card-warning .muted,
.card-warning .text-muted {
  color: var(--surface-warning-text-muted);
  /* Override any inherited opacity-derived muted color */
  opacity: 1;
}
.card-warning code,
.card-warning kbd {
  color: var(--surface-warning-text);
  background: rgba(0,0,0,0.06);
}
.card-warning a:not(.btn) {
  color: var(--surface-warning-text);
  text-decoration: underline;
}

/* DANGER — pale red */
.card-danger {
  background: var(--surface-danger-bg);
  border-color: var(--surface-danger-border);
  color: var(--surface-danger-text);
}
.card-danger .muted,
.card-danger .text-muted {
  color: var(--surface-danger-text-muted);
  opacity: 1;
}
.card-danger code,
.card-danger kbd {
  color: var(--surface-danger-text);
  background: rgba(0,0,0,0.06);
}
.card-danger a:not(.btn) {
  color: var(--surface-danger-text);
  text-decoration: underline;
}

/* SUCCESS — pale green */
.card-success {
  background: var(--surface-success-bg);
  border-color: var(--surface-success-border);
  color: var(--surface-success-text);
}
.card-success .muted,
.card-success .text-muted {
  color: var(--surface-success-text-muted);
  opacity: 1;
}
.card-success code,
.card-success kbd {
  color: var(--surface-success-text);
  background: rgba(0,0,0,0.06);
}
.card-success a:not(.btn) {
  color: var(--surface-success-text);
  text-decoration: underline;
}

/* INFO — pale blue */
.card-info {
  background: var(--surface-info-bg);
  border-color: var(--surface-info-border);
  color: var(--surface-info-text);
}
.card-info .muted,
.card-info .text-muted {
  color: var(--surface-info-text-muted);
  opacity: 1;
}
.card-info code,
.card-info kbd {
  color: var(--surface-info-text);
  background: rgba(0,0,0,0.06);
}
.card-info a:not(.btn) {
  color: var(--surface-info-text);
  text-decoration: underline;
}

/* CRITICAL — solid dark red. White text inverts the muted contrast
   pair: muted is LIGHT pink (red-200) on dark red, not dark text. */
.card-critical {
  background: var(--surface-critical-bg);
  border-color: var(--surface-critical-border);
  color: var(--surface-critical-text);
}
.card-critical .muted,
.card-critical .text-muted {
  color: var(--surface-critical-text-muted);
  opacity: 1;
}
.card-critical code,
.card-critical kbd {
  color: var(--surface-critical-text);
  background: rgba(255,255,255,0.12);
}
.card-critical a:not(.btn) {
  color: var(--surface-critical-text);
  text-decoration: underline;
}

/* ============== Buttons ============== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: var(--button-height, 52px);
  padding: 0 26px;
  border: none;
  border-radius: var(--radius-pill, 999px);
  font-weight: var(--font-weight-semibold, 600);
  font-size: 16px;
  letter-spacing: -0.01em;
  cursor: pointer;
  transition: all 0.18s var(--ease);
  white-space: nowrap;
  text-decoration: none;
  user-select: none;
}
.btn-primary {
  background: var(--gradient);
  color: #fff;
  box-shadow: 0 8px 24px rgba(255,107,53,0.25);
}
.btn-primary:hover {
  transform: translateY(-1px);
  box-shadow: 0 12px 32px rgba(255,107,53,0.35);
}
.btn-primary:active { transform: translateY(0); }
.btn-secondary {
  background: var(--surface-2);
  color: var(--text);
  border: 1px solid var(--border-2);
}
.btn-secondary:hover { background: var(--surface-3); border-color: rgba(255,255,255,0.22); }
.btn-ghost {
  background: transparent; color: var(--text-muted);
  border: 1px solid var(--border);
}
.btn-ghost:hover { color: var(--text); border-color: var(--border-2); }

.btn-sm { height: 40px; padding: 0 18px; font-size: 14px; }
.btn-lg { height: 60px; padding: 0 32px; font-size: 17px; }
.btn-block { width: 100%; }
.btn:disabled,
.btn-primary:disabled,
.btn-secondary:disabled,
.btn-ghost:disabled { opacity: 0.5; cursor: not-allowed; }

/* v9.73.0 — Premium UI consistency.
   The original variants (.btn-primary / .btn-secondary / .btn-ghost) only
   carried color rules; layout (height, padding, radius, font-size) lived
   on .btn alone. So `class="btn-primary"` without the `btn` base shipped
   with default browser sizing — visibly inconsistent with `class="btn btn-primary"`.
   Audit at v9.73.0 ship time: 41 admin.html `class="btn-X..."` occurrences
   shipped without the .btn base. Fix: have the variants carry the same
   base layout. Backward-compatible with the sites that already write
   `btn btn-X` (the rules are identical; later cascade simply re-asserts
   the same values). */
.btn-primary,
.btn-secondary,
.btn-ghost {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: var(--button-height, 52px);
  padding: 0 26px;
  border-radius: var(--radius-pill, 999px);
  font-family: inherit;
  font-weight: var(--font-weight-semibold, 600);
  font-size: 16px;
  letter-spacing: -0.01em;
  cursor: pointer;
  transition: all 0.18s var(--ease);
  white-space: nowrap;
  text-decoration: none;
  user-select: none;
}
/* Allow .btn-sm / .btn-lg to override sizing whether or not .btn is present */
.btn-primary.btn-sm, .btn-secondary.btn-sm, .btn-ghost.btn-sm {
  height: 40px; padding: 0 18px; font-size: 14px;
}
.btn-primary.btn-lg, .btn-secondary.btn-lg, .btn-ghost.btn-lg {
  height: 60px; padding: 0 32px; font-size: 17px;
}

/* v9.73.0 — Report action row utility.
   Used at the top of every report-style admin tab (Cockpit, Money Safety,
   Overview, ROI, Cities, Attribution Review, Debug Console, Audit Trail,
   Edge Cases, Health Check). Consistent gap, alignment, wrap, and
   bottom margin so the action row reads identically across tabs. */
.report-actions {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
  margin: 0 0 14px;
}
.report-actions .report-last-run {
  font-size: 12px;
  color: var(--text-muted);
  margin-left: 4px;
}
.report-actions > .spacer { flex: 1 1 auto; }

/* ============== Forms ============== */
label {
  display: block;
  font-size: 13px; font-weight: 500;
  color: var(--text-muted);
  margin: 18px 0 8px;
  letter-spacing: 0.01em;
}
input[type="text"], input[type="email"], input[type="tel"], input[type="date"],
input[type="password"], input[type="number"], input:not([type]), select, textarea {
  width: 100%;
  padding: 14px 16px;
  background: var(--bg-2);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: 15px;
  transition: border-color 0.15s var(--ease), background 0.15s var(--ease);
}
input:focus, select:focus, textarea:focus {
  /* v9.34 phase A — accessibility baseline.
     Pre-A: outline:none killed keyboard focus indicators entirely.
     Post-A: 2px accent ring with offset + brighter border. The ring
     uses box-shadow so it doesn't shift layout (outline does on
     some renderers). Mouse users still see clean borders; keyboard
     users get a visible focus state per WCAG 2.4.7. */
  outline: 2px solid transparent;
  outline-offset: 2px;
  border-color: var(--accent);
  background: var(--surface);
  box-shadow: 0 0 0 3px rgba(255, 107, 53, 0.25);  /* accent at 25% — matches --accent default; recolored via CSS var if a campaign overrides */
}
input::placeholder { color: var(--text-dim); }

.checkbox-row {
  display: flex; align-items: flex-start;
  gap: 12px;
  font-weight: 400;
  color: var(--text-muted);
  font-size: 14px;
  line-height: 1.5;
  margin-top: 16px;
  cursor: pointer;
}
.checkbox-row input { width: 18px; height: 18px; margin-top: 2px; flex-shrink: 0; accent-color: var(--accent); }

/* ============== Code display ============== */
.code-display {
  position: relative;
  text-align: center;
  padding: 32px 24px;
  border-radius: var(--radius);
  background:
    linear-gradient(var(--surface), var(--surface)) padding-box,
    var(--gradient) border-box;
  border: 1.5px solid transparent;
}
.code-display .code {
  font-family: var(--mono);
  font-size: clamp(28px, 5vw, 44px);
  font-weight: 700;
  letter-spacing: 0.04em;
  background: var(--gradient);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  /* v9.39 phase G1 — long codes (12+ chars) must wrap inside the
     card on 320px screens, not push the card off-viewport. The
     gradient text styling above is preserved. */
  overflow-wrap: anywhere;
  word-break: break-word;
  max-width: 100%;
}

/* v9.39 phase G1 — long-string wrap utilities.
   Long referral URLs, tracking links, customer emails, and caption
   previews must NOT push card width on small viewports. The default
   word-break breaks only at whitespace; URLs and emails have no
   whitespace and will overflow without overflow-wrap:anywhere.
   These classes are sprinkled on the elements that hold these
   strings (dashboard event links, success-pane code display, etc.)
   so the rule scopes precisely instead of being a global break-all
   that would also break English copy in mid-word. */
.url, .referral-link, .tracking-link, .email, .caption-preview {
  overflow-wrap: anywhere;
  word-break: break-word;
  max-width: 100%;
}

/* ============== Progress ============== */
.progress {
  position: relative;
  height: 10px;
  background: var(--surface-3);
  border-radius: 999px;
  overflow: hidden;
}
.progress-fill {
  position: absolute; inset: 0;
  width: 0;
  background: var(--gradient);
  border-radius: 999px;
  transition: width 0.6s var(--ease);
  box-shadow: 0 0 16px rgba(255,107,53,0.35);
}

/* ============== Badges & status pills ============== */
.badge {
  display: inline-flex;
  align-items: center;
  height: 26px;
  padding: 0 10px;
  border-radius: var(--radius-pill, 999px);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.02em;
  background: var(--surface-3);
  color: var(--text);
}
.badge-grad   { background: var(--gradient); color: #fff; }
.badge-green  { background: rgba(52,211,153,0.15); color: var(--green); }
.badge-yellow { background: rgba(251,191,36,0.15); color: var(--yellow); }
.badge-red    { background: rgba(248,113,113,0.15); color: var(--red); }

/* ============== Flash messages ============== */
.flash {
  padding: 14px 16px;
  border-radius: var(--radius-sm);
  font-size: 14px;
  line-height: 1.5;
  margin: 14px 0;
}
/* Dark-shell variants — translucent tint over dark bg, status-color text */
.flash-success { background: rgba(52,211,153,0.10); border: 1px solid rgba(52,211,153,0.25); color: var(--green); }
.flash-warn    { background: rgba(251,191,36,0.10); border: 1px solid rgba(251,191,36,0.25); color: var(--yellow); }
.flash-error   { background: rgba(248,113,113,0.10); border: 1px solid rgba(248,113,113,0.25); color: var(--red); }

/* v9.60 — surface-aware variants that pair with .card-warning/danger/success/info.
   Use these inside .card-warning etc. (or anywhere a solid pale-color
   alert is wanted). Names match Bootstrap/Tailwind conventions
   (warning/danger) so the v9.59 upload UI's existing class names work
   without renaming. */
.flash-warning {
  background: var(--surface-warning-bg);
  border: 1px solid var(--surface-warning-border);
  color: var(--surface-warning-text);
}
.flash-warning .muted { color: var(--surface-warning-text-muted); opacity: 1; }

.flash-danger {
  background: var(--surface-danger-bg);
  border: 1px solid var(--surface-danger-border);
  color: var(--surface-danger-text);
}
.flash-danger .muted { color: var(--surface-danger-text-muted); opacity: 1; }

.flash-info {
  background: var(--surface-info-bg);
  border: 1px solid var(--surface-info-border);
  color: var(--surface-info-text);
}
.flash-info .muted { color: var(--surface-info-text-muted); opacity: 1; }

/* ============== Nav / topbar ============== */
.topbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 18px 24px;
  border-bottom: 1px solid var(--border);
  background: rgba(10,10,16,0.7);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  position: sticky; top: 0; z-index: 50;
}
.topbar .brand {
  font-family: var(--display);
  font-weight: 700;
  font-size: 18px;
  letter-spacing: -0.02em;
}
.topbar .brand .dot {
  display: inline-block; width: 8px; height: 8px;
  background: var(--gradient); border-radius: 50%;
  margin-right: 8px; vertical-align: middle;
}
.topbar nav { display: flex; gap: 8px; align-items: center; }

/* ============== Tabs (admin) ============== */
.tabs {
  display: flex; gap: 4px;
  padding: 6px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  overflow-x: auto;
  margin-bottom: 24px;
}
.tab {
  padding: 9px 16px;
  border-radius: var(--radius-sm, 10px);
  font-size: 14px; font-weight: 500;
  color: var(--text-muted);
  white-space: nowrap;
  cursor: pointer;
  transition: all 0.15s var(--ease);
}
.tab:hover { color: var(--text); }
.tab.active { background: var(--surface-3); color: var(--text); }

.panel { animation: fadeIn 0.3s var(--ease); }
@keyframes fadeIn { from {opacity:0;transform:translateY(4px)} to {opacity:1;transform:translateY(0)} }

/* ============== Tables ============== */
table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}
th, td {
  text-align: left;
  padding: 14px 12px;
  border-bottom: 1px solid var(--border);
}
th {
  font-weight: 500; font-size: 12px;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
tr:last-child td { border-bottom: none; }
tbody tr:hover { background: rgba(255,255,255,0.02); }

/* ============== Event link cards ============== */
.event-card {
  display: flex; flex-direction: column;
  gap: 10px;
  padding: 20px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  transition: border-color 0.15s var(--ease);
}
.event-card:hover { border-color: var(--border-2); }
.event-card .city {
  font-family: var(--display);
  font-weight: 700;
  font-size: 20px;
  letter-spacing: -0.025em;
  line-height: 1.15;
}
.event-card .url {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--text-muted);
  background: var(--bg-2);
  padding: 8px 10px;
  border-radius: 8px;
  word-break: break-all;
  max-height: 60px;
  overflow: hidden;
}
.event-card .actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 4px; }
.event-grid { display: grid; gap: 12px; grid-template-columns: 1fr; }
@media (min-width: 720px) { .event-grid { grid-template-columns: 1fr 1fr; } }

/* ============== Stats / metric tiles ============== */
.metric {
  padding: 22px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.metric .label { font-size: 12px; color: var(--text-muted); letter-spacing: 0.04em; text-transform: uppercase; }
.metric .value { font-family: var(--display); font-size: clamp(28px, 4vw, 40px); font-weight: 600; letter-spacing: -0.02em; margin-top: 6px; }
.metric .sub   { font-size: 13px; color: var(--text-muted); margin-top: 2px; }

.metric-grid { display: grid; gap: 12px; grid-template-columns: 1fr 1fr; }
@media (min-width: 720px) { .metric-grid { grid-template-columns: repeat(4, 1fr); } }

/* ============== Step explainer (3 step) ============== */
.steps {
  display: grid; gap: 16px;
  grid-template-columns: 1fr;
}
@media (min-width: 720px) { .steps { grid-template-columns: 1fr 1fr 1fr; } }
.step {
  padding: 24px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.step .num {
  font-family: var(--display);
  font-size: 36px; font-weight: 600;
  background: var(--gradient);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  line-height: 1;
}
.step h3 { margin-top: 12px; font-size: 18px; }
.step p { color: var(--text-muted); margin-top: 6px; font-size: 14px; line-height: 1.5; }

/* ============== Share buttons ============== */
.share-row {
  display: flex; flex-wrap: wrap; gap: 8px;
  margin-top: 16px;
  /* v9.39 phase G1 — never let the row force the parent card wider
     than the viewport. flex-wrap:wrap above handles overflow gracefully
     because direct children get min-width:0 below. */
  max-width: 100%;
}
/* v9.39 phase G1 — flex children default to min-width:auto, which
   refuses to shrink below their content's intrinsic width. A long
   button label ("Share via WhatsApp Status") plus its icon would force
   the parent flex container to be at least that wide, blocking wrap.
   min-width:0 lets the children shrink so flex-wrap can do its job. */
.share-row > *, .action-row > *, .flex-row > * { min-width: 0; }
.share-btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 10px 14px; font-size: 13px; font-weight: 500;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill, 999px);
  cursor: pointer;
  transition: all 0.15s var(--ease);
  /* The button itself must also be willing to shrink and wrap its
     label rather than overflow horizontally. */
  max-width: 100%;
}
.share-btn:hover { background: var(--surface-3); }

/* ============== Toast ============== */
.toast {
  position: fixed;
  left: 50%; bottom: 32px;
  transform: translateX(-50%) translateY(100px);
  background: var(--surface-2);
  border: 1px solid var(--border-2);
  color: var(--text);
  padding: 12px 18px;
  border-radius: 999px;
  font-size: 14px; font-weight: 500;
  box-shadow: 0 12px 32px rgba(0,0,0,0.5);
  opacity: 0;
  transition: all 0.3s var(--ease);
  pointer-events: none;
  z-index: 100;
}
.toast.show {
  transform: translateX(-50%) translateY(0);
  opacity: 1;
}

/* ============== Reveal animation ============== */
.reveal {
  opacity: 0;
  transform: translateY(12px);
  animation: reveal 0.7s var(--ease) forwards;
}
.reveal-2 { animation-delay: 0.1s; }
.reveal-3 { animation-delay: 0.2s; }
.reveal-4 { animation-delay: 0.3s; }
@keyframes reveal { to { opacity: 1; transform: translateY(0); } }

/* ============== Misc ============== */
hr { border: none; border-top: 1px solid var(--border); margin: 24px 0; }
.text-center { text-align: center; }
.flex-row { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.flex-grow { flex: 1; }

/* Mobile tweaks */
@media (max-width: 640px) {
  .hero { padding: 64px 20px 40px; }
  .card { padding: 22px; }
  .topbar { padding: 14px 20px; }
  /* Mobile finger-friendly share buttons. 44pt minimum tap target per Apple
     HIG; we go a bit larger because they're typed copy with text labels. */
  .share-btn { padding: 13px 18px; font-size: 14px; min-height: 44px; }
  .share-row { gap: 10px; }
  /* Stack the success-pane CTAs on small screens so "Copy my link" is the
     unmistakable primary action, not crammed beside a ghost button. */
  .card.card-highlight .flex-row { flex-direction: column; align-items: stretch; }
  .card.card-highlight .flex-row .btn-lg { width: 100%; }
  .card.card-highlight .flex-row .btn-ghost { width: 100%; }

  /* v9.39 phase G1 — mobile overflow prevention.
     Reduce shell padding from 24px to 16px so cards aren't crammed
     against narrow-viewport edges. */
  .shell, .shell-narrow {
    padding-left: 16px;
    padding-right: 16px;
  }
  .container {
    padding-left: 16px;
    padding-right: 16px;
  }
  /* Cards must never exceed viewport width. Their content (URLs, codes)
     wraps via the wrap utilities defined above the media query. */
  .card, .panel { max-width: 100%; }

  /* Share-row buttons — go full width so they stack cleanly rather
     than sit at irregular widths. Spec example: Copy / WhatsApp /
     Text / Copy Caption all stack 1-per-row at 320px. */
  .share-row .share-btn,
  .share-row .btn,
  .action-row .btn { flex: 1 1 100%; }

  /* Stat / row utility children — relax the 200/220px min-width so
     2-column layouts don't force a 440px+ minimum and overflow. */
  .row > * { min-width: 0; }
  .card-row > * { min-width: 0; }
  .stats { grid-template-columns: 1fr; }

  /* v9.39 phase G1.1 — the signup form actual overflow fix.
     .card-row is used for the form's 2-column input rows
     (first/last name, phone/city, instagram/tiktok). Pre-fix,
     `.card-row > * { min-width: 220px }` forced 2 children at 220px
     + 16px gap = 456px, overflowing the ~288px-wide card content
     area on a 320px viewport. The min-width:0 above lets them shrink
     to ~136px each but inputs at 136px are unusably narrow.
     Right fix: collapse to single column so each input gets the
     full card width. flex-direction:column flips the row into a
     stack; align-items:stretch ensures children fill width. */
  .card-row {
    flex-direction: column;
    align-items: stretch;
  }
  .card-row > * { width: 100%; }

  /* v9.39 phase G2 — modal-content padding shrunk on mobile.
     Backdrop already has padding: 40px 20px so the modal-content
     can keep its own padding tight. 18px keeps it readable while
     leaving more room for inner tables / forms. */
  .modal-content { padding: 18px; }

  /* Topbar — at narrow widths the brand + email + sign-out button
     can collide. Allow wrap; the brand still anchors left, the sign
     out button drops to a second row when needed. */
  .topbar {
    flex-wrap: wrap;
    gap: 8px;
  }
  .topbar nav { flex-shrink: 1; min-width: 0; }
  .topbar #nav-email {
    /* The email is the longest text in the topbar and the most likely
       to push the row wide. Truncate visually rather than wrap (the
       email is just identification context, not interactive). */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 60vw;
  }

  /* v9.72.0 — mobile horizontal scroll audit.
     Inline `min-width: NNNpx` on flex children is desktop-side intent
     (e.g. side-by-side fields with sane minimums on a 1200px shell).
     On a 320–430px viewport, ANY min-width ≥ 280px overflows the page
     before flex-wrap can rescue it — even wrap requires children to
     be ≤ container before it kicks in. The clean fix is to force any
     inline min-width to 0 on mobile so flex-wrap can do its job. The
     `!important` is necessary because we're overriding inline style.
     Cost: a deliberate mobile min-width on an inline-styled element
     is killed too — but on phones, deliberate min-widths are usually
     wrong anyway (content should fluidly fill the viewport). */
  [style*="min-width:"] { min-width: 0 !important; }

  /* v9.72.0 — long unbreakable strings inside table cells.
     UUIDs, sha hashes, IDs, and URLs in code/anchor children of <td>
     don't break by default. The .card:has(> table) wrap from v9.39
     gives the TABLE a horizontal scroll, but a single 64-char hash in
     a cell still pushes a column to 600px+, which makes the table
     scrollable EVERY VIEW even when the rest of the data is narrow.
     Force long content to break inside cells so table widths reflect
     the data shape, not the worst-case ID. `overflow-wrap: anywhere`
     is the modern, aggressive break point that breaks even at no
     break-opportunity (long unbroken strings); `word-break: break-word`
     is the older fallback. Use both. */
  td code, td a, td span[title] {
    overflow-wrap: anywhere;
    word-break: break-word;
  }

  /* v9.72.0 — aspect-ratio preview blocks (v9.70.0 artwork preview).
     Fixed `width: 320px` boxes overflow the shell padding on a 320px
     viewport. Cap to viewport-relative so they shrink gracefully.
     This rule targets ANY preview box that uses inline aspect-ratio
     (the v9.70.0 pattern); future preview blocks inherit the fix. */
  [style*="aspect-ratio"] { max-width: 100%; }
}

/* ============================================================
   Admin compatibility classes
   The admin reuses some simpler primitives. These are scoped so
   they don't conflict with the public design system.
   ============================================================ */
.container { max-width: 1200px; margin: 0 auto; padding: 24px; }

/* v9.162.9 — when `.container` wraps the admin shell, lift its 1200px
   cap and 24px padding so the admin grid fills the viewport. The
   v9.162.8 fix removed `max-width: 1480px` from `.admin-main`, but the
   parent `.container` was still capping the whole layout at 1200px and
   adding 24px of horizontal padding — that's why operators didn't see
   any visible change after v9.162.8 deployed.

   Targeted via `:has()` so other `.container` usage on the site (the
   referral signup page, public screens, etc.) keeps its 1200px reading
   width unchanged. The admin shell owns its own padding via
   `.admin-sidebar { padding: 28px 16px 16px }` and `.admin-main
   { padding: 36px 44px 80px }`, so removing the `.container` padding
   here doesn't lose any spacing. */
.container:has(> .admin-shell) {
  max-width: none;
  padding: 0;
}

.stats { display: grid; gap: 12px; grid-template-columns: 1fr 1fr; margin: 16px 0 28px; }
@media (min-width: 720px) { .stats { grid-template-columns: repeat(4, 1fr); } }
.stat {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 18px;
}
.stat .label { font-size: 12px; color: var(--text-muted); letter-spacing: 0.04em; text-transform: uppercase; }
.stat .value { font-family: var(--display); font-size: 28px; font-weight: 600; margin-top: 4px; }

.row { display: flex; gap: 12px; flex-wrap: wrap; }
.row > * { flex: 1; min-width: 200px; }

.copy-btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 12px; font-size: 13px; font-weight: 500;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: 8px;
  cursor: pointer; transition: all 0.15s var(--ease);
  text-decoration: none;
}
.copy-btn:hover { background: var(--surface-3); }

.badge-grey { background: rgba(255,255,255,0.08); color: var(--text-muted); }

/* When admin uses bare <nav> for its tab bar */
nav.admin-nav {
  display: flex; gap: 4px; align-items: center; flex-wrap: wrap;
  padding: 14px 20px;
  background: rgba(10,10,16,0.7);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 50;
}
nav.admin-nav strong {
  font-family: var(--display);
  font-weight: 700; font-size: 16px;
  letter-spacing: -0.02em;
  margin-right: 16px;
}
nav.admin-nav .tab {
  padding: 8px 14px; border-radius: 8px;
  font-size: 13px; font-weight: 500;
  color: var(--text-muted); cursor: pointer;
  transition: all 0.15s var(--ease);
  text-decoration: none;
}
nav.admin-nav .tab:hover { color: var(--text); }
nav.admin-nav .tab.active { background: var(--surface-3); color: var(--text); }
nav.admin-nav a#logout { font-size: 13px; color: var(--text-muted); }
nav.admin-nav a#logout:hover { color: var(--text); }

/* Admin-specific: panels use minimal top spacing */
.panel h1 { font-size: 28px; margin-bottom: 6px; }
.panel h3 { font-size: 16px; }

/* v9.41 phase H12 — buyer success celebration (§8 premium UX).
   Subtle but felt: when the success card reveals, the eyebrow gets
   a brief shimmer and the card a soft pulse glow. No JS, no library —
   pure CSS animation triggered by adding the .celebrate class to
   the success card on reveal. Respects prefers-reduced-motion. */
@keyframes celebrate-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(127, 223, 176, 0.5); }
  60%  { box-shadow: 0 0 0 16px rgba(127, 223, 176, 0); }
  100% { box-shadow: 0 0 0 0 rgba(127, 223, 176, 0); }
}
@keyframes celebrate-shimmer {
  0%   { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}
.success-card-celebrate {
  animation: celebrate-pulse 1.2s ease-out 0.2s 1;
}
.success-card-celebrate .eyebrow {
  background: linear-gradient(
    90deg,
    var(--accent) 0%,
    rgba(255,255,255,0.95) 50%,
    var(--accent) 100%
  );
  background-size: 200% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: celebrate-shimmer 1.6s ease-in-out 0.2s 1;
}
@media (prefers-reduced-motion: reduce) {
  .success-card-celebrate,
  .success-card-celebrate .eyebrow {
    animation: none;
  }
}

/* Group-chat psychology nudge — soft glow, slightly emphasized. */
.success-share-nudge {
  font-weight: 500;
  color: var(--text);
  opacity: 0.95;
}

/* ============== v9.61 — Mobile bottom-nav ==================
   Desktop default: bottom-nav hidden (display:none).
   Mobile (<= 640px): bottom-nav visible at viewport bottom; the
   horizontal admin-nav strip is hidden. A bottom sheet opens when
   a group button is tapped.

   The bar sits in fixed position with env(safe-area-inset-bottom)
   padding so on iPhones with the home indicator the buttons don't
   sit ON TOP of the indicator (un-tappable area).

   Tokens used: --surface-default-bg, --surface-default-text,
   --surface-default-text-muted (all v9.60 semantic tokens). The
   buttons are dark-on-dark and the active state uses --surface-3
   for parity with the desktop tab-bar.
   ============================================================ */

.mobile-nav-bar {
  display: none;  /* hidden on desktop; mobile breakpoint flips to flex */
}

.mobile-nav-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.55);
  z-index: 998;
}
.mobile-nav-backdrop:not([hidden]) { display: block; }

.mobile-nav-sheet {
  display: none;
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 999;
  background: var(--surface);
  border-top: 1px solid var(--border-2);
  border-top-left-radius: 16px;
  border-top-right-radius: 16px;
  /* Safe-area: iPhones with the home indicator need ≥ 8px padding-bottom
     beyond env(safe-area-inset-bottom); without it the last list item
     can fall behind the indicator. */
  padding: 12px 12px calc(env(safe-area-inset-bottom, 0px) + 16px);
  max-height: 70vh;
  overflow-y: auto;
  box-shadow: 0 -4px 24px rgba(0,0,0,0.4);
  animation: mobileSheetIn 0.18s var(--ease);
}
.mobile-nav-sheet:not([hidden]) { display: block; }

@keyframes mobileSheetIn {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}

.mobile-nav-sheet-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 4px 8px 12px;
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  border-bottom: 1px solid var(--border);
}
.mobile-nav-sheet-header button {
  background: transparent;
  border: 0;
  color: var(--text-muted);
  font-size: 18px;
  width: 32px;
  height: 32px;
  border-radius: 8px;
  cursor: pointer;
}
.mobile-nav-sheet-header button:hover {
  background: var(--surface-2);
  color: var(--text);
}

.mobile-nav-sheet-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding-top: 8px;
}
.mobile-nav-sheet-body .mobile-nav-sheet-tab {
  display: flex;
  align-items: center;
  padding: 14px 12px;
  /* iOS HIG: 44pt minimum tap target; we use 48px to give a clear
     border around the text. */
  min-height: 48px;
  font-size: 15px;
  border-radius: 10px;
  color: var(--text);
  text-decoration: none;
  cursor: pointer;
  background: transparent;
  border: 0;
  text-align: left;
}
.mobile-nav-sheet-body .mobile-nav-sheet-tab:active,
.mobile-nav-sheet-body .mobile-nav-sheet-tab:hover {
  background: var(--surface-2);
}
.mobile-nav-sheet-body .mobile-nav-sheet-tab.active {
  background: var(--surface-3);
  color: var(--text);
  font-weight: 600;
}

@media (max-width: 640px) {
  /* Hide the desktop horizontal-scroll tab strip — replaced by bottom-nav. */
  nav.admin-nav {
    display: none;
  }
  /* Show the bottom bar. */
  .mobile-nav-bar {
    display: flex;
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 997;
    background: var(--surface);
    border-top: 1px solid var(--border-2);
    /* Safe-area for iPhone home indicator. */
    padding: 6px 4px calc(env(safe-area-inset-bottom, 0px) + 6px);
    /* Prevent rubber-band scroll from showing background behind the bar. */
    backdrop-filter: blur(12px);
  }
  .mobile-nav-btn {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    padding: 6px 2px;
    /* 44pt min tap target. With 6 buttons across a 360px viewport,
       each is ~58px wide × 50px tall — comfortable. */
    min-height: 50px;
    background: transparent;
    border: 0;
    color: var(--text-muted);
    font-size: 11px;
    font-weight: 500;
    cursor: pointer;
    border-radius: 8px;
  }
  .mobile-nav-btn:hover,
  .mobile-nav-btn:active,
  .mobile-nav-btn.active {
    color: var(--text);
    background: var(--surface-2);
  }
  .mobile-nav-icon {
    font-size: 20px;
    line-height: 1;
  }

  /* Reserve space at the bottom of the page so the fixed bar doesn't
     overlap the last bit of content. The bar is ~62px tall + safe-area
     padding; main needs at least that much padding-bottom. */
  main#main-content {
    padding-bottom: calc(70px + env(safe-area-inset-bottom, 0px));
  }
}

/* ============== v9.61.1 — Cockpit tab ============== */

.cockpit-tile-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 12px;
}
@media (max-width: 640px) {
  /* On mobile, single column — tiles are full-width for easy tap. */
  .cockpit-tile-grid {
    grid-template-columns: 1fr;
  }
}

/* A cockpit tile = a card with a label, big number, and a sublabel.
   Tappable: hover/active states give a subtle lift cue. The whole tile
   is the click target (44pt+ tap area). */
.cockpit-tile {
  display: block;
  padding: 16px;
  border-radius: var(--radius-sm);
  background: var(--surface);
  border: 1px solid var(--border);
  cursor: pointer;
  transition: transform 0.12s var(--ease), background 0.12s var(--ease), border-color 0.12s var(--ease);
  /* Override link-default styling */
  text-decoration: none;
  color: inherit;
  /* Adequate height even when content is tiny — keeps tile-grid columns
     visually balanced when one tile has more text than another. */
  min-height: 96px;
}
.cockpit-tile:hover,
.cockpit-tile:focus-visible {
  background: var(--surface-2);
  border-color: var(--border-2);
  transform: translateY(-1px);
}
.cockpit-tile:active {
  transform: translateY(0);
  background: var(--surface-3);
}
.cockpit-tile-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  font-weight: 600;
}
.cockpit-tile-value {
  font-size: 28px;
  font-weight: 700;
  color: var(--text);
  line-height: 1.1;
  margin-top: 6px;
}
.cockpit-tile-sublabel {
  font-size: 12px;
  color: var(--text-muted);
  margin-top: 4px;
}

/* The hero status card — uses .card-success/warning/critical from v9.60
   for surface-aware colors. Larger padding + bigger heading because it's
   the first thing operators see. */
.cockpit-hero {
  padding: 18px;
  border-radius: var(--radius-sm);
  border-width: 1px;
  border-style: solid;
}
.cockpit-hero-status {
  font-size: 22px;
  font-weight: 700;
  margin-bottom: 4px;
}
.cockpit-hero-detail {
  font-size: 13px;
  /* color is inherited from the .card-X variant — already surface-correct. */
}

/* Top-warnings list — each row is a card-warning or card-danger. The
   row is tappable; right side has an "Open →" affordance. */
.cockpit-attention-row {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 12px 14px;
  margin-bottom: 8px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  text-decoration: none;
  /* Background and color come from the surface-X variant the row carries
     (card-danger / card-warning). */
}
.cockpit-attention-row-body { flex: 1; min-width: 0; }
.cockpit-attention-row-label { font-weight: 600; font-size: 14px; }
.cockpit-attention-row-why {
  font-size: 12px;
  margin-top: 2px;
  /* color: inherits from the surface-X-text-muted of the parent card-X */
}
.cockpit-attention-row-arrow {
  font-size: 18px;
  font-weight: 700;
  flex-shrink: 0;
  align-self: center;
}

/* ============== v9.61.2 — Money Safety count tiles ============== */

/* Money Safety count tiles. The grid lives in the existing
   #msc-counts container (auto-fit minmax(260px,1fr)). Each tile is
   the entire click target for "Open" actions; static tiles look
   identical but don't have hover/focus affordance.

   Tap target: minimum 88px tall (label + value + arrow comfortably
   exceeds iOS HIG ≥ 44pt for tappable tiles). */

.msc-count-tile {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 14px;
  min-height: 88px;
  /* Reset link defaults for the action variant */
  text-decoration: none;
  color: inherit;
}

.msc-count-tile-action {
  cursor: pointer;
  transition: transform 0.12s var(--ease), background 0.12s var(--ease), border-color 0.12s var(--ease);
}
.msc-count-tile-action:hover,
.msc-count-tile-action:focus-visible {
  background: var(--surface-2);
  border-color: var(--border-2);
  transform: translateY(-1px);
}
.msc-count-tile-action:active {
  transform: translateY(0);
  background: var(--surface-3);
}
.msc-count-tile-action:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.msc-count-tile-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  font-weight: 500;
}
.msc-count-tile-value {
  font-size: 22px;
  font-weight: 600;
  margin-top: 4px;
  line-height: 1.1;
}
.msc-count-tile-arrow {
  margin-top: auto;
  padding-top: 8px;
  font-size: 11px;
  color: var(--text-muted);
  font-weight: 500;
}
.msc-count-tile-action:hover .msc-count-tile-arrow,
.msc-count-tile-action:focus-visible .msc-count-tile-arrow {
  color: var(--text);
}

/* ===========================================================
   v9.83.0 — Orders dashboard mobile cards (Phase 10 of 23)
   ===========================================================
   The Orders tab renders both the desktop 9-column table and a
   parallel card-per-order layout on every loadOrders. Below 640px
   (matching the rest of the admin's mobile breakpoint) the desktop
   table hides and the cards show. JS doesn't detect viewport — CSS
   does, so the layout flips automatically on rotate/resize.
*/

/* Default — desktop. Hide the cards container outright. */
.orders-mobile-cards {
  display: none;
}

/* Card structure — only matters when visible (mobile), but defining
   the styles outside the @media rule keeps them in source order with
   the show/hide rules. The card itself uses the existing --bg-2 /
   --border tokens for visual consistency with the rest of the admin. */
.orders-card {
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-2);
}
.orders-card-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  padding-bottom: 8px;
  margin-bottom: 8px;
  border-bottom: 1px solid var(--border);
}
.orders-card-id {
  font-size: 14px;
  font-weight: 600;
}
.orders-card-amount {
  font-size: 14px;
  font-weight: 600;
  white-space: nowrap;
}
.orders-card-row {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  padding: 4px 0;
  font-size: 13px;
}
.orders-card-label {
  color: var(--muted);
  flex: 0 0 auto;
  min-width: 70px;
}
.orders-card-value {
  text-align: right;
  flex: 1 1 auto;
  word-break: break-word;
}
.orders-card-empty {
  padding: 24px;
  text-align: center;
  border: 1px dashed var(--border);
  border-radius: 8px;
}

/* Mobile: hide the desktop table, show the cards as a vertical stack. */
@media (max-width: 640px) {
  .orders-desktop-table {
    display: none;
  }
  .orders-mobile-cards {
    display: flex;
    flex-direction: column;
    gap: 8px;
  }
}

/* ===========================================================
   v9.84.0 — Orders dashboard mobile filter drawer (Phase 11 of 23)
   ===========================================================
   On screens ≤640px the 16 filter controls collapse into a slide-down
   drawer toggled by a "Filters" button (with active-count badge).
   On desktop the toggle is hidden and the controls row stays inline.
*/

/* Default — desktop. Toggle button row is hidden; filter controls
   are always shown (existing inline display:flex from admin.html). */
.orders-mobile-only {
  display: none;
}

/* Active-count badge inside the Filters button. Always small;
   accent-colored only when count > 0 (default appearance is
   dimmed muted style so an "all clear" button looks balanced). */
.orders-filter-toggle-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: 10px;
  background: var(--accent);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
}
.orders-filter-toggle-caret {
  display: inline-block;
  font-size: 12px;
  color: var(--muted);
  transition: transform 150ms ease;
}
#orders-filter-toggle[aria-expanded="true"] .orders-filter-toggle-caret {
  transform: rotate(180deg);
}

/* v9.150.0 — Desktop sizing for the orders-filter dropdowns + inputs.
   The global rule at line ~796 sets `select, input, textarea { width: 100% }`
   so that form elements fill their container on mobile-first layouts.
   But the orders page filter row is a flex-wrap inline cluster of 10+
   selects + 6 inputs; without these overrides every element stretched
   to 100% of the row, forcing each onto its own line and producing a
   16-row tall column. Operator report: "the orders page has the
   dropdowns stacked as full length, causing the page to be long, all
   these drop down filters should be in one row and not as wide."
   Fix: explicit auto-width with sensible min/max so the controls size
   to their content, fit several per row at typical viewport widths,
   and still wrap gracefully when the row would overflow. Inline
   max-width on the text inputs (admin.html lines 1052-1055) caps them;
   date inputs need the explicit min/max here. The mobile media query
   below (max-width: 640px) overrides BACK to width:100% for the
   touch-stack drawer experience — that's intentional. */
.orders-filter-select {
  width: auto;
  min-width: 140px;
  max-width: 220px;
  padding: 6px 10px;
  font-size: 13px;
  line-height: 1.4;
}
.orders-filter-text {
  width: auto;
  min-width: 100px;
  padding: 6px 10px;
  font-size: 13px;
  line-height: 1.4;
}
.orders-filter-date {
  width: auto;
  min-width: 130px;
  max-width: 160px;
  padding: 6px 10px;
  font-size: 13px;
  line-height: 1.4;
}

/* Mobile: show the toggle, hide the filter controls by default,
   reveal them when toggle bears .open. The .open class is added
   by JS in v9.84.0 in lockstep with aria-expanded. */
@media (max-width: 640px) {
  .orders-mobile-only {
    display: block;
  }
  /* Stack the filter controls vertically when revealed — wide
     buttons / inputs are easier to tap than an inline-wrapped row. */
  #orders-filter-controls {
    display: none;
    flex-direction: column;
    align-items: stretch;
    gap: 10px;
  }
  #orders-filter-controls.open {
    display: flex;
  }
  /* Each control fills the drawer width. Inline max-widths from
     admin.html are overridden so 140-180px constraints don't crop
     the drawer's natural fluid width. */
  #orders-filter-controls > select,
  #orders-filter-controls > input {
    max-width: none;
    width: 100%;
    min-height: 40px;     /* finger-friendly tap target */
    font-size: 14px;
  }
  /* Clear-all button — full-width at the bottom of the drawer. */
  #orders-clear-filters {
    margin-left: 0 !important;     /* override admin.html inline margin-left:auto */
    width: 100%;
  }
}

/* ===========================================================
   v9.85.0 — Orders dashboard row/card click affordance (Phase 12 of 23)
   ===========================================================
   Both the desktop table rows and mobile cards become clickable
   in v9.85.0 (open the order detail modal). These styles provide
   the visual affordance so operators see they're interactive.
*/

/* Desktop — table rows. */
#orders-table tbody tr[data-order-id] {
  cursor: pointer;
}
#orders-table tbody tr[data-order-id]:hover {
  background: var(--bg-2);
}

/* Mobile — cards. */
.orders-card {
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease;
}
.orders-card:hover,
.orders-card:focus-within {
  background: var(--bg);
  border-color: var(--accent);
}

/* ===========================================================
   v9.86.0 — Payload Inspector inside order detail modal (Phase 13 of 23)
   ===========================================================
   The 33-field provenance object renders as grouped <details>
   sections per ORDERS_PROVENANCE_GROUPS. Each group is a 2-col
   label-value grid that scales cleanly across desktop and mobile.
*/

.orders-prov-group {
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-2);
  overflow: hidden;
}
.orders-prov-group > summary {
  list-style: none;
  padding: 8px 12px;
  background: var(--bg-2);
}
.orders-prov-group > summary::-webkit-details-marker {
  display: none;
}
.orders-prov-group > summary::before {
  content: '▸';
  display: inline-block;
  margin-right: 8px;
  color: var(--muted);
  font-size: 11px;
  transition: transform 120ms ease;
}
.orders-prov-group[open] > summary::before {
  transform: rotate(90deg);
}
.orders-prov-group > summary:hover {
  background: var(--bg);
}
.orders-prov-rows {
  padding: 6px 12px 12px;
  display: grid;
  grid-template-columns: minmax(160px, 28%) 1fr;
  gap: 4px 16px;
  font-size: 12px;
  border-top: 1px solid var(--border);
  background: var(--bg);
}
.orders-prov-row {
  display: contents;
}
.orders-prov-label {
  padding: 4px 0;
  color: var(--muted);
  font-size: 12px;
}
.orders-prov-value {
  padding: 4px 0;
  word-break: break-word;
  font-size: 12px;
}

/* Mobile: stack label above value rather than 2-col grid. The
   labels become a top line of slightly muted text; the value
   sits right below it. Easier to read at narrow widths. */
@media (max-width: 640px) {
  .orders-prov-rows {
    grid-template-columns: 1fr;
    gap: 2px 0;
  }
  .orders-prov-label {
    padding-bottom: 0;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.4px;
  }
  .orders-prov-value {
    padding-top: 0;
    padding-bottom: 8px;
  }
}

/* ============================================================
   v9.131.0 hotfix — orders summary tile layout (.kpi-* family).

   The orders summary panel renders 23 clickable filter tiles via
   #orders-summary.kpi-grid > .kpi.{kpi-clickable,active} > .kpi-label
   + .kpi-value. These class names have been referenced from JS since
   v9.78.0 / v9.82.0 but were NEVER defined in CSS — div blocks stacked
   vertically and the .active state's full-width orange background
   created a giant horizontal bar.

   Mirror the .cockpit-tile-* shape: same grid, same min-height, same
   label/value typography. Different class name = different surface
   so future tweaks don't entangle.
   ============================================================ */

.kpi-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 8px;
  margin-bottom: 12px;
}
@media (max-width: 640px) {
  /* On mobile, 2 tiles per row — keeps tap targets >= 44pt while still
     fitting the 23 tiles without an enormous scroll. */
  .kpi-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

.kpi {
  display: block;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  background: var(--surface);
  border: 1px solid var(--border);
  min-height: 64px;
  /* Override link-default styling for tiles that get role="button" */
  text-decoration: none;
  color: inherit;
}
.kpi-clickable {
  cursor: pointer;
  transition: transform 0.12s var(--ease), background 0.12s var(--ease), border-color 0.12s var(--ease);
}
.kpi-clickable:hover,
.kpi-clickable:focus-visible {
  background: var(--surface-2);
  border-color: var(--border-2);
  transform: translateY(-1px);
}
.kpi-clickable:active {
  transform: translateY(0);
  background: var(--surface-3);
}
.kpi.active {
  /* The active state is also set inline by renderOrdersSummary
     (background + color) — these declarations are belt-and-braces. */
  background: var(--accent);
  color: #fff;
  border-color: transparent;
}
.kpi.active .kpi-label,
.kpi.active .kpi-value {
  color: #fff;
}

.kpi-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
  font-weight: 600;
  /* Truncate long labels to keep tile heights consistent across the grid. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.kpi-value {
  font-size: 22px;
  font-weight: 700;
  color: var(--text);
  line-height: 1.1;
  margin-top: 4px;
}

/* ════════════════════════════════════════════════════════════════════
   v9.158.0 — Apple/macOS-inspired admin shell.
   Migrated from inline <style> in admin.html to keep the document
   <head> compact (which several positional tests rely on) and to
   follow the single-stylesheet convention.
   ════════════════════════════════════════════════════════════════════ */

/* v9.158.2 — The hidden tab registry MUST stay hidden. The legacy
   `nav.admin-nav { display: flex }` rule above (line ~1309) overrides
   the HTML5 [hidden] attribute (which is just `display: none` from
   the user-agent stylesheet — author CSS wins). Without this rule the
   47-anchor registry renders as a wall of links above the topbar.
   Use !important + attribute selector for maximum specificity so this
   rule pins down the registry no matter what other rules apply. */
nav.admin-nav[hidden] { display: none !important; }
/* ────────────────────────────────────────────────────────────────
       Reset + tokens that matter for the shell
       ──────────────────────────────────────────────────────────────── */
    body { background: #0a0a10; }

    /* The new admin shell wraps everything below the topbar. We swap
       the old single-row .admin-nav for a topbar + sidebar + main
       three-part layout. .admin-nav still wraps the data-tab
       registry that activateTab() and the mobile-nav test rely on. */

    /* ────────────────────────────────────────────────────────────────
       TOP BAR — the persistent global navigation
       ──────────────────────────────────────────────────────────────── */
    .admin-topbar {
      display: flex; align-items: center;
      gap: 28px;
      padding: 14px 28px;
      background: linear-gradient(180deg, rgba(20,20,30,0.92) 0%, rgba(15,15,23,0.86) 100%);
      backdrop-filter: blur(18px) saturate(140%);
      -webkit-backdrop-filter: blur(18px) saturate(140%);
      border-bottom: 1px solid rgba(255,255,255,0.06);
      position: sticky; top: 0; z-index: 60;
      min-height: 64px;
    }
    .admin-brand {
      display: flex; align-items: center; gap: 12px;
      flex-shrink: 0;
    }
    .admin-brand-logo {
      width: 36px; height: 36px;
      border-radius: 10px;
      background: linear-gradient(135deg, var(--accent, #ff6b35) 0%, var(--accent-2, #e83e8c) 100%);
      display: grid; place-items: center;
      color: #fff;
      box-shadow: 0 6px 16px -8px var(--accent, #ff6b35);
    }
    .admin-brand-logo svg { width: 18px; height: 18px; }
    .admin-brand-text { display: flex; flex-direction: column; line-height: 1.15; }
    .admin-brand-text strong {
      font-family: var(--display, system-ui);
      font-weight: 600; font-size: 15px; letter-spacing: -0.01em;
      color: var(--text, #f5f5f0);
    }
    .admin-brand-text span {
      font-size: 12px; color: var(--text-muted, rgba(245,245,240,0.6));
      font-weight: 400;
    }

    /* Primary nav (categories) */
    .admin-primary-nav {
      display: flex; gap: 2px; align-items: center;
      flex: 1;
      overflow: hidden;
    }
    .admin-primary-nav .pn-btn {
      appearance: none; border: 0; background: transparent;
      color: var(--text-muted, rgba(245,245,240,0.62));
      font: inherit;
      font-size: 13.5px; font-weight: 500;
      padding: 7px 14px;
      border-radius: 8px;
      cursor: pointer;
      transition: background 0.18s var(--ease, ease), color 0.18s var(--ease, ease);
      white-space: nowrap;
      letter-spacing: -0.005em;
    }
    .admin-primary-nav .pn-btn:hover {
      color: var(--text, #f5f5f0);
      background: rgba(255,255,255,0.03);
    }
    .admin-primary-nav .pn-btn[aria-current="page"] {
      color: var(--text, #f5f5f0);
      background: rgba(255,255,255,0.07);
      font-weight: 600;
    }
    .admin-primary-nav .pn-btn:focus-visible {
      outline: 2px solid var(--accent, #ff6b35);
      outline-offset: 2px;
    }

    /* Top-right utilities */
    .admin-topbar-utils {
      display: flex; align-items: center; gap: 6px;
      flex-shrink: 0;
    }
    .admin-topbar-utils .util-btn {
      appearance: none; border: 1px solid transparent;
      background: transparent;
      color: var(--text-muted, rgba(245,245,240,0.62));
      font: inherit;
      font-size: 13px;
      padding: 7px 12px;
      border-radius: 8px;
      cursor: pointer;
      transition: all 0.15s var(--ease, ease);
      text-decoration: none;
      display: inline-flex; align-items: center; gap: 6px;
      white-space: nowrap;
    }
    .admin-topbar-utils .util-btn:hover {
      color: var(--text, #f5f5f0);
      background: rgba(255,255,255,0.04);
    }
    .admin-topbar-utils .util-btn svg { width: 16px; height: 16px; opacity: 0.85; }
    .admin-topbar-utils .util-icon-btn {
      width: 34px; height: 34px;
      padding: 0;
      display: grid; place-items: center;
      border-radius: 9px;
    }
    .admin-topbar-utils .user-chip {
      display: inline-flex; align-items: center; gap: 8px;
      padding: 4px 10px 4px 4px;
      border-radius: 999px;
      background: rgba(255,255,255,0.04);
      border: 1px solid rgba(255,255,255,0.06);
      font-size: 12.5px; color: var(--text-muted, rgba(245,245,240,0.62));
    }
    .admin-topbar-utils .user-chip-avatar {
      width: 26px; height: 26px; border-radius: 50%;
      background: rgba(255,255,255,0.08);
      color: var(--text, #f5f5f0);
      display: grid; place-items: center;
      font-size: 11px; font-weight: 600;
    }

    /* ────────────────────────────────────────────────────────────────
       SHELL — sidebar + main layout
       ──────────────────────────────────────────────────────────────── */
    .admin-shell {
      display: grid;
      grid-template-columns: 240px 1fr;
      min-height: calc(100vh - 64px);
    }
    .admin-sidebar {
      border-right: 1px solid rgba(255,255,255,0.05);
      padding: 28px 16px 16px;
      background: rgba(8,8,14,0.55);
      display: flex; flex-direction: column;
      gap: 4px;
      position: sticky; top: 64px;
      height: calc(100vh - 64px);
      overflow-y: auto;
    }
    .admin-sidebar .sb-section {
      display: block;
      font-size: 11px; font-weight: 500;
      letter-spacing: 0.08em; text-transform: uppercase;
      color: var(--text-dim, rgba(245,245,240,0.42));
      padding: 14px 12px 6px;
    }
    .admin-sidebar .sb-link {
      display: flex; align-items: center; gap: 12px;
      padding: 9px 12px;
      border-radius: 10px;
      color: var(--text-muted, rgba(245,245,240,0.62));
      text-decoration: none;
      font-size: 13.5px;
      font-weight: 450;
      transition: all 0.15s var(--ease, ease);
      cursor: pointer;
      letter-spacing: -0.005em;
    }
    .admin-sidebar .sb-link svg {
      width: 18px; height: 18px;
      opacity: 0.7;
      flex-shrink: 0;
    }
    .admin-sidebar .sb-link:hover {
      color: var(--text, #f5f5f0);
      background: rgba(255,255,255,0.03);
    }
    .admin-sidebar .sb-link:hover svg { opacity: 0.95; }
    .admin-sidebar .sb-link[aria-current="page"] {
      background: rgba(255,255,255,0.06);
      color: var(--text, #f5f5f0);
      font-weight: 500;
    }
    .admin-sidebar .sb-link[aria-current="page"] svg { opacity: 1; }
    .admin-sidebar .sb-link:focus-visible {
      outline: 2px solid var(--accent, #ff6b35);
      outline-offset: 1px;
    }
    /* v9.158.2 — sidebar action items are <button> elements (they fire
       wizards rather than navigating). Reset browser defaults so they
       sit visually identical to anchor sb-links. */
    button.sb-link.sb-action {
      appearance: none; border: 0; background: transparent;
      font: inherit; text-align: left; width: 100%;
      cursor: pointer;
    }
    .admin-sidebar .sb-footer {
      margin-top: auto;
      padding-top: 16px;
      border-top: 1px solid rgba(255,255,255,0.05);
    }
    .admin-sidebar .sb-footer .sb-link {
      background: rgba(255,255,255,0.025);
      border: 1px solid rgba(255,255,255,0.04);
      justify-content: space-between;
      padding-right: 14px;
    }

    /* Main content area — give panels room to breathe.
       v9.162.8 — removed the `max-width: 1480px` cap so the admin
       console fills the full viewport on wide displays (1440p, 4K,
       ultra-wide). The previous cap was a comfortable max for 1080p
       laptops but wasted significant horizontal space on bigger
       monitors — operators reviewing wide tables (orders, payouts,
       referrers) had to scroll horizontally even though the screen
       had plenty of room.

       The grid layout (`.admin-shell { grid-template-columns: 240px 1fr }`)
       already drives the main column to fill the leftover space, so no
       additional width property is needed here — `width: 100%` was
       redundant against the grid track and is removed too. The mobile
       breakpoint below still overrides padding correctly. */
    .admin-main {
      padding: 36px 44px 80px;
      min-width: 0;  /* permit grid item to shrink instead of forcing overflow */
    }

    /* ────────────────────────────────────────────────────────────────
       CATEGORY OVERVIEW page — title, subtitle, metrics, divider, cards
       ──────────────────────────────────────────────────────────────── */
    .cat-overview .cat-header {
      margin-bottom: 28px;
    }
    .cat-overview .cat-title {
      font-family: var(--display, system-ui);
      font-size: 32px; font-weight: 600;
      letter-spacing: -0.02em; line-height: 1.1;
      color: var(--text, #f5f5f0);
      margin: 0 0 8px;
    }
    .cat-overview .cat-subtitle {
      font-size: 15px; line-height: 1.5;
      color: var(--text-muted, rgba(245,245,240,0.62));
      max-width: 720px;
      margin: 0;
    }
    .cat-overview .cat-metrics {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
      gap: 14px;
      margin-bottom: 28px;
    }
    .cat-overview .metric-tile {
      background: var(--surface, #16161f);
      border: 1px solid rgba(255,255,255,0.06);
      border-radius: 16px;
      padding: 18px 20px;
      display: flex; flex-direction: column;
      gap: 4px;
      position: relative;
    }
    .cat-overview .metric-tile .metric-icon {
      position: absolute; top: 18px; left: 20px;
      width: 30px; height: 30px;
      display: grid; place-items: center;
      background: rgba(255,255,255,0.04);
      border: 1px solid rgba(255,255,255,0.05);
      border-radius: 8px;
      color: var(--text-muted, rgba(245,245,240,0.62));
    }
    .cat-overview .metric-tile.has-icon { padding-left: 64px; }
    .cat-overview .metric-tile .metric-icon svg { width: 16px; height: 16px; }
    .cat-overview .metric-tile .metric-value {
      font-family: var(--display, system-ui);
      font-size: 30px; font-weight: 600;
      letter-spacing: -0.02em; line-height: 1;
      color: var(--text, #f5f5f0);
    }
    .cat-overview .metric-tile .metric-label {
      font-size: 13px;
      color: var(--text-muted, rgba(245,245,240,0.62));
    }
    .cat-overview .metric-tile .metric-status {
      display: inline-flex; align-items: center; gap: 6px;
      font-size: 12px;
      color: var(--text-muted, rgba(245,245,240,0.62));
      margin-top: 4px;
    }
    .cat-overview .metric-tile .metric-status::before {
      content: ""; width: 7px; height: 7px;
      border-radius: 50%;
      background: var(--green, #34d399);
    }
    .cat-overview .metric-tile.is-warn .metric-status::before { background: var(--yellow, #fbbf24); }
    .cat-overview .metric-tile.is-error .metric-status::before { background: var(--red, #f87171); }
    .cat-overview .metric-tile.is-info .metric-status::before { background: #6aa9ff; }

    .cat-overview .cat-divider {
      height: 1px;
      background: rgba(255,255,255,0.06);
      margin: 8px 0 28px;
    }

    /* Card grid for child pages */
    .cat-overview .cat-cards {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
      gap: 16px;
    }
    .cat-card {
      display: flex; flex-direction: column;
      gap: 10px;
      padding: 22px 24px 20px;
      background: var(--surface, #16161f);
      border: 1px solid rgba(255,255,255,0.06);
      border-radius: 18px;
      cursor: pointer;
      text-decoration: none;
      color: inherit;
      position: relative;
      transition: transform 0.18s var(--ease, ease),
                  background 0.18s var(--ease, ease),
                  border-color 0.18s var(--ease, ease);
    }
    .cat-card:hover {
      transform: translateY(-1px);
      background: rgba(28,28,40,0.92);
      border-color: rgba(255,255,255,0.12);
    }
    .cat-card:focus-visible {
      outline: 2px solid var(--accent, #ff6b35);
      outline-offset: 2px;
    }
    .cat-card .card-icon {
      width: 36px; height: 36px;
      display: grid; place-items: center;
      background: rgba(255,255,255,0.04);
      border: 1px solid rgba(255,255,255,0.05);
      border-radius: 10px;
      color: var(--text, #f5f5f0);
      margin-bottom: 4px;
    }

    /* v9.158.2 — action cards (wizard launchers in Settings) are
       <button> elements. Reset browser defaults so they look identical
       to anchor cards. */
    button.cat-card-action {
      appearance: none;
      font: inherit;
      text-align: left;
      width: 100%;
    }
    button.cat-card-action:focus-visible {
      outline: 2px solid var(--accent, #ff6b35);
      outline-offset: 2px;
    }
    .cat-card .card-icon svg { width: 18px; height: 18px; opacity: 0.85; }
    .cat-card .card-title {
      font-family: var(--display, system-ui);
      font-size: 17px; font-weight: 600;
      letter-spacing: -0.01em;
      color: var(--text, #f5f5f0);
    }
    .cat-card .card-desc {
      font-size: 13.5px; line-height: 1.5;
      color: var(--text-muted, rgba(245,245,240,0.62));
    }
    .cat-card .card-meta {
      display: flex; align-items: center; justify-content: space-between;
      margin-top: auto;
      padding-top: 6px;
    }
    .cat-card .card-arrow {
      width: 22px; height: 22px;
      display: grid; place-items: center;
      color: var(--text-dim, rgba(245,245,240,0.42));
      transition: color 0.18s var(--ease, ease), transform 0.18s var(--ease, ease);
    }
    .cat-card:hover .card-arrow {
      color: var(--text, #f5f5f0);
      transform: translateX(2px);
    }

    /* Status pill (used inside cards + metrics) */
    .pill-status {
      display: inline-flex; align-items: center; gap: 6px;
      font-size: 12px;
      padding: 3px 9px 3px 7px;
      border-radius: 999px;
      background: rgba(255,255,255,0.05);
      color: var(--text-muted, rgba(245,245,240,0.7));
      line-height: 1;
    }
    .pill-status::before {
      content: ""; width: 6px; height: 6px;
      border-radius: 50%;
      background: var(--green, #34d399);
    }
    .pill-status.is-warn::before  { background: var(--yellow, #fbbf24); }
    .pill-status.is-error::before { background: var(--red, #f87171); }
    .pill-status.is-info::before  { background: #6aa9ff; }
    .pill-status.is-neutral::before { background: rgba(255,255,255,0.4); }

    /* ────────────────────────────────────────────────────────────────
       MOBILE — replace topbar with stacked header + horizontal pills
       ──────────────────────────────────────────────────────────────── */
    .admin-mobile-primary,
    .admin-mobile-secondary { display: none; }

    @media (max-width: 900px) {
      .admin-shell { grid-template-columns: 1fr; }
      .admin-sidebar { display: none; }
      .admin-main { padding: 24px 18px 80px; }
      .admin-primary-nav { display: none; }
      .admin-topbar {
        padding: 12px 16px;
        gap: 12px;
        min-height: 56px;
      }
      .admin-topbar-utils .util-btn:not(.user-chip):not(.util-icon-btn-keep):not(#logout) {
        display: none;
      }
      .admin-mobile-primary, .admin-mobile-secondary {
        display: flex;
        gap: 6px;
        padding: 10px 16px 8px;
        overflow-x: auto;
        scrollbar-width: none;
        -ms-overflow-style: none;
        background: rgba(10,10,16,0.92);
        backdrop-filter: blur(14px);
        border-bottom: 1px solid rgba(255,255,255,0.04);
        position: sticky;
        z-index: 50;
      }
      .admin-mobile-primary { top: 56px; }
      .admin-mobile-secondary {
        top: 102px;
        padding-top: 6px;
        padding-bottom: 12px;
      }
      .admin-mobile-primary::-webkit-scrollbar,
      .admin-mobile-secondary::-webkit-scrollbar { display: none; }
      .admin-mobile-primary .mp-btn,
      .admin-mobile-secondary .ms-btn {
        appearance: none; border: 0; background: transparent;
        color: var(--text-muted, rgba(245,245,240,0.62));
        font: inherit; font-size: 14px; font-weight: 500;
        padding: 7px 13px;
        border-radius: 999px;
        cursor: pointer;
        white-space: nowrap;
        transition: background 0.18s var(--ease, ease), color 0.18s var(--ease, ease);
        flex-shrink: 0;
        min-height: 32px;
      }
      .admin-mobile-primary .mp-btn[aria-current="page"],
      .admin-mobile-secondary .ms-btn[aria-current="page"] {
        background: rgba(255,255,255,0.1);
        color: var(--text, #f5f5f0);
        font-weight: 600;
      }
    }

    /* On mobile, category overview cards go full-width with horizontal layout */
    @media (max-width: 700px) {
      .cat-overview .cat-cards { grid-template-columns: 1fr; gap: 10px; }
      .cat-overview .cat-metrics { grid-template-columns: 1fr 1fr; gap: 10px; }
      .cat-card {
        flex-direction: row; align-items: center; gap: 14px;
        padding: 16px;
      }
      .cat-card .card-icon { margin-bottom: 0; flex-shrink: 0; }
      .cat-card .card-body { flex: 1; min-width: 0; }
      .cat-card .card-title { font-size: 15.5px; }
      .cat-card .card-desc { font-size: 13px; line-height: 1.4; }
      .cat-card .card-arrow { flex-shrink: 0; }
      .cat-card .card-meta { display: contents; }
      .cat-overview .cat-title { font-size: 26px; }
      .cat-overview .metric-tile { padding: 14px 14px 14px 50px; }
      .cat-overview .metric-tile .metric-icon { top: 14px; left: 12px; width: 28px; height: 28px; }
      .cat-overview .metric-tile .metric-value { font-size: 24px; }
    }

    /* ────────────────────────────────────────────────────────────────
       Hide the old data-tab registry — it's preserved for activateTab()
       lookups + the existing mobile-nav test, but never visible.
       ──────────────────────────────────────────────────────────────── */
    .admin-tab-registry { display: none !important; }

    /* v9.158.0 — Hide the legacy bottom-nav bar in favor of the new
       horizontal pill rows. The bar's DOM remains so the existing
       mobile-nav test (which checks for it) keeps passing, and so the
       sheet machinery can still be invoked programmatically. */
    .mobile-nav-bar { display: none !important; }
  

/* ════════════════════════════════════════════════════════════════════
   v9.159.0 — Theme system admin UI
   ════════════════════════════════════════════════════════════════════
   Element classes used by admin.html's #theme panel: style/palette
   pickers, the live preview pane, and supporting chrome. All scoped
   to .ts-* prefix so they don't collide with anything else. */

/* ── Picker grid ───────────────────────────────────────────────── */
.ts-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 14px;
  margin-top: 14px;
}
@media (max-width: 720px) {
  .ts-grid { grid-template-columns: 1fr; }
}

.ts-card {
  appearance: none;
  text-align: left;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 14px;
  cursor: pointer;
  transition: border-color 140ms ease, transform 140ms ease, box-shadow 140ms ease;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.ts-card:hover { border-color: var(--border-2); transform: translateY(-1px); }
.ts-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.ts-card.is-active {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px rgba(255,107,53,0.18);
}

.ts-card-header {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; min-height: 22px;
}
.ts-card-header strong { font-size: 14px; font-weight: 700; }

.ts-tag {
  display: inline-flex; align-items: center;
  font-size: 10px; font-weight: 600; letter-spacing: 0.06em;
  padding: 2px 6px; border-radius: 4px;
  text-transform: uppercase;
  background: rgba(255,255,255,0.06); color: var(--text-muted);
}
.ts-tag-inspired { background: rgba(96,165,250,0.14); color: #93c5fd; }

.ts-card-footer .muted { font-size: 12px; line-height: 1.4; }

/* ── Style thumbnail ────────────────────────────────────────────── */
.ts-style-thumb {
  background: rgba(255,255,255,0.03);
  border: 1px solid rgba(255,255,255,0.05);
  min-height: 90px;
  display: flex; align-items: center; justify-content: center;
}
.ts-thumb-card {
  background: var(--bg);
  width: 100%;
  padding: 10px;
  display: flex; flex-direction: column; gap: 6px;
  border: 1px solid rgba(255,255,255,0.08);
}
.ts-thumb-line {
  height: 6px; border-radius: 4px;
  background: rgba(255,255,255,0.18);
}
.ts-thumb-btn {
  align-self: flex-start;
  background: var(--accent); color: #fff;
  font-size: 11px; font-weight: 600;
  padding: 0 14px;
  margin-top: 2px;
}

/* ── Palette swatches ───────────────────────────────────────────── */
.ts-palette-tabs {
  display: flex; gap: 4px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 3px;
  margin-top: 12px;
  width: max-content;
}
.ts-palette-tab {
  appearance: none; background: transparent; color: var(--text-muted);
  border: 0; padding: 6px 12px; border-radius: 5px;
  font-size: 13px; font-weight: 500; cursor: pointer;
  transition: background 120ms;
}
.ts-palette-tab:hover { color: var(--text); }
.ts-palette-tab.is-active {
  background: var(--surface-3); color: var(--text);
}

.ts-swatches {
  display: flex; gap: 4px;
}
.ts-swatch {
  width: 22px; height: 22px; border-radius: 5px;
  border: 1px solid rgba(255,255,255,0.08);
  flex-shrink: 0;
}
.ts-pal-mini {
  display: flex; gap: 4px; padding: 8px; border-radius: 8px;
  font-size: 11px; font-weight: 600;
  margin-top: 2px;
}
.ts-pal-chip {
  padding: 3px 8px; border-radius: 4px; flex: 1;
  text-align: center;
}

/* ── Scope picker ───────────────────────────────────────────────── */
.ts-scope-row {
  display: flex; flex-direction: column; gap: 8px; margin-top: 12px;
}
.ts-scope-opt {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  cursor: pointer;
  background: var(--surface);
  font-size: 13px;
  transition: border-color 120ms;
}
.ts-scope-opt:hover { border-color: var(--border-2); }
.ts-scope-opt input[type="radio"] { margin: 0; }

/* ── Contrast banner ────────────────────────────────────────────── */
.ts-contrast-banner {
  margin: 14px 0;
  padding: 12px 14px;
  border-radius: 8px;
  border: 1px solid;
  font-size: 13px;
}
.ts-contrast-banner[data-tone="warn"] { background: var(--surface-warning-bg, rgba(251,191,36,0.10)); border-color: var(--surface-warning-border, rgba(251,191,36,0.4)); color: var(--surface-warning-text, #fbbf24); }
.ts-contrast-banner[data-tone="fail"] { background: var(--surface-danger-bg, rgba(248,113,113,0.10)); border-color: var(--surface-danger-border, rgba(248,113,113,0.4)); color: var(--surface-danger-text, #f87171); }
.ts-contrast-title { font-weight: 600; margin-bottom: 6px; }
.ts-contrast-list { margin: 6px 0 8px; padding-left: 20px; font-size: 12px; }

/* ── Live preview ───────────────────────────────────────────────── */
.ts-preview-root {
  margin-top: 16px;
  padding: 28px;
  border-radius: 12px;
  border: 1px solid var(--border);
  background: var(--bg, #0a0a10);
  color: var(--text, #f5f5f0);
  font-family: var(--font-family, var(--body));
  /* Critical: this container's CSS variables are SET by JS via
     applyPreviewTokens(). Components inside reference those vars. */
}

.ts-preview-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
}
@media (max-width: 720px) {
  .ts-preview-grid { grid-template-columns: 1fr; }
}

/* Generic preview card (uses preview-root tokens) */
.ts-prev-card {
  background: var(--card, var(--surface));
  border: 1px solid var(--border);
  border-radius: var(--radius, 12px);
  padding: var(--card-padding, 18px);
  display: flex; flex-direction: column; gap: 8px;
  box-shadow: var(--shadow-sm);
}
.ts-prev-hero { grid-column: 1 / -1; }

.ts-prev-eyebrow {
  font-size: 10px; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--text-muted);
  font-weight: 600;
}
.ts-prev-h {
  font-size: 22px; font-weight: var(--font-weight-bold, 700);
  margin: 4px 0; line-height: 1.2;
  font-family: var(--font-display, var(--font-family));
}
.ts-prev-h-sm { font-size: 16px; font-weight: 700; margin: 2px 0; }
.ts-prev-h-accent {
  background: var(--gradient, var(--accent));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.ts-prev-muted { color: var(--text-muted); font-size: 13px; line-height: 1.4; }
.ts-prev-label { font-size: 12px; color: var(--text-muted); font-weight: 600; }
.ts-prev-input {
  background: var(--surface-2, var(--surface));
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm, 8px);
  height: var(--input-height, 38px);
  padding: 0 12px;
  font-family: inherit; font-size: 13px;
  width: 100%;
}

.ts-prev-btn {
  appearance: none; cursor: default;
  border: 1px solid transparent;
  border-radius: var(--radius-pill, 999px);
  padding: 0 18px;
  height: var(--button-height, 40px);
  font-family: inherit; font-size: 13px;
  font-weight: var(--font-weight-semibold, 600);
  margin-right: 8px;
  display: inline-flex; align-items: center;
}
.ts-prev-btn-primary { background: var(--primary, var(--accent)); color: var(--primary-text, #fff); }
.ts-prev-btn-secondary { background: var(--secondary, var(--surface-2)); color: var(--secondary-text, var(--text)); border-color: var(--border); }
.ts-prev-btn-sm { height: calc(var(--button-height, 40px) * 0.8); font-size: 12px; padding: 0 12px; background: var(--primary); color: var(--primary-text); }

.ts-prev-stack { display: flex; flex-direction: column; gap: 10px; }
.ts-prev-alert {
  border-radius: var(--radius-sm, 10px);
  padding: 12px 14px;
  border: 1px solid;
}
.ts-prev-alert-title { font-weight: 600; font-size: 13px; margin-bottom: 2px; }
.ts-prev-alert-body  { font-size: 12px; opacity: 0.9; }
.ts-prev-success { background: var(--success-bg); color: var(--success-text); border-color: var(--success); }
.ts-prev-warning { background: var(--warning-bg); color: var(--warning-text); border-color: var(--warning); }
.ts-prev-danger  { background: var(--danger-bg);  color: var(--danger-text);  border-color: var(--danger); }
.ts-prev-info    { background: var(--info-bg);    color: var(--info-text);    border-color: var(--info); }

.ts-prev-chips { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 4px; }
.ts-prev-chip {
  font-size: 11px; font-weight: 600;
  padding: 4px 10px;
  border-radius: var(--radius-pill, 999px);
  background: var(--surface-2);
  color: var(--text-muted);
  border: 1px solid var(--border);
}
.ts-prev-chip-success { background: var(--success-bg); color: var(--success-text); border-color: var(--success); }
.ts-prev-chip-warning { background: var(--warning-bg); color: var(--warning-text); border-color: var(--warning); }
.ts-prev-chip-danger  { background: var(--danger-bg);  color: var(--danger-text);  border-color: var(--danger); }

.ts-prev-event {
  background: var(--card, var(--surface));
  border: 1px solid var(--border);
  border-radius: var(--radius, 12px);
  overflow: hidden;
  display: flex; flex-direction: column;
  box-shadow: var(--shadow-md);
}
.ts-prev-event-art {
  height: 80px;
  background: var(--gradient, linear-gradient(135deg, var(--primary), var(--accent-2, var(--primary))));
}
.ts-prev-event-body { padding: var(--card-padding, 16px); display: flex; flex-direction: column; gap: 6px; }
.ts-prev-event-date { font-size: 10px; letter-spacing: 0.14em; color: var(--text-muted); font-weight: 700; text-transform: uppercase; }
.ts-prev-event-title { font-size: 14px; font-weight: 700; line-height: 1.3; }
.ts-prev-event-meta { font-size: 12px; color: var(--text-muted); }
.ts-prev-event-cta { display: flex; align-items: center; gap: 8px; margin-top: 4px; }

.ts-prev-progress {
  height: 6px;
  background: var(--surface-2);
  border-radius: 999px;
  overflow: hidden;
  margin-top: 6px;
}
.ts-prev-progress-bar {
  height: 100%;
  background: var(--gradient, var(--primary));
  border-radius: 999px;
}

/* ── Action row ─────────────────────────────────────────────────── */
.ts-action-row {
  display: flex; align-items: center; gap: 10px;
  margin-top: 16px; flex-wrap: wrap;
}
.ts-flash:empty { display: none; }

/* ════════════════════════════════════════════════════════════════════
   v9.159.0 — Admin footer (build version + drawer)
   ════════════════════════════════════════════════════════════════════
   Always-visible footer at the bottom of the admin shell. Non-fixed
   so it doesn't overlap the existing mobile bottom-nav; sits below
   page content. Tap → drawer with version detail + copy. */

.admin-footer {
  margin-top: 32px;
  padding: 10px 16px 14px;
  border-top: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-muted);
  font-size: 12px;
  display: flex; justify-content: center;
}
.admin-footer-btn {
  appearance: none;
  background: transparent;
  border: 1px solid transparent;
  color: inherit;
  font-family: inherit; font-size: inherit;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 10px;
  border-radius: var(--radius-sm, 8px);
  cursor: pointer;
  transition: background 120ms, border-color 120ms;
  flex-wrap: wrap;
  justify-content: center;
}
.admin-footer-btn:hover {
  background: var(--surface-2);
  border-color: var(--border);
  color: var(--text);
}
.admin-footer-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.admin-footer-app     { font-weight: 600; color: var(--text); }
.admin-footer-version { font-weight: 600; }
.admin-footer-env     { text-transform: uppercase; letter-spacing: 0.06em; font-size: 11px; }
.admin-footer-checked { font-size: 11px; color: var(--text-dim); margin-left: 6px; }
.admin-footer-sep     { opacity: 0.5; }

.admin-footer-dot {
  width: 8px; height: 8px; border-radius: 50%;
  display: inline-block;
  background: var(--green, #34d399);
  flex-shrink: 0;
}
.admin-footer-dot[data-status="warn"] { background: var(--yellow, #fbbf24); }
.admin-footer-dot[data-status="fail"] { background: var(--red, #f87171); }

@media (max-width: 720px) {
  .admin-footer { font-size: 11px; padding: 8px 12px 12px; }
  .admin-footer-btn { padding: 4px 8px; gap: 4px; }
  .admin-footer-env-sep, .admin-footer-checked { display: none; }
}

/* ── Version drawer ─────────────────────────────────────────────── */
.admin-version-drawer {
  position: fixed; inset: 0;
  z-index: 9998;
  display: flex; align-items: flex-end; justify-content: center;
}
.admin-version-drawer[hidden] { display: none; }
.admin-version-backdrop {
  position: absolute; inset: 0;
  background: rgba(0,0,0,0.5);
  backdrop-filter: blur(4px);
  cursor: pointer;
}
.admin-version-panel {
  position: relative;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg, 18px) var(--radius-lg, 18px) 0 0;
  padding: 18px 22px 22px;
  box-shadow: var(--shadow-lg, 0 -16px 48px rgba(0,0,0,0.5));
  max-width: 520px;
  width: 100%;
  margin: 0 auto;
  max-height: 80vh;
  overflow-y: auto;
}
@media (min-width: 720px) {
  .admin-version-drawer { align-items: center; }
  .admin-version-panel  { border-radius: var(--radius-lg, 18px); margin: 0; }
}
.admin-version-head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 10px;
}
.admin-version-head h3 {
  font-size: 16px; font-weight: 700; margin: 0;
}
.admin-version-list {
  display: grid; gap: 6px;
  margin: 12px 0 16px;
  font-size: 13px;
  font-family: var(--mono, ui-monospace, monospace);
}
.admin-version-row {
  display: grid;
  grid-template-columns: 130px 1fr;
  gap: 12px;
  padding: 4px 0;
  border-bottom: 1px solid var(--border);
}
.admin-version-row:last-child { border-bottom: 0; }
.admin-version-row dt { color: var(--text-muted); font-weight: 500; margin: 0; }
.admin-version-row dd { color: var(--text); margin: 0; word-break: break-all; }
.admin-version-actions {
  display: flex; align-items: center; gap: 12px;
  flex-wrap: wrap;
}
@media (max-width: 480px) {
  .admin-version-row { grid-template-columns: 1fr; gap: 2px; }
  .admin-version-row dt { font-size: 11px; }
}

/* ════════════════════════════════════════════════════════════════════
   v9.160.0 — Tab interlinks (clickable summary tiles + table rows)
   ════════════════════════════════════════════════════════════════════ */

/* .stat-link — clickable Overview/summary tile.
   The underlying .stat rule sets background/padding/border-radius;
   we add hover affordance + remove the anchor underline so the
   visual stays clean. */
.stat-link {
  display: block;
  text-decoration: none;
  color: inherit;
  cursor: pointer;
  transition: border-color 140ms ease, background 140ms ease, transform 140ms ease;
  position: relative;
}
.stat-link:hover {
  border-color: var(--border-2);
  background: var(--surface-2);
  transform: translateY(-1px);
}
.stat-link:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.stat-link::after {
  content: '→';
  position: absolute;
  top: 12px;
  right: 14px;
  font-size: 13px;
  color: var(--text-dim);
  opacity: 0;
  transition: opacity 140ms ease, transform 140ms ease;
}
.stat-link:hover::after {
  opacity: 0.8;
  transform: translateX(2px);
}

/* .codepool-row-link — clickable code-pool row */
.codepool-row-link {
  cursor: pointer;
  transition: background 140ms ease;
}
.codepool-row-link:hover {
  background: var(--surface-2);
}
.codepool-row-link:hover td:first-child {
  color: var(--accent);
}

/* .cities-row-highlighted — flash + persistent highlight when a row
   is the deep-link target of #cities/?city=... */
.cities-row-highlighted {
  background: rgba(255, 107, 53, 0.08);
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  animation: ts-row-flash 0.6s ease-out 2;
}
@keyframes ts-row-flash {
  0%   { background: rgba(255, 107, 53, 0.32); }
  100% { background: rgba(255, 107, 53, 0.08); }
}

/* Generic .interlink-cell — wraps numeric cells in tables (Cities,
   Leaderboard, ROI). Looks like text by default but hovers as a link. */
.interlink-cell {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px dotted transparent;
  transition: color 100ms, border-color 100ms;
  cursor: pointer;
}
.interlink-cell:hover,
.interlink-cell:focus-visible {
  color: var(--accent);
  border-bottom-color: var(--accent);
  outline: none;
}
.interlink-cell.interlink-zero {
  /* Don't dress up cells that show "0" — nothing to drill into */
  cursor: default;
  pointer-events: none;
  color: var(--text-dim);
}
