/* =============================================================================
   VANTAGE — Design System v2
   Premium dark-mode SaaS for TikTok Shop product intelligence
   ============================================================================= */

/* 1. TOKENS ================================================================ */
@import url('https://api.fontshare.com/v2/css?f[]=cabinet-grotesk@700,800&f[]=satoshi@400,500,700&display=swap');

/* 0. [hidden] MEANS HIDDEN (2026-08-25) =====================================
   This rule looks redundant and is not. `[hidden]{display:none}` ships in the
   UA stylesheet, and a UA declaration loses to ANY author declaration of
   `display` at ANY specificity -- there is no specificity contest, author
   origin simply wins. So one line further down this file, `.btn` at line 517
   declaring `display:inline-flex` is enough to make `<button class="btn"
   hidden>` paint in full, and to make every `el.hidden = true` in the page
   scripts inert on screen while a DOM harness asserting `el.hidden === true`
   stays perfectly happy. The IDL reflects; the layout does not follow.

   Two shipped bugs and one latent, all the same cause. The video library's
   select bar and undo snackbar painted on every visit from 2026-08-17 to
   2026-08-25. The scripts history bar rendered with zero rows and its
   batch-delete button rendered outside select mode, found 2026-08-17. The
   third is the onboarding tour's Back button at templates/dashboard.html:525,
   measured in a browser on 2026-08-25 at 61x44px with its `hidden` attribute
   set -- latent only because no route renders that template since
   /app/dashboard was retired to /app, not because the cascade behaved itself.
   Seven pages each rediscovered this and wrote a
   page-scoped patch of their own; this is the rule all seven were standing in
   for. Those patches stay where they are -- they carry the dated forensics,
   and this rule makes them redundant, not wrong.

   `!important` on purpose: the rule has to outrank every class-level `display`
   in this file AND every one somebody adds after today. Specificity alone only
   beats the classes that exist right now, which is exactly the trap the
   page-scoped patches kept falling into. Nothing in this codebase uses
   `hidden="until-found"`, so no element needs the content-visibility escape
   hatch this would otherwise break. Audited repo-wide 2026-08-25: no element
   that carries `hidden` also carries an inline `display`, so no author style
   is being overridden here that anyone wrote on purpose.
   ========================================================================= */
[hidden] { display: none !important; }

:root {
  /* Spacing — 4px grid */
  --space-1:4px; --space-2:8px; --space-3:12px; --space-4:16px;
  --space-5:20px; --space-6:24px; --space-7:28px; --space-8:32px;
  --space-10:40px; --space-12:48px; --space-14:56px; --space-16:64px;

  /* === LIGHT MODE (redesign-v3 2026-07-14: paper neutrals, rose tint gone) ===
     :root doubles as the light theme — pages without a data-theme attr
     (and users who chose light) get these. Handoff §1 "Light" values;
     where §1 gives only a base color the -bg/-line/-dim derivatives are
     sensible ink-on-paper picks, kept in lockstep with the dark twins. */
  --bg-base:#faf9f6;
  --bg-surface:#ffffff;
  --bg-elevated:#f4f3ef;
  --bg-hover:#edece7;
  --bg-active:#e5e4de;

  --border:rgba(20,24,16,0.10);
  --border-strong:rgba(20,24,16,0.17);

  --text-primary:#16181d;
  --text-secondary:#4b4a45;
  --text-muted:#6d6a63;
  --text-faint:#9b9890;

  --accent:#e8384d;
  --accent-hover:#cc2e42;
  --accent-glow:rgba(232,56,77,0.08);
  /* redesign-v3 2026-07-14: --accent-dim/--accent-border were consumed by
     ~9 templates + one main.css rule but never DEFINED anywhere (they
     silently fell back or computed to nothing). Defined here so the
     accent tints are theme-correct instead of dark-hardcoded fallbacks. */
  --accent-dim:rgba(232,56,77,0.08);
  --accent-border:rgba(232,56,77,0.25);
  /* Premium primary-CTA depth (2026-08-19) — light-theme values. */
  --accent-lift:#f24d61;
  --accent-deep:#cc2e42;
  --accent-shadow:rgba(232,56,77,0.32);

  /* Porcelain primary CTA (2026-08-31, owner-picked). The dark theme's slab is
     #EDEFF3 with #0D1016 ink; on this LIGHT theme that slab would sit on
     --bg-base #faf9f6 and disappear (1.06:1 against the page), so the pair is
     INVERTED rather than re-picked: the same two colors, ink as the fill and
     porcelain as the label. Contrast on the contrast-critical pair is
     identical either way -- 16.5:1, against a 7:1 target -- so the button
     reads the same weight in both themes. The depth stack is re-tuned, not
     reused: an inset white top edge at .9 is a highlight on a pale slab and a
     hard white scratch on a dark one, and a near-black 30px drop is invisible
     on paper, so both are pulled back here. */
  --porcelain:#0D1016;
  --porcelain-hover:#05070A;
  --porcelain-ink:#EDEFF3;
  --porcelain-depth:inset 0 1px 0 rgba(255,255,255,.14),0 2px 4px -2px rgba(0,0,0,.35),0 14px 30px -18px rgba(0,0,0,.55);
  --porcelain-depth-hover:inset 0 1px 0 rgba(255,255,255,.18),0 4px 10px -4px rgba(0,0,0,.4),0 18px 36px -20px rgba(0,0,0,.6);

  /* Semantic data colors (redesign-v3). --win = the chartreuse "worth it"
     family; --verified/--modelled/--stale map to the trust labels;
     --rising/--cooling are momentum directions; --caution/--risk replace
     gold/danger for warnings and verdict SKIP. */
  --win:#5e7a0a; --win-bg:#e9f4c2; --win-line:rgba(94,122,10,0.30); --win-ink:#1f2a05;
  --verified:#0b7d6e;   --verified-bg:rgba(11,125,110,0.08);  --verified-line:rgba(11,125,110,0.28);
  --modelled:#7657d6;   --modelled-bg:rgba(118,87,214,0.08);  --modelled-line:rgba(118,87,214,0.28);
  --rising:#059669;     --rising-bg:rgba(5,150,105,0.08);     --rising-line:rgba(5,150,105,0.26);
  --cooling:#ea580c;    --cooling-bg:rgba(234,88,12,0.08);    --cooling-line:rgba(234,88,12,0.26);
  --caution:#8a5f06;    --caution-bg:rgba(138,95,6,0.10);     --caution-line:rgba(138,95,6,0.30);
  --risk:#dc2626;       --risk-bg:rgba(220,38,38,0.08);       --risk-line:rgba(220,38,38,0.26);

  /* Legacy aliases — the pre-v3 CSS + templates consume these names
     everywhere (113 uses of --teal alone). Aliased to the nearest v3
     semantic token so nothing silently loses its color; new code should
     use the semantic names directly. */
  --teal:var(--verified);
  --teal-dim:rgba(11,125,110,0.10);
  --gold:var(--caution);
  --gold-dim:rgba(138,95,6,0.10);
  --success:var(--rising);
  --success-dim:rgba(5,150,105,0.08);
  --warning:var(--cooling);
  --warning-dim:rgba(234,88,12,0.08);
  --danger:var(--risk);
  --danger-dim:rgba(220,38,38,0.08);

  --shadow-sm:0 1px 3px rgba(0,0,0,0.08),0 1px 2px rgba(0,0,0,0.04);
  --shadow-md:0 4px 12px rgba(0,0,0,0.08),0 2px 4px rgba(0,0,0,0.04);
  --shadow-lg:0 12px 32px rgba(0,0,0,0.10),0 4px 8px rgba(0,0,0,0.04);
}

/* === DARK MODE (default — the head bootstrap in base.html stamps
   data-theme="dark" before first paint; base_public hardcodes it) ===
   redesign-v3 2026-07-14: handoff §1 "Dark (default)" values. */
[data-theme="dark"] {
  --bg-base:#0a0b0e;
  --bg-surface:#121318;
  --bg-elevated:#191b22;
  --bg-hover:#20222b;
  --bg-active:#2a2b35;

  --border:rgba(255,255,255,0.07);
  --border-strong:rgba(255,255,255,0.14);

  --text-primary:#f2f1ed;
  --text-secondary:#b5b2ab;
  --text-muted:#8b8880;
  --text-faint:#5c5954;

  --accent:#ff5272;
  --accent-hover:#ff3458;
  --accent-glow:rgba(255,82,114,0.14);
  --accent-dim:rgba(255,82,114,0.10);
  --accent-border:rgba(255,82,114,0.30);
  /* Premium primary-CTA depth (2026-08-19): the flat accent fill read cheap.
     --accent-lift/--accent-deep give the button a top-lit vertical gradient,
     --accent-shadow is the colored glow that replaces the old grey drop. */
  --accent-lift:#ff6f8a;
  --accent-deep:#e93a5c;
  --accent-shadow:rgba(255,82,114,0.45);

  /* Porcelain primary CTA (2026-08-31, owner-picked) -- the values as tuned,
     for the dark ground the app actually ships on. See the light twin in
     :root for why that one is the inverse rather than the same slab. */
  --porcelain:#EDEFF3;
  --porcelain-hover:#FFFFFF;
  --porcelain-ink:#0D1016;
  --porcelain-depth:inset 0 1px 0 rgba(255,255,255,.9),0 2px 4px -2px rgba(0,0,0,.55),0 14px 30px -18px rgba(0,0,0,.95);
  --porcelain-depth-hover:inset 0 1px 0 rgba(255,255,255,.95),0 4px 8px -3px rgba(0,0,0,.6),0 18px 36px -20px rgba(0,0,0,1);

  --win:#d6f25b; --win-bg:rgba(214,242,91,0.10); --win-line:rgba(214,242,91,0.30); --win-ink:#1f2a05;
  --verified:#36d9c4;   --verified-bg:rgba(54,217,196,0.10);  --verified-line:rgba(54,217,196,0.30);
  --modelled:#a78bfa;   --modelled-bg:rgba(167,139,250,0.12); --modelled-line:rgba(167,139,250,0.30);
  --rising:#5ee89c;     --rising-bg:rgba(94,232,156,0.10);    --rising-line:rgba(94,232,156,0.28);
  --cooling:#fb923c;    --cooling-bg:rgba(251,146,60,0.10);   --cooling-line:rgba(251,146,60,0.28);
  --caution:#f5a623;    --caution-bg:rgba(245,166,35,0.12);   --caution-line:rgba(245,166,35,0.30);
  --risk:#ff6b6b;       --risk-bg:rgba(255,107,107,0.10);     --risk-line:rgba(255,107,107,0.28);

  /* Legacy aliases — see the :root note. */
  --teal:var(--verified);
  --teal-dim:rgba(54,217,196,0.12);
  --gold:var(--caution);
  --gold-dim:rgba(245,166,35,0.12);
  --success:var(--rising);
  --success-dim:rgba(94,232,156,0.12);
  --warning:var(--cooling);
  --warning-dim:rgba(251,146,60,0.12);
  --danger:var(--risk);
  --danger-dim:rgba(255,107,107,0.12);

  --shadow-sm:0 1px 3px rgba(0,0,0,0.6);
  --shadow-md:0 4px 16px rgba(0,0,0,0.5);
  --shadow-lg:0 12px 40px rgba(0,0,0,0.7);
}

:root {
  /* Radius — redesign-v3 2026-07-14 semantic scale (chips 6 / controls 9 /
     panels 12 / frames 16). The legacy names alias into it so every
     existing border-radius:var(--radius) picks up the 9px control radius
     without a sweep (was 8px; the 1px shift is the spec, not drift). */
  --radius-chip:6px;
  --radius-control:9px;
  --radius-panel:12px;
  --radius-frame:16px;
  --radius-sm:var(--radius-chip);
  --radius:var(--radius-control);
  --radius-md:var(--radius-control); /* manual_editor.html consumed this name; it was never defined (computed to 0) */
  --radius-lg:var(--radius-panel);
  --radius-xl:var(--radius-frame);
  --radius-full:9999px;

  /* Typography */
  --font-display:'Cabinet Grotesk','Satoshi',system-ui,sans-serif;
  --font-body:'Satoshi',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
  --text-xs:.75rem; --text-sm:.8125rem; --text-base:.9375rem;
  --text-lg:1.125rem; --text-xl:1.25rem; --text-2xl:1.5rem;
  --text-3xl:2rem; --text-4xl:2.5rem; --text-5xl:3.25rem;

  /* Layout */
  --sidebar-w:240px;
  --topbar-h:56px;
  --content-max:1200px;

  /* Safe-area insets — consumed by any fixed top bars, bottom nav,
     drawers, and modals so the layout respects the iPhone notch and
     the home-indicator strip when installed as a PWA. */
  --safe-top:env(safe-area-inset-top, 0px);
  --safe-bottom:env(safe-area-inset-bottom, 0px);
  --safe-left:env(safe-area-inset-left, 0px);
  --safe-right:env(safe-area-inset-right, 0px);

  /* Motion */
  --ease:cubic-bezier(.16,1,.3,1);
  --dur-fast:120ms;
  --dur:180ms;
  --dur-slow:320ms;
}

/* 2. RESET + TYPOGRAPHY ==================================================== */
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
/* Native-feel polish (Phase 4b):
   - Tap-highlight turned off globally so iOS/Android don't flash a grey
     rectangle on every tap; we provide our own :active states.
   - `-webkit-overflow-scrolling:touch` on <html> enables momentum scroll
     on iOS for any scrollable descendant.
   - touch-action:manipulation is applied ONLY to interactive elements
     below, not globally — charts, product-detail hero images, and any
     pinch-zoomable surfaces keep their default touch behavior. */
*{-webkit-tap-highlight-color:transparent}
/* max-width + overflow-x clamp at both roots. Belt + suspenders:
   some rogue child (carousel card flex:0 0 340px, campaign banner
   marquee, off-canvas sidebar translated -100%, etc.) can still
   inflate the layout past the viewport.
   `overflow-x:clip` is stronger than `hidden` on iOS Safari —
   `hidden` creates a scroll container that still counts the painted
   bounds of position:fixed descendants (so the -100% sidebar adds a
   sliver to the document). `clip` simply hard-clips with no scroll
   container. Keep `hidden` as a fallback for browsers without
   `clip` support. Same treatment on .app-shell so fixed children
   of the shell can't extend the document either. */
html{font-size:16px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-rendering:optimizeLegibility;max-width:100vw;width:100%;overflow-x:hidden;overflow-x:clip;-webkit-overflow-scrolling:touch}
body{font-family:var(--font-body);font-size:var(--text-base);line-height:1.5;color:var(--text-primary);background:var(--bg-base);min-height:100vh;min-height:100dvh;max-width:100vw;width:100%;overflow-x:hidden;overflow-x:clip}
button,a,.btn,.filter-chip,.filter-mobile-trigger,
.product-card,.creator-card,.creator-similar-card,.brand-card,
.mob-tab,.sidebar-link,.settings-tab,.mob-more__link{
  touch-action:manipulation;
}
a{color:var(--accent);text-decoration:none;transition:color var(--dur-fast)}
a:hover{color:var(--accent-hover)}
img{max-width:100%;display:block}
ul,ol{list-style:none}
button{cursor:pointer;font-family:inherit;border:none;background:none;color:inherit}
input,textarea,select{font-family:inherit;font-size:inherit;color:inherit}
h1,h2,h3,h4,h5,h6{font-family:var(--font-display);font-weight:700;line-height:1.15;color:var(--text-primary)}

.text-xs{font-size:var(--text-xs)}.text-sm{font-size:var(--text-sm)}.text-base{font-size:var(--text-base)}
.text-lg{font-size:var(--text-lg)}.text-xl{font-size:var(--text-xl)}.text-2xl{font-size:var(--text-2xl)}
.text-secondary{color:var(--text-secondary)}.text-muted{color:var(--text-muted)}.text-faint{color:var(--text-faint)}.text-accent{color:var(--accent)}.text-teal{color:var(--teal)}
.font-display{font-family:var(--font-display)}.font-medium{font-weight:500}.font-semibold{font-weight:600}.font-bold{font-weight:700}
.line-clamp-1{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.line-clamp-2{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}

/* 3. LAYOUT ================================================================ */

/* App shell — also gets the clip treatment so the off-canvas sidebar
   (position:fixed; translateX(-100%)) can't inflate the document. */
.app-shell{display:flex;min-height:100vh;max-width:100vw;overflow-x:hidden;overflow-x:clip;position:relative}

/* Sidebar */
.sidebar{position:fixed;top:8px;left:8px;bottom:8px;width:calc(var(--sidebar-w) - 16px);background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-lg);display:flex;flex-direction:column;z-index:100;overflow-y:auto;box-shadow:var(--shadow-sm)}
.sidebar-logo{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-5);height:var(--topbar-h);border-bottom:1px solid var(--border)}
.sidebar-logo svg{flex-shrink:0}
.sidebar-wordmark{font-family:var(--font-display);font-size:var(--text-xl);font-weight:800;letter-spacing:-.02em;background:linear-gradient(135deg,var(--text-primary),var(--text-muted));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}

.sidebar-nav{flex:1;padding:var(--space-3)}
.sidebar-section{padding:var(--space-4) var(--space-4) var(--space-2);font-size:var(--text-xs);font-weight:600;text-transform:uppercase;letter-spacing:.08em;color:var(--text-muted)}
.sidebar-link{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-3) var(--space-4);border-radius:var(--radius);color:var(--text-secondary);font-size:var(--text-sm);font-weight:500;transition:all var(--dur-fast);border-left:2px solid transparent;margin-bottom:2px}
.sidebar-link:hover{color:var(--text-primary);background:var(--bg-hover)}
/* redesign-v3 2026-07-14 (handoff §3): active item = 2px left accent border +
   accent-tinted bg + PRIMARY text (near-white in dark, near-black in light),
   not accent-colored text — pink stays reserved for actions, rule §0.2. */
.sidebar-link.active{color:var(--text-primary);background:var(--accent-glow);border-left-color:var(--accent)}
.sidebar-link .lucide{width:20px;height:20px;flex-shrink:0}

/* Collapsible nav groups (2026-06-15) — replaces the flat sidebar list with
   foldable Discover / Research / Create / Lists / Manage / Admin sections so the
   rail stays readable as tools are added. The group head reuses the old
   .sidebar-section look but is a button with a rotating chevron; smooth height
   via the grid 0fr/1fr trick; honors reduced-motion. Toggle JS lives in
   base.html under the sidebar. */
.nav-group{margin-bottom:2px}
.nav-group__head{display:flex;align-items:center;gap:6px;width:100%;padding:var(--space-4) var(--space-4) var(--space-2);background:none;border:none;cursor:pointer;font-size:var(--text-xs);font-weight:600;text-transform:uppercase;letter-spacing:.08em;color:var(--text-muted);transition:color var(--dur-fast)}
.nav-group__head:hover{color:var(--text-secondary)}
.nav-group__head > span{flex:1;text-align:left}
.nav-group__chev{width:14px;height:14px;flex-shrink:0;opacity:.5;transition:transform var(--dur-fast)}
.nav-group--collapsed .nav-group__chev{transform:rotate(-90deg)}
.nav-group__items{display:grid;grid-template-rows:1fr;transition:grid-template-rows .22s ease}
.nav-group--collapsed .nav-group__items{grid-template-rows:0fr}
.nav-group__inner{overflow:hidden;min-height:0}
@media (prefers-reduced-motion:reduce){.nav-group__items{transition:none}.nav-group__chev{transition:none}}

/* redesign-v3 2026-07-14 (handoff §3): TODAY/FIND/CREATE/TRACK are static
   sections headed by mono EYEBROWS (plain divs, not buttons); only
   EARN/ACCOUNT/Admin keep the .nav-group fold above. Eyebrow spec: mono
   10-11px, uppercase, .12-.16em tracking, --text-muted. The fold-group
   heads adopt the same type so open groups read identically to static
   ones (they stay buttons for the chevron + localStorage mechanic). */
.nav-section{margin-bottom:2px}
.nav-eyebrow{padding:var(--space-4) var(--space-4) var(--space-2);font-family:var(--font-mono);font-size:10.5px;font-weight:600;text-transform:uppercase;letter-spacing:.14em;color:var(--text-muted)}
.nav-group__head{font-family:var(--font-mono);font-size:10.5px;letter-spacing:.14em}

/* Region toggle (added 2026-05-07) — UK market support. Two pill buttons
   inside the sidebar nav. The active pill gets the accent border + tinted
   background; inactive stays muted. Click handler in base.html POSTs
   /api/region/set and reloads. */
.region-toggle{display:flex;gap:6px;padding:var(--space-3) var(--space-4) var(--space-2);align-items:center}
.region-pill{flex:1;display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:6px 10px;border-radius:var(--radius);background:var(--bg-elevated);border:1px solid var(--border);color:var(--text-muted);font-size:12px;font-weight:600;cursor:pointer;transition:all var(--dur-fast)}
.region-pill:hover{color:var(--text-primary);background:var(--bg-hover)}
.region-pill--active{color:var(--accent);background:var(--accent-glow);border-color:var(--accent)}

.sidebar-footer{padding:var(--space-4);border-top:1px solid var(--border)}
/* redesign-v3 2026-07-14 (handoff §3/1b): trial/plan card above the user row. */
.sidebar-plan-card{display:flex;flex-direction:column;gap:4px;margin-bottom:var(--space-3);padding:var(--space-3) var(--space-4);background:var(--bg-elevated);border:1px solid var(--border);border-radius:var(--radius)}
.sidebar-plan-card__label{font-family:var(--font-mono);font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.12em;color:var(--text-secondary)}
.sidebar-plan-card__meta{font-family:var(--font-mono);font-size:11px;line-height:1.4;color:var(--text-muted)}
.sidebar-plan-card__link{font-family:var(--font-mono);font-size:10px;font-weight:700;letter-spacing:.12em;color:var(--accent)}
.sidebar-plan-card__link:hover{color:var(--accent-hover)}
.sidebar-user{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-2);border-radius:var(--radius);transition:background var(--dur-fast)}
.sidebar-user:hover{background:var(--bg-hover)}
.sidebar-user .user-meta{flex:1;min-width:0}
.sidebar-signout{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;min-width:36px;border-radius:var(--radius);color:var(--text-muted);flex-shrink:0;transition:background var(--dur-fast),color var(--dur-fast);-webkit-tap-highlight-color:transparent}
.sidebar-signout:hover{background:var(--bg-hover);color:var(--accent)}
.sidebar-signout .lucide{width:18px;height:18px}
@media (max-width:900px){.sidebar-signout{width:44px;height:44px;min-width:44px}}

/* Watchlist: desktop table, mobile card stack below 768px */
.watchlist-desktop{display:block}
.watchlist-mobile{display:none}
@media (max-width:767px){
  .watchlist-desktop{display:none}
  .watchlist-mobile{display:block}
}
.watchlist-card{background:var(--bg-surface);border:1px solid var(--border);border-radius:12px;padding:16px;margin-bottom:12px}
.watchlist-card-header{display:flex;gap:12px;margin-bottom:16px;align-items:center}
.watchlist-card-thumb{width:56px;height:56px;border-radius:8px;object-fit:cover;flex-shrink:0;background:var(--bg-elevated)}
.watchlist-card-thumb--fallback{display:flex;align-items:center;justify-content:center;color:var(--text-muted);font-weight:700;font-size:20px}
.watchlist-card-title{display:block;font-weight:600;font-size:14px;line-height:1.35;color:var(--text-primary);margin-bottom:4px;text-decoration:none;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.watchlist-card-meta{font-size:12px;color:var(--text-muted)}
.watchlist-card-stats{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:14px;padding:12px 0;border-top:1px solid var(--border);border-bottom:1px solid var(--border)}
.watchlist-card-stats .stat-label{font-size:10px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.04em;font-weight:600}
.watchlist-card-stats .stat-value{font-weight:700;font-size:15px;margin-top:2px;font-variant-numeric:tabular-nums;color:var(--text-primary)}
.watchlist-card-stats .stat-delta{font-size:11px;font-weight:600;margin-top:2px;font-variant-numeric:tabular-nums}
.watchlist-card-actions{display:flex;gap:8px}
.watchlist-card-actions .btn{flex:1;min-height:44px;justify-content:center}

/* Trial banner: hide the secondary detail text under 768px so the
   primary countdown + CTA fit cleanly on a 375px viewport. */
@media (max-width:767px){
  .trial-banner-detail{display:none}
}

/* Hidden Gems mobile stacked cards (iPhone 375px fix). Desktop table
   keeps the .hidden-gems-table-wrap + .product-table render. At
   <=768px the table hides and an <ul> of cards takes over so the 5
   stats columns don't horizontally overflow. */
.hidden-gems-mobile-list{display:none}
@media (max-width:768px){
  .hidden-gems-table-wrap{display:none}
  .hidden-gems-mobile-list{
    display:block;list-style:none;padding:0;margin:0 0 16px;
  }
}
.hidden-gems-mobile-row{
  background:var(--bg-surface);
  border:1px solid var(--border);
  border-radius:12px;
  padding:12px;
  margin-bottom:10px;
  display:flex;flex-direction:column;gap:10px;
}
.hidden-gems-mobile-header{display:flex;gap:10px;align-items:flex-start}
.hidden-gems-mobile-rank{
  font-weight:700;color:var(--accent);min-width:28px;font-size:13px;padding-top:2px;
}
.hidden-gems-mobile-thumb{
  width:56px;height:56px;border-radius:8px;object-fit:cover;
  flex-shrink:0;background:var(--bg-elevated);
}
.hidden-gems-mobile-thumb--fallback{
  display:flex;align-items:center;justify-content:center;
  color:#fff;font-weight:800;font-size:22px;
}
.hidden-gems-mobile-meta{flex:1;min-width:0}
.hidden-gems-mobile-title{
  display:block;font-weight:600;font-size:14px;line-height:1.3;
  color:var(--text-primary);text-decoration:none;
  overflow:hidden;text-overflow:ellipsis;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
}
.hidden-gems-mobile-sub{
  display:flex;flex-wrap:wrap;gap:6px;align-items:center;margin-top:4px;
  font-size:11px;color:var(--text-faint);
}
.hidden-gems-mobile-tier-badge{
  display:inline-flex;align-items:center;gap:3px;
  color:#fff;padding:2px 8px;border-radius:999px;font-size:10px;font-weight:700;
  white-space:nowrap;
}
.hidden-gems-mobile-stats{
  display:grid;grid-template-columns:repeat(3, minmax(0, 1fr));gap:8px;
  padding-top:8px;border-top:1px solid var(--border);
}
.hidden-gems-mobile-stat{display:flex;flex-direction:column;gap:2px;min-width:0}
.hidden-gems-mobile-stat-label{
  font-size:10px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.04em;font-weight:600;
}
.hidden-gems-mobile-stat-value{
  font-size:13px;font-weight:700;color:var(--text-primary);
  font-variant-numeric:tabular-nums;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}

/* FAB overlap guard — the chat-fab + upgrade-fab + mob-bar stack at
   the bottom of mobile viewports combine to about 130-160px of
   obstruction. Give the page's scroll container room so the last row
   isn't eclipsed by the floating UI. */
@media (max-width:900px){
  .page-content{padding-bottom:180px}
}

/* =====================================================================
   2026-04-27 mobile PWA polish pass
   - All Products list: switch to single-column stacked cards matching
     Hidden Gems sizing (user-reported "slightly off" on PWA)
   - Product detail: hero image cap, stat-grid stacking, action-button
     single-column, stats tabs full width, description / chart clip
   ===================================================================== */
@media (max-width:768px){
  /* === All Products: 1-column on mobile so card width matches the
        Hidden Gems mobile cards exactly. Was 2-column which made each
        card slightly narrower than Hidden Gems. === */
  #productGrid{grid-template-columns:1fr !important;gap:10px !important}
  /* Reuse the Hidden Gems padding/font sizing on All-Products cards
     so they feel identical. */
  .product-card{padding:0;min-width:0}
  .product-card .product-card-img{max-height:200px}
  .product-card .product-card-name{font-size:14px;line-height:1.35}
}

/* Product detail page — aggressive overflow cleanup for PWA mobile.
   Iteration 2 (2026-04-27): the previous block kept rules at the
   default specificity which the existing <=640px block was
   overriding via cascade order. This block goes after the pricing
   grid (further down the file) and uses much stronger selectors +
   !important on every constraint so the actual rendered viewport
   collapses cleanly regardless of inline styles or earlier rules.
   See the dedicated block at line ~XXXX below — moved out of
   here to fix specificity issues. */

/* Sprint 6.3B — Pricing page side-by-side comparison */
.pricing-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:20px;max-width:960px;margin:24px auto}
/* 3-tier layout (Lookup / Pro / Studio) — wider cap so all three sit side by
   side instead of Studio wrapping below the fold. auto-fit collapses to 2 (or 1)
   cleanly when Studio isn't available / on narrow screens. */
.pricing-grid--3{max-width:1160px}
.plan-card{background:var(--bg-surface);border:1px solid var(--border);border-radius:12px;padding:28px;position:relative;display:flex;flex-direction:column}
.plan-featured{border:2px solid var(--accent);box-shadow:0 10px 28px rgba(255,82,114,0.12)}
.plan-badge{position:absolute;top:-12px;right:20px;background:var(--accent);color:#fff;padding:4px 12px;border-radius:999px;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:0.05em}
.plan-title{font-size:20px;font-weight:700;color:var(--text-primary);margin-bottom:6px}
.plan-price{display:flex;align-items:baseline;gap:6px;margin-bottom:6px}
.plan-price__amount{font-size:36px;font-weight:800;color:var(--text-primary);line-height:1}
.plan-price__period{font-size:14px;color:var(--text-muted)}
.plan-desc{color:var(--text-muted);font-size:14px;margin:0 0 20px}
.plan-features{list-style:none;padding:0;margin:0 0 24px;flex:1}
.plan-features li{padding:8px 0;padding-left:24px;position:relative;font-size:14px;color:var(--text-primary);border-bottom:1px solid var(--border)}
.plan-features li:last-child{border-bottom:none}
.plan-features li::before{content:'✓';position:absolute;left:0;color:var(--accent);font-weight:700}
.plan-features__muted{color:var(--text-muted) !important}
.plan-features__muted::before{content:'—' !important;color:var(--text-muted) !important}
.plan-cta{width:100%;justify-content:center;padding:12px 20px;font-size:15px;font-weight:700;margin-top:auto}
.plan-note{text-align:center;font-size:11px;color:var(--text-muted);margin:10px 0 0}

/* --- the one offer story, signed-in half (2026-08-31, web Wave 1) ---------
   The paywall now says what / and /subscribe say: one free-entry band above
   the grid, one what-you-get line per card with the enumeration one disclosure
   away, a risk-reversal line under every price, and a pinned CTA on phones.
   The card markup itself is untouched because the billing-cycle toggle and the
   checkout handler read its class names. */
.plan-offerbar{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:10px;
  background:var(--verified-bg);border:1px solid var(--verified-line);border-radius:999px;
  padding:10px 20px;margin:0 auto 20px;max-width:max-content;text-align:center}
.plan-offerbar__b{font-family:var(--font-mono, ui-monospace, monospace);font-size:11px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--verified)}
.plan-offerbar__t{font-size:13.5px;color:var(--text-secondary)}
/* THE FLOATING DECIMAL (2026-08-31 REFIX of the same day's partial fix). The
   first pass only relaxed the tracking here, which does not touch the cause:
   `.plan-price.font-mono` on the PARENT hands the amount a monospaced face,
   and in a monospaced face the decimal point takes a full character cell, so a
   dollars-and-cents price reads as two numbers with a stray dot between them.
   No amount of negative tracking closes that without crushing the digits. The
   amount takes the display face (an explicit font-family on the child beats
   the parent's inherited one); tabular figures keep the three cards' columns
   aligned, which is the only thing the mono face was buying here. Same fix,
   same reason, as .cr-plan__amount and .plan__amount on the public pages. */
.plan-price__amount{font-family:var(--font-display);font-variant-numeric:tabular-nums;
  letter-spacing:-0.02em}
/* The recommended plan is RAISED once the cards sit side by side, matching /
   and /subscribe. Below that the grid is a single column and a translated card
   reads as a rendering fault rather than as emphasis, so the raise stops and
   the accent border carries it alone. */
@media(min-width:880px){.plan-featured{transform:translateY(-8px)}}
.plan-risk{font-size:12px;color:var(--text-muted);margin:0 0 10px}
.plan-tag{font-size:14px;color:var(--text-primary);margin:0 0 14px}
.plan-more{margin:0 0 18px}
.plan-more summary{font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--text-muted);
  cursor:pointer;list-style:none;padding:6px 0}
.plan-more summary::-webkit-details-marker{display:none}
.plan-more summary::after{content:'+';float:right;font-size:14px;line-height:1}
.plan-more[open] summary::after{content:'\2212'}
.plan-more summary:hover{color:var(--text-primary)}
.plan-more .plan-features{margin:8px 0 0}
.plan-pincta{display:none}
@media (max-width:640px){
  /* THE BAR HAS TO CLEAR THE APP SHELL (2026-08-31 REFIX). This composition
     came over from the public pricing page, which is a standalone document
     with nothing at the bottom edge. The signed-in shell puts `.tabbar` there:
     position:fixed, bottom:0, 64px + safe area, z-index 1000. A sticky bar at
     bottom:0 with z-index 30 lands in exactly that band and is painted over
     for the whole scroll, so the buy action the bar exists to keep on screen
     was never once on screen. It now sits ON TOP OF the tab bar's height and
     keeps a z-index BELOW it, because the shell's own navigation outranks a
     page's call to action. The safe-area inset moves to the tabbar's offset:
     the tabbar already absorbs it, and paying it twice left a dead strip.
     `.tabbar` is display:none above 900px and the bar itself above 640px, so
     the two never both exist off phones. */
  .plan-pincta{display:flex;position:sticky;bottom:calc(64px + var(--safe-bottom));
    z-index:30;align-items:center;
    justify-content:space-between;gap:12px;background:var(--bg-elevated);
    border-top:1px solid var(--border-strong);padding:10px 14px}
  .plan-pincta__plan{display:flex;flex-direction:column;line-height:1.25}
  .plan-pincta__plan b{font-size:13px;color:var(--text-primary)}
  .plan-pincta__plan span{font-size:12px;color:var(--text-muted);font-variant-numeric:tabular-nums}
  .plan-pincta__go{min-height:44px;padding:0 18px;display:inline-flex;align-items:center}
  /* clearance so the bar never covers the last card */
  .pricing-grid{padding-bottom:64px}
}

.pricing-faq{max-width:720px;margin:48px auto 24px;padding:0 16px}
.pricing-faq details{margin-bottom:10px;padding:14px 18px;background:var(--bg-surface);border:1px solid var(--border);border-radius:10px;cursor:pointer}
.pricing-faq summary{font-weight:600;color:var(--text-primary);list-style:none;position:relative;padding-right:32px}
.pricing-faq summary::-webkit-details-marker{display:none}
.pricing-faq summary::after{content:'+';position:absolute;right:0;top:50%;transform:translateY(-50%);font-size:20px;line-height:1;color:var(--text-muted);transition:transform 0.15s}
.pricing-faq details[open] summary::after{content:'−'}
.pricing-faq details p{margin:12px 0 0;color:var(--text-muted);font-size:14px;line-height:1.55}

/* THE PHONE LADDER, ON THE SAME BREAKPOINT AND THE SAME MECHANISM AS THE TWO
   PUBLIC GRIDS (2026-08-31 REFIX). This was `max-width:767px` with
   `.plan-pro{order:0};.plan-lookup{order:1}` while / and /subscribe collapse at
   879px with `.cr-plan--pick{order:-1}`, and the wave's own raise above starts
   at 880px. Three defects came out of that one number:
     - 641-879px: the raise had not started, `.plan-pincta` had already ended,
       and auto-fit wrapped three cards 2+1, so the paywall showed NO
       recommendation at all across a 240px band while /subscribe at the same
       width showed the recommended card first in one column.
     - `order:0` is the initial value, so `.plan-pro{order:0}` moved nothing.
       Only `.plan-lookup{order:1}` fired, which pushed the $9.99 entry plan
       BELOW the $79.99 one and read Creator, Studio, Starter on a phone while
       both public grids read Creator, Starter, Studio.
     - the emphasis was keyed to a TIER (`.plan-pro`), so the day the
       recommendation moves, the ladder silently keeps recommending the old one.
   It is keyed to `.plan-featured` now, which is the class that carries the
   badge, so the two cannot disagree. Same three tiers, one price ladder. */
@media (max-width:879px){
  .pricing-grid{grid-template-columns:1fr;gap:14px;margin:16px auto;max-width:420px}
  .plan-featured{order:-1}
  .plan-card{padding:22px}
}

/* Hidden Gems tier-filter chip strip.
   Desktop (default): single row, horizontally scrollable if it
   overflows. Mobile: wrap to multiple rows so all 6 tier chips
   ('All tiers' / Viral / Gems / Early / Daily Movers / Just
   Restocked) are fully visible. Without this rule the last 1-2
   chips sit off the right edge of the PWA viewport and require
   horizontal scrolling that not every user discovers.
   -webkit-overflow-scrolling adds iOS Safari momentum scroll for
   the desktop fallback path. */
.gems-tier-chips{-webkit-overflow-scrolling:touch}
@media (max-width:767px){
  .gems-tier-chips{flex-wrap:wrap !important;overflow-x:visible !important;row-gap:6px}
}

/* Onboarding tour (driver.js) theme — applied via popoverClass: 'vantage-tour' */
.driver-popover.vantage-tour{background:var(--bg-surface);color:var(--text-primary);border:1px solid var(--border);border-radius:12px;box-shadow:0 16px 44px rgba(0,0,0,0.45)}
.driver-popover.vantage-tour .driver-popover-title{color:var(--text-primary);font-weight:700;font-size:15px}
.driver-popover.vantage-tour .driver-popover-description{color:var(--text-muted);font-size:13px;line-height:1.5}
.driver-popover.vantage-tour .driver-popover-progress-text{color:var(--text-faint);font-size:11px;letter-spacing:0.06em;text-transform:uppercase;font-weight:600}
.driver-popover.vantage-tour .driver-popover-footer button{background:var(--accent);color:#fff;border:none;border-radius:8px;padding:8px 14px;font-weight:600;font-size:13px;box-shadow:none;text-shadow:none}
.driver-popover.vantage-tour .driver-popover-footer button:hover{background:var(--accent-hover,#e23b5d)}
.driver-popover.vantage-tour .driver-popover-footer button.driver-popover-prev-btn{background:transparent;color:var(--text-muted);border:1px solid var(--border)}
.driver-popover.vantage-tour .driver-popover-close-btn{color:var(--text-muted)}
.driver-popover.vantage-tour .driver-popover-arrow-side-left.driver-popover-arrow,
.driver-popover.vantage-tour .driver-popover-arrow-side-right.driver-popover-arrow,
.driver-popover.vantage-tour .driver-popover-arrow-side-top.driver-popover-arrow,
.driver-popover.vantage-tour .driver-popover-arrow-side-bottom.driver-popover-arrow{border-color:var(--bg-surface)}

/* Avatar */
.avatar{width:32px;height:32px;border-radius:var(--radius-full);background:var(--accent-glow);color:var(--accent);display:flex;align-items:center;justify-content:center;font-size:var(--text-xs);font-weight:700;flex-shrink:0}
.avatar-lg{width:48px;height:48px;font-size:var(--text-base)}
.avatar img{width:100%;height:100%;border-radius:inherit;object-fit:cover}

.user-meta{overflow:hidden}
.user-name{font-size:var(--text-sm);font-weight:600;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* Main area */
.main-area{flex:1;margin-left:var(--sidebar-w);min-height:100vh;display:flex;flex-direction:column;min-width:0}/* min-width:0 2026-06-12 — the 2026-05-03 structural overflow fix added this under @media 768px only; desktop kept min-width:auto, so the first wide nowrap descendant (the DFYD campaign marquee) blew .main-area out to ~4000px and centered page content landed off-screen right. Same fix, all viewports. */

/* Topbar */
.topbar{position:sticky;top:0;height:calc(var(--topbar-h) + env(safe-area-inset-top,0px));padding-top:env(safe-area-inset-top,0px);display:flex;align-items:center;gap:var(--space-4);padding-left:var(--space-6);padding-right:var(--space-6);background:var(--bg-surface);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border-bottom:1px solid var(--border);z-index:50}
/* min-width:0 lets the flex child actually shrink below its intrinsic
   content size. Without this, a long placeholder string inside the
   input ("Search products, paste a TikTok link, or enter a creator
   name…") keeps the topbar wider than the viewport on 375px. */
.topbar-search{flex:1 1 0;min-width:0;max-width:420px;position:relative}
.topbar-search .lucide{position:absolute;left:12px;top:50%;transform:translateY(-50%);width:18px;height:18px;color:var(--text-muted);pointer-events:none}
.topbar-search .input{padding-left:40px;height:40px;background:var(--bg-surface)}
.topbar-right{margin-left:auto;display:flex;align-items:center;gap:var(--space-3)}

/* Page content */
.page-content{flex:1;padding:var(--space-6);max-width:var(--content-max);width:100%;margin:0 auto}
.page-header{margin-bottom:var(--space-6)}
.page-title{font-size:var(--text-2xl);font-weight:700;margin-bottom:var(--space-1)}
.page-subtitle{font-size:var(--text-sm);color:var(--text-secondary)}

/* 4. COMPONENTS ============================================================ */

/* Buttons */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--space-2);height:44px;padding:0 20px;border-radius:var(--radius);font-size:14px;font-weight:600;transition:all var(--dur) var(--ease);white-space:nowrap;user-select:none;position:relative;overflow:hidden}
.btn .lucide{width:18px;height:18px}
.btn:disabled,.btn.loading{opacity:.55;pointer-events:none}
.btn .btn-spinner{display:none;width:18px;height:18px;border:2px solid transparent;border-top-color:currentColor;border-radius:50%;animation:spin .6s linear infinite}
.btn.loading .btn-label{visibility:hidden}
.btn.loading .btn-spinner{display:block;position:absolute}
@keyframes spin{to{transform:rotate(360deg)}}

/* Porcelain primary CTA (2026-08-31, owner-picked treatment; supersedes the
   2026-08-19 top-lit coral lozenge). The gradient lozenge is gone: coral is no
   longer a button FILL anywhere in the product, it survives inside the button
   as the 6px ::before dot. The 1px rim stays for the reason it was added --
   under box-sizing:border-box a borderless primary is 2px shorter than the
   bordered ghost/secondary it sits beside -- but it is the fill color now, so
   it does its height job without drawing a ring. Radius moves off
   var(--radius) (9px) to --radius-panel (12px), which is what .btn-lg already
   resolved to, so the big sign-in/checkout buttons keep the exact geometry
   they had and the small ones join them. text-shadow is dropped outright:
   dark ink on a pale slab needs no help and a shadow under it reads as
   smudge. .btn keeps overflow:hidden, so the dot cannot escape the radius. */
/* LABEL WEIGHT AND TRACKING ARE PART OF THE TREATMENT (2026-08-31, second
   pass, owner: "the text inside the white button looks weird"). The fill
   inverted last night but the label kept the metrics it wore when it was
   white-on-coral, and those metrics do not survive the inversion:
     - weight. .btn is 600, which is right for pale ink on the dark ground
       every OTHER button in this file sits on. Dark ink on a pale slab loses
       apparent weight to irradiation -- the light ground bleeds into the
       stems -- so the same 600 reads a notch lighter here than it did as
       white-on-coral, and the old coral fill was also carrying a text-shadow
       that added weight this one correctly dropped. 700 puts the label back
       where it looked before, and it is scoped to the primary rather than to
       .btn so the ghosts, which did not change ground, keep 600.
     - tracking. `normal` was fine on coral; on the pale slab the counters
       want a hair of air. .01em is the same number the app's PrimaryButton
       ships (letterSpacing 0.16 at 16pt), so the two surfaces now round to
       one value instead of two that merely look similar. */
.btn-primary{
  background:var(--porcelain);
  color:var(--porcelain-ink);
  border:1px solid var(--porcelain);
  border-radius:var(--radius-panel);
  box-shadow:var(--porcelain-depth);
  text-shadow:none;
  font-weight:700;
  letter-spacing:.01em;
}
.btn-primary::before{
  content:"";
  flex:none;
  width:6px;height:6px;
  border-radius:50%;
  background:var(--accent);
}
/* THE DOT WAS PUSHING THE LABEL OFF CENTRE (measured 2026-08-31, in a browser,
   on /login at 1280 and 375). .btn centres its flex row, so what gets centred
   is the GROUP -- dot + gap + label -- and the label therefore sits
   (6px + 8px) / 2 = exactly 7px right of the button's own centre. Measured at
   7.0 on every primary on both surfaces at both widths, which on the 354px
   sign-in button leaves 122px of empty slab on the left against 108px on the
   right. That asymmetry is what reads as "weird": the eye centres on the
   words, not on the words-plus-a-dot, so a label the CSS believes is centred
   looks nudged.
   This is a mirror of the dot on the far side: an empty, same-width ::after
   picks up the same row gap, so the row is 6+8 | label | 8+6 and the LABEL is
   what the centring lands on. Preferred over compensating with asymmetric
   padding because it is derived rather than arithmetic -- change the gap and
   both sides still match -- and it stays correct when the button also carries
   an icon, where the group that deserves the centre is icon + label.
   The two widths MUST stay equal; tests/test_porcelain_primary.py asserts it
   in all three families. */
.btn-primary::after{
  content:"";
  flex:none;
  width:6px;height:6px;
}
.btn-primary:hover{
  background:var(--porcelain-hover);
  border-color:var(--porcelain-hover);
  transform:translateY(-1px);
  box-shadow:var(--porcelain-depth-hover);
}
.btn-primary:active{
  transform:translateY(0);
  box-shadow:inset 0 2px 4px rgba(0,0,0,.22),0 1px 4px -2px rgba(0,0,0,.5);
}

.btn-ghost{background:transparent;color:var(--text-secondary);border:1px solid var(--border-strong)}
.btn-ghost:hover{color:var(--text-primary);background:var(--bg-hover);border-color:var(--border-strong)}

/* Secondary/tertiary (redesign-v3 §2). .btn-secondary is the spec name for
   the 1px border-strong outline button (same recipe as the legacy
   .btn-ghost, which stays for existing markup); .btn-tertiary is the
   quiet elevated-surface variant. */
.btn-secondary{background:transparent;color:var(--text-secondary);border:1px solid var(--border-strong)}
.btn-secondary:hover{color:var(--text-primary);background:var(--bg-hover)}
.btn-tertiary{background:var(--bg-elevated);color:var(--text-secondary);border:1px solid var(--border)}
.btn-tertiary:hover{color:var(--text-primary);background:var(--bg-hover);border-color:var(--border-strong)}

.btn-danger{background:var(--danger-dim);color:var(--danger);border:1px solid rgba(248,113,113,.2)}
.btn-danger:hover{background:rgba(248,113,113,.2)}

.btn-sm{height:36px;padding:0 14px;font-size:var(--text-xs)}
.btn-lg{height:52px;padding:0 var(--space-8);font-size:var(--text-base);border-radius:var(--radius-lg)}
.btn-icon{width:44px;height:44px;padding:0}
.btn-icon.btn-sm{width:36px;height:36px}

/* Cards */
.card{background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-lg);padding:var(--space-5);transition:all var(--dur) var(--ease)}
.card-hover:hover{border-color:var(--border-strong);transform:translateY(-1px);box-shadow:var(--shadow-md)}

/* Inputs */
.input,.select,.textarea{width:100%;height:44px;padding:0 14px;background:var(--bg-base);border:1px solid var(--border-strong);border-radius:var(--radius);color:var(--text-primary);font-size:var(--text-sm);transition:border-color var(--dur-fast),box-shadow var(--dur-fast);outline:none}
.input:focus,.select:focus,.textarea:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-glow)}
.input::placeholder{color:var(--text-muted)}
.textarea{height:auto;min-height:100px;resize:vertical;padding:10px 14px}
.select{appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%238a8885' stroke-width='2'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 12px center;padding-right:40px;cursor:pointer}
.input-group{display:flex;flex-direction:column;gap:var(--space-2)}
.input-label{font-size:var(--text-sm);font-weight:500;color:var(--text-secondary)}

/* Badges */
.badge{display:inline-flex;align-items:center;gap:var(--space-1);padding:2px 8px;border-radius:var(--radius-full);font-size:var(--text-xs);font-weight:600;line-height:1.4}
.badge-accent{background:var(--accent-glow);color:var(--accent)}
.badge-teal{background:var(--teal-dim);color:var(--teal)}
.badge-green{background:var(--success-dim);color:var(--success)}
.badge-gold{background:var(--gold-dim);color:var(--gold)}
.badge-red{background:var(--danger-dim);color:var(--danger)}
.badge-muted{background:var(--bg-hover);color:var(--text-secondary)}

/* Tables */
.table-wrap{overflow-x:auto;border-radius:var(--radius-lg);border:1px solid var(--border)}
.table{width:100%;border-collapse:collapse;font-size:var(--text-sm)}
.table th{text-align:left;padding:var(--space-3) var(--space-4);font-weight:600;font-size:var(--text-xs);text-transform:uppercase;letter-spacing:.06em;color:var(--text-muted);background:var(--bg-elevated);border-bottom:1px solid var(--border);white-space:nowrap}
.table th.sortable{cursor:pointer;user-select:none}.table th.sortable:hover{color:var(--text-muted)}
.table td{padding:var(--space-3) var(--space-4);border-bottom:1px solid var(--border);vertical-align:middle}
.table tbody tr{transition:background var(--dur-fast)}.table tbody tr:hover{background:var(--bg-hover)}
.table tbody tr:last-child td{border-bottom:none}

/* Tabs */
.tabs{display:flex;gap:var(--space-1);border-bottom:1px solid var(--border);margin-bottom:var(--space-6)}
.tab{padding:var(--space-3) var(--space-5);font-size:var(--text-sm);font-weight:500;color:var(--text-secondary);border-bottom:2px solid transparent;margin-bottom:-1px;cursor:pointer;transition:all var(--dur-fast)}
.tab:hover{color:var(--text-primary)}
.tab.active{color:var(--accent);border-bottom-color:var(--accent)}
.tab-content{display:none}.tab-content.active{display:block}

/* Pill tabs (date range, etc) */
.pill-tabs{display:flex;gap:2px;background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius);padding:3px;width:fit-content}
.pill-tab{padding:var(--space-2) var(--space-4);border-radius:var(--radius-sm);font-size:var(--text-sm);font-weight:500;color:var(--text-secondary);cursor:pointer;transition:all var(--dur-fast)}
.pill-tab:hover{color:var(--text-primary)}
.pill-tab.active{background:var(--accent);color:#fff}

/* Toggle switch */
.toggle{position:relative;display:inline-block;width:44px;height:24px;cursor:pointer}
.toggle input{display:none}
.toggle-track{position:absolute;inset:0;background:var(--bg-elevated);border:1px solid var(--border);border-radius:var(--radius-full);transition:background var(--dur)}
.toggle input:checked+.toggle-track{background:var(--accent);border-color:var(--accent)}
.toggle-knob{position:absolute;top:3px;left:3px;width:18px;height:18px;background:#fff;border-radius:50%;transition:transform var(--dur) var(--ease);pointer-events:none}
.toggle input:checked~.toggle-knob{transform:translateX(20px)}

/* 5. STAT CARDS ============================================================ */
.stat-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--space-4);margin-bottom:var(--space-6)}
.stat-card{background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-lg);padding:var(--space-5);transition:all var(--dur) var(--ease)}
.stat-card--link{text-decoration:none;cursor:pointer;display:block}
.stat-card--link:hover{border-color:var(--accent);transform:translateY(-2px);box-shadow:var(--shadow-md)}
.stat-hint{font-size:11px;color:var(--text-faint);margin-top:6px;opacity:0;transition:opacity var(--dur)}
.stat-card--link:hover .stat-hint{opacity:1}
.stat-label{font-size:var(--text-xs);font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--text-muted);margin-bottom:var(--space-2)}
.stat-value{font-family:var(--font-display);font-size:var(--text-3xl);font-weight:800;line-height:1;color:var(--text-primary)}
.stat-trend{display:inline-flex;align-items:center;gap:var(--space-1);margin-top:var(--space-2);font-size:var(--text-xs);font-weight:600}
.stat-trend.up{color:var(--success)}.stat-trend.down{color:var(--danger)}

/* 6. PRODUCT CARDS ========================================================= */
.product-grid{column-count:3;column-gap:var(--space-4)}
.product-card{break-inside:avoid;background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-lg);overflow:hidden;margin-bottom:var(--space-4);transition:all var(--dur) var(--ease)}
.product-card:hover{border-color:var(--border-strong);box-shadow:var(--shadow-md);transform:translateY(-2px)}
.product-card-img{position:relative;aspect-ratio:1;background:var(--bg-elevated);overflow:hidden}
.product-card-img img{width:100%;height:100%;object-fit:cover;transition:transform var(--dur-slow) var(--ease)}
.product-card:hover .product-card-img img{transform:scale(1.04)}
.product-card-img-fallback{width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;color:var(--text-muted);font-size:12px}
.product-card-img-fallback .lucide{width:32px;height:32px}

.badge-overlay-tl{position:absolute;top:var(--space-3);left:var(--space-3)}
.badge-overlay-tr{position:absolute;top:var(--space-3);right:var(--space-3)}

.product-card-body{padding:var(--space-4)}
.product-card-name{font-size:var(--text-sm);font-weight:600;margin-bottom:var(--space-2);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.product-card-commission{font-family:var(--font-display);font-size:var(--text-xl);font-weight:800;color:var(--accent);margin-bottom:2px}
.product-card-price{font-size:var(--text-sm);color:var(--text-secondary);margin-bottom:var(--space-3)}

.product-card-gmv{margin-bottom:var(--space-4)}
.product-card-gmv-label{font-size:var(--text-xs);color:var(--text-muted);margin-bottom:4px}
.product-card-gmv-bar{height:4px;background:var(--bg-elevated);border-radius:var(--radius-full);overflow:hidden}
.product-card-gmv-fill{height:100%;background:var(--teal);border-radius:var(--radius-full);transition:width var(--dur-slow) var(--ease)}

.product-card-stats{display:flex;gap:var(--space-3);margin:var(--space-3) 0;padding:var(--space-3) 0;border-top:1px solid var(--border);border-bottom:1px solid var(--border)}
.product-card-stat{display:flex;align-items:center;gap:4px;font-size:11px;color:var(--text-secondary)}
.product-card-stat i{color:var(--text-muted)}
.product-card-stat small{color:var(--text-muted)}

.product-card-actions{display:flex;gap:var(--space-2)}
.product-card-actions .btn{flex:1;height:36px;font-size:var(--text-xs)}

/* Horizontal scroll row */
.scroll-row{display:flex;gap:var(--space-4);overflow-x:auto;padding-bottom:var(--space-3);scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;position:relative}
.scroll-row::-webkit-scrollbar{height:4px}.scroll-row::-webkit-scrollbar-track{background:transparent}.scroll-row::-webkit-scrollbar-thumb{background:var(--bg-hover);border-radius:var(--radius-full)}
.scroll-card{flex:0 0 220px;scroll-snap-align:start}

/* Fade gradient on scroll row right edge */
.scroll-row-wrap{position:relative}
.scroll-row-wrap::after{content:'';position:absolute;top:0;right:0;bottom:0;width:60px;background:linear-gradient(to right,transparent,var(--bg-base));pointer-events:none;z-index:2}
.scroll-arrow{position:absolute;top:50%;transform:translateY(-50%);z-index:5;width:36px;height:36px;border-radius:var(--radius-full);background:var(--bg-surface);border:1px solid var(--border);display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:var(--shadow-sm);color:var(--text-muted);transition:all var(--dur-fast)}
.scroll-arrow:hover{color:var(--text-primary);box-shadow:var(--shadow-md)}
.scroll-arrow--left{left:-8px}
.scroll-arrow--right{right:-8px}
@media(max-width:768px){.scroll-arrow{display:none}}

/* 7. SECTION HEADERS ======================================================= */
.section-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--space-4)}
.section-title{font-size:var(--text-lg);font-weight:700}
.section-link{font-size:var(--text-sm);font-weight:500;color:var(--text-secondary);display:flex;align-items:center;gap:var(--space-1)}
.section-link:hover{color:var(--text-primary)}

/* 8. FILTER BAR ============================================================ */
.filter-bar{position:sticky;top:var(--topbar-h);z-index:40;display:flex;align-items:center;gap:var(--space-3);padding:var(--space-4) var(--space-6);background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-lg);margin-bottom:var(--space-5);box-shadow:var(--shadow-sm)}

/* Filter card (products page) */
.filter-card{background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-lg);padding:16px 20px;margin-bottom:var(--space-5);box-shadow:var(--shadow-sm)}
.filter-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:10px}
.filter-input{height:36px;padding:0 10px;background:var(--bg-elevated);border:1px solid var(--border);border-radius:var(--radius);color:var(--text-primary);font-size:13px;font-family:inherit;outline:none;transition:border-color var(--dur-fast)}
.filter-input:focus{border-color:var(--accent)}
.filter-input::placeholder{color:var(--text-faint)}
select.filter-input{appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 8px center;padding-right:28px;cursor:pointer}
.filter-actions{display:flex;align-items:center;gap:8px}
.filter-count-label{margin-left:auto;font-size:13px;color:var(--text-muted);font-weight:500}
.filter-bar .input,.filter-bar .select{width:auto;min-width:160px;height:40px;background:var(--bg-surface)}
.filter-count{font-size:var(--text-sm);color:var(--text-secondary);margin-left:auto;white-space:nowrap}

/* 9. DETAIL LAYOUT ========================================================= */
/* Kalodata-style proportions: fixed-width 320px image column on the left,
   data column flexes to fill the rest. minmax(0, 1fr) on the data column
   prevents long product names / description URLs from forcing the grid
   wider than its container. */
.detail-layout{display:grid;grid-template-columns:320px minmax(0, 1fr);gap:var(--space-8);align-items:start}

/* Product Description — cap prose width for readability on ultrawide
   monitors where the description card spans the full page. 72ch keeps
   line length in the comfortable 60-80-char reading range; line-height
   1.65 matches the existing prose cadence. */
.product-description{max-width:72ch;line-height:1.65}

/* Skeleton loading states for the product detail page's async-loaded
   trend chart + videos (Sprint perf/product-detail-async-rendering).
   Light-theme shimmer uses rgba white at 0.45 so it's visible on the
   pink #f9f0f1 canvas; the base block tone sits slightly darker than
   the bg-surface so the shimmer band has contrast. */
.skeleton-block,
.skeleton-video-card{
  position:relative;
  background:var(--bg-elevated, rgba(255,82,114,0.07));
  border-radius:var(--radius-lg);
  overflow:hidden;
}
.skeleton-chart{width:100%;height:300px}
.skeleton-video-card{width:100%;height:88px;margin-bottom:8px}
.skeleton-video-card:last-child{margin-bottom:0}
.skeleton-shimmer{
  position:absolute;inset:0;
  background:linear-gradient(90deg,
    transparent 0%,
    rgba(255,255,255,0.45) 50%,
    transparent 100%);
  background-size:200% 100%;
  animation:skeleton-shimmer 1.5s infinite;
}
@keyframes skeleton-shimmer{
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
.chart-error-state{
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  height:300px;gap:8px;color:var(--text-muted);text-align:center;padding:20px;
}
.chart-error-state i{width:24px;height:24px;opacity:0.6}
.chart-error-state p{margin:0;font-size:14px}
.detail-back{display:inline-flex;align-items:center;gap:var(--space-2);font-size:var(--text-sm);color:var(--text-secondary);margin-bottom:var(--space-5)}.detail-back:hover{color:var(--text-primary)}
.detail-back .lucide{width:16px;height:16px}
.detail-image{width:100%;aspect-ratio:1;border-radius:var(--radius-lg);background:var(--bg-elevated);overflow:hidden;margin-bottom:var(--space-5)}
.detail-image img{width:100%;height:100%;object-fit:cover}
.detail-tags{display:flex;flex-wrap:wrap;gap:var(--space-2);margin-top:var(--space-4)}

/* 10. CHART COMPONENTS ===================================================== */
.chart-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:var(--space-4)}
.chart-card{background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-lg);padding:var(--space-5);min-width:0;max-width:100%;overflow:hidden}
.chart-title{font-size:var(--text-sm);font-weight:600;color:var(--text-secondary);margin-bottom:var(--space-4)}
.chart-wrap{position:relative;height:240px}

/* 11. COMMISSION CALCULATOR ================================================ */
.calc-widget{background:var(--bg-elevated);border:1px solid var(--border);border-radius:var(--radius-lg);padding:var(--space-5)}
.calc-widget-title{font-size:var(--text-sm);font-weight:600;color:var(--text-muted);margin-bottom:var(--space-4);display:flex;align-items:center;gap:var(--space-2)}
.calc-result{margin-top:var(--space-4);padding:var(--space-4);background:var(--accent-glow);border-radius:var(--radius);text-align:center}
.calc-result-value{font-family:var(--font-display);font-size:var(--text-2xl);font-weight:800;color:var(--accent)}
.calc-result-label{font-size:var(--text-xs);color:var(--text-muted);margin-top:var(--space-1)}

/* 12. VIDEO CARDS ========================================================== */
.video-card{display:flex;gap:var(--space-3);padding:var(--space-3);border-radius:var(--radius);transition:background var(--dur-fast)}
.video-card:hover{background:var(--bg-hover)}
.video-thumb{width:72px;height:72px;border-radius:var(--radius);background:var(--bg-elevated);overflow:hidden;flex-shrink:0;position:relative}
.video-thumb img{width:100%;height:100%;object-fit:cover}
.video-creator{font-size:var(--text-sm);font-weight:600;color:var(--accent);margin-bottom:2px}
.video-views{font-size:var(--text-xs);color:var(--text-muted)}

/* 13. PAGINATION =========================================================== */
.pagination{display:flex;align-items:center;justify-content:center;gap:var(--space-1);margin-top:var(--space-6)}
.page-btn{width:40px;height:40px;display:flex;align-items:center;justify-content:center;border-radius:var(--radius);font-size:var(--text-sm);font-weight:500;color:var(--text-muted);transition:all var(--dur-fast)}
.page-btn:hover{background:var(--bg-hover);color:var(--text-primary)}
.page-btn.active{background:var(--accent);color:#fff}
.page-btn:disabled{opacity:.3;cursor:not-allowed}

/* 14. EMPTY STATES ========================================================= */
.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:var(--space-16) var(--space-6)}
.empty-icon{width:80px;height:80px;margin-bottom:var(--space-5);color:var(--text-muted)}
.empty-title{font-size:var(--text-lg);font-weight:600;margin-bottom:var(--space-2)}
.empty-desc{font-size:var(--text-sm);color:var(--text-secondary);max-width:360px;margin-bottom:var(--space-5)}

/* 15. SKELETON LOADERS ===================================================== */
.skeleton{background:var(--bg-elevated);border-radius:var(--radius);position:relative;overflow:hidden}
.skeleton::after{content:'';position:absolute;inset:0;background:linear-gradient(90deg,transparent 0%,rgba(255,255,255,.04) 50%,transparent 100%);animation:shimmer 1.5s infinite}
@keyframes shimmer{0%{transform:translateX(-100%)}100%{transform:translateX(100%)}}
.skeleton-text{height:14px;margin-bottom:var(--space-2)}.skeleton-text:last-child{width:60%}
.skeleton-heading{height:24px;width:40%;margin-bottom:var(--space-4)}
.skeleton-card{height:200px}
.skeleton-avatar{width:32px;height:32px;border-radius:var(--radius-full)}

/* 16. FLASH / TOAST MESSAGES =============================================== */
.flash-container{position:fixed;top:var(--space-4);right:var(--space-4);z-index:9999;display:flex;flex-direction:column;gap:var(--space-2);pointer-events:none}
.flash-msg{padding:var(--space-3) var(--space-5);border-radius:var(--radius);font-size:var(--text-sm);font-weight:500;pointer-events:auto;animation:flashIn .3s var(--ease) forwards;display:flex;align-items:center;gap:var(--space-3);box-shadow:var(--shadow-lg);max-width:400px;background:var(--bg-surface)}
.flash-msg.success{border:1px solid rgba(74,222,128,.3);color:var(--success)}
.flash-msg.error{border:1px solid rgba(248,113,113,.3);color:var(--danger)}
.flash-msg.info{border:1px solid rgba(45,212,191,.3);color:var(--teal)}
/* Welcome flash for fresh signups — pink accent + wider so the
   "check Promotions / Spam" line reads without wrapping awkwardly. */
.flash-msg.welcome{border:1px solid var(--accent);color:var(--accent);background:linear-gradient(135deg,var(--accent-glow),var(--bg-surface));max-width:440px;line-height:1.4}
.flash-msg.out{animation:flashOut .3s var(--ease) forwards}
@keyframes flashIn{from{opacity:0;transform:translateX(40px)}to{opacity:1;transform:translateX(0)}}
@keyframes flashOut{from{opacity:1;transform:translateX(0)}to{opacity:0;transform:translateX(40px)}}
.flash-close{margin-left:auto;opacity:.5;cursor:pointer;padding:2px}.flash-close:hover{opacity:1}

/* 17. CONFIRM MODAL ======================================================== */
.modal-overlay{position:fixed;inset:0;background:rgba(0,0,0,.6);backdrop-filter:blur(4px);display:flex;align-items:center;justify-content:center;z-index:10000;opacity:0;visibility:hidden;transition:all var(--dur)}
.modal-overlay.active{opacity:1;visibility:visible}
.modal-dialog{background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-xl);padding:var(--space-6);max-width:400px;width:90%;box-shadow:var(--shadow-lg)}
.modal-title{font-size:var(--text-lg);font-weight:700;margin-bottom:var(--space-2)}
.modal-desc{font-size:var(--text-sm);color:var(--text-muted);margin-bottom:var(--space-6)}
.modal-actions{display:flex;gap:var(--space-3);justify-content:flex-end}

/* 18. RANK BADGES ========================================================== */
.rank-badge{width:28px;height:28px;border-radius:var(--radius-full);display:flex;align-items:center;justify-content:center;font-size:var(--text-xs);font-weight:700}
.rank-1{background:linear-gradient(135deg,#f59e0b,#d97706);color:#000}
.rank-2{background:linear-gradient(135deg,#94a3b8,#64748b);color:#000}
.rank-3{background:linear-gradient(135deg,#d97706,#92400e);color:#fff}
.rank-default{background:var(--bg-hover);color:var(--text-muted)}

/* =========================================================================
   PAGE-SPECIFIC STYLES
   ========================================================================= */

/* PUBLIC LAYOUT */
.public-body{background:var(--bg-base)}
.public-nav{position:fixed;top:0;left:0;right:0;height:64px;display:flex;align-items:center;padding:0 var(--space-8);background:rgba(13,13,15,.7);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border-bottom:1px solid var(--border);z-index:100}
.public-nav-logo{display:flex;align-items:center;gap:var(--space-3)}
.public-nav-right{margin-left:auto;display:flex;align-items:center;gap:var(--space-6)}
.public-nav-right a{font-size:var(--text-sm);font-weight:500;color:var(--text-muted)}.public-nav-right a:hover{color:var(--text-primary)}
.public-footer{padding:var(--space-8);border-top:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;max-width:1100px;margin:0 auto}
.public-footer-links{display:flex;gap:var(--space-6)}
.public-footer-links a{font-size:var(--text-sm);color:var(--text-faint)}.public-footer-links a:hover{color:var(--text-muted)}

/* HERO */
.hero{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-12);align-items:center;padding:140px var(--space-8) var(--space-16);max-width:1100px;margin:0 auto}
.hero-eyebrow{display:inline-flex;align-items:center;gap:var(--space-2);padding:4px 12px;border-radius:var(--radius-full);background:var(--teal-dim);color:var(--teal);font-size:var(--text-xs);font-weight:600;margin-bottom:var(--space-5)}
.hero-eyebrow .lucide{width:14px;height:14px}
.hero-title{font-family:var(--font-display);font-size:var(--text-5xl);font-weight:800;letter-spacing:-.03em;line-height:1.08;margin-bottom:var(--space-5)}
.hero-sub{font-size:var(--text-lg);color:var(--text-muted);max-width:55ch;line-height:1.65;margin-bottom:var(--space-8)}
.hero-ctas{display:flex;gap:var(--space-4);flex-wrap:wrap}

/* Mini dashboard mockup in hero */
.hero-mockup{background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-xl);padding:var(--space-5);box-shadow:var(--shadow-lg)}
.mockup-row{display:flex;gap:var(--space-3);padding:var(--space-3) 0;border-bottom:1px solid var(--border);align-items:center}
.mockup-row:last-child{border-bottom:none}
.mockup-dot{width:36px;height:36px;border-radius:var(--radius);background:var(--bg-elevated);flex-shrink:0}
.mockup-lines{flex:1}
.mockup-line{height:8px;border-radius:4px;background:var(--bg-elevated);margin-bottom:6px}
.mockup-line:last-child{width:60%;margin-bottom:0}
.mockup-badge-sm{padding:2px 8px;border-radius:var(--radius-full);font-size:10px;font-weight:700}

/* Social proof */
.social-proof{padding:var(--space-8);text-align:center;border-top:1px solid var(--border);border-bottom:1px solid var(--border);display:flex;justify-content:center;gap:var(--space-10);flex-wrap:wrap}
.social-stat{font-size:var(--text-sm);color:var(--text-faint);font-weight:500}
.social-stat strong{color:var(--text-muted);font-weight:700}

/* Features */
.features{padding:var(--space-16) var(--space-8);max-width:1100px;margin:0 auto}
.features-row{display:grid;gap:var(--space-5);margin-bottom:var(--space-5)}
.features-row-asym{grid-template-columns:1.2fr .8fr}
.features-row-3{grid-template-columns:repeat(3,1fr)}

.feature-card{background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-xl);padding:var(--space-6)}
.feature-card-lg{padding:var(--space-8);min-height:340px;display:flex;flex-direction:column;justify-content:flex-end}
.feature-icon{width:40px;height:40px;border-radius:var(--radius);display:flex;align-items:center;justify-content:center;margin-bottom:var(--space-4)}
.feature-icon .lucide{width:20px;height:20px}
.feature-icon.accent{background:var(--accent-glow);color:var(--accent)}
.feature-icon.teal{background:var(--teal-dim);color:var(--teal)}
.feature-icon.gold{background:var(--gold-dim);color:var(--gold)}
.feature-title{font-size:var(--text-lg);font-weight:700;margin-bottom:var(--space-2)}
.feature-desc{font-size:var(--text-sm);color:var(--text-muted);line-height:1.65}

/* Pricing */
.pricing{padding:var(--space-16) var(--space-8);max-width:480px;margin:0 auto;text-align:center}
.pricing-heading{font-size:var(--text-3xl);font-weight:800;margin-bottom:var(--space-2)}
.pricing-sub{color:var(--text-muted);margin-bottom:var(--space-8)}
.pricing-card{background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-xl);padding:var(--space-8);text-align:left;box-shadow:0 0 60px -12px rgba(255,77,109,.08)}
.pricing-amount{font-family:var(--font-display);font-size:var(--text-4xl);font-weight:800}
.pricing-amount span{font-size:var(--text-base);font-weight:500;color:var(--text-muted)}
.pricing-features{margin:var(--space-6) 0;display:flex;flex-direction:column;gap:var(--space-3)}
.pricing-feature{display:flex;align-items:center;gap:var(--space-3);font-size:var(--text-sm);color:var(--text-muted)}
.pricing-feature .lucide{width:18px;height:18px;color:var(--teal);flex-shrink:0}
.coupon-pill{margin-top:var(--space-5);padding:var(--space-3) var(--space-4);border:1px dashed var(--accent);border-radius:var(--radius);background:var(--accent-glow);text-align:center}
.coupon-code{font-family:var(--font-display);font-weight:800;color:var(--accent);font-size:var(--text-lg);letter-spacing:.04em}
.coupon-desc{font-size:var(--text-sm);color:var(--text-muted);margin-top:2px}

/* AUTH PAGE */
.auth-page{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:var(--space-6);background:var(--bg-base);background-image:radial-gradient(680px 380px at 50% -90px, rgba(255,82,114,.07), transparent 62%),url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.03'/%3E%3C/svg%3E")}
.auth-card{width:100%;max-width:420px}
.auth-logo{display:flex;align-items:center;gap:var(--space-3);justify-content:center;margin-bottom:var(--space-8)}
.auth-form{background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-xl);padding:var(--space-8);box-shadow:var(--shadow-lg)}
.auth-form .input-group{margin-bottom:var(--space-5)}
.auth-form .btn{width:100%;margin-top:var(--space-4)}
.auth-divider{display:flex;align-items:center;gap:var(--space-4);margin:var(--space-6) 0;color:var(--text-faint);font-size:var(--text-xs)}
.auth-divider::before,.auth-divider::after{content:'';flex:1;height:1px;background:var(--border)}
.auth-footer{text-align:center;margin-top:var(--space-5);font-size:var(--text-sm);color:var(--text-muted)}

/* SUBSCRIBE PAGE */
.subscribe-layout{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-10);max-width:900px;margin:0 auto;align-items:start}
.subscribe-features{padding-top:var(--space-6)}
.subscribe-features h2{font-size:var(--text-2xl);margin-bottom:var(--space-6)}
.subscribe-feature-item{display:flex;align-items:flex-start;gap:var(--space-3);margin-bottom:var(--space-4);font-size:var(--text-sm);color:var(--text-muted)}
.subscribe-feature-item .lucide{width:20px;height:20px;color:var(--teal);flex-shrink:0;margin-top:2px}
.subscribe-payment{background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-xl);padding:var(--space-8);box-shadow:0 0 60px -12px rgba(255,77,109,.08)}
.coupon-row{display:flex;gap:var(--space-2);margin-top:var(--space-2)}
.coupon-row .input{flex:1;height:42px;font-size:14px}
.coupon-row .btn{height:42px;min-width:72px}
.coupon-result{margin-top:6px;font-size:12px;font-weight:500}
.coupon-result.valid{color:var(--success)}.coupon-result.invalid{color:var(--danger)}

/* PayPal subscribe CTA — richer than the default .btn-primary.
   Accent-pink gradient, soft glow, lift on hover. Height 54px so it
   reads as the page's primary conversion action vs. the small filter
   and coupon buttons nearby. */
.subscribe-paypal-btn{
  display:block;width:100%;
  height:54px;padding:0 20px;
  border:none;border-radius:12px;
  background:linear-gradient(135deg,#ff5272 0%,#ff3f63 50%,#e8384d 100%);
  background-size:200% 100%;
  color:#fff;font-size:15px;font-weight:700;
  cursor:pointer;
  box-shadow:0 6px 20px -4px rgba(232,56,77,.45), 0 2px 6px rgba(232,56,77,.25), inset 0 1px 0 rgba(255,255,255,.18);
  transition:transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease), background-position var(--dur) var(--ease);
  position:relative;overflow:hidden;
  -webkit-tap-highlight-color:transparent;
  letter-spacing:.01em;
}
.subscribe-paypal-btn:hover{
  transform:translateY(-1px);
  background-position:100% 0;
  box-shadow:0 10px 28px -4px rgba(232,56,77,.55), 0 4px 10px rgba(232,56,77,.3), inset 0 1px 0 rgba(255,255,255,.22);
}
.subscribe-paypal-btn:active{
  transform:translateY(0);
  box-shadow:0 3px 10px -2px rgba(232,56,77,.4), inset 0 1px 0 rgba(255,255,255,.1);
}
.subscribe-paypal-btn:disabled,.subscribe-paypal-btn.loading{
  opacity:.6;pointer-events:none;
}
.subscribe-paypal-btn .btn-spinner{
  display:none;width:18px;height:18px;
  border:2px solid transparent;border-top-color:currentColor;
  border-radius:50%;animation:spin .6s linear infinite;
  position:absolute;left:50%;top:50%;margin:-9px 0 0 -9px;
}
.subscribe-paypal-btn.loading .btn-label{visibility:hidden}
.subscribe-paypal-btn.loading .btn-spinner{display:block}

/* SETTINGS VERTICAL TABS */
.settings-layout{display:grid;grid-template-columns:200px 1fr;gap:var(--space-8)}
.settings-tabs{display:flex;flex-direction:column;gap:2px}
.settings-tab{padding:var(--space-3) var(--space-4);border-radius:var(--radius);font-size:var(--text-sm);font-weight:500;color:var(--text-muted);cursor:pointer;transition:all var(--dur-fast);text-align:left}
.settings-tab:hover{color:var(--text-primary);background:var(--bg-hover)}
.settings-tab.active{color:var(--accent);background:var(--accent-glow)}

/* 19. MOBILE BOTTOM TAB BAR + DRAWER SHELL =================================
   Mobile layout kicks in at 900px and below — wider than our old 768px
   break so the single-column nav fits comfortably on phablets too.
   Product-grid rules inside a separate 768px block below stay put.    */

/* Hidden by default; shown inside the mobile shell block. */
.mob-bar{display:none}
.topbar-hamburger{display:none}
.drawer-backdrop{display:none}

@media(max-width:900px){
  /* --- 4-tab bottom bar --- */
  .mob-bar{display:flex;position:fixed;bottom:0;left:0;right:0;z-index:1000;
    background:var(--bg-surface);border-top:1px solid var(--border);
    padding-bottom:var(--safe-bottom);
    height:calc(64px + var(--safe-bottom));align-items:stretch}
  .mob-tab{flex:1;display:flex;flex-direction:column;align-items:center;
    justify-content:center;gap:3px;padding:8px 4px;text-decoration:none;
    color:var(--text-faint);min-height:56px;
    -webkit-tap-highlight-color:transparent;transition:color 150ms}
  /* Force every mob-tab icon to inherit its parent's text color.
     The Creators tab's 'users' lucide icon was rendering pink on
     inactive tabs because lucide-rendered <svg> retained attributes
     that short-circuited the color cascade for that specific icon.
     Explicit color:inherit + stroke:currentColor normalises all four. */
  .mob-tab i,.mob-tab svg{width:22px;height:22px;color:inherit;stroke:currentColor;fill:none}
  .mob-tab span{font-size:10px;font-weight:600;line-height:1}
  .mob-tab:active{opacity:.7}
  .mob-tab--active{color:var(--accent)}

  /* --- Sidebar becomes off-canvas drawer --- */
  .sidebar{
    /* override the desktop `position:fixed; top:8px; left:8px; bottom:8px`
       inset + the rounded corners — drawer takes the full height and
       butts flush to the screen edge. */
    top:0 !important;left:0 !important;bottom:0 !important;
    width:min(85vw, 320px) !important;
    border-radius:0 !important;border:none !important;
    border-right:1px solid var(--border) !important;
    padding-top:var(--safe-top);
    padding-bottom:var(--safe-bottom);
    transform:translateX(-100%);
    transition:transform var(--dur) var(--ease);
    z-index:1100;
    box-shadow:0 0 40px rgba(0,0,0,0.25);
  }
  .sidebar--open{transform:translateX(0)}

  .drawer-backdrop{
    display:block;position:fixed;inset:0;z-index:1099;
    background:rgba(0,0,0,0.5);
    opacity:0;pointer-events:none;
    transition:opacity var(--dur) var(--ease);
  }
  .drawer-backdrop--open{opacity:1;pointer-events:auto}

  .main-area{margin-left:0 !important;
    padding-bottom:calc(64px + var(--safe-bottom) + 8px) !important}
  .upgrade-fab{bottom:calc(64px + var(--safe-bottom) + 12px) !important}
  .chat-fab{bottom:calc(64px + var(--safe-bottom) + 12px) !important}
  .chat-drawer{bottom:calc(128px + var(--safe-bottom)) !important}

  /* --- Compact topbar with hamburger --- */
  .topbar{height:52px;padding:0 var(--space-3);gap:var(--space-3)}
  .topbar-search{max-width:none}
  .topbar-search .input{height:40px}
  .topbar-hamburger{
    display:inline-flex;align-items:center;justify-content:center;
    width:44px;height:44px;flex-shrink:0;
    background:transparent;border:1px solid transparent;border-radius:var(--radius);
    color:var(--text-primary);cursor:pointer;
    -webkit-tap-highlight-color:transparent;
  }
  .topbar-hamburger:active{background:var(--bg-hover)}
  /* On phones we hide the theme toggle + log-out icons — the drawer is
     the canonical place to reach settings, and cramming them into the
     40px-high search row makes the input too narrow. */
  .topbar-right .theme-toggle,.topbar-right a.btn-icon{display:none}
}

@media(max-width:768px){
.mob-tab--active{color:var(--accent)}

/* Compact topbar further at phone width */
.topbar-search{max-width:none}

/* Single column products */
.product-grid{column-count:1 !important}
.product-table-wrap{font-size:12px}

/* Stat grid */
.stat-grid{grid-template-columns:1fr 1fr !important;gap:var(--space-3) !important}

/* Filter card mobile */
.filter-card{padding:12px}
.filter-row{flex-direction:column;gap:6px}
.filter-input{width:100% !important;min-width:0 !important}
.filter-actions{flex-wrap:wrap}

/* Detail layout single col */
.detail-layout{grid-template-columns:1fr !important}

/* Chart grid single col */
.chart-grid{grid-template-columns:1fr !important}
}

/* 20. ANIMATIONS =========================================================== */
@keyframes fadeIn{from{opacity:0}to{opacity:1}}
@keyframes slideUp{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.5}}
.anim-fade-in{animation:fadeIn .4s var(--ease) forwards}
.anim-slide-up{animation:slideUp .5s var(--ease) forwards}
.anim-pulse{animation:pulse 2s ease-in-out infinite}

/* 21. RESPONSIVE =========================================================== */
@media(max-width:1024px){
  .stat-grid{grid-template-columns:repeat(2,1fr)}
  .product-grid{column-count:2}
  .chart-grid{grid-template-columns:1fr}
  /* Tablet: shrink the image column + tighten gap, but KEEP the
     two-column layout. The 768px breakpoint below collapses to
     single-column for true mobile. */
  .detail-layout{grid-template-columns:260px minmax(0, 1fr);gap:var(--space-5)}
  .hero{grid-template-columns:1fr;padding-top:120px}
  .hero-mockup{display:none}
  .features-row-asym,.features-row-3{grid-template-columns:1fr}
  .subscribe-layout{grid-template-columns:1fr}
  .settings-layout{grid-template-columns:1fr}
  .settings-tabs{flex-direction:row;overflow-x:auto}
}

@media(max-width:768px){
  .page-content{padding:var(--space-4)}
  .hero{padding:100px var(--space-5) var(--space-10)}
  .hero-title{font-size:var(--text-3xl)}
  .hero-sub{font-size:var(--text-base)}
  .public-nav{padding:0 var(--space-4)}
  .public-nav-right{gap:var(--space-3)}
  .social-proof{flex-direction:column;gap:var(--space-4)}
}

@media(max-width:480px){
  .stat-grid{grid-template-columns:1fr}
  .hero-ctas{flex-direction:column;align-items:stretch}
}

/* 22. UTILITIES ============================================================ */
.flex{display:flex}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-between{justify-content:space-between}
.gap-2{gap:var(--space-2)}.gap-3{gap:var(--space-3)}.gap-4{gap:var(--space-4)}.gap-6{gap:var(--space-6)}
.mt-2{margin-top:var(--space-2)}.mt-4{margin-top:var(--space-4)}.mt-6{margin-top:var(--space-6)}
.mb-2{margin-bottom:var(--space-2)}.mb-4{margin-bottom:var(--space-4)}.mb-6{margin-bottom:var(--space-6)}
.w-full{width:100%}.text-center{text-align:center}
.hidden{display:none !important}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}

/* 23. UPGRADE FAB + BANNER ================================================= */
.upgrade-fab{position:fixed;right:24px;bottom:32px;z-index:100;display:flex;align-items:center;gap:8px;padding:12px 20px;background:var(--accent);color:#fff;font-weight:600;font-size:14px;border-radius:var(--radius-full);text-decoration:none;box-shadow:0 4px 20px rgba(255,77,109,.45);transition:transform var(--dur) var(--ease),box-shadow var(--dur) ease}
.upgrade-fab:hover{transform:translateY(-2px);box-shadow:0 8px 32px rgba(255,77,109,.55);color:#fff}
.upgrade-fab:active{transform:translateY(0)}
.upgrade-fab i{width:16px;height:16px}
@media(max-width:767px){.upgrade-fab{bottom:80px;right:16px}}

.upgrade-banner{display:flex;align-items:center;gap:10px;padding:10px 20px;background:rgba(255,77,109,.1);border-bottom:1px solid rgba(255,77,109,.2);font-size:14px;color:var(--text-primary)}
.upgrade-banner__cta{margin-left:auto;color:var(--accent);font-weight:600;text-decoration:none;white-space:nowrap}
.upgrade-banner__cta:hover{text-decoration:underline}

/* 27. SCORE RING (REMOVED) =================================================
   Score ring component deleted in the products-page cleanup. Data layer
   (_calc_score / cached_score) stays intact in Python for future surfaces. */

/* 28. CATEGORY GRADIENT PLACEHOLDERS ======================================= */
.product-img-placeholder{border-radius:var(--radius);width:100%;aspect-ratio:1;display:flex;align-items:center;justify-content:center;font-size:28px;font-weight:800;color:rgba(255,255,255,.5)}
.cat-beauty{background:linear-gradient(135deg,#f953c6,#b91d73)}
.cat-electronics{background:linear-gradient(135deg,#4776e6,#1cb5e0)}
.cat-fashion{background:linear-gradient(135deg,#f7971e,#ffd200)}
.cat-home{background:linear-gradient(135deg,#56ab2f,#a8e063)}
.cat-health{background:linear-gradient(135deg,#11998e,#38ef7d)}
.cat-food{background:linear-gradient(135deg,#f46b45,#eea849)}
.cat-sports{background:linear-gradient(135deg,#396afc,#2948ff)}
.cat-pets{background:linear-gradient(135deg,#f7971e,#ff6b6b)}
.cat-default{background:linear-gradient(135deg,#434343,#2c2c2c)}

/* 29. TAP BADGE + CALLOUT ================================================== */
.tap-badge{display:inline-flex;align-items:center;gap:4px;font-size:11px;font-weight:700;color:var(--teal);background:var(--teal-dim);border:1px solid rgba(54,217,196,.25);border-radius:var(--radius-full);padding:3px 8px}

.tap-callout{background:linear-gradient(135deg,rgba(54,217,196,.06),rgba(54,217,196,.02));border:1px solid rgba(54,217,196,.25);border-radius:var(--radius-lg);padding:20px 24px;margin-bottom:24px}
.tap-callout__header{display:flex;align-items:center;gap:8px;margin-bottom:16px}
.tap-callout__title{font-size:14px;font-weight:700;color:var(--teal)}
.tap-callout__partner{font-size:12px;color:var(--text-muted);margin-left:auto}
.tap-callout__expiry{font-size:12px;color:var(--text-muted);margin:10px 0 14px}
.tap-callout__rates{display:flex;align-items:center;gap:16px;margin-bottom:16px}
.tap-rate{display:flex;flex-direction:column;gap:2px}
.tap-rate-label{font-size:11px;color:var(--text-muted);text-transform:uppercase;letter-spacing:.06em}
.tap-rate-value{font-size:24px;font-weight:800;color:var(--text-primary)}
.tap-rate-value--highlight{color:var(--teal)}
.tap-rate-arrow{font-size:18px;color:var(--text-faint)}

.btn-tap-copy{display:inline-flex;align-items:center;gap:8px;padding:10px 18px;background:var(--teal-dim);border:1px solid rgba(54,217,196,.3);border-radius:var(--radius);color:var(--teal);font-size:13px;font-weight:600;cursor:pointer;transition:background var(--dur)}
.btn-tap-copy:hover{background:rgba(54,217,196,.2)}

/* 30. NAV BADGE ============================================================ */
.nav-badge{margin-left:auto;background:var(--accent);color:#fff;font-size:10px;font-weight:700;min-width:18px;height:18px;border-radius:var(--radius-full);display:flex;align-items:center;justify-content:center;padding:0 5px}

/* 31. USER TIER BADGE ====================================================== */
.user-tier-badge{font-size:9px;font-weight:800;letter-spacing:.08em;padding:2px 6px;border-radius:4px}
.user-tier-badge--pro{background:var(--accent-glow);color:var(--accent);border:1px solid rgba(255,82,114,.3)}
/* Studio ($79 top tier) — teal to read as the premium "above Pro" badge. */
.user-tier-badge--studio{background:rgba(45,212,191,.16);color:var(--teal);border:1px solid rgba(45,212,191,.4)}
.user-tier-badge--free{background:rgba(255,255,255,.06);color:var(--text-muted);border:1px solid var(--border)}
/* Studio "Best Value" plan badge on the subscribe pricing card (teal vs the
   accent "Most Popular" on Pro). */
.plan-badge--studio{background:var(--teal,#2dd4bf);color:#06302a}

/* 32. TAP PRODUCT SEARCH DROPDOWN ========================================== */
.tap-product-dropdown{position:absolute;top:100%;left:0;right:0;max-height:280px;overflow-y:auto;background:var(--bg-elevated);border:1px solid var(--border-strong);border-radius:var(--radius);z-index:50;box-shadow:var(--shadow-lg);margin-top:4px}
.tap-product-option{padding:10px 14px;cursor:pointer;display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid var(--border);transition:background var(--dur-fast)}
.tap-product-option:hover{background:var(--bg-hover)}
.tap-product-option:last-child{border-bottom:none}

/* 33. STATS PANEL (Product Detail) ========================================= */
.stats-panel{background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-lg);overflow:hidden}
.stats-tabs{display:flex;border-bottom:1px solid var(--border);background:var(--bg-base)}
.stats-tab{flex:1;padding:12px 8px;font-size:13px;font-weight:500;color:var(--text-secondary);background:none;border:none;cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1px;transition:all var(--dur-fast)}
.stats-tab:hover{color:var(--text-primary);background:rgba(255,255,255,.02)}
.stats-tab--active{color:var(--accent);border-bottom-color:var(--accent)}

.stats-grid{display:grid;grid-template-columns:1fr 1fr;gap:1px;background:var(--border)}
.stat-card-mini{background:var(--bg-surface);padding:16px 18px;display:flex;flex-direction:column;gap:3px}
.stat-card-mini .stat-label{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.07em;color:var(--text-muted)}
.stat-card-mini .stat-value{font-size:20px;font-weight:700;color:var(--text-primary);line-height:1.2}
.stat-delta{display:flex;align-items:center;gap:3px;font-size:11px;font-weight:500;margin-top:2px}
.stat-delta--up{color:var(--success)}
.stat-delta--down{color:var(--danger)}

.stats-commission-row{display:grid;grid-template-columns:repeat(3,1fr);border-top:1px solid var(--border);background:var(--bg-base)}
.commission-highlight{padding:12px 18px;display:flex;flex-direction:column;gap:2px;border-right:1px solid var(--border)}
.commission-highlight:last-child{border-right:none}
.commission-highlight__label{font-size:11px;text-transform:uppercase;letter-spacing:.07em;color:var(--text-muted);font-weight:600}
.commission-highlight__value{font-size:16px;font-weight:700;color:var(--accent)}

/* 24. FEATURES SECTION (Landing Page v2) =================================== */
/* Landing section backgrounds */
.landing-section--alt{background:rgba(255,255,255,0.03)}
.landing-section--dark{background:#0a0a0c}

.features-section{padding:clamp(64px,8vw,120px) 0;background:#0e0e10}
.features-container{max-width:1100px;margin:0 auto;padding:0 24px}
.features-header{margin-bottom:56px}
.features-eyebrow{display:inline-block;font-size:12px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--accent);margin-bottom:12px}
.features-title{font-size:clamp(28px,3.5vw,44px);font-weight:700;line-height:1.15;color:var(--text-primary)}

/* Feature Hero */
.feature-hero{display:grid;grid-template-columns:1fr 1fr;gap:48px;align-items:center;background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-xl);padding:48px;margin-bottom:24px}
@media(max-width:768px){.feature-hero{grid-template-columns:1fr;padding:28px}}
.feature-tag{display:inline-block;font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--teal);background:var(--teal-dim);border:1px solid rgba(45,212,191,.2);border-radius:var(--radius-full);padding:3px 10px;margin-bottom:16px}
.feature-hero__content h3{font-size:clamp(20px,2vw,26px);font-weight:700;color:var(--text-primary);margin-bottom:12px;line-height:1.3}
.feature-hero__content p{font-size:15px;color:var(--text-muted);line-height:1.7;margin-bottom:20px}
.feature-list{list-style:none;display:flex;flex-direction:column;gap:10px}
.feature-list li{display:flex;align-items:center;gap:10px;font-size:14px;color:#c5c3bf}
.feature-list li i{width:16px;height:16px;color:var(--teal);flex-shrink:0}

/* Product Card Mockup */
.mockup-card-stack{display:flex;flex-direction:column;gap:10px}
.mockup-product-card{background:var(--bg-elevated);border:1px solid var(--border);border-radius:10px;display:flex;gap:12px;padding:12px;align-items:flex-start}
.mockup-product-card--2{opacity:.85;transform:scale(.98)}
.mockup-product-card--3{opacity:.7;transform:scale(.96)}
.mockup-img{width:52px;height:52px;border-radius:var(--radius);flex-shrink:0}
.mockup-img--1{background:linear-gradient(135deg,#ff4d6d,#ff8a65)}
.mockup-img--2{background:linear-gradient(135deg,#2dd4bf,#7c3aed)}
.mockup-img--3{background:linear-gradient(135deg,#fbbf24,#f97316)}
.mockup-card-body{flex:1;min-width:0}
.mockup-badge{font-size:11px;font-weight:600;padding:2px 8px;border-radius:var(--radius-full);display:inline-block;margin-bottom:4px}
.mockup-badge--hot{background:var(--accent-glow);color:var(--accent)}
.mockup-badge--warm{background:var(--gold-dim);color:var(--gold)}
.mockup-title{font-size:13px;font-weight:600;color:var(--text-primary);margin-bottom:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mockup-meta{display:flex;justify-content:space-between;font-size:12px;margin-bottom:6px}
.mockup-commission{color:var(--accent);font-weight:600}
.mockup-price{color:var(--text-muted)}
.mockup-bar{height:3px;background:var(--border);border-radius:var(--radius-full)}
.mockup-bar-fill{height:100%;background:linear-gradient(90deg,#ff4d6d,#ff8a65);border-radius:var(--radius-full)}

/* Features Grid (below hero) */
.features-grid{display:grid;grid-template-columns:1.4fr 1fr;gap:24px;margin-bottom:24px}
@media(max-width:768px){.features-grid{grid-template-columns:1fr}}
.features-stack{display:flex;flex-direction:column;gap:24px}
.feature-card{background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-lg);padding:28px}
.feature-card--large{padding:32px}
.feature-card .feature-icon,.feature-discord-banner .feature-icon{width:22px;height:22px;color:var(--accent);margin-bottom:14px}
.feature-card h3{font-size:18px;font-weight:700;color:var(--text-primary);margin-bottom:10px}
.feature-card p{font-size:14px;color:var(--text-muted);line-height:1.7}

/* Mini Calc Mockup */
.mini-calc-mockup{margin-top:20px;background:var(--bg-base);border:1px solid var(--border);border-radius:var(--radius);padding:16px}
.calc-row{display:flex;justify-content:space-between;font-size:13px;color:var(--text-muted);padding:6px 0;border-bottom:1px solid rgba(255,255,255,.05)}
.calc-row:last-child{border-bottom:none}
.calc-row--total{padding-top:10px}
.calc-value{color:var(--text-primary);font-weight:500}
.calc-value--highlight{color:var(--teal);font-weight:700;font-size:15px}

/* Discord Banner */
.feature-discord-banner{background:linear-gradient(135deg,#141416,#1a1a2e);border:1px solid rgba(88,101,242,.3);border-radius:var(--radius-lg);padding:32px 40px;display:flex;align-items:center;justify-content:space-between;gap:32px}
@media(max-width:768px){.feature-discord-banner{flex-direction:column;padding:24px}}
.feature-discord-banner__text{display:flex;align-items:flex-start;gap:16px}
.feature-discord-banner__text .feature-icon{color:#5865f2;margin-bottom:0;flex-shrink:0;margin-top:2px}
.feature-discord-banner__text h3{font-size:18px;font-weight:700;color:var(--text-primary);margin-bottom:6px}
.feature-discord-banner__text p{font-size:14px;color:var(--text-muted);line-height:1.7;max-width:400px}
.feature-discord-banner__text code{background:rgba(255,255,255,.1);padding:2px 6px;border-radius:4px;font-size:13px;color:var(--text-primary)}

.discord-msg-mockup{display:flex;gap:12px;align-items:flex-start;background:#36393f;border-radius:var(--radius);padding:12px 16px;min-width:280px}
.discord-avatar{width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,#5865f2,#2dd4bf);flex-shrink:0}
.discord-name{display:block;font-size:13px;font-weight:700;color:#5865f2;margin-bottom:4px}
.discord-bubble{font-size:13px;color:#dcddde;line-height:1.5}
.discord-bubble a{color:#5865f2}

/* 25. PRICING SECTION (Landing Page v2) ==================================== */
.pricing-section{padding:clamp(64px,8vw,120px) 0;background:var(--bg-surface)}
.pricing-container{max-width:1000px;margin:0 auto;padding:0 24px}
.pricing-header{margin-bottom:48px}
.pricing-sub{font-size:16px;color:var(--text-muted);margin-top:12px;max-width:480px}
.pricing-layout{display:grid;grid-template-columns:1fr 340px;gap:32px;align-items:start}
@media(max-width:768px){.pricing-layout{grid-template-columns:1fr}}

.pricing-card{background:var(--bg-elevated);border:1px solid rgba(255,77,109,.3);border-radius:var(--radius-xl);padding:36px;box-shadow:0 0 40px rgba(255,77,109,.06)}
.pricing-card__header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:20px}
.pricing-plan-name{font-size:13px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:.08em;margin-bottom:6px}
.pricing-price{display:flex;align-items:baseline;gap:4px}
.pricing-amount{font-size:40px;font-weight:800;color:var(--text-primary);line-height:1}
.pricing-period{font-size:15px;color:var(--text-muted)}
.pricing-badge{font-size:11px;font-weight:700;color:var(--accent);background:var(--accent-glow);border:1px solid rgba(255,77,109,.3);border-radius:var(--radius-full);padding:4px 10px;white-space:nowrap}
.pricing-coupon-callout{display:flex;align-items:center;gap:8px;background:var(--teal-dim);border:1px solid rgba(45,212,191,.2);border-radius:var(--radius);padding:10px 14px;font-size:13px;color:var(--teal);margin-bottom:24px}
.pricing-coupon-callout i{width:14px;height:14px;flex-shrink:0}
.pricing-coupon-callout strong{color:var(--text-primary)}

.pricing-features{list-style:none;display:flex;flex-direction:column;gap:12px;margin-bottom:28px}
.pricing-features li{display:flex;align-items:flex-start;gap:10px;font-size:14px;color:#c5c3bf;line-height:1.5}
.pricing-features li i{width:16px;height:16px;color:var(--teal);flex-shrink:0;margin-top:2px}
.pricing-features code{background:rgba(255,255,255,.08);padding:1px 5px;border-radius:4px;font-size:12px}

/* Converted with the rest on 2026-08-31 even though NO template renders this
   class today (grepped; it is dead CSS kept for a plan-card layout that was
   replaced). A coral-filled primary left in the sheet is a landmine: the next
   person to revive the class ships the retired treatment and nothing catches
   it, because the pins only look at the families that render. */
.btn-pricing-cta{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;text-align:center;padding:14px 24px;background:var(--porcelain);color:var(--porcelain-ink);font-weight:700;font-size:15px;border-radius:12px;text-decoration:none;box-shadow:var(--porcelain-depth);transition:background var(--dur) ease,transform var(--dur) ease;margin-bottom:12px}
.btn-pricing-cta::before{content:"";flex:none;width:6px;height:6px;border-radius:50%;background:var(--accent)}
.btn-pricing-cta:hover{background:var(--porcelain-hover);transform:translateY(-1px);color:var(--porcelain-ink)}
.pricing-card__note{font-size:12px;color:var(--text-faint);text-align:center}

/* Value Props */
.pricing-value-props{display:flex;flex-direction:column;gap:20px}
.value-prop{display:flex;gap:14px;align-items:flex-start}
.value-prop i{width:20px;height:20px;color:var(--accent);flex-shrink:0;margin-top:2px}
.value-prop strong{display:block;font-size:14px;font-weight:600;color:var(--text-primary);margin-bottom:4px}
.value-prop p{font-size:13px;color:var(--text-muted);line-height:1.6;margin:0}

/* 26. ADMIN SCAN STATUS ==================================================== */
.scan-status-box{padding:14px 18px;border-radius:var(--radius);font-size:var(--text-sm);line-height:1.6}
.scan-status-info{background:var(--teal-dim);border:1px solid rgba(45,212,191,.2);color:var(--teal)}
.scan-status-success{background:var(--success-dim);border:1px solid rgba(74,222,128,.2);color:var(--success)}
.scan-status-error{background:var(--danger-dim);border:1px solid rgba(248,113,113,.2);color:var(--danger)}

/* 34. VIEW TOGGLE ========================================================== */
.view-toggle{display:flex;gap:2px;background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius);padding:2px}
.view-toggle-btn{padding:6px 10px;border-radius:var(--radius-sm);cursor:pointer;color:var(--text-muted);transition:all var(--dur-fast);display:flex;align-items:center}
.view-toggle-btn:hover{color:var(--text-primary)}
.view-toggle-btn.active{background:var(--accent);color:#fff}
.view-toggle-btn .lucide{width:16px;height:16px}

/* 35. PRODUCT TABLE VIEW =================================================== */
.product-table-wrap{overflow-x:auto;border-radius:var(--radius-lg);border:1px solid var(--border);background:var(--bg-surface);box-shadow:var(--shadow-sm)}
.product-table{width:100%;border-collapse:separate;border-spacing:0;font-size:13px}
.product-table th{text-align:left;padding:11px 14px;font-weight:600;font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--text-muted);background:var(--bg-elevated);border-bottom:1px solid var(--border);white-space:nowrap;position:sticky;top:0;z-index:2}
.th-sort{color:var(--text-muted);text-decoration:none;cursor:pointer;transition:color var(--dur-fast)}.th-sort:hover{color:var(--text-primary)}.th-sort--active{color:var(--accent);font-weight:700}
.product-table td{padding:12px 14px;border-bottom:1px solid var(--border);vertical-align:middle;color:var(--text-primary)}
.product-table tbody tr{transition:background 100ms ease}
.product-table tbody tr:hover{background:var(--bg-hover)}
.product-table tbody tr:last-child td{border-bottom:none}
.product-table .product-row-name{display:flex;align-items:center;gap:12px;min-width:240px}
.product-row-thumb{width:48px;height:48px;border-radius:var(--radius);object-fit:cover;background:var(--bg-elevated);flex-shrink:0;border:1px solid var(--border)}
.product-row-thumb-fallback{width:48px;height:48px;border-radius:var(--radius);display:flex;align-items:center;justify-content:center;font-size:16px;font-weight:700;color:rgba(255,255,255,.5);flex-shrink:0}
.product-table .commission-cell{color:var(--accent);font-weight:700;font-size:15px}
.product-table .growth-badge{display:inline-flex;align-items:center;gap:3px;font-size:11px;font-weight:600;padding:3px 10px;border-radius:var(--radius-full)}
.growth-badge--up{background:var(--success-dim);color:var(--success)}
.growth-badge--down{background:var(--danger-dim);color:var(--danger)}
.growth-badge--flat{background:transparent;color:var(--text-faint)}

/* 36. FEE CALCULATOR ======================================================= */
.fee-calc{background:var(--bg-elevated);border:1px solid var(--border);border-radius:var(--radius-lg);padding:var(--space-5)}
.fee-calc-title{font-size:var(--text-sm);font-weight:600;color:var(--text-secondary);margin-bottom:var(--space-4);display:flex;align-items:center;gap:var(--space-2)}
.fee-calc-row{display:flex;justify-content:space-between;align-items:center;padding:8px 0;border-bottom:1px solid var(--border);font-size:var(--text-sm)}
.fee-calc-row:last-child{border-bottom:none}
.fee-calc-row .label{color:var(--text-muted)}
.fee-calc-row .value{font-weight:600;color:var(--text-primary);font-variant-numeric:tabular-nums}
.fee-calc-row .value.highlight{color:var(--teal);font-size:var(--text-lg)}
.fee-calc-inputs{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-3);margin-bottom:var(--space-4)}

/* 37. PAYWALL OVERLAY ====================================================== */
.paywall-overlay{position:relative;margin-top:-60px;padding-top:80px;background:linear-gradient(to bottom,transparent 0%,var(--bg-base) 40%);text-align:center;padding-bottom:var(--space-8)}
.paywall-card{display:inline-flex;flex-direction:column;align-items:center;background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-xl);padding:32px 40px;box-shadow:var(--shadow-md);max-width:420px}

/* Free user table blur (rows 6+) */
.product-table tbody tr:nth-child(n+6) .free-blur{filter:blur(6px);user-select:none;pointer-events:none}

/* Clickable rows/cards: any element with [data-row-href] gets pointer cursor.
   Navigation is handled by the delegated click listener in main.js, which
   ignores clicks whose target is an interactive descendant (a, button, etc). */
[data-row-href]{cursor:pointer}

/* Out-of-stock treatment: grayscale + dim + corner badge on cards,
   grayscale only on table rows (no good place for a badge in a row),
   and a yellow warning banner on the product detail page. Applied
   when product.product_status == 'out_of_stock' in templates. */
.product-card--unavailable,
.product-row--unavailable{position:relative;filter:grayscale(.85);opacity:.6;transition:opacity 150ms ease}
.product-card--unavailable:hover,
.product-row--unavailable:hover{opacity:.8}
.product-card-badge--oos{position:absolute;top:8px;right:8px;background:#6b6b6b;color:#fff;font-size:11px;font-weight:700;padding:3px 8px;border-radius:4px;text-transform:uppercase;letter-spacing:.03em;z-index:2;pointer-events:none}
/* Fresh-drop pill CSS REMOVED 2026-05-22 with the AA-drops feature. */
.oos-banner{background:#fff3cd;border:1px solid #ffe69c;color:#664d03;padding:12px 16px;border-radius:8px;margin-bottom:16px;display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}
.oos-banner__headline{font-weight:700}
.oos-banner__detail{font-size:13px;color:#856404;display:block;margin-top:2px}

/* 38. THEME TOGGLE ========================================================= */
.theme-toggle{width:36px;height:36px;display:flex;align-items:center;justify-content:center;border-radius:var(--radius);cursor:pointer;color:var(--text-muted);transition:all var(--dur-fast)}
.theme-toggle:hover{color:var(--text-primary);background:var(--bg-hover)}

/* 38. AI CHAT WIDGET ======================================================= */
.chat-fab{position:fixed;bottom:24px;right:24px;z-index:200;width:52px;height:52px;border-radius:var(--radius-full);background:var(--accent);color:#fff;display:flex;align-items:center;justify-content:center;box-shadow:0 4px 20px rgba(232,56,77,.35);cursor:pointer;transition:all var(--dur) var(--ease);border:none}
.chat-fab:hover{transform:scale(1.08);box-shadow:0 6px 28px rgba(232,56,77,.45)}
.chat-fab .lucide{width:24px;height:24px}
/* Old 767px override left stale 'bottom:80px' which sat ON TOP of the
   new 64px tab bar. Neutralised — the mobile-shell rule at <=900px
   above now places the fab above the tab bar + safe-area cleanly. */
@media(max-width:767px){.chat-fab{bottom:calc(64px + var(--safe-bottom) + 16px)}}

.chat-drawer{position:fixed;bottom:88px;right:24px;z-index:200;width:380px;max-height:520px;background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-xl);box-shadow:var(--shadow-lg);display:none;flex-direction:column;overflow:hidden}
.chat-drawer.open{display:flex}
@media(max-width:480px){.chat-drawer{right:8px;left:8px;width:auto;bottom:calc(64px + var(--safe-bottom) + 76px);max-height:70vh}}

.chat-header{display:flex;align-items:center;justify-content:space-between;padding:14px 16px;border-bottom:1px solid var(--border)}
.chat-messages{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:12px;min-height:200px;max-height:340px}
.chat-msg{padding:10px 14px;border-radius:var(--radius-lg);font-size:var(--text-sm);line-height:1.55;max-width:90%;word-wrap:break-word}
.chat-msg--ai{background:var(--bg-elevated);color:var(--text-primary);align-self:flex-start;border-bottom-left-radius:4px}
.chat-msg--user{background:var(--accent);color:#fff;align-self:flex-end;border-bottom-right-radius:4px}
.chat-msg--loading{color:var(--text-muted);font-style:italic}
.chat-input-wrap{display:flex;gap:8px;padding:12px 16px;border-top:1px solid var(--border)}
.chat-input-wrap .input{flex:1}

/* 39. FAVORITE / WATCHLIST BUTTON ========================================== */
.fav-btn{position:absolute;bottom:8px;right:8px;z-index:5;width:32px;height:32px;border-radius:var(--radius-full);background:rgba(0,0,0,0.45);backdrop-filter:blur(4px);display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all var(--dur-fast);border:none;color:rgba(255,255,255,0.7)}
.fav-btn:hover{background:rgba(0,0,0,0.6);color:#fff;transform:scale(1.1)}
.fav-btn.active{color:var(--accent);background:rgba(232,56,77,0.2)}
.fav-btn .lucide{width:16px;height:16px}

/* Inline favorite button for table rows */
.fav-btn-inline{width:28px;height:28px;border-radius:var(--radius-full);display:flex;align-items:center;justify-content:center;cursor:pointer;border:none;background:transparent;color:var(--text-faint);transition:all var(--dur-fast)}
.fav-btn-inline:hover{color:var(--accent);background:var(--accent-glow)}
.fav-btn-inline.active{color:var(--accent)}

/* 40. CAMPAIGN ANNOUNCEMENT BANNER ======================================== */
.campaign-banner{overflow:hidden;max-height:44px;transition:all .3s ease}
.campaign-banner__inner{display:flex;align-items:center;gap:var(--space-3);padding:0 var(--space-4);height:44px;font-size:13px;font-weight:500;color:#fff;position:relative}
.campaign-banner__icon{flex-shrink:0;display:flex;align-items:center;opacity:.9}
.campaign-banner__marquee{flex:1;min-width:0;align-self:stretch;overflow:hidden;position:relative;mask-image:linear-gradient(90deg,transparent,#000 5%,#000 95%,transparent)}
.campaign-banner__track{position:absolute;inset:0 auto 0 0;display:flex;align-items:center;gap:var(--space-16);white-space:nowrap;animation:marquee 25s linear infinite;width:max-content}/* position:absolute 2026-06-12 — removes the nowrap track from intrinsic sizing so message length can never widen the banner's flex ancestors (belt to .main-area's min-width:0 braces). */
.campaign-banner__track:hover{animation-play-state:paused}
.campaign-banner__text{display:inline-flex;gap:var(--space-2);align-items:center}
.campaign-banner__text strong{font-weight:800;letter-spacing:.02em}
.campaign-banner__link{color:inherit;text-decoration:underline;text-underline-offset:2px;font-weight:700;margin-left:var(--space-2);opacity:.9;transition:opacity .2s}
.campaign-banner__link:hover{opacity:1}
.campaign-banner__right{display:flex;align-items:center;gap:var(--space-3);flex-shrink:0}
.campaign-banner__countdown{font-size:12px;font-weight:700;font-variant-numeric:tabular-nums;background:rgba(0,0,0,.2);padding:2px 8px;border-radius:var(--radius-full);letter-spacing:.03em}
.campaign-banner__close{background:none;border:none;color:rgba(255,255,255,.7);cursor:pointer;display:flex;align-items:center;padding:4px;border-radius:var(--radius-full);transition:all .2s}
.campaign-banner__close:hover{color:#fff;background:rgba(255,255,255,.15)}

@keyframes marquee{
  0%{transform:translateX(0)}
  100%{transform:translateX(-50%)}
}

/* Color schemes */
.campaign-banner--fire{background:linear-gradient(135deg,#e8384d 0%,#f97316 50%,#e8384d 100%);background-size:200% 200%;animation:bannerShimmer 4s ease infinite}
.campaign-banner--gold{background:linear-gradient(135deg,#d97706 0%,#f59e0b 50%,#d97706 100%);background-size:200% 200%;animation:bannerShimmer 4s ease infinite}
.campaign-banner--teal{background:linear-gradient(135deg,#0d9488 0%,#2dd4bf 50%,#0d9488 100%);background-size:200% 200%;animation:bannerShimmer 4s ease infinite}
.campaign-banner--purple{background:linear-gradient(135deg,#7c3aed 0%,#a78bfa 50%,#7c3aed 100%);background-size:200% 200%;animation:bannerShimmer 4s ease infinite}
.campaign-banner--coral{background:linear-gradient(135deg,#e8384d 0%,#f472b6 50%,#e8384d 100%);background-size:200% 200%;animation:bannerShimmer 4s ease infinite}
/* Campaign-calendar schemes (2026-06-12) — themed per TikTok Shop
   event so auto-created banners read like the campaign they announce.
   Same shimmer mechanics as the originals. */
.campaign-banner--valentine{background:linear-gradient(135deg,#be185d 0%,#f472b6 50%,#be185d 100%);background-size:200% 200%;animation:bannerShimmer 4s ease infinite}
.campaign-banner--spring{background:linear-gradient(135deg,#15803d 0%,#4ade80 50%,#15803d 100%);background-size:200% 200%;animation:bannerShimmer 4s ease infinite}
.campaign-banner--summer{background:linear-gradient(135deg,#ea580c 0%,#facc15 50%,#ea580c 100%);background-size:200% 200%;animation:bannerShimmer 4s ease infinite}
.campaign-banner--bts{background:linear-gradient(135deg,#4338ca 0%,#818cf8 50%,#4338ca 100%);background-size:200% 200%;animation:bannerShimmer 4s ease infinite}
.campaign-banner--patriot{background:linear-gradient(135deg,#1e3a8a 0%,#dc2626 50%,#1e3a8a 100%);background-size:200% 200%;animation:bannerShimmer 4s ease infinite}
.campaign-banner--halloween{background:linear-gradient(135deg,#c2410c 0%,#6b21a8 50%,#c2410c 100%);background-size:200% 200%;animation:bannerShimmer 4s ease infinite}
.campaign-banner--bfcm{background:linear-gradient(135deg,#0a0a0a 0%,#b45309 45%,#fbbf24 50%,#b45309 55%,#0a0a0a 100%);background-size:200% 200%;animation:bannerShimmer 4s ease infinite}
.campaign-banner--holiday{background:linear-gradient(135deg,#b91c1c 0%,#15803d 50%,#b91c1c 100%);background-size:200% 200%;animation:bannerShimmer 5s ease infinite}
.campaign-scheme-dot--valentine{background:linear-gradient(135deg,#be185d,#f472b6)}
.campaign-scheme-dot--spring{background:linear-gradient(135deg,#15803d,#4ade80)}
.campaign-scheme-dot--summer{background:linear-gradient(135deg,#ea580c,#facc15)}
.campaign-scheme-dot--bts{background:linear-gradient(135deg,#4338ca,#818cf8)}
.campaign-scheme-dot--patriot{background:linear-gradient(135deg,#1e3a8a,#dc2626)}
.campaign-scheme-dot--halloween{background:linear-gradient(135deg,#c2410c,#6b21a8)}
.campaign-scheme-dot--bfcm{background:linear-gradient(135deg,#0a0a0a,#fbbf24)}
.campaign-scheme-dot--holiday{background:linear-gradient(135deg,#b91c1c,#15803d)}

@keyframes bannerShimmer{
  0%,100%{background-position:0% 50%}
  50%{background-position:100% 50%}
}

/* Admin campaign page helpers */
.campaign-scheme-dot{width:10px;height:10px;border-radius:var(--radius-full);display:inline-block;margin-right:4px;vertical-align:middle}
.campaign-scheme-dot--fire{background:#e8384d}
.campaign-scheme-dot--gold{background:#d97706}
.campaign-scheme-dot--teal{background:#0d9488}
.campaign-scheme-dot--purple{background:#7c3aed}
.campaign-scheme-dot--coral{background:#f472b6}

.status-badge{font-size:10px;font-weight:700;padding:2px 8px;border-radius:var(--radius-full);letter-spacing:.05em;text-transform:uppercase}
.status-badge--live{background:rgba(13,148,136,.15);color:#0d9488}
.status-badge--upcoming{background:rgba(217,119,6,.15);color:#d97706}
.status-badge--active{background:rgba(37,99,235,.15);color:#2563eb}
.status-badge--off{background:rgba(156,163,175,.15);color:#9ca3af}

/* Mobile: smaller banner */
@media(max-width:768px){
  .campaign-banner__inner{height:38px;font-size:12px;padding:0 var(--space-3)}
  .campaign-banner{max-height:38px}
  .campaign-banner__countdown{font-size:10px;padding:2px 6px}
}

/* 41. SKIP LINK ============================================================ */
.skip-link{position:absolute;top:-100px;left:16px;background:var(--accent);color:#fff;padding:8px 16px;border-radius:var(--radius);font-weight:700;font-size:14px;z-index:9999;transition:top .2s}
.skip-link:focus{top:16px}

/* 42. FOCUS VISIBLE STYLES ================================================= */
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:2px}
button:focus-visible,a:focus-visible,.btn:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
input:focus-visible,select:focus-visible,textarea:focus-visible{outline:2px solid var(--teal);outline-offset:0}

/* 43. HOW IT WORKS GRID (landing) ========================================== */
.how-it-works-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:32px;text-align:left}
@media(max-width:768px){.how-it-works-grid{grid-template-columns:1fr;gap:24px}}

/* 44. TESTIMONIALS ========================================================= */
.testimonials-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:24px}
.testimonial-card{background:rgba(255,255,255,0.04);border:1px solid rgba(255,255,255,0.08);border-radius:12px;padding:24px;text-align:left}
.testimonial-quote{font-size:14px;color:var(--text-secondary);line-height:1.7;margin-bottom:16px;font-style:italic}
.testimonial-author{display:flex;align-items:center;gap:12px}
.testimonial-avatar{width:36px;height:36px;border-radius:var(--radius-full);display:flex;align-items:center;justify-content:center;color:#fff;font-weight:700;font-size:14px;flex-shrink:0}
.testimonial-author strong{display:block;font-size:13px;color:var(--text-primary)}
.testimonial-author span{font-size:11px;color:var(--text-faint)}
@media(max-width:768px){.testimonials-grid{grid-template-columns:1fr;gap:16px}}

/* 45. FAQ ACCORDION ======================================================== */
.faq-list{display:flex;flex-direction:column;gap:0}
.faq-item{border-bottom:1px solid rgba(255,255,255,0.08);padding:0}
.faq-question{font-size:15px;font-weight:600;color:var(--text-primary);padding:16px 0;cursor:pointer;list-style:none;display:flex;justify-content:space-between;align-items:center}
.faq-question::after{content:'+';font-size:20px;font-weight:400;color:var(--text-faint);transition:transform .2s}
.faq-item[open] .faq-question::after{content:'-'}
.faq-answer{font-size:14px;color:var(--text-muted);line-height:1.7;padding:0 0 16px}
.faq-question::-webkit-details-marker{display:none}

/* 46. MOBILE FILTER IMPROVEMENTS =========================================== */
@media(max-width:768px){
  .filter-card{padding:var(--space-3)}
  .filter-row{flex-direction:column;gap:var(--space-2)}
  .filter-row .filter-input{width:100%!important;min-width:0!important}
  .filter-actions{flex-wrap:wrap;gap:var(--space-2)}
}

/* 47. MOBILE BOTTOM NAV CONTENT PADDING ==================================== */
@media(max-width:768px){
  .page-content{padding-bottom:100px}
}

/* 48. LANDING TRENDING TABLE MOBILE ======================================== */
@media(max-width:640px){
  [style*="grid-template-columns:40px 1fr 80px 80px 80px"]{
    grid-template-columns:28px 1fr 60px 60px!important;
    font-size:11px!important;
  }
}

/* 49. BRAND HUNTER PREMIUM STYLES ========================================= */

/* Brand avatar fallback (letter circle — like Discord avatars) */
.brand-avatar-fallback{width:100%;height:100%;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,var(--accent),#d97706);color:#fff;font-size:32px;font-weight:800;font-family:var(--font-display);letter-spacing:-.02em}

/* Brand detail header avatar fallback */
.brand-header-avatar-fallback{width:64px;height:64px;border-radius:var(--radius-full);background:linear-gradient(135deg,var(--accent),#d97706);display:flex;align-items:center;justify-content:center;color:#fff;font-size:24px;font-weight:800;font-family:var(--font-display);border:2px solid var(--border);flex-shrink:0}

/* Hidden Gem badge (card overlay) */
.brand-gem-badge{position:absolute;bottom:8px;right:8px;background:linear-gradient(135deg,#d97706,#f59e0b);color:#fff;font-size:10px;font-weight:700;padding:3px 8px;border-radius:var(--radius);letter-spacing:.03em;box-shadow:0 2px 8px rgba(217,119,6,.4);animation:gemShimmer 3s ease infinite;background-size:200% 200%}

/* Hidden Gem inline badge (detail header) */
.brand-gem-badge-inline{display:inline-flex;align-items:center;gap:2px;background:linear-gradient(135deg,#d97706,#f59e0b);color:#fff;font-size:11px;font-weight:700;padding:2px 10px;border-radius:var(--radius);letter-spacing:.03em;animation:gemShimmer 3s ease infinite;background-size:200% 200%}

@keyframes gemShimmer{
  0%,100%{background-position:0% 50%}
  50%{background-position:100% 50%}
}

/* Brand letter avatar (compact, for tables and lists) */
.brand-letter-avatar{width:36px;height:36px;border-radius:var(--radius-full);background:linear-gradient(135deg,var(--accent),#d97706);display:flex;align-items:center;justify-content:center;color:#fff;font-weight:800;font-size:14px;font-family:var(--font-display);flex-shrink:0}

/* 50. BRAND HUNTER v2 — SCAN UI =========================================== */

/* Page range radio buttons */
.scan-range-group{display:flex;gap:var(--space-2);flex-wrap:wrap;margin:var(--space-4) 0}
.scan-range-opt{position:relative}
.scan-range-opt input[type="radio"]{position:absolute;opacity:0;pointer-events:none}
.scan-range-opt label{display:inline-flex;align-items:center;gap:6px;padding:8px 16px;border-radius:var(--radius);border:1px solid var(--border);background:var(--bg-surface);color:var(--text-secondary);font-size:13px;font-weight:600;cursor:pointer;transition:all var(--dur-fast)}
.scan-range-opt input:checked+label{border-color:var(--accent);background:var(--accent-glow);color:var(--accent)}
.scan-range-opt:hover label{border-color:var(--border-strong)}

/* Gold shimmer for Hidden Gems option */
.scan-range--gem input:checked+label{border-color:#d97706;background:rgba(217,119,6,.1);color:#d97706}
.scan-range--gem label{border-color:rgba(217,119,6,.3);animation:gemBorderPulse 3s ease infinite}
@keyframes gemBorderPulse{
  0%,100%{border-color:rgba(217,119,6,.3)}
  50%{border-color:rgba(217,119,6,.6)}
}

/* Custom range inputs */
.scan-custom-inputs{display:none;align-items:center;gap:var(--space-2);margin-top:var(--space-2)}
.scan-custom-inputs.visible{display:flex}
.scan-custom-inputs input{width:70px}

/* Scan progress bar */
.scan-progress{background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-lg);padding:var(--space-4);margin-bottom:var(--space-6)}
.scan-progress__info{display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--space-3);font-size:13px;font-weight:600}
.scan-progress__bar{height:8px;background:var(--bg-elevated);border-radius:var(--radius-full);overflow:hidden}
.scan-progress__fill{height:100%;border-radius:var(--radius-full);background:linear-gradient(90deg,var(--accent),#f97316,var(--teal));background-size:200% 100%;animation:scanBarShimmer 2s linear infinite;transition:width .5s ease}
@keyframes scanBarShimmer{
  0%{background-position:200% 0}
  100%{background-position:0 0}
}
.scan-progress__detail{font-size:12px;color:var(--text-muted);margin-top:var(--space-2);font-variant-numeric:tabular-nums}

/* Brand search dropdown */
.brand-search-wrap{position:relative}
.brand-search-dropdown{position:absolute;top:100%;left:0;right:0;background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius);max-height:240px;overflow-y:auto;z-index:20;box-shadow:var(--shadow-lg);display:none}
.brand-search-dropdown.visible{display:block}
.brand-search-item{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-3) var(--space-4);cursor:pointer;transition:background var(--dur-fast);font-size:13px}
.brand-search-item:hover{background:var(--bg-hover)}
.brand-search-item__avatar{width:32px;height:32px;border-radius:var(--radius-full);background:linear-gradient(135deg,var(--accent),#d97706);display:flex;align-items:center;justify-content:center;color:#fff;font-weight:700;font-size:13px;overflow:hidden;flex-shrink:0}
.brand-search-item__avatar img{width:100%;height:100%;object-fit:cover}
.brand-search-item__info{min-width:0}
.brand-search-item__name{font-weight:600;color:var(--text-primary)}
.brand-search-item__cat{font-size:11px;color:var(--text-faint)}

/* 51. FREEMIUM BLUR (non-subscriber data gating) ========================= */
.blurred-data{filter:blur(5px);user-select:none;pointer-events:none;display:inline-block;min-width:2ch}
.freemium-gate{background:var(--bg-surface);border:1px solid var(--accent);border-radius:var(--radius-lg);padding:var(--space-4) var(--space-6);display:flex;align-items:center;justify-content:space-between;gap:var(--space-4);margin-bottom:var(--space-6);flex-wrap:wrap}
.freemium-gate__text{font-size:var(--text-sm);color:var(--text-secondary);font-weight:500}
.freemium-gate__text strong{color:var(--text-primary)}
.freemium-gate__cta{flex-shrink:0}

/* 52. TRENDING PREVIEW TABLE (landing page) =============================== */
.trending-preview-grid{display:grid;grid-template-columns:40px 1fr 80px 80px 80px;gap:0}
@media(max-width:640px){.trending-preview-grid{grid-template-columns:28px 1fr 60px 60px;font-size:11px}.trending-preview-grid .tp-col-videos{display:none}}

/* 53. CREATOR PROFILE PAGE =============================================== */
.creator-header{padding:var(--space-5) var(--space-6)}
.creator-header__row{display:flex;align-items:flex-start;gap:var(--space-5);flex-wrap:wrap}
.creator-header__avatar-wrap{flex-shrink:0}
.creator-avatar{width:80px;height:80px;border-radius:var(--radius-full);object-fit:cover;border:2px solid var(--border);background:var(--bg-elevated);display:block}
.creator-avatar--fallback{display:flex;align-items:center;justify-content:center;font-weight:800;font-size:28px;color:var(--accent);background:var(--accent-glow)}
.creator-header__info{flex:1;min-width:260px}
.creator-header__name-row{display:flex;align-items:center;gap:8px;margin-bottom:4px;flex-wrap:wrap}
.creator-header__name{font-size:var(--text-2xl);font-weight:800;font-family:var(--font-display,'Cabinet Grotesk',sans-serif);line-height:1.1}
.creator-verified-badge{display:inline-flex;color:#3b82f6}
.creator-header__handle-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:var(--space-3);color:var(--text-muted);font-size:13px}
.creator-handle{color:var(--text-muted);font-size:13px}
.creator-handle-copy{background:none;border:none;color:var(--text-faint);cursor:pointer;padding:2px 4px;border-radius:4px;display:inline-flex;align-items:center}
.creator-handle-copy i{width:12px;height:12px}
.creator-handle-copy:hover{color:var(--text-secondary);background:var(--bg-hover)}
.creator-region{color:var(--text-muted);font-size:13px;font-weight:500}
.creator-bio{font-style:italic;color:var(--text-muted);font-size:13px;line-height:1.6;max-width:640px;margin-top:var(--space-2)}
.creator-bio-toggle{background:none;border:none;color:var(--accent);cursor:pointer;font-size:12px;font-weight:600;padding:2px 0;margin-top:2px}
.creator-header__actions{display:flex;gap:8px;flex-wrap:wrap;align-self:flex-start}
.creator-stats{grid-template-columns:repeat(4,1fr)}
@media(max-width:900px){.creator-stats{grid-template-columns:repeat(2,1fr)}}
@media(max-width:640px){.creator-header__row{flex-direction:column;align-items:stretch}.creator-header__actions{align-self:stretch}}

.creator-tabs-bar{display:flex;gap:4px;border-bottom:1px solid var(--border);margin-bottom:var(--space-5);overflow-x:auto;scrollbar-width:none}
.creator-tabs-bar::-webkit-scrollbar{display:none}
.creator-tab{background:none;border:none;padding:10px 16px;font-size:13px;font-weight:700;color:var(--text-muted);cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1px;display:inline-flex;align-items:center;gap:6px;white-space:nowrap;transition:color var(--dur-fast)}
.creator-tab i{width:14px;height:14px}
.creator-tab:hover{color:var(--text-secondary)}
.creator-tab--active{color:var(--accent);border-bottom-color:var(--accent)}

.creator-sort-row{display:flex;align-items:center;gap:10px;margin-bottom:var(--space-4);flex-wrap:wrap}
.creator-sort-btns{display:flex;gap:4px;background:var(--bg-elevated);padding:3px;border-radius:var(--radius);border:1px solid var(--border)}
.creator-sort-btn{background:none;border:none;padding:6px 12px;font-size:12px;font-weight:600;color:var(--text-muted);cursor:pointer;border-radius:calc(var(--radius) - 2px);transition:all var(--dur-fast)}
.creator-sort-btn:hover{color:var(--text-secondary)}
.creator-sort-btn--active{background:var(--bg-surface);color:var(--text-primary);box-shadow:0 1px 2px rgba(0,0,0,0.1)}

.creator-video-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--space-4)}
@media(max-width:800px){.creator-video-grid{grid-template-columns:repeat(2,1fr)}}
@media(max-width:500px){.creator-video-grid{grid-template-columns:1fr}}
.creator-video-card{background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-lg);overflow:hidden;transition:transform var(--dur-fast),border-color var(--dur-fast);text-decoration:none;color:inherit;display:block;position:relative;cursor:pointer}
.creator-video-card:hover{transform:translateY(-2px);border-color:var(--accent)}
.creator-video-card:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* Video modal */
.video-modal{position:fixed;inset:0;z-index:10000;display:none;align-items:center;justify-content:center}
.video-modal--open{display:flex}
.video-modal__backdrop{position:absolute;inset:0;background:rgba(0,0,0,0.82);backdrop-filter:blur(6px)}
.video-modal__content{position:relative;width:min(90vw,400px);aspect-ratio:9/16;max-height:90vh;background:#000;border-radius:var(--radius-lg);overflow:hidden;display:flex;flex-direction:column}
.video-modal__frame{flex:1;position:relative}
.video-modal__frame iframe{position:absolute;inset:0;width:100%;height:100%;border:0;display:block}
.video-modal__close{position:absolute;top:10px;right:10px;z-index:2;width:34px;height:34px;border-radius:50%;background:rgba(0,0,0,0.65);color:#fff;border:none;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:background var(--dur-fast)}
.video-modal__close:hover{background:rgba(0,0,0,0.9)}
.video-modal__external{padding:10px;background:#111;color:#a78bfa;text-align:center;font-size:12px;font-weight:600;text-decoration:none}
.video-modal__external:hover{color:#c4b5fd}
.video-modal--open .video-modal__content{animation:vm-pop .18s ease}
@keyframes vm-pop{from{opacity:0;transform:scale(.97)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){.video-modal--open .video-modal__content{animation:none}}
.creator-video-thumb-wrap{position:relative;width:100%;aspect-ratio:9/16;max-height:320px;background:var(--bg-elevated);overflow:hidden}
.creator-video-thumb{width:100%;height:100%;object-fit:cover;display:block}
.creator-video-thumb-fallback{width:100%;height:100%;display:flex;align-items:center;justify-content:center;color:var(--text-faint)}
.creator-video-shop{position:absolute;top:8px;right:8px;background:rgba(0,0,0,0.75);color:#fff;font-size:11px;font-weight:700;padding:3px 8px;border-radius:var(--radius-full);backdrop-filter:blur(4px)}
.creator-video-meta{padding:var(--space-3)}
.creator-video-title{font-size:13px;font-weight:600;color:var(--text-primary);line-height:1.4;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;margin-bottom:6px;min-height:2.8em}
.creator-video-stats{display:flex;align-items:center;gap:12px;font-size:11px;color:var(--text-faint);font-variant-numeric:tabular-nums}
.creator-video-stats span{display:inline-flex;align-items:center;gap:3px}
.creator-video-stats i{width:12px;height:12px}

.creator-products-wrap{overflow-x:auto}
.creator-products-table{width:100%}
.creator-product-thumb{width:40px;height:40px;border-radius:var(--radius);object-fit:cover;background:var(--bg-elevated);display:block}
.creator-product-thumb--fallback{background:var(--bg-elevated)}

.creator-growth-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--space-3);margin-bottom:var(--space-5)}
@media(max-width:700px){.creator-growth-grid{grid-template-columns:repeat(2,1fr)}}
.creator-growth-card{padding:var(--space-4);display:flex;flex-direction:column;gap:6px}
.creator-growth-period{font-size:11px;font-weight:600;color:var(--text-faint);text-transform:uppercase;letter-spacing:0.04em}
.creator-growth-number-row{display:flex;align-items:center;gap:6px;font-variant-numeric:tabular-nums;font-weight:800;font-size:var(--text-2xl);line-height:1.1}
.creator-growth-number{font-family:var(--font-display,'Cabinet Grotesk',sans-serif)}
.creator-growth-ico{flex-shrink:0}
.creator-growth-caption{font-size:11px;color:var(--text-faint);font-weight:500}
.creator-growth-card--up .creator-growth-number-row{color:#10b981}
.creator-growth-card--down .creator-growth-number-row{color:#ef4444}
.creator-growth-card--flat .creator-growth-number-row{color:var(--text-faint)}
/* legacy classes kept so older templates don't break */
.creator-growth-value{font-size:var(--text-xl);font-weight:700;font-variant-numeric:tabular-nums;margin-top:4px;display:flex;align-items:center;gap:6px}
.creator-growth-arrow{font-size:14px}
.creator-behavior-card{padding:var(--space-4) var(--space-5)}
.creator-behavior-row{display:flex;justify-content:space-between;align-items:center;gap:var(--space-4);padding:var(--space-3) 0;border-bottom:1px solid var(--border);flex-wrap:wrap}
.creator-behavior-row:last-child{border-bottom:none}
.creator-behavior-label{font-size:13px;color:var(--text-muted);font-weight:600}
.creator-behavior-value{font-size:14px;font-weight:700;color:var(--text-primary);text-align:right}
.creator-behavior-pills{display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end}
.creator-duration-pill{padding:4px 10px;border-radius:var(--radius-full);background:var(--bg-elevated);border:1px solid var(--border);font-size:12px;font-weight:600;color:var(--text-muted)}
.creator-duration-pill--active{background:var(--accent-glow);border-color:var(--accent);color:var(--accent)}

.creator-similar-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--space-3);margin-bottom:var(--space-6)}
@media(max-width:700px){.creator-similar-grid{grid-template-columns:1fr}}
.creator-similar-card{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-3) var(--space-4)}
.creator-similar-avatar{width:48px;height:48px;border-radius:var(--radius-full);object-fit:cover;flex-shrink:0;background:var(--bg-elevated)}
.creator-similar-avatar--fallback{display:flex;align-items:center;justify-content:center;font-weight:800;color:var(--accent);background:var(--accent-glow);font-size:16px}
.creator-similar-body{flex:1;min-width:0}
.creator-similar-name{font-size:14px;font-weight:700;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.creator-similar-handle{font-size:11px;color:var(--text-faint)}
.creator-similar-stats{display:flex;gap:8px;flex-wrap:wrap;margin-top:4px;font-size:11px;color:var(--text-muted);font-variant-numeric:tabular-nums}

.creator-empty{text-align:center;padding:var(--space-8) var(--space-4);color:var(--text-muted);font-size:14px}
.creator-empty i{width:40px;height:40px;color:var(--text-faint);margin-bottom:var(--space-3)}
.skeleton-thumb{width:100%;aspect-ratio:9/16;max-height:320px;background:linear-gradient(90deg,var(--bg-elevated) 0%,var(--bg-hover) 50%,var(--bg-elevated) 100%);background-size:200% 100%;animation:skeletonPulse 1.4s infinite}
.skeleton-line{height:12px;background:linear-gradient(90deg,var(--bg-elevated) 0%,var(--bg-hover) 50%,var(--bg-elevated) 100%);background-size:200% 100%;animation:skeletonPulse 1.4s infinite;border-radius:4px;margin:8px 12px}
.skeleton-circle{width:48px;height:48px;border-radius:50%;background:linear-gradient(90deg,var(--bg-elevated) 0%,var(--bg-hover) 50%,var(--bg-elevated) 100%);background-size:200% 100%;animation:skeletonPulse 1.4s infinite;flex-shrink:0}
.creator-video-card--skeleton,.creator-similar-card--skeleton{pointer-events:none}
@keyframes skeletonPulse{0%{background-position:200% 0}100%{background-position:-200% 0}}

/* 54. ONBOARDING TOUR ==================================================== */
.onboarding-tour{position:fixed;inset:0;z-index:10001;display:flex;align-items:center;justify-content:center;animation:tourFadeIn .25s ease-out}
.onboarding-tour--closing{animation:tourFadeOut .2s ease-in forwards}
.onboarding-tour__backdrop{position:absolute;inset:0;background:rgba(0,0,0,0.7);backdrop-filter:blur(8px)}
.onboarding-tour__card{position:relative;width:min(92vw,440px);background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-lg);padding:var(--space-6) var(--space-5);box-shadow:var(--shadow-lg);text-align:center;animation:tourSlideUp .35s cubic-bezier(.16,1,.3,1)}
.onboarding-tour__progress{display:flex;justify-content:center;gap:6px;margin-bottom:var(--space-5)}
.onboarding-tour__dot{width:28px;height:4px;border-radius:var(--radius-full);background:var(--bg-elevated);transition:background var(--dur-fast)}
.onboarding-tour__dot--active{background:var(--accent)}
.onboarding-tour__icon{font-size:48px;line-height:1;margin-bottom:var(--space-3)}
.onboarding-tour__title{font-size:var(--text-xl);font-weight:800;font-family:var(--font-display,'Cabinet Grotesk',sans-serif);margin-bottom:var(--space-2);color:var(--text-primary)}
.onboarding-tour__body{font-size:14px;line-height:1.6;color:var(--text-muted);margin-bottom:var(--space-5);padding:0 var(--space-2)}
.onboarding-tour__body strong{color:var(--accent);font-weight:700}
.onboarding-tour__actions{display:flex;align-items:center;gap:var(--space-2)}
@keyframes tourFadeIn{from{opacity:0}to{opacity:1}}
@keyframes tourFadeOut{from{opacity:1}to{opacity:0}}
@keyframes tourSlideUp{from{opacity:0;transform:translateY(12px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}

/* 55. CANCELLATION FLOW ================================================== */
.cancel-flow{position:fixed;inset:0;z-index:10000;display:none;align-items:center;justify-content:center}
.cancel-flow--open{display:flex;animation:tourFadeIn .2s ease-out}
.cancel-flow__backdrop{position:absolute;inset:0;background:rgba(0,0,0,0.75);backdrop-filter:blur(6px)}
.cancel-flow__card{position:relative;width:min(92vw,480px);background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-lg);padding:var(--space-6) var(--space-5);box-shadow:var(--shadow-lg);animation:tourSlideUp .3s cubic-bezier(.16,1,.3,1)}
.cancel-flow__close{position:absolute;top:12px;right:12px;background:none;border:none;color:var(--text-faint);cursor:pointer;padding:6px;border-radius:var(--radius);display:flex;align-items:center;justify-content:center}
.cancel-flow__close:hover{background:var(--bg-elevated);color:var(--text-secondary)}
.cancel-flow__icon{font-size:40px;line-height:1;text-align:center;margin-bottom:var(--space-3)}
.cancel-flow__title{font-size:var(--text-xl);font-weight:800;font-family:var(--font-display,'Cabinet Grotesk',sans-serif);text-align:center;margin-bottom:var(--space-2)}
.cancel-flow__sub{font-size:13px;color:var(--text-muted);line-height:1.6;text-align:center;margin-bottom:var(--space-5);padding:0 var(--space-2)}
.cancel-flow__option{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-4);background:var(--bg-elevated);border:1px solid var(--border);border-radius:var(--radius);cursor:pointer;transition:all var(--dur-fast);margin-bottom:var(--space-2)}
.cancel-flow__option:hover{border-color:var(--accent);transform:translateX(2px)}
.cancel-flow__option--highlight{border-color:rgba(124,58,237,0.4);background:linear-gradient(135deg,rgba(124,58,237,0.08),transparent)}
.cancel-flow__option--highlight:hover{border-color:var(--accent)}
.cancel-flow__option--danger:hover{border-color:#f87171}
.cancel-flow__option-icon{width:36px;height:36px;border-radius:var(--radius);background:var(--bg-surface);display:flex;align-items:center;justify-content:center;font-size:18px;flex-shrink:0}
.cancel-flow__option-title{font-size:14px;font-weight:700;color:var(--text-primary);margin-bottom:2px}
.cancel-flow__option-sub{font-size:12px;color:var(--text-muted);line-height:1.4}
.cancel-flow__option-arrow{color:var(--text-faint);font-weight:700;margin-left:auto;padding-left:var(--space-2)}
.cancel-flow__duration-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--space-2);margin-top:var(--space-4)}
.cancel-flow__duration{padding:var(--space-4);background:var(--bg-elevated);border:1px solid var(--border);border-radius:var(--radius);font-size:14px;font-weight:700;color:var(--text-primary);cursor:pointer;transition:all var(--dur-fast)}
.cancel-flow__duration:hover{border-color:var(--accent);color:var(--accent);transform:translateY(-1px)}
.cancel-flow__actions{display:flex;justify-content:space-between;gap:var(--space-2);margin-top:var(--space-5)}
.cancel-flow__pane--success{text-align:center}

/* 56. SOCIAL PROOF BAND (landing) ======================================== */
.social-proof-band{padding:clamp(32px,4vw,56px) 24px;background:var(--bg-surface);border-top:1px solid var(--border);border-bottom:1px solid var(--border)}
.social-proof-band__inner{max-width:960px;margin:0 auto;display:flex;justify-content:space-around;align-items:center;gap:var(--space-4);flex-wrap:wrap}
.social-proof-band__stat{text-align:center;min-width:120px}
.social-proof-band__num{font-family:var(--font-display,'Cabinet Grotesk',sans-serif);font-size:clamp(24px,3vw,36px);font-weight:800;color:var(--text-primary);line-height:1.1}
.social-proof-band__label{font-size:12px;color:var(--text-muted);margin-top:4px;text-transform:uppercase;letter-spacing:.04em}
.social-proof-band__divider{width:1px;height:36px;background:var(--border)}
.social-proof-band__trust{text-align:center;font-size:13px;color:var(--text-muted);margin-top:var(--space-5);font-style:italic}
@media(max-width:640px){.social-proof-band__divider{display:none}.social-proof-band__inner{gap:var(--space-5)}}

/* 57. MOBILE "MORE" BOTTOM SHEET ========================================= */
.mob-more{display:none;position:fixed;inset:0;z-index:1200}
.mob-more--open{display:block}
.mob-more__backdrop{position:absolute;inset:0;background:rgba(0,0,0,0.55);backdrop-filter:blur(4px);animation:fadeIn .2s ease-out}
.mob-more__sheet{position:absolute;left:0;right:0;bottom:0;background:var(--bg-surface);border-top-left-radius:20px;border-top-right-radius:20px;box-shadow:0 -8px 32px rgba(0,0,0,0.2);padding-bottom:calc(var(--space-4) + env(safe-area-inset-bottom));max-height:85vh;overflow-y:auto;animation:sheetSlideUp .28s cubic-bezier(.16,1,.3,1)}
.mob-more__handle{width:40px;height:4px;background:var(--border);border-radius:2px;margin:10px auto 4px}
.mob-more__user{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-4) var(--space-5);border-bottom:1px solid var(--border)}
.mob-more__user .avatar,.mob-more__user .avatar-lg{width:44px;height:44px;flex-shrink:0;font-size:16px}
.mob-more__section{padding:var(--space-4) var(--space-5) var(--space-2);font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--text-faint)}
.mob-more__link{display:flex;align-items:center;gap:var(--space-3);padding:14px var(--space-5);color:var(--text-primary);font-size:15px;font-weight:500;text-decoration:none;transition:background var(--dur-fast);-webkit-tap-highlight-color:transparent}
.mob-more__link:active{background:var(--bg-hover)}
.mob-more__link i{width:20px;height:20px;flex-shrink:0;color:var(--text-muted)}
.mob-more__link--active{color:var(--accent);background:var(--accent-glow)}
.mob-more__link--active i{color:var(--accent)}
@keyframes sheetSlideUp{from{transform:translateY(100%)}to{transform:translateY(0)}}

/* 58. MOBILE POLISH (< 768px) ============================================ */
@media(max-width:768px){
  /* Force grid view on the Products page — scope by ID so we don't
     also hide Brand Hunter / Favorites / Creators product-style tables
     which share the .product-table-wrap class. */
  #productTable{display:none !important}
  #productGrid{display:grid !important}
  /* .view-toggle was scoped to .products-page but the body never gets
     that class — table/grid toggle is desktop-only so just hide it
     unconditionally on mobile. */
  .view-toggle{display:none !important}

  /* Mobile product grid: 2 up at phone width. Use minmax(0,1fr)
     (not the implicit minmax(auto,1fr)) so long product names can't
     push the track width past the viewport; pair with min-width:0
     on each card so flex/grid children are truly shrinkable. */
  .product-grid{column-count:unset !important;column-gap:0 !important;padding:0 16px;max-width:100vw;box-sizing:border-box}
  #productGrid{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;max-width:100vw;box-sizing:border-box}
  .product-card{break-inside:avoid;margin-bottom:0;min-width:0;max-width:100%;overflow:hidden}
  .product-card .product-card-body{padding:10px;min-width:0}
  .product-card .product-card-name{font-size:13px;line-height:1.35;-webkit-line-clamp:2;word-break:break-word;overflow-wrap:anywhere}
  .product-card-commission{font-size:18px !important}
  .product-card-stats{font-size:11px}
  /* Keep cover images from dominating the card */
  .product-card-img,.product-img-placeholder{max-height:180px}

  /* (Batch 2, BUG F) Above-the-fold was eating ~300px before any
     actual content. Tighten page-header padding + margin so the
     title + first content row lift into view. */
  .page-header{padding:12px 0 8px !important;margin-bottom:8px !important}
  .page-title{font-size:22px !important;margin-bottom:2px !important;line-height:1.15}
  .page-subtitle{font-size:13px;line-height:1.4;margin:0}
  .page-content{padding:8px var(--space-4) var(--space-6) !important}

  /* Filter card — simpler on mobile */
  .filter-card{padding:12px;margin-bottom:var(--space-4);border-radius:var(--radius)}
  .filter-row{flex-direction:column;gap:8px;align-items:stretch}
  .filter-input{width:100% !important;min-width:0 !important;height:40px}
  .filter-actions{flex-wrap:wrap;gap:6px}
  .filter-count-label{margin-left:0 !important;width:100%;text-align:center;margin-top:4px}

  /* Stat grid: 2-up on small, 1-up on xs */
  .stat-grid{grid-template-columns:1fr 1fr !important;gap:10px !important}
  .stat-card{padding:var(--space-3) var(--space-4)}
  .stat-value{font-size:var(--text-xl)}

  /* Detail action row: stack full-width buttons at < 640px.
     Until then (641–768) they can still sit side-by-side. */
  .tap-callout{padding:var(--space-4);border-radius:var(--radius-lg)}
  .tap-callout__rates{flex-wrap:wrap;gap:10px}

  /* Upgrade + chat FABs — move up above the bottom tab bar */
  .upgrade-fab,.chat-fab{bottom:calc(80px + env(safe-area-inset-bottom) + 8px) !important;right:12px !important}

  /* 2026-06-08 FIX — mobile table overflow (the "30D SAL clipped / columns cut
     off" bug). These tables already drop most columns on mobile via
     .col-mobile-hide, so the old `min-width:600px` floor — added long ago to
     enable horizontal scroll — just stretched the ~4 remaining columns across
     600px and clipped them inside the ~346px wrapper. The PAGE never overflowed
     (which is why every cache-clear and code review looked fine); the TABLE was
     pinned at 600px. Drop the floor + fixed layout so the table sizes to its
     visible columns; long brand/product names ellipsize instead of ballooning. */
  .product-table,.creator-products-table{min-width:0;table-layout:fixed;width:100%}
  .brand-hunter-table-wrap,.product-table-wrap,.creator-products-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch}
  /* Kill per-column min-width floors so fixed layout can shrink to fit —
     including the INLINE `min-width:200px` on the Brand <th> (needs !important to
     beat the inline style). */
  .product-table th,.product-table td{min-width:0}
  .product-table th[style*="min-width"]{min-width:0 !important}
  /* Long names ellipsize instead of stretching the table. `:not(.btn)` protects
     the inline "View" action button in the last cell. */
  .product-table td > div{min-width:0}
  .product-table td a[href]:not(.btn){display:block;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .product-table td .product-row-name,
  .product-table td .product-row-name > div,
  .product-table td:nth-child(2) > div > div{min-width:0;overflow:hidden}

  /* Tap-target minimum 44px on all primary buttons */
  .btn{min-height:42px;padding:10px 16px}
  .btn-sm{min-height:36px;padding:8px 12px;font-size:12px}

  /* Topbar search gets full width + slightly larger text */
  .topbar-search .input{height:44px;font-size:14px}

  /* Creator profile tweaks */
  .creator-header__actions{width:100%}
  .creator-header__actions .btn{flex:1;justify-content:center}
  .creator-video-grid{grid-template-columns:repeat(2,1fr) !important;gap:10px !important}
  .creator-similar-grid{grid-template-columns:1fr !important}

  /* Onboarding tour: tighter on mobile */
  .onboarding-tour__card{padding:var(--space-5) var(--space-4)}
  .onboarding-tour__icon{font-size:40px}
  .onboarding-tour__title{font-size:var(--text-lg)}
  .onboarding-tour__body{font-size:13px}

  /* Settings: tabs scroll horizontally */
  .settings-tabs{display:flex !important;flex-direction:row !important;gap:0 !important;overflow-x:auto;border-bottom:1px solid var(--border);padding:0 var(--space-2);-webkit-overflow-scrolling:touch}
  .settings-tab{white-space:nowrap;padding:12px 14px !important;border-bottom:2px solid transparent;border-radius:0}
  .settings-tab.active{background:transparent !important;border-bottom-color:var(--accent)}

  /* Hide the / keyboard-shortcut hint badge — not useful on mobile */
  .topbar-search kbd{display:none !important}
}

/* 59. MOBILE PRODUCT DETAIL (< 640px) — stack CTAs full-width =========== */
@media(max-width:640px){
  .detail-layout{gap:var(--space-4) !important}
  .detail-image{aspect-ratio:1/1;max-height:320px}

  /* Stack the action-button row into full-width blocks */
  .detail-layout .btn-primary,
  .detail-layout .btn-ghost,
  .detail-layout .btn-danger{width:100%;justify-content:center}
  .detail-layout > div > div[style*="flex-wrap:wrap"]:first-of-type{gap:8px !important}

  /* TAP callout: full-width buttons, centered */
  .tap-callout .btn-tap-copy,
  .tap-callout .btn{width:100%;justify-content:center;min-height:44px}
  .tap-callout > div[style*="flex-wrap:wrap"]{flex-direction:column !important;gap:8px !important}

  /* Admin hide button: return to normal flow (drop the margin-left:auto) */
  #adminHideBtn{margin-left:0 !important}

  /* Stats panel tabs get larger targets */
  .stats-tab{padding:10px 14px;font-size:13px}

  /* Commission row stays readable */
  .stats-commission-row{gap:8px}
  .commission-highlight__label{font-size:11px}
  .commission-highlight__value{font-size:14px}

  /* Top video card: smaller thumbnail, fuller text */
  .video-card{flex-direction:row;align-items:stretch}
  .video-thumb{width:88px;min-width:88px;aspect-ratio:1/1}

  /* Chart card: tighter padding */
  .chart-card{padding:var(--space-4);min-width:0;max-width:100%}
  .chart-wrap{height:200px !important;max-width:100%;overflow:hidden}
  .chart-wrap canvas{max-width:100% !important;height:auto !important}
}

/* BUG 5 — ensure charts on analytics page clip to the card at all
   mobile breakpoints, not just <=640px. Chart.js with
   maintainAspectRatio:false respects the container width once the
   container has a definite max-width. */
@media(max-width:900px){
  .chart-grid{grid-template-columns:minmax(0,1fr) !important;max-width:100vw}
  .chart-card{min-width:0;max-width:100%;overflow:hidden}
  .chart-wrap{max-width:100%}
  .chart-wrap canvas{max-width:100% !important}
}

/* 60. VERY SMALL PHONES (< 380px) — allow single-column on last-mile ==== */
@media(max-width:380px){
  .stat-grid{grid-template-columns:1fr !important}
  .creator-video-grid{grid-template-columns:1fr !important}
  .page-title{font-size:var(--text-xl)}
  .mob-tab span{font-size:9px}
}

/* 61. BRAND HUNTER DETAIL — mobile card layout ========================== */
.brand-products-mobile{display:none}
@media(max-width:768px){
  .brand-products-mobile{display:grid;grid-template-columns:1fr;gap:10px;margin-bottom:var(--space-5)}
  .brand-products-desktop{display:none !important}
}
.brand-products-mobile__card{display:flex;gap:12px;padding:10px;background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-lg);text-decoration:none;color:inherit;box-shadow:var(--shadow-sm);-webkit-tap-highlight-color:transparent;transition:transform var(--dur-fast)}
.brand-products-mobile__card:active{transform:scale(0.98)}
.brand-products-mobile__img-wrap{position:relative;width:90px;height:90px;flex-shrink:0;border-radius:var(--radius);overflow:hidden;background:var(--bg-elevated)}
.brand-products-mobile__img{width:100%;height:100%;object-fit:cover;display:block}
.brand-products-mobile__fallback{width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-size:28px;font-weight:800;color:rgba(255,255,255,0.7)}
.brand-products-mobile__gem{position:absolute;top:4px;right:4px;font-size:12px;background:rgba(0,0,0,0.6);border-radius:var(--radius-full);padding:2px 5px}
.brand-products-mobile__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:6px}
.brand-products-mobile__title{font-size:13px;font-weight:600;line-height:1.35;color:var(--text-primary);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.brand-products-mobile__stats{display:grid;grid-template-columns:repeat(4,1fr);gap:6px}
.brand-products-mobile__stat{display:flex;flex-direction:column;gap:1px;min-width:0}
.brand-products-mobile__stat-label{font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--text-faint)}
.brand-products-mobile__stat-value{font-size:12px;font-weight:700;font-variant-numeric:tabular-nums;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.brand-products-mobile__meta{display:flex;gap:6px;font-size:11px;color:var(--text-muted);margin-top:2px}

/* 61b. PRODUCT DETAIL — Top Creators mobile card layout ================== */
.top-creators-desktop{display:block}
.top-creators-mobile{display:none}
@media(max-width:768px){
  .top-creators-desktop{display:none !important}
  .top-creators-mobile{display:grid;grid-template-columns:1fr;gap:8px}
}
.top-creators-mobile__card{display:flex;gap:10px;padding:10px;background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-lg);text-decoration:none;color:inherit;box-shadow:var(--shadow-sm);-webkit-tap-highlight-color:transparent;transition:transform var(--dur-fast);align-items:center}
.top-creators-mobile__card:active{transform:scale(0.98)}
.top-creators-mobile__rank{flex-shrink:0;width:28px;font-size:13px;font-weight:700;color:var(--text-faint);font-variant-numeric:tabular-nums;text-align:center}
.top-creators-mobile__avatar{flex-shrink:0;width:48px;height:48px;border-radius:50%;overflow:hidden;background:var(--bg-elevated);display:flex;align-items:center;justify-content:center}
.top-creators-mobile__avatar img{width:100%;height:100%;object-fit:cover;display:block}
.top-creators-mobile__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}
.top-creators-mobile__name{font-size:13px;font-weight:700;line-height:1.2;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.top-creators-mobile__handle{font-size:11px;color:var(--text-faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.top-creators-mobile__stats{display:grid;grid-template-columns:repeat(4,1fr);gap:4px;margin-top:2px}
.top-creators-mobile__stat{display:flex;flex-direction:column;gap:1px;min-width:0}
.top-creators-mobile__stat-label{font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--text-faint)}
.top-creators-mobile__stat-value{font-size:12px;font-weight:700;font-variant-numeric:tabular-nums;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* 61c. VIRAL VIDEOS — discovery grid card layout ========================= */
.viral-videos-grid{
  display:grid;
  grid-template-columns:repeat(auto-fill, minmax(260px, 1fr));
  gap:14px;
  margin-bottom:24px;
}
@media(max-width:768px){
  .viral-videos-grid{grid-template-columns:repeat(auto-fill, minmax(160px, 1fr));gap:10px}
}
.viral-video-card{
  background:var(--bg-surface);
  border:1px solid var(--border);
  border-radius:var(--radius-lg);
  overflow:hidden;
  display:flex;
  flex-direction:column;
  box-shadow:var(--shadow-sm);
  transition:transform var(--dur-fast), box-shadow var(--dur-fast);
}
.viral-video-card:hover{transform:translateY(-2px);box-shadow:var(--shadow-md)}
.viral-video-card__thumb{
  position:relative;
  aspect-ratio:9/16;
  background:linear-gradient(135deg, rgba(232,56,77,.18), rgba(232,56,77,.04));
  overflow:hidden;
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}
.viral-video-card__thumb img{width:100%;height:100%;object-fit:cover;display:block}
/* Hover play affordance so a card reads as a tappable video, not a static image. */
.viral-video-card__play{position:absolute;inset:0;z-index:2;display:flex;align-items:center;justify-content:center;background:linear-gradient(180deg,rgba(0,0,0,0) 40%,rgba(0,0,0,.30));opacity:0;transition:opacity var(--dur-fast);pointer-events:none}
.viral-video-card__play span{width:46px;height:46px;border-radius:50%;background:rgba(255,255,255,.94);display:flex;align-items:center;justify-content:center;color:#111;box-shadow:0 6px 18px rgba(0,0,0,.35)}
.viral-video-card:hover .viral-video-card__play,.viral-video-card__thumb:focus-visible .viral-video-card__play{opacity:1}
.viral-video-card__thumb-fallback{
  width:100%;height:100%;
  display:flex;align-items:center;justify-content:center;
  color:rgba(255,255,255,0.5);
}
.viral-video-card__duration{
  position:absolute;bottom:6px;right:6px;
  background:rgba(0,0,0,0.6);color:#fff;
  border-radius:4px;padding:2px 6px;
  font-size:11px;font-weight:600;
  font-variant-numeric:tabular-nums;
}
.viral-video-card__rank{
  position:absolute;top:6px;left:6px;
  background:var(--accent);color:#fff;
  border-radius:var(--radius-full);
  padding:3px 8px;font-size:11px;font-weight:700;
  font-variant-numeric:tabular-nums;
  box-shadow:var(--shadow-sm);
}
.viral-video-card__ai-badge{
  position:absolute;top:6px;right:6px;
  background:rgba(124,58,237,.92);color:#fff;
  border-radius:var(--radius-full);
  padding:2px 8px;font-size:10px;font-weight:700;
  letter-spacing:.06em;
}
.viral-video-card__body{
  padding:10px;
  display:flex;flex-direction:column;gap:8px;
  flex:1;
}
.viral-video-card__creator{
  display:flex;align-items:center;gap:8px;
  text-decoration:none;color:inherit;
  min-width:0;
}
.viral-video-card__creator img,
.viral-video-card__creator-avatar-fallback{
  width:32px;height:32px;border-radius:50%;
  flex-shrink:0;background:var(--bg-elevated);
  object-fit:cover;
  display:flex;align-items:center;justify-content:center;
  font-size:13px;font-weight:700;color:rgba(255,255,255,0.7);
}
.viral-video-card__creator-name{
  font-size:13px;font-weight:600;color:var(--text-primary);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.viral-video-card__creator-handle{
  font-size:11px;color:var(--text-faint);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.viral-video-card__stats{
  display:grid;grid-template-columns:repeat(2, 1fr);gap:6px;
  padding:8px;
  background:var(--bg-base);
  border-radius:var(--radius);
}
/* Stay at 2x2 on mobile (was 4x1 — squeezed values like "$8.7K"
   into ~40px wide cells, truncating). The card itself shrinks at
   <=768px via the grid template, so the stats grid follows the
   narrower card. */
@media(max-width:768px){
  .viral-video-card__stats{grid-template-columns:repeat(2, 1fr);gap:4px;padding:6px}
  .viral-video-card__stat-label{font-size:10px}
  .viral-video-card__stat-value{font-size:13px}
}
.viral-video-card__stat{display:flex;flex-direction:column;gap:1px;min-width:0}
.viral-video-card__stat-label{
  font-size:9px;font-weight:700;
  text-transform:uppercase;letter-spacing:.04em;
  color:var(--text-faint);
}
.viral-video-card__stat-value{
  font-size:12px;font-weight:700;
  font-variant-numeric:tabular-nums;
  color:var(--text-primary);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.viral-video-card__product{
  display:flex;align-items:center;gap:8px;
  padding:6px 8px;
  background:var(--bg-base);
  border-radius:var(--radius);
  text-decoration:none;
  color:inherit;
  font-size:12px;
  border:1px solid transparent;
  transition:border-color var(--dur-fast), background var(--dur-fast);
}
.viral-video-card__product:hover{
  border-color:var(--border);
  background:var(--bg-elevated);
}
.viral-video-card__product img{
  width:24px;height:24px;border-radius:4px;flex-shrink:0;
  object-fit:cover;
}
.viral-video-card__product-name{
  flex:1;min-width:0;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  color:var(--text-muted);
}

/* 62. TAP CALLOUT — bulletproof mobile stacking ========================= */
@media(max-width:640px){
  /* Ensure every TAP button is full-width on its own line.
     Targets both known classes and any inline-styled button row. */
  .tap-callout button,
  .tap-callout .btn,
  .tap-callout .btn-tap-copy{width:100% !important;justify-content:center !important;min-height:44px !important;margin:0 !important}
  .tap-callout{padding:var(--space-4) !important}
  /* Force the button row inside .tap-callout to stack vertically.
     Previous rule was `.tap-callout > div` which was far too broad —
     it also captured .tap-callout__header / __rates / __expiry and
     had to be counter-reset below. Now scoped to a single class on
     the template wrapper so __header + __rates keep their default
     row layout without any reset. */
  .tap-callout__actions{display:flex;flex-direction:column;gap:8px !important;flex-wrap:nowrap !important}
  .tap-callout__rates{justify-content:space-between}
}

/* 61b. INSTALL BANNER (Phase 4a) =========================================
   Bottom banner shown once per 30d to returning logged-in users on
   non-conversion-critical pages. Two variants share the same DOM:
     - Android/Chrome: "Install Vantage [Install]"
     - iOS Safari:     "Tap [share] then Add to Home Screen" (no CTA)
   Stacks ABOVE the bottom tab bar and the update toast. Uses the
   existing .card/.btn vocabulary. */
.install-banner{
  position:fixed;
  left:50%;transform:translate(-50%, 130%);
  bottom:24px;
  z-index:1300;
  display:flex;align-items:center;gap:12px;
  width:min(calc(100vw - 24px), 520px);
  padding:12px 14px;
  background:var(--bg-surface);
  border:1px solid var(--border);
  border-radius:var(--radius-lg);
  box-shadow:var(--shadow-lg);
  opacity:0;pointer-events:none;
  transition:transform var(--dur) var(--ease), opacity var(--dur) var(--ease);
}
.install-banner--open{transform:translate(-50%, 0);opacity:1;pointer-events:auto}
.install-banner__mark{flex-shrink:0;width:36px;height:36px;border-radius:var(--radius);
  background:var(--bg-base);display:inline-flex;align-items:center;justify-content:center}
.install-banner__body{flex:1;min-width:0}
.install-banner__title{font-size:13px;font-weight:700;color:var(--text-primary);line-height:1.2;margin-bottom:2px}
.install-banner__sub{font-size:12px;color:var(--text-muted);line-height:1.35}
.install-banner__sub .ios-share-inline{vertical-align:-2px;margin:0 2px;color:var(--accent)}
.install-banner__cta{white-space:nowrap;flex-shrink:0}
.install-banner__close{
  background:transparent;border:none;
  width:32px;height:32px;min-width:32px;flex-shrink:0;
  border-radius:var(--radius);
  display:inline-flex;align-items:center;justify-content:center;
  color:var(--text-muted);cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}
.install-banner__close:hover{background:var(--bg-hover);color:var(--text-primary)}
@media(max-width:900px){
  .install-banner{
    left:12px;right:12px;width:auto;
    transform:translateY(130%);
    /* Sits 80px above the tab bar so it coexists with the update toast
       (which lives at +12px) without overlap. */
    bottom:calc(64px + var(--safe-bottom) + 80px);
  }
  .install-banner--open{transform:translateY(0)}
  .install-banner__title{font-size:14px}
  .install-banner__sub{font-size:12.5px}
}

/* 61a. SERVICE-WORKER UPDATE TOAST (Phase 3c) ============================
   Rendered by base.html at the root of the DOM. Hidden by default; the
   SW-registration script adds .sw-toast--open when a new SW is installed
   and waiting. Uses the existing .card/.btn vocabulary so visually it
   reads as a pinned card, not a foreign component.

   z-index ordering (tallest wins): drawer-backdrop 1099 < drawer 1100 <
   modal/sheet 1200 < sw-toast 1300 < install banner 1300.
   The toast sits ABOVE the bottom tab bar (1000). On mobile its `bottom`
   offset clears the tab bar + safe-area. */
.sw-toast{
  position:fixed;left:50%;
  transform:translate(-50%, 120%);
  bottom:24px;
  z-index:1300;
  display:flex;align-items:center;gap:10px;
  max-width:calc(100vw - 24px);
  padding:10px 14px;
  background:var(--bg-surface);
  border:1px solid var(--border);
  border-radius:var(--radius-lg);
  box-shadow:var(--shadow-lg);
  font-size:13px;font-weight:600;
  opacity:0;pointer-events:none;
  transition:transform var(--dur) var(--ease), opacity var(--dur) var(--ease);
}
.sw-toast--open{transform:translate(-50%, 0);opacity:1;pointer-events:auto}
.sw-toast__msg{display:inline-flex;align-items:center;gap:8px;color:var(--text-primary)}
.sw-toast__btn{white-space:nowrap}
.sw-toast__close{
  background:transparent;border:none;
  width:32px;height:32px;min-width:32px;
  border-radius:var(--radius);
  display:inline-flex;align-items:center;justify-content:center;
  color:var(--text-muted);cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}
.sw-toast__close:hover{background:var(--bg-hover);color:var(--text-primary)}
@media(max-width:900px){
  .sw-toast{
    left:12px;right:12px;
    transform:translateY(120%);
    bottom:calc(64px + var(--safe-bottom) + 12px);
    max-width:none;
  }
  .sw-toast--open{transform:translateY(0)}
}

/* 62a. GENERIC MODAL → BOTTOM SHEET ON MOBILE (Phase 2f) ==================
   .modal-overlay / .modal-dialog is the baseline confirm-modal defined
   in base.html (used by confirmAction() for delete confirmations, cancel
   flows without the dedicated .cancel-flow sheet, etc.). On phones the
   centered 400px card shrinks to 90vw and gets awkwardly small — worse,
   its Confirm/Cancel buttons end up in the middle of the screen where
   the thumb has to stretch. Transform into a bottom sheet at <=640px:
   anchored to the bottom edge, full-width, safe-area padding, drag
   handle, slide-up animation. Buttons still wrap via the existing
   .modal-actions flex row.

   The onboarding tour (.onboarding-tour) and cancellation flow
   (.cancel-flow) already have their own sheet-style mobile rules and
   are left alone here. */
@media(max-width:640px){
  .modal-overlay{align-items:flex-end}
  .modal-dialog{
    width:100% !important;max-width:none !important;
    border-bottom-left-radius:0;border-bottom-right-radius:0;
    border-top-left-radius:20px;border-top-right-radius:20px;
    padding:var(--space-5) var(--space-4)
      calc(var(--space-5) + var(--safe-bottom)) var(--space-4);
    animation:sheetSlideUp .22s var(--ease);
    border-bottom:none;
    position:relative;
  }
  /* Drag handle */
  .modal-dialog::before{
    content:'';
    display:block;width:40px;height:4px;border-radius:2px;
    background:var(--border);
    position:absolute;top:10px;left:50%;transform:translateX(-50%);
  }
  .modal-dialog > :first-child{margin-top:14px}
  /* Actions: stack Confirm above Cancel, primary action full-width at
     the bottom where the thumb naturally rests. */
  .modal-actions{
    display:flex;flex-direction:column-reverse;gap:8px;
    margin-top:var(--space-5);
  }
  .modal-actions .btn{width:100%;min-height:44px}
}

/* 62b. MOBILE FILTER BOTTOM-SHEET (Phase 2e) =============================
   Desktop renders the product filter-card inline at the top of the
   products page. On phones we collapse it into a trigger button and
   turn the same <form> element into a bottom sheet — no duplicate
   markup, the Flask submit target stays the same, and every input
   keeps its existing name attribute so ?category=... URLs still work.
*/
.filter-mobile-trigger{display:none}  /* desktop: inline filter is visible */
.filter-sheet-backdrop{display:none}
.filter-sheet-header{display:none}    /* sheet chrome hidden on desktop */

@media(max-width:768px){
  /* Hide the inline filter card; open on demand. */
  #filterForm.filter-card{
    display:none;
    position:fixed;left:0;right:0;bottom:0;top:auto;z-index:1200;
    max-height:calc(100dvh - var(--safe-top) - 40px);
    overflow-y:auto;
    border-radius:20px 20px 0 0;
    padding:0;
    box-shadow:0 -8px 32px rgba(0,0,0,0.18);
    padding-bottom:calc(var(--safe-bottom) + 12px);
    animation:sheetSlideUp .25s var(--ease);
  }
  #filterForm.filter-card.filter-sheet--open{display:block}

  #filterForm .filter-row{padding:var(--space-4)}
  #filterForm .filter-actions{
    padding:var(--space-3) var(--space-4);
    border-top:1px solid var(--border);
    position:sticky;bottom:0;background:var(--bg-surface);
    padding-bottom:calc(var(--space-3) + var(--safe-bottom));
  }

  /* Sheet header */
  .filter-sheet-header{
    display:flex;align-items:center;justify-content:space-between;
    padding:14px var(--space-4);border-bottom:1px solid var(--border);
    position:sticky;top:0;background:var(--bg-surface);z-index:1;
    font-weight:700;font-size:15px;
  }
  .filter-sheet-close{
    display:inline-flex;align-items:center;justify-content:center;
    width:44px;height:44px;border-radius:var(--radius);
    background:transparent;border:none;cursor:pointer;
    color:var(--text-muted);-webkit-tap-highlight-color:transparent;
  }
  .filter-sheet-close:active{background:var(--bg-hover)}

  .filter-sheet-backdrop{
    display:block;position:fixed;inset:0;z-index:1199;
    background:rgba(0,0,0,0.5);
    opacity:0;pointer-events:none;
    transition:opacity var(--dur) var(--ease);
  }
  .filter-sheet-backdrop--open{opacity:1;pointer-events:auto}

  /* Mobile trigger button — the primary filter CTA on the products
     page. Keep it visually the same weight as a .btn-primary (pink
     accent) so the user registers it as actionable. Using the accent
     glow tint on white rather than a solid pink fill so the trigger
     doesn't overpower the scroll-card below it. */
  .filter-mobile-trigger{
    display:inline-flex;align-items:center;gap:8px;
    height:44px;padding:0 16px;
    background:var(--accent-glow);border:1px solid var(--accent);
    border-radius:var(--radius);color:var(--accent);
    font-size:14px;font-weight:700;cursor:pointer;
    -webkit-tap-highlight-color:transparent;
    margin-bottom:var(--space-4);
  }
  .filter-mobile-trigger:active{background:rgba(255,82,114,0.18)}
  .filter-mobile-trigger i{width:16px;height:16px;color:var(--accent)}
  .filter-mobile-trigger__count{
    background:var(--accent);color:#fff;font-size:11px;font-weight:700;
    min-width:18px;height:18px;border-radius:var(--radius-full);
    display:inline-flex;align-items:center;justify-content:center;
    padding:0 5px;margin-left:4px;
  }
}
@keyframes sheetSlideUp{from{transform:translateY(100%)}to{transform:translateY(0)}}

/* 62c. SUBSCRIBE + SETTINGS LAYOUT STACKS (Phase 2e) ======================
   .subscribe-layout is a 1fr 1fr grid on desktop. On phones, stack
   single-column with the payment card on TOP of the features list —
   users should see the price + PayPal button first on mobile per
   the Vantage PWA spec. We do NOT reorder the features themselves. */
@media(max-width:900px){
  .subscribe-layout{grid-template-columns:1fr !important;gap:var(--space-6) !important}
  .subscribe-payment{order:-1}

  /* Settings sidebar + content stack. (The existing 768px rule handles
     flex-direction; bumping breakpoint to 900px matches the new mobile
     shell threshold so there's no awkward 768-900 dead zone.) */
  .settings-layout{grid-template-columns:1fr !important;gap:var(--space-5) !important}
  .settings-tabs{flex-direction:row !important;overflow-x:auto;
    border-bottom:1px solid var(--border);padding-bottom:0;gap:0 !important;
    -webkit-overflow-scrolling:touch}
  .settings-tab{white-space:nowrap;padding:12px 14px !important;
    border-bottom:2px solid transparent;border-radius:0}
  .settings-tab.active{background:transparent !important;
    border-bottom-color:var(--accent)}
}

/* 63. MOBILE iOS INPUT ZOOM FIX + TOUCH TARGETS (Phase 2c + 2d) ==========
   Safari on iOS auto-zooms the viewport when the focused input has a
   computed font-size < 16px. Bumping every editable control to 16px
   kills that behaviour. We also enforce 44x44 tap targets on controls
   that desktop rendered at 28–36px — the a11y + Apple HIG minimum. */
@media(max-width:768px){
  /* --- iOS zoom fix: every text input gets 16px at phone width --- */
  input,select,textarea,
  .input,.select,.textarea,
  .filter-input,
  input[type="text"],input[type="email"],input[type="password"],
  input[type="search"],input[type="url"],input[type="number"],
  input[type="tel"],input[type="date"]{
    font-size:16px !important;
  }

  /* --- Tap targets: 44x44 minimum --- */
  /* btn-sm at mobile is still a button the thumb has to hit; bump it
     from 36px to 44px while keeping the tighter padding. */
  .btn-sm{height:44px;min-width:44px;padding:0 16px}
  .btn-icon.btn-sm{width:44px;height:44px}

  /* Favorite heart + inline row controls bumped to 44x44 but we clamp
     the visible art to the original size via padding so layout isn't
     visually noisier — just easier to tap. */
  .fav-btn{width:44px;height:44px;padding:6px}
  .fav-btn .lucide{width:18px;height:18px}
  .fav-btn-inline{width:44px;height:44px;padding:8px}

  /* Filter inputs — already 40px from the shell rules; the 16px font
     override pushes them to a comfortable tappable height. */
  .filter-input{height:44px !important;padding:0 12px}
  select.filter-input{padding-right:32px}

  /* Product-card action buttons at 36px → 44px. */
  .product-card-actions .btn{height:44px}

  /* Bottom-tab bar tabs: already 56px min-height; confirm explicit
     min-width so the last two tabs don't shrink below 44px on 360px
     viewports. */
  .mob-tab{min-height:56px;min-width:44px}

  /* Topbar icon buttons: theme toggle / log-out button any that
     survive the hide rule in the 900px mobile block. */
  .topbar-right .btn-icon,.topbar-right .btn.btn-sm{min-height:44px;min-width:44px}

  /* Settings tab chips + other nav-like buttons */
  .settings-tab{min-height:44px}
}

/* 63c. SCORE RING ON NARROW CARDS (Bugfix Batch, BUG 8) =================
   Desktop: .badge-overlay-tr is top/right var(--space-3) (12px) and
   the score-ring-wrap is 44x44. That fits fine in a 280+ px card.
   Mobile products grid cards are 2-up at calc((100vw - gutters)/2),
   which at 375px ≈ 170px wide. The 44px ring starting at 12px right
   edge + rendering past the SVG's 44px width was breaching the card's
   right edge because the ring's viewBox extends stroke outside the
   nominal 44x44 box. Snug it to 8px right and shrink to 40px on
   phones. */
@media(max-width:640px){
  .product-card .badge-overlay-tr,
  .product-card-img .badge-overlay-tr{top:8px !important;right:8px !important}
}

/* 63b. CAMPAIGN BANNER MOBILE (Batch 3 — restore marquee) ================
   Earlier bugfix disabled the animation on phones to kill a visual
   glitch (two text copies overlapping mid-loop). That also killed
   the marquee entirely. Restore smooth scrolling with proper
   constraints: two identical copies inside the track, animate the
   whole track -50% so the second copy seamlessly slots in behind
   the first when the first wraps offscreen.

   Guardrails still in effect:
   - .campaign-banner {overflow:hidden; max-width:100vw} clips any
     excess that would otherwise bleed past the viewport.
   - .campaign-banner__track {width:max-content} makes the track the
     exact sum of its children so -50% lands on the start of the
     duplicate copy.
   - The mask-image gradient is softened (but kept) so the text fades
     in and out at the edges rather than hard-cutting. */
@media(max-width:640px){
  .campaign-banner{overflow:hidden;max-width:100vw;position:relative}
  .campaign-banner__marquee{
    flex:1;min-width:0;overflow:hidden;position:relative;
    mask-image:linear-gradient(90deg,transparent 0,#000 24px,#000 calc(100% - 24px),transparent 100%);
  }
  .campaign-banner__track{
    display:inline-flex;
    gap:48px;
    white-space:nowrap;
    width:max-content;
    animation:marquee 28s linear infinite;
  }
  .campaign-banner__track > *{flex-shrink:0}
  /* Slightly smaller text on phone so the message reads without
     wrapping mid-word when the animation pauses on touch. */
  .campaign-banner__inner{padding:0 12px}
  .campaign-banner__text{font-size:12px}
}

/* 64. MOBILE OVERFLOW HARDENING (Bugfix Batch, BUG 1) ====================
   Bugs found during live iPhone walkthrough at 375px: dashboard KPI
   cards in a row escaping the viewport, trending carousel cards the
   wrong width for phone-width scroll-snap, recently-viewed rendered
   as a horizontally scrolling 8-column table. These rules narrow-
   focus on the dashboard, the trending row, and that recently-viewed
   table. General .topbar-search / html-body clamps are above. */

/* (Batch 2, BUG G) Three full-width stacked stat cards ate ~360px
   of vertical real estate above the fold. Switch to a compact 2-col
   mini grid; the odd third card spans full width on the 2nd row,
   which is still far shorter than 3 full cards stacked. */
@media(max-width:900px){
  .page-content > .stat-grid:first-of-type,
  .stat-grid{grid-template-columns:1fr 1fr !important;gap:8px !important}
  .stat-card{padding:12px !important;min-height:0 !important}
  .stat-card .stat-label{font-size:11px;letter-spacing:.04em;margin-bottom:4px}
  .stat-card .stat-value{font-size:22px;line-height:1.1}
  .stat-card .stat-hint{font-size:11px}
}
@media(max-width:380px){
  /* Very narrow — 2-col breaks the stat-value typography. Fall back
     to 1-col on the last-mile phones. */
  .page-content > .stat-grid:first-of-type,
  .stat-grid{grid-template-columns:1fr !important}
}

@media(max-width:640px){
  /* (Batch 2, BUG B) Trending carousel: device walkthrough showed a
     single card taking ~95vw with barely any neighbor peek. Goal is
     one card fully visible with a ~60-80px slice of the next card,
     so the "scroll me" affordance is obvious. Trim the card to
     calc(100vw - 96px) = ~280px at 375w; cap max-width so landscape
     / tablet portrait doesn't balloon the card. */
  .scroll-row{padding:0 16px 12px 16px;scroll-snap-type:x mandatory;gap:12px !important;scrollbar-width:none;-ms-overflow-style:none}
  .scroll-row::-webkit-scrollbar{display:none}
  .scroll-row .scroll-card{
    flex:0 0 calc(100vw - 96px);
    max-width:280px;
    min-width:240px;
    scroll-snap-align:start;
  }
  /* Fade gradient on .scroll-row-wrap::after can introduce a few extra
     px beyond the clamp on some iOS versions — safer hidden. */
  .scroll-row-wrap::after{display:none}
  .scroll-arrow{display:none}

  /* Recently-viewed on dashboard: template now renders a parallel
     .recent-cards list that's shown only on mobile; the 8-column
     table is suppressed at phone width. See templates/dashboard.html. */
  .recent-cards{display:grid !important;grid-template-columns:1fr;gap:10px}
  .dashboard-recent-table{display:none !important}
}
@media(min-width:641px){
  /* Hide the mobile card list on tablets and up; the original table
     renders per the existing markup. */
  .recent-cards{display:none !important}
}

/* 65. BATCH 2 — PRODUCT CARD POLISH ======================================
   BUG C: Score ring overlapped the photo and was unreadable against
          busy backgrounds. Give it a white circular backdrop with
          subtle shadow so the ring + number always read.
   BUG D: Favorite heart was a translucent dark circle that looked
          disabled. Swap to a white-circle + pink-outline heart; pink
          fill when favorited.
   BUG E: View Details CTA deleted from every card in the products
          grid; making the whole card tappable via a root <a>. The
          .product-card-actions rule is still kept below for legacy
          templates that haven't migrated yet, but hidden at <=640px. */
.product-card--link{display:block;text-decoration:none;color:inherit}
.product-card--link:hover{color:inherit}
.product-card--link:active{transform:scale(0.995)}

/* Favorite heart — white circle + pink heart outline. Pink fill when active. */
.product-card .fav-btn,
.product-card--link .fav-btn{
  background:rgba(255,255,255,0.96);
  color:var(--accent);
  box-shadow:0 2px 8px rgba(0,0,0,0.12);
  backdrop-filter:none;
  width:40px;height:40px;
}
.product-card .fav-btn svg,
.product-card--link .fav-btn svg{
  color:var(--accent);stroke:var(--accent);fill:none;
  width:20px;height:20px;
}
.product-card .fav-btn.active svg,
.product-card--link .fav-btn.active svg{fill:var(--accent)}
.product-card .fav-btn:hover,
.product-card--link .fav-btn:hover{
  background:#fff;transform:scale(1.08);
  box-shadow:0 4px 12px rgba(0,0,0,0.18);
}
.product-card .fav-btn:active,
.product-card--link .fav-btn:active{transform:scale(0.92)}

/* Hide the legacy "View Details" button at phone width; the card is
   already tappable via its root <a>. */
@media(max-width:640px){
  .product-card-actions{display:none !important}
}

/* 66. BATCH 2 — CUTOFF MOP-UP (BUGS H, I, J) =============================
   H: .tap-list-actions stack full-width on phones so neither button
      is cut off by the card's right edge.
   I: .th-sort gets tighter padding + smaller ▾ on mobile so the
      brand-hunter table columns fit more of their label before the
      horizontal-scroll edge.
   J: .btn-rescan__label hides at phone width, leaving just the
      refresh-cw icon; aria-label on the anchor preserves access. */
@media(max-width:640px){
  .tap-list-actions{flex-direction:column !important;gap:8px !important}
  .tap-list-actions .btn{width:100% !important;flex:none !important}
  .th-sort{padding:4px 6px;font-size:11px;letter-spacing:.02em}
  .btn-rescan__label{display:none !important}
  .btn-rescan{padding:0 10px;min-width:40px}
}

/* 67. POLISH BATCH (BUG 4 + 6 + 7) =======================================
   4: AI widget close button was obscured by the chat-fab overlapping
      the drawer's bottom-right. Hide the fab whenever the drawer is
      open; it's a redundant CTA at that point anyway.
   6: Admin templates overflowed at mobile — form rows stayed 2-up
      (datetime-local fields squeezed off-screen) and wide tables ran
      past the viewport. Stack form rows, box-size inputs, allow
      tables to scroll horizontally cleanly.
   7: Global search dropdown had low contrast on admin pages —
      background was solid but stacking context made it feel like the
      dropdown bled onto the coupon form behind it. Bumped z-index
      and ensured opaque background + solid border. */

/* 4: hide the chat-fab whenever the chat-drawer is open */
body:has(.chat-drawer.open) .chat-fab,
html:has(.chat-drawer.open) .chat-fab{display:none !important}

/* 7: global search dropdown — hard overlay on top of everything */
#globalSearchTabs{
  z-index:1200 !important;
  background:var(--bg-surface) !important;
  border:1px solid var(--border) !important;
  box-shadow:var(--shadow-lg) !important;
}

/* 6: admin form overflow cleanup */
@media(max-width:900px){
  .admin-page, .admin-container{max-width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}
  .admin-page input[type="text"],
  .admin-page input[type="url"],
  .admin-page input[type="email"],
  .admin-page input[type="number"],
  .admin-page input[type="datetime-local"],
  .admin-page select,
  .admin-page textarea,
  form input[type="text"],
  form input[type="url"],
  form input[type="email"],
  form input[type="number"],
  form input[type="datetime-local"],
  form select,
  form textarea{max-width:100%;box-sizing:border-box}
  .admin-page .product-table-wrap,
  .admin-page .table-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch;max-width:100%}
  .admin-page .product-table,
  .admin-page .table{min-width:560px}
}
@media(max-width:640px){
  /* Stack the "Starts At" / "Ends At" datetime row that admin forms
     use — the 2-col grid forces each field below a readable width. */
  .admin-page form [style*="grid-template-columns:1fr 1fr"],
  .admin-page form [style*="grid-template-columns: 1fr 1fr"],
  form [style*="grid-template-columns:1fr 1fr 1fr"]:not(.detail-layout){grid-template-columns:1fr !important;gap:12px !important}
}

/* 68. PRODUCT GALLERY (Sprint 1 / Commit 1) ==============================
   Multi-image carousel on product detail. Main image is zoomable via
   lightbox; thumb strip scrolls horizontally on mobile with snap. */
.product-gallery{display:flex;flex-direction:column;gap:12px;margin-bottom:var(--space-5)}
.product-gallery__main{position:relative;aspect-ratio:1;background:var(--bg-elevated);border-radius:var(--radius-lg);overflow:hidden;max-width:320px}
.product-gallery__main img{width:100%;height:100%;object-fit:cover;cursor:zoom-in;transition:opacity 180ms ease;display:block}
.product-gallery__thumbs{display:flex;gap:8px;overflow-x:auto;scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;padding-bottom:4px;scrollbar-width:none;-ms-overflow-style:none}
.product-gallery__thumbs::-webkit-scrollbar{display:none}
.product-gallery__thumb{flex:0 0 64px;width:64px;height:64px;border:2px solid transparent;border-radius:var(--radius);overflow:hidden;padding:0;background:var(--bg-elevated);cursor:pointer;scroll-snap-align:start;transition:border-color 180ms ease;-webkit-tap-highlight-color:transparent}
.product-gallery__thumb img{width:100%;height:100%;object-fit:cover;display:block}
.product-gallery__thumb--active{border-color:var(--accent)}
.product-gallery__thumb:hover{border-color:var(--accent)}

.gallery-lightbox{position:fixed;inset:0;background:rgba(0,0,0,0.92);display:flex;align-items:center;justify-content:center;z-index:2000;padding:20px;cursor:zoom-out}
.gallery-lightbox img{max-width:100%;max-height:100%;object-fit:contain}
.gallery-lightbox__close{position:absolute;top:16px;right:16px;width:44px;height:44px;border-radius:50%;background:rgba(255,255,255,0.12);color:#fff;border:none;font-size:26px;line-height:1;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;-webkit-tap-highlight-color:transparent}
.gallery-lightbox__close:hover{background:rgba(255,255,255,0.22)}

/* Recent-viewed mobile card look — reuses the .card vocabulary. */
.recent-cards{display:none}
.recent-card{
  display:flex;gap:12px;align-items:center;
  padding:10px;background:var(--bg-surface);
  border:1px solid var(--border);border-radius:var(--radius-lg);
  text-decoration:none;color:inherit;
  -webkit-tap-highlight-color:transparent;
}
.recent-card__thumb{
  width:56px;height:56px;flex-shrink:0;border-radius:var(--radius);
  background:var(--bg-elevated);overflow:hidden;position:relative;
}
.recent-card__thumb img{width:100%;height:100%;object-fit:cover;display:block}
.recent-card__body{flex:1;min-width:0}
.recent-card__title{font-size:13px;font-weight:700;color:var(--text-primary);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-bottom:4px}
.recent-card__meta{display:flex;gap:10px;font-size:11px;color:var(--text-muted);
  font-variant-numeric:tabular-nums}
.recent-card__meta strong{color:var(--text-primary);font-weight:700}
.recent-card__arrow{color:var(--text-faint);font-size:18px;flex-shrink:0}

/* Mobile column hiding utility — drop a <th>/<td> at narrow widths.
   Used on creator_profile Promoted Products table to fit 8 columns
   into a 360px phone viewport without horizontal scroll. */
@media (max-width:768px){
  .col-mobile-hide{display:none !important}
}

/* Contact-page channel rows — Telegram / Discord / Email. */
.contact-channel{
  display:flex;align-items:center;gap:12px;
  padding:12px;border-radius:var(--radius);
  text-decoration:none;color:inherit;
  border:1px solid var(--border);background:var(--bg-elevated);
  margin-bottom:8px;
  transition:border-color .15s ease,transform .15s ease;
}
.contact-channel:last-child{margin-bottom:0}
.contact-channel:hover{border-color:var(--accent);transform:translateX(2px)}
.contact-channel__icon{
  width:36px;height:36px;border-radius:var(--radius);
  display:flex;align-items:center;justify-content:center;flex-shrink:0;
}
.contact-channel__body{flex:1;min-width:0}
.contact-channel__title{font-size:13px;font-weight:700;color:var(--text-primary);line-height:1.3}
.contact-channel__sub{font-size:11px;color:var(--text-muted);margin-top:2px;line-height:1.3}

/* First-action grid — empty-state replacement for Recently Viewed
   on the dashboard. Three equal columns at desktop, stacks on
   mobile. Audit fix U1 2026-05-03. */
.first-action-grid{
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--space-4);
}
.first-action-tile{
  display:flex;flex-direction:column;gap:8px;
  padding:var(--space-5) var(--space-4);
  background:var(--bg-surface);border:1px solid var(--border);
  border-radius:var(--radius-lg);text-decoration:none;color:inherit;
  transition:border-color .15s ease,transform .15s ease,box-shadow .15s ease;
  position:relative;overflow:hidden;
}
.first-action-tile:hover{
  border-color:var(--accent-border);transform:translateY(-2px);
  box-shadow:0 8px 24px -12px rgba(0,0,0,.18);
}
.first-action-tile__icon{
  width:36px;height:36px;border-radius:var(--radius);background:var(--accent-glow);
  display:flex;align-items:center;justify-content:center;color:var(--accent);
  margin-bottom:4px;
}
.first-action-tile__icon svg{width:18px;height:18px}
.first-action-tile__title{font-size:14px;font-weight:700;color:var(--text-primary);line-height:1.3}
.first-action-tile__sub{font-size:12px;color:var(--text-muted);line-height:1.5;flex:1}
.first-action-tile__cta{
  font-size:12px;font-weight:700;color:var(--accent);text-transform:uppercase;
  letter-spacing:.04em;display:inline-flex;align-items:center;gap:4px;margin-top:6px;
}
@media (max-width:768px){
  .first-action-grid{grid-template-columns:1fr}
}

/* =====================================================================
   2026-04-27 MOBILE PWA PRODUCT DETAIL — aggressive override v2
   Placed at the end of the file so cascade order beats every earlier
   rule on equal-specificity ties. Iteration 1 (line ~310) had the
   right intent but lost to the existing <=640px block on .detail-image
   max-height + .stats-tab padding. This block uses !important on every
   property and explicit child selectors for all the user-reported
   PWA detail-page issues:
     - hero image too large (now max 260px square, centered)
     - product name truncated (forced wrap)
     - GMV cut off beside 7D Sales (1fr stack)
     - action buttons (View on TikTok / Copy / Watch / Hide) bleeding
     - 'All Time' stat tab off-screen
     - description / chart bleeding
   ===================================================================== */
@media (max-width:768px){
  /* Page-level safety net: clip detail page to viewport so nothing
     can horizontally overflow. */
  .detail-layout{
    max-width:100vw !important;
    overflow-x:hidden !important;
    grid-template-columns:1fr !important;
  }
  .detail-layout > div{
    min-width:0 !important;
    max-width:100% !important;
  }
  .detail-layout *{min-width:0}

  /* HERO IMAGE — small, square, centered. Was 320px max-height and
     full viewport width which made it WIDER not smaller. Now caps
     both dimensions so the image is genuinely smaller overall. */
  .detail-image,
  .product-gallery,
  .product-gallery__main{
    max-width:260px !important;
    max-height:260px !important;
    width:100% !important;
    margin-left:auto !important;
    margin-right:auto !important;
    aspect-ratio:1 / 1 !important;
  }
  .detail-image img,
  .product-gallery__main img{
    max-width:260px !important;
    max-height:260px !important;
    width:100% !important;
    height:100% !important;
    object-fit:cover !important;
  }
  .product-gallery__thumbs{
    max-width:260px !important;
    margin-left:auto !important;margin-right:auto !important;
  }

  /* PRODUCT NAME (H1) — force wrap, no inherited nowrap, smaller
     font so long names fit without overflow. */
  .detail-layout h1{
    font-size:18px !important;
    line-height:1.3 !important;
    max-width:100% !important;
    white-space:normal !important;
    word-break:break-word !important;
    overflow-wrap:anywhere !important;
    margin-top:12px !important;
  }

  /* 7D SALES / GMV STAT GRID — stack vertically. Inline style is
     `grid-template-columns:1fr 1fr` which is high specificity but
     attribute selector + !important overrides. */
  .detail-layout > div > div[style*="grid-template-columns:1fr 1fr"]{
    grid-template-columns:1fr !important;
    gap:8px !important;
    max-width:100% !important;
  }
  .detail-layout > div > div[style*="grid-template-columns:1fr 1fr"] > .card{
    max-width:100% !important;
    min-width:0 !important;
  }
  /* Reduce the stat values so even the GMV "$1,234,567" doesn't
     overflow on iPhone SE (320px viewport). */
  .detail-layout > div > div[style*="grid-template-columns:1fr 1fr"] .card > div[style*="text-2xl"],
  .detail-layout > div > div[style*="grid-template-columns:1fr 1fr"] .card > div[style*="font-weight:800"]{
    font-size:20px !important;
    word-break:break-all;
  }

  /* ACTION BUTTON ROW — every button full width, in a column. Cover
     both the buttons row (flex-wrap:wrap) and any button anywhere
     in the detail-layout. The previous rule used `> div > div` which
     left grandchildren uncovered; this is broader. */
  .detail-layout div[style*="flex-wrap:wrap"]:not([style*="grid-template"]),
  .detail-layout div[style*="display:flex"][style*="gap:var(--space-3)"]{
    flex-direction:column !important;
    align-items:stretch !important;
    gap:8px !important;
    flex-wrap:nowrap !important;
  }
  .detail-layout button.btn,
  .detail-layout a.btn,
  .detail-layout .btn-primary,
  .detail-layout .btn-ghost,
  .detail-layout .btn-danger,
  .detail-layout .btn-tap-copy{
    width:100% !important;
    max-width:100% !important;
    margin-left:0 !important;
    margin-right:0 !important;
    justify-content:center !important;
    box-sizing:border-box !important;
  }
  /* Admin Hide button has inline margin-left:auto — kill it. */
  #adminHideBtn{margin-left:0 !important}

  /* STATS PANEL TABS (7 Days / 30 Days / All Time) — must scroll
     horizontally if 3 tabs don't fit. Was set up but lost to the
     <=640px stats-tab padding override. Reset here. */
  .stats-panel{max-width:100% !important;overflow:hidden !important}
  .stats-tabs{
    display:flex !important;
    overflow-x:auto !important;
    -webkit-overflow-scrolling:touch !important;
    scrollbar-width:none !important;
    flex-wrap:nowrap !important;
  }
  .stats-tabs::-webkit-scrollbar{display:none !important}
  .stats-tab{
    flex:1 1 0 !important;
    min-width:0 !important;
    padding:10px 4px !important;
    font-size:12px !important;
    white-space:nowrap !important;
  }

  /* Stats grid (Units / Revenue / etc) — 1-column on mobile. */
  .stats-grid{
    grid-template-columns:1fr !important;
    max-width:100% !important;
  }
  .stat-card-mini{
    padding:12px 14px !important;
    max-width:100% !important;
    min-width:0 !important;
  }
  .stat-card-mini .stat-value{font-size:18px !important;word-break:break-word}

  /* PRODUCT DESCRIPTION — bound everything to viewport width. */
  .product-description,
  .product-description *{
    max-width:100% !important;
    min-width:0 !important;
    overflow-wrap:anywhere !important;
  }
  .product-description img{
    height:auto !important;
    display:block !important;
  }
  .product-description p{
    word-break:break-word !important;
    font-size:13px !important;
  }

  /* SALES TREND CHART — clip to card. Chart.js renders the canvas
     at the size of its parent; if the parent has any horizontal
     padding leak the canvas grows past the card. */
  .chart-card{
    padding:12px !important;
    min-width:0 !important;
    max-width:100% !important;
    overflow:hidden !important;
    box-sizing:border-box !important;
  }
  .chart-wrap{
    height:200px !important;
    max-width:100% !important;
    overflow:hidden !important;
  }
  .chart-wrap canvas{
    max-width:100% !important;
    height:auto !important;
  }
  .chart-title{font-size:13px !important;margin-bottom:8px !important}

  /* Opportunity Score breakdown card — bars were leaking on the
     right column when grid collapsed. Constrain. */
  .detail-layout > div > .card{
    max-width:100% !important;
    min-width:0 !important;
    overflow:hidden !important;
  }

  /* TAP callout (Boosted Commission) buttons — same full-width
     rule as the other action buttons. */
  .tap-callout,.tap-callout *{max-width:100% !important;min-width:0}
  .tap-callout__rates{flex-wrap:wrap !important}
  .tap-callout > div[style*="flex-wrap:wrap"],
  .tap-callout__actions{
    flex-direction:column !important;
    align-items:stretch !important;
    gap:8px !important;
  }
}

/* =============================================================================
   TERMINAL REDESIGN — Phase 1 (2026-05-03)
   Source: design handoff (design_handoff_vantage_terminal/README.md).

   Strategy: append-only. The new system layers on top of the existing
   tokens — Geist becomes the new primary type but Cabinet/Satoshi stay
   as the fallback so nothing breaks if Google Fonts is blocked. The
   chartreuse "win" color is reserved for a single hero metric per
   row (Sale Likelihood ≥70%), not slathered everywhere. Pink stays
   as brand-and-CTA only.

   See README.md §Color usage rules — the whole point of the redesign
   is restraint. At most one --win number per row.
   ============================================================================= */

/* ---------- New type families ---------- */
/* Geist + Geist Mono + Instrument Serif loaded via base.html <link>.
   Cabinet/Satoshi fonts (loaded at the top of this file) stay as the
   fallback chain so the existing visual identity holds if Google
   Fonts is blocked. */

:root {
  --font-mono:  'Geist Mono', ui-monospace, SFMono-Regular, 'SF Mono', Consolas, monospace;
  --font-serif: 'Instrument Serif', Georgia, serif;  /* italic accent only */
}

/* Make Geist the new primary for both display + body. Cabinet/Satoshi
   stay as the next fallback so any system without Geist still looks
   like the old Vantage instead of plain system-ui. */
:root {
  --font-display: 'Geist', 'Cabinet Grotesk', 'Satoshi', system-ui, sans-serif;
  --font-body:    'Geist', 'Satoshi', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}

/* ---------- "Win" color — chartreuse for the single hero metric ----------
   redesign-v3 2026-07-14: --win/--rising/--cooling (and the rest of the
   semantic set) moved to the top-of-file theme blocks so there is ONE
   source of truth. This block deliberately no longer redefines them —
   the old copies here carried slightly-off values (--win-line .25 vs the
   spec .30) and silently overrode the palette because they came later
   in the cascade. */

/* ---------- Utility classes ---------- */
/* redesign-v3 2026-07-14: tabular-nums on the mono utility too (§0.3 says
   EVERY number is Geist Mono + tabular), not only .num below. */
.font-mono     { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.font-serif    { font-family: var(--font-serif); font-style: italic; }
.tabular       { font-variant-numeric: tabular-nums; }
.eyebrow {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  color: var(--text-muted);
  letter-spacing: .14em;
  text-transform: uppercase;
}
.text-win      { color: var(--win); }
.bg-win        { background: var(--win-bg); }

/* All numeric cells in tables and stat strips. The whole point: every
   number in the UI shares the same monospaced + tabular shape, so
   columns visually align without needing fixed widths. */
.num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* ---------- Sidebar wordmark — drop the gradient ---------- */
/* Per the audit: the gradient text trope is the single most "generic
   SaaS" tell on the entire page. Sidebar wordmark goes solid, with a
   small mono "TERMINAL" tag underneath signalling pro-tool identity. */
.sidebar-wordmark {
  /* !important on the gradient-cancellation properties keeps the
     fix robust against the original .sidebar-wordmark rule (line ~177)
     which uses background:linear-gradient + -webkit-text-fill-color:
     transparent. Same specificity = last-write-wins, so a future
     bundler reorder could resurrect the gradient text without these.
     Audit fix 2026-05-03. */
  background: none !important;
  -webkit-text-fill-color: currentColor !important;
  background-clip: initial !important;
  -webkit-background-clip: initial !important;
  color: var(--text-primary);
  font-family: var(--font-display);
  font-weight: 800;
  letter-spacing: -.025em;
}
.sidebar-wordmark-tag {
  display: block;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  color: var(--text-muted);
  letter-spacing: .14em;
  text-transform: uppercase;
  margin-top: -2px;
  line-height: 1;
}

/* ---------- Likelihood pill — tier-based, the visual anchor of the redesign ----------
   Server-side branches: tier = 'win' if pct >= 70 else 'mid' if pct >= 50 else 'low'.
   Replaces the old per-percentage rainbow with three deliberate states:
     win  — chartreuse, ≥70% sale chance, this is the row's hero number
     mid  — primary text, neutral, 50-69 (ok bet, not a winner)
     low  — muted, <50 (background noise, just there for completeness)
   This is what makes the table read like a single instrument: at most
   one chartreuse number per row, everything else recedes. */
.likelihood-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 13px;
  padding: 2px 8px;
  border-radius: 99px;
  font-variant-numeric: tabular-nums;
  letter-spacing: .02em;
  white-space: nowrap;
}
.likelihood-pill--win {
  color: var(--win);
  background: var(--win-bg);
  border: 1px solid var(--win-line);
}
.likelihood-pill--mid {
  color: var(--text-primary);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
}
.likelihood-pill--low {
  color: var(--text-muted);
  background: transparent;
  border: 1px solid var(--border);
}

/* ---------- Status footer ---------- */
/* Persistent live-sync footer. Adds the "real data, real-time" trust
   signal that affiliates expect from a $30/mo tool. Renders once at
   the bottom of base.html, sticky. */
.app-statusbar {
  position: sticky;
  bottom: 0;
  z-index: 50;
  height: 28px;
  padding: 0 18px;
  background: var(--bg-surface);
  border-top: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 18px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--text-muted);
  letter-spacing: .06em;
}
.app-statusbar .dot {
  width: 6px;
  height: 6px;
  border-radius: 99px;
  background: var(--rising);
  box-shadow: 0 0 8px var(--rising);
  display: inline-block;
  margin-right: 6px;
  /* Subtle pulse so it reads as "live". */
  animation: vantage-status-pulse 2.4s ease-in-out infinite;
}
@keyframes vantage-status-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.55; }
}
.app-statusbar kbd {
  font-family: var(--font-mono);
  font-size: 10px;
  padding: 1px 5px;
  border: 1px solid var(--border);
  border-radius: 3px;
  margin-right: 4px;
  color: var(--text-secondary);
  background: var(--bg-elevated);
}
.app-statusbar .status-cell {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

/* Mobile: drop kbd hints + later cells, keep the live-status + counts. */
@media (max-width: 768px) {
  .app-statusbar kbd,
  .app-statusbar .status-cell:nth-child(n+6) {
    display: none;
  }
  .app-statusbar {
    gap: 10px;
    padding: 0 12px;
  }
}

/* =============================================================================
   TERMINAL REDESIGN — Phase 2 (2026-05-03)
   Source: design handoff (design_handoff_vantage_terminal/README.md)
   Sections §2 sparkline · §5 page header · §6 KPI strip · §7 hero card ·
   §8 empty state · §9 loading skeleton · §4 tabs.

   Same constraints as Phase 1: append-only, every existing class still
   works as before. Phase 2 layers structure + content components on
   top of the foundational tokens shipped in Phase 1.
   ============================================================================= */

/* ---------- Page header — eyebrow + display title with serif accent ---------- */
.page-header .eyebrow {
  display: block;
  margin-bottom: 6px;
}
.page-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(24px, 3.4vw, 34px);
  letter-spacing: -.025em;
  margin: 0;
  line-height: 1;
}

/* ---------- Tabs — promote the inline-styled tab anchors to a class ---------- */
/* The existing inline-styled <a> tags in products.html still work — these
   styles ALSO match a .tab class on those anchors so we can clean up
   the markup over time without breaking anything. */
.products-tabs-bar {
  display: flex;
  gap: 0;
  border-bottom: 1px solid var(--border);
  margin-bottom: 16px;
  overflow-x: auto;
  scrollbar-width: none;
}
.products-tabs-bar::-webkit-scrollbar { display: none; }
.products-tabs-bar .tab {
  padding: 10px 16px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-muted);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  text-decoration: none;
  white-space: nowrap;
  font-family: var(--font-body);
  transition: color 0.15s, border-color 0.15s;
}
.products-tabs-bar .tab:hover { color: var(--text-secondary); }
.products-tabs-bar .tab--active {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

/* ---------- KPI strip — single bordered band on the dashboard ---------- */
/* Replaces the 4 separate stat cards. Mono labels, tabular numerals, all
   numbers visually align on a single baseline. Delta uses --win when
   positive (chartreuse) — matches the rest of the system. */
.kpi-strip {
  display: flex;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
  overflow: hidden;
  flex-wrap: wrap;
}
.kpi-strip .kpi {
  padding: 14px 22px;
  flex: 1;
  min-width: 140px;
  border-right: 1px solid var(--border);
}
.kpi-strip .kpi:last-child { border-right: none; }
.kpi-label {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--text-muted);
  letter-spacing: .14em;
  text-transform: uppercase;
  font-weight: 600;
}
.kpi-value {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 26px;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
  margin-top: 4px;
  display: flex;
  align-items: baseline;
  gap: 8px;
  color: var(--text-primary);
}
.kpi-value .delta {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--win);
  font-weight: 600;
}
.kpi-value .delta--down { color: var(--cooling); }
.kpi-value .delta--flat { color: var(--text-muted); }

/* Mobile: stack vertically below 600px so each KPI gets its own row. */
@media (max-width: 600px) {
  .kpi-strip { flex-direction: column; }
  .kpi-strip .kpi {
    border-right: none;
    border-bottom: 1px solid var(--border);
  }
  .kpi-strip .kpi:last-child { border-bottom: none; }
  .kpi-value { font-size: 22px; }
}

/* ---------- Sparkline — replaces the +12% growth badge in table rows ---------- */
/* SVG polyline rendered server-side from a numeric list via the
   `spark_points` Jinja filter. Color is bound to momentum tier:
   rising → green, cooling → orange, flat → muted. Visually quieter
   than a colored badge — adds info density without adding noise. */
.spark {
  display: inline-block;
  vertical-align: middle;
}
.spark-empty {
  display: inline-block;
  width: 70px;
  height: 22px;
  border-bottom: 1px dashed var(--border);
  vertical-align: middle;
  opacity: 0.5;
}

/* ---------- Hero card likelihood — README §7 ----------
   Lifts Sale Likelihood out of the body meta strip and makes it the
   top-band hero of every grid card. The 42px display number IS the
   card's most important element. Color tracks the same win/mid/low
   tiers the table likelihood pill uses, so the chartreuse chimes
   between table view and grid view consistently. */
.product-card-hero {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  padding: 14px 14px 10px;
  border-bottom: 1px solid var(--border);
  gap: 12px;
}
.card-likelihood {
  min-width: 0;
  flex: 1;
}
.card-likelihood-num {
  font-family: var(--font-display);
  font-size: 42px;
  font-weight: 700;
  letter-spacing: -.03em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  display: block;
  margin-top: 2px;
}
.card-likelihood-num small {
  font-size: 24px;
  font-weight: 600;
  margin-left: 1px;
}
.card-likelihood--win .card-likelihood-num,
.card-likelihood--win .card-likelihood-num small { color: var(--win); }
.card-likelihood--mid .card-likelihood-num,
.card-likelihood--mid .card-likelihood-num small { color: var(--text-primary); }
.card-likelihood--low .card-likelihood-num,
.card-likelihood--low .card-likelihood-num small { color: var(--text-muted); }
.card-flags {
  display: flex;
  flex-direction: column;
  gap: 4px;
  align-items: flex-end;
  flex-shrink: 0;
}
.flag {
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 700;
  padding: 2px 6px;
  border-radius: 3px;
  letter-spacing: .08em;
  white-space: nowrap;
}
.flag--hot {
  background: var(--win);
  color: var(--win-ink);
}
.flag--tap {
  color: var(--accent);
  border: 1px solid var(--accent);
  background: transparent;
}
.flag--rising {
  color: var(--rising);
  border: 1px solid var(--rising);
  background: transparent;
}

/* ---------- Empty state — README §8 ---------- */
/* Replaces the generic "try clearing filters" shrug with actionable
   single-axis loosening suggestions. Each suggestion is a clickable
   row showing the filter to drop + the count of products that would
   surface. Server computes 3 such alternatives — see views.py. */
.empty-state {
  max-width: 540px;
  margin: 60px auto;
  padding: 32px 28px;
  text-align: center;
}
.empty-state .eyebrow {
  display: inline-block;
  margin-bottom: 12px;
}
.empty-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 28px;
  letter-spacing: -.02em;
  margin: 0 0 12px;
  line-height: 1.2;
  color: var(--text-primary);
}
.empty-desc {
  color: var(--text-secondary);
  font-size: 14px;
  line-height: 1.55;
  margin: 0 0 24px;
}
.empty-suggestions {
  display: flex;
  flex-direction: column;
  gap: 8px;
  text-align: left;
}
.suggestion {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 16px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  text-decoration: none;
  color: var(--text-primary);
  font-size: 14px;
  font-weight: 500;
  transition: border-color 0.15s, transform 0.15s;
}
.suggestion:hover {
  border-color: var(--accent);
  transform: translateX(2px);
}
.suggestion .suggestion-label { flex: 1; min-width: 0; }
.suggestion .suggestion-count {
  font-family: var(--font-mono);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}

/* ---------- Loading skeleton — README §9 ---------- */
.skeleton {
  background: linear-gradient(110deg,
    var(--bg-elevated) 25%,
    var(--bg-hover) 50%,
    var(--bg-elevated) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.4s linear infinite;
  border-radius: 4px;
}
@keyframes shimmer {
  from { background-position: 200% 0; }
  to   { background-position: -200% 0; }
}
.loading-status {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-muted);
  letter-spacing: .12em;
  text-transform: uppercase;
  padding: 8px 12px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-surface);
}
.loading-status .dot {
  width: 6px;
  height: 6px;
  border-radius: 99px;
  background: var(--rising);
  box-shadow: 0 0 8px var(--rising);
  animation: vantage-status-pulse 2.4s ease-in-out infinite;
}

/* ---------- Landing page wordmark — drop the gradient ----------
   Mirrors the sidebar fix so logged-out marketing surfaces also
   stop reading as "generic SaaS template". */
.landing-wordmark,
.brand-wordmark {
  background: none !important;
  -webkit-text-fill-color: currentColor !important;
  color: var(--text-primary);
  font-family: var(--font-display);
  font-weight: 800;
  letter-spacing: -.025em;
}

/* ---------- Auth form labels + inputs ----------
   Used by templates/auth.html for the email/password fields. The
   existing .auth-form / .auth-card styles take care of the surrounding
   shell; these add the field-level polish (mono eyebrow labels +
   tightened input row spacing). */
.auth-label {
  display: block;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  color: var(--text-muted);
  letter-spacing: .08em;
  text-transform: uppercase;
  margin-bottom: 6px;
}
.auth-input {
  width: 100%;
  padding: 12px 14px;
  font-size: 14px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  color: var(--text-primary);
  font-family: var(--font-body);
  transition: border-color 0.15s, box-shadow 0.15s;
}
.auth-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-glow);
}

/* 69. MOBILE PWA OVERFLOW CLEANUP (Sprint — 2026-05-02) ===================
   Targeted fixes for templates Brandon's testing on his phone (PWA at
   ~360–414px). Rules below extend the existing admin-page + table
   patterns to cover surfaces that were still bleeding off-screen.
   No layout changes above 900px — desktop rendering is unchanged. */

/* Contact page right-rail must stack below the form below 900px.
   The .contact-layout grid is `1fr 360px` at desktop — collapse to a
   single column so the account-ID block + alt-channels stack below
   the form instead of clipping off the right edge. */
@media(max-width:900px){
  .contact-layout{
    grid-template-columns:1fr !important;
    max-width:100%;
  }
}
/* Stack the Name | Email grid inside the contact form on small phones */
@media(max-width:600px){
  .contact-layout form > div[style*="grid-template-columns:1fr 1fr"]{
    grid-template-columns:1fr !important;
  }
}

/* Admin pages: extra mobile guardrails on top of the existing
   ".admin-page .product-table-wrap" rule (which only triggers when the
   <body>-equivalent wrapper has the class — base.html now sets it on
   page-content for admin routes). */
@media(max-width:900px){
  /* Page-header titles ran long on /app/admin/affiliates ("13 affiliates
     $0.00 pending payout") and pushed the row width past viewport. */
  .admin-page .page-title{font-size:20px;line-height:1.2;word-break:break-word}
  .admin-page .page-title .font-serif{display:block;font-size:14px;margin-top:2px}

  /* Tables that don't use .product-table-wrap (e.g. admin_tap.html uses
     .table-wrap, admin_affiliates.html uses an inline overflow-x:auto
     wrapper). Force them into a horizontal-scroll container at phone
     widths so the table never overflows the viewport. */
  .admin-page .table-wrap,
  .admin-page > .card > div[style*="overflow-x:auto"]{
    overflow-x:auto;
    -webkit-overflow-scrolling:touch;
    max-width:100%;
  }
  .admin-page .table{min-width:560px}

  /* Admin form-input grids (1fr 1fr / 1fr 1fr 1fr inline styles) — the
     existing 640px rule stacked them, lift to 900px so they stack on
     larger phones too (414px landscape, foldables). */
  .admin-page form [style*="grid-template-columns:1fr 1fr"],
  .admin-page form [style*="grid-template-columns: 1fr 1fr"],
  .admin-page form [style*="grid-template-columns:1fr 1fr 1fr"],
  .admin-page form [style*="grid-template-columns: 1fr 1fr 1fr"]{
    grid-template-columns:1fr !important;
    gap:12px !important;
  }

  /* Cards with inline max-width:640px / 700px / 900px — let them flex */
  .admin-page .card[style*="max-width:640px"],
  .admin-page .card[style*="max-width:680px"],
  .admin-page .card[style*="max-width:700px"],
  .admin-page .card[style*="max-width:900px"]{max-width:100% !important}

  /* admin_affiliates inline edit form: each form-row was a flex of
     selects + a pct input + Save button. On mobile, stack vertically
     so every control gets full width and the Save button isn't pushed
     off-screen. Same for the mark-paid row. */
  .admin-page form[action*="/update"],
  .admin-page form[action*="/mark-paid"]{
    flex-direction:column !important;
    align-items:stretch !important;
  }
  .admin-page form[action*="/update"] select,
  .admin-page form[action*="/update"] input[type="text"],
  .admin-page form[action*="/mark-paid"] input[type="text"]{
    width:100% !important;
    min-width:0 !important;
  }
  .admin-page form[action*="/update"] button,
  .admin-page form[action*="/mark-paid"] button{
    width:100%;
    justify-content:center;
  }

  /* admin.html top-of-page configuration cards use a lot of inline
     pages-input grids — same stack-on-mobile treatment. */
  .admin-page .input{max-width:100%;box-sizing:border-box}
}

/* Stats grid on the brands/products/admin pages should stack 2-up on
   phones (the existing rule does this, but lookup_app and admin pages
   don't always have the .stat-grid class — keep the existing one). */
@media(max-width:600px){
  /* Brand Hunter detail header inline stat strip — 4 stats wrap onto
     two rows at phone width. Already wraps via flex-wrap, just tighten
     the gaps so labels don't push past viewport. */
  .admin-page .product-table-wrap,
  .admin-page .table-wrap{font-size:12px}
}

/* lookup_app result card: the .lookup-stats grid drops to 2-up at
   600px (already in the template). Add 1-up at 380px for cramped
   phones where 2 stats still hit the edge. */
@media(max-width:380px){
  .lookup-stats{grid-template-columns:1fr !important}
  .lookup-result-img{width:64px !important;height:64px !important}
}

/* Brand Hunter top-bar admin "Deep Scan" details panel had inputs
   sitting on a flex row that didn't wrap. */
@media(max-width:768px){
  .scan-range-group{flex-wrap:wrap}
  .scan-custom-inputs{flex-wrap:wrap}
}

/* admin_affiliates Actions cell uses min-width:380px to keep all the
   inline form controls on one line at desktop. On mobile we stack
   them (rule above) so the min-width is no longer needed and just
   pushes the table off-screen. */
@media(max-width:768px){
  .aff-actions-th{min-width:0 !important}
}


/* ====================================================================
   Mobile topbar — search input was too tall + placeholder too long
   for narrow phones. The topbar holds: hamburger | search input |
   Upgrade-to-Pro CTA | sign-out icon. At 360px we need every pixel.
   ==================================================================== */
@media(max-width:768px){
  /* Tighten the input itself — was 40px tall with full padding.
     38px gives us a few extra pixels of width for the placeholder
     and visually balances better next to the 36px Upgrade button. */
  .topbar-search .input{
    height:38px;font-size:13px;
    padding-left:34px;padding-right:10px;
  }
  .topbar-search .lucide{width:16px;height:16px;left:10px}

  /* Replace the long placeholder with a short one on mobile. The
     ::placeholder color stays muted via the .input rules above. */
  .topbar-search .input::placeholder{font-size:12.5px}

  /* The "Upgrade to Pro" CTA in topbar-right was using padding:6px
     16px → ~95px wide with the full text. On phones, drop the
     padding + shorten the visible label so search has more room. */
  .topbar-right{gap:8px}
  .topbar-right .btn-primary{
    padding:6px 10px !important;font-size:12px !important;
    white-space:nowrap;
  }
}

/* On very narrow viewports (320-380px iPhone SE, mid-range Android)
   the placeholder text gets clipped. Use the longer attribute
   selector to swap it for a shorter version client-side. Combined
   with shrinking the input padding, this gives the input ~210px
   of usable text width on a 360px screen — enough for "TikTok
   link or product ID". */
@media(max-width:420px){
  .topbar-search .input{font-size:13px;height:38px}
  /* Shorter placeholder via CSS — works because the attr selector
     matches the original placeholder string. We swap it with the
     `content` of an ::placeholder override (Chrome 90+, Safari 14+,
     all modern PWA hosts). On older browsers the long placeholder
     just gets clipped, which is the pre-fix behavior. */
  .topbar-search .input[placeholder]::placeholder{
    /* Browsers don't actually let CSS rewrite placeholder content,
       only style it. The real fix is the JS swap below — this rule
       is just here to shrink the font to keep the long placeholder
       visible until the JS runs. */
    font-size:12px;letter-spacing:-0.01em;
  }

  /* "Upgrade to Pro" → just show "Pro" + the lightning icon on
     very narrow phones to save another ~40px of width. The full
     label returns at 421px+. */
  .topbar-right .btn-primary .upgrade-text-long{display:none}
  .topbar-right .btn-primary .upgrade-text-short{display:inline}
}
@media(min-width:421px){
  .topbar-right .btn-primary .upgrade-text-short{display:none}
}

/* ============================================================================
   Mobile overflow clamp — 2026-05-03 audit fix from Brandon's iPhone 17 Pro
   screenshots. Multiple pages (dashboard, brand hunter, creators, admin,
   tap-lists, affiliates) showed content cut off at the right edge:
     - Campaign banner close button half off-screen
     - Stat-card labels truncated ("TOTAL PROD" instead of "TOTAL PRODUCTS")
     - Search inputs wider than their card containers
     - Button rows where the rightmost button extended past viewport
     - Table headers cut off (those should be inside scrolling .table-wrap)

   Root causes diagnosed:
     1. CSS Grid `1fr 1fr` resolves to `minmax(auto, 1fr)`, which
        REFUSES to shrink children below their min-content width. A
        long label inside a stat-card pushes the card past its 1fr
        slot → grid overflows → page overflows. Fix: use minmax(0, 1fr).
     2. Flex children without explicit `min-width:0` also refuse to
        shrink below content width. Fix: clamp on mobile.
     3. Some inputs/buttons don't have width:100% so they grow to
        fit their placeholder/label text. Fix: clamp via parent .card.
     4. .page-content didn't have overflow-x:clip — body did, but a
        single element rooted at <main> with margin-left manipulation
        could still escape. Fix: add to .page-content too.
   ============================================================================ */
@media (max-width:768px) {
  /* --- Box-model + overflow defense for the page wrapper -------------- */
  .page-content {
    overflow-x: hidden;
    overflow-x: clip;
    box-sizing: border-box;
    max-width: 100%;
  }

  /* --- Grid: force shrink-to-zero so 1fr columns don't push out ------- */
  .stat-grid,
  .stat-grid--2,
  .stat-grid--3,
  .stat-grid--4 {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
  }

  /* --- Stat cards: never wider than their grid slot ------------------- */
  .stat-card {
    min-width: 0 !important;
    max-width: 100% !important;
    box-sizing: border-box !important;
    overflow: hidden;
  }
  .stat-card .stat-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .stat-card .stat-value {
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* --- KPI strip (affiliate dashboard) - same treatment --------------- */
  .kpi-strip {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
  }
  .kpi {
    min-width: 0 !important;
    max-width: 100% !important;
    overflow: hidden;
  }
  .kpi .kpi-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* --- Cards always respect their parent width ------------------------ */
  .card {
    max-width: 100%;
    box-sizing: border-box;
    min-width: 0;
  }

  /* --- Form inputs inside cards never wider than card ----------------- */
  .card .input,
  .card input[type="text"],
  .card input[type="email"],
  .card input[type="number"],
  .card input[type="search"],
  .card input[type="url"],
  .card textarea {
    max-width: 100%;
    box-sizing: border-box;
  }

  /* --- Flex button rows wrap on mobile so they never overflow --------- */
  .card > div[style*="display:flex"],
  .card .flex {
    flex-wrap: wrap;
  }

  /* --- Buttons size to content but cap at parent width ---------------- */
  .btn {
    max-width: 100%;
  }

  /* --- Campaign banner: clamp close button into viewport -------------- */
  .campaign-banner,
  .campaign-banner__inner {
    max-width: 100vw;
    overflow: hidden;
    box-sizing: border-box;
  }
  .campaign-banner__right {
    /* Was flex-shrink:0 + no max-width. Cram it tight on mobile so
       the close X never gets clipped. */
    flex-shrink: 0;
  }

  /* --- Tables MUST scroll inside their wrap, not push body ------------ */
  .table-wrap,
  .product-table-wrap,
  .creator-products-wrap,
  .brand-hunter-table-wrap,
  .admin-table-wrap {
    overflow-x: auto !important;
    max-width: 100% !important;
    -webkit-overflow-scrolling: touch !important;
  }

  /* --- Anything explicitly white-space:nowrap that's NOT a scrollable
         child (table cells, kbd hints) should ellipsis at the end ----- */
  .card-title,
  .card-subtitle,
  .page-title {
    overflow: hidden;
    text-overflow: ellipsis;
    word-break: break-word;
    overflow-wrap: anywhere;
    white-space: normal;  /* was nowrap on some pages */
  }
}

/* Very narrow phones — single-column stat grids (was 380px, bumping to
   480px so iPhone 17 Pro 393px also gets single-column when labels are
   long). The 1fr-with-minmax fix above prevents overflow but the cards
   look cramped in 2-col on a 393px viewport. Better to stack. */
@media (max-width:480px) {
  .stat-grid,
  .kpi-strip {
    grid-template-columns: minmax(0, 1fr) !important;
  }
}

/* ============================================================================
   MOBILE COMPACTNESS PASS — 2026-05-03 (Brandon's iPhone 17 Pro feedback)
   The earlier "Mobile overflow clamp" pass (above) fixed clipping but the
   cards still felt desktop-sized: stat cards full-width with a tiny number
   floating in 65px of vertical space, brand-row avatars at 36px, page
   titles at 22px+. This pass makes the *visual scale* feel mobile-native:
     - Stat cards go HORIZONTAL (label left, value right) so each card is
       ~36px tall instead of ~75px. Three stats fit in ~120px instead of
       ~250px.
     - Brand-row avatars shrink 36→28px, table cell padding 12→8px
     - Page titles drop 22→19px, page-header margin tightens
     - Card paddings reduced across the board
   All rules use !important to win against the earlier mobile passes that
   set the same selectors with !important. Source-order tiebreaker also
   favors this section since it sits at the bottom of the file.
   ============================================================================ */
@media (max-width:640px) {
  /* --- Page header: tighter top + smaller title -------------------------- */
  .page-header{
    margin-bottom:12px !important;
    padding-top:4px !important;
  }
  .page-title{
    font-size:19px !important;
    line-height:1.2 !important;
    margin-bottom:2px !important;
  }
  .page-subtitle{
    font-size:12px !important;
    line-height:1.35 !important;
  }

  /* --- Stat cards: horizontal layout (label-left, value-right).
         Massively reduces card height. Works even with single-column grid
         because each card just becomes a one-line strip. ---------------- */
  .stat-card{
    padding:10px 12px !important;
    min-height:0 !important;
    display:flex !important;
    flex-direction:row !important;
    align-items:center !important;
    justify-content:space-between !important;
    gap:8px !important;
  }
  .stat-card .stat-label{
    font-size:10px !important;
    letter-spacing:.05em !important;
    margin-bottom:0 !important;
    flex:1 1 auto !important;
    min-width:0 !important;
    text-overflow:ellipsis !important;
    overflow:hidden !important;
    white-space:nowrap !important;
  }
  .stat-card .stat-value{
    font-size:18px !important;
    line-height:1.1 !important;
    flex-shrink:0 !important;
    text-align:right !important;
  }
  .stat-card .stat-hint,
  .stat-card .stat-trend{
    display:none !important;
  }

  /* If a stat-card uses .stat-card--link (clickable), keep arrow visible
     but don't push value off-screen */
  .stat-card--link{display:flex !important}

  /* --- Tighter stat-grid gap (was 8-10px, drop to 6px on phone) -------- */
  .stat-grid,
  .kpi-strip{
    gap:6px !important;
    margin-bottom:14px !important;
  }

  /* --- Card default padding tighter on mobile -------------------------- */
  .card{
    padding:14px !important;
  }
  .card.mb-6{margin-bottom:14px !important}
  .card.mb-4{margin-bottom:10px !important}

  /* --- Product table rows: less breathing room ------------------------- */
  .product-table th{
    padding:8px 10px !important;
    font-size:10px !important;
  }
  .product-table td{
    padding:9px 10px !important;
    font-size:13px !important;
  }
  .product-table .product-row-name{
    gap:8px !important;
    min-width:0 !important;
  }

  /* --- Brand letter avatar: smaller in lists --------------------------- */
  .brand-letter-avatar{
    width:28px !important;
    height:28px !important;
    font-size:12px !important;
  }
  /* Header detail page avatar: still prominent but not desktop-huge */
  .brand-header-avatar-fallback{
    width:48px !important;
    height:48px !important;
    font-size:18px !important;
  }

  /* --- Filter card: tighten padding ------------------------------------ */
  .filter-card{padding:10px !important}
  .filter-input{height:38px !important;font-size:13px !important}

  /* --- Buttons: btn-sm tighter for inline filter rows ------------------ */
  .filter-row .btn,
  .filter-row .btn-sm{
    height:36px !important;
    padding:0 12px !important;
    font-size:12px !important;
  }

  /* --- Section/card headings inside cards ------------------------------ */
  .card h1,.card h2,.card h3{
    font-size:15px !important;
    line-height:1.25 !important;
  }

  /* --- Detail page back-link tighter ----------------------------------- */
  .detail-back{
    font-size:12px !important;
    margin-bottom:8px !important;
  }
}

/* iPhone 17 Pro is 393px CSS-wide, so it hits this breakpoint. Tighten
   even further for the smallest phones. */
@media (max-width:420px){
  .page-title{font-size:18px !important}
  .stat-card .stat-value{font-size:17px !important}
  .stat-card .stat-label{font-size:9.5px !important}
}

/* ============================================================================
   STRUCTURAL OVERFLOW FIX — 2026-05-03 (verified via /static/mobile-debug.html
   probe across 21 screenshots from Brandon's iPhone 17 Pro at 402px viewport)

   ROOT CAUSE: .main-area is `flex:1; display:flex` with no `min-width:0`.
   In flex/grid contexts, children default to `min-width:auto` which means
   they refuse to shrink below their intrinsic content width. So whenever
   any descendant has a fixed/min width that exceeds viewport (admin form
   inputs at 562px, aa-banner section at 562px, brand-hunter table column
   widths summing past 400px, etc.), .main-area expands to fit them.

   At a 402px viewport we measured .main-area at 470 / 594 / 622 / 624 /
   634 / 796 / 833px depending on the page's widest descendant. Everything
   below (.topbar, .app-statusbar, .page-content, .page-header, .page-title,
   .page-subtitle, .stat-grid, .card) inherited that width and bled past
   the right edge — that's the 30-40px clip Brandon saw on every page.

   FIX: clamp .app-shell, .main-area, and their key flex children to
   viewport width with min-width:0 so they CAN shrink. overflow-x:clip
   contains any remaining wide descendants (it doesn't break position:
   sticky on .topbar/.app-statusbar the way overflow:hidden would).

   Tables that need horizontal scroll keep it via .product-table-wrap's
   own overflow-x:auto — that wrap creates its own scroll boundary INSIDE
   the clipped main-area, so the rest of the page doesn't leak.
   ============================================================================ */
@media (max-width:768px) {
  /* Top-level shell: never wider than viewport */
  .app-shell {
    max-width: 100vw !important;
    min-width: 0 !important;
    overflow-x: clip !important;
  }

  /* The page wrapper that was bleeding on every authenticated page */
  .main-area,
  main.main-area {
    max-width: 100vw !important;
    min-width: 0 !important;
    overflow-x: clip !important;
    box-sizing: border-box !important;
  }

  /* Sticky bars + page content inside main-area — clamp width but
     don't add overflow:clip here (would break their internal layout
     and potentially clip the topbar's hamburger menu) */
  .topbar,
  .app-statusbar,
  .page-content,
  #main-content {
    max-width: 100% !important;
    min-width: 0 !important;
    box-sizing: border-box !important;
  }

  /* Direct children of page-content (page-header, stat-grid, .card,
     section.aa-banner, addForm, etc.) all need shrink permission so a
     wide grandchild doesn't push them past parent. */
  .page-content > *,
  #main-content > * {
    max-width: 100% !important;
    min-width: 0 !important;
    box-sizing: border-box !important;
  }

  /* aa-banner section was measured at 562px on admin pages — clamp
     its width and any inputs/forms inside */
  .aa-banner,
  .aa-banner > *,
  section.aa-banner {
    max-width: 100% !important;
    min-width: 0 !important;
    box-sizing: border-box !important;
  }

  /* Inputs across forms (admin pages had a 562x46 input pushing
     main-area to 594px) — clamp every form control to its container */
  input,
  .input,
  textarea,
  select,
  .input-group,
  .input-group > *,
  form,
  form > * {
    max-width: 100% !important;
    box-sizing: border-box !important;
  }
  input,
  .input,
  textarea,
  select {
    min-width: 0 !important;
  }

  /* Cards (.card mb-6 was inheriting +176-216px overflow on admin) */
  .card,
  .card > * {
    max-width: 100% !important;
    box-sizing: border-box !important;
  }
  .card { min-width: 0 !important; }

  /* Page header chunks (h1.page-title, p.page-subtitle, .stat-grid mb-6)
     were all inheriting parent overflow — clamp explicitly so even if a
     future ancestor regresses, these stay inside viewport */
  .page-header,
  .page-header > *,
  .page-title,
  .page-subtitle,
  .stat-grid {
    max-width: 100% !important;
    min-width: 0 !important;
    box-sizing: border-box !important;
  }
}

/* ============================================================================
   Terminal cohesion pass — 2026-06-12
   One restrained system applied app-wide instead of per-page tweaks:
   themed scrollbars, brand ::selection, a faint top-glow + noise
   atmosphere (dark only — light theme stays clean), a single card
   hover treatment, and button press feedback. Transforms are disabled
   under prefers-reduced-motion.
   ============================================================================ */

/* Themed scrollbars — the OS-default light scrollbar was the last
   non-dark element on every dark page. .scroll-row keeps its own
   slimmer 4px variant (more specific, unaffected). */
html{scrollbar-width:thin;scrollbar-color:var(--bg-active) transparent}
*::-webkit-scrollbar{width:10px;height:10px}
*::-webkit-scrollbar-track{background:transparent}
*::-webkit-scrollbar-thumb{background:var(--bg-active);border-radius:8px;border:2px solid transparent;background-clip:padding-box}
*::-webkit-scrollbar-thumb:hover{background:var(--border-strong);border:2px solid transparent;background-clip:padding-box}

/* Brand text selection */
::selection{background:rgba(255,82,114,.30);color:#fff}
[data-theme="light"] ::selection{background:rgba(232,56,77,.22);color:var(--text-primary)}

/* Atmosphere — dark theme only. The same fractal-noise texture the
   auth page already uses, plus a faint accent/teal glow bleeding in
   from the top edge. Cards have solid surfaces, so the glow lives in
   the page background and gutters: depth without competing with data. */
[data-theme="dark"] body{
  background-image:
    radial-gradient(900px 420px at 50% -120px, rgba(255,82,114,.055), transparent 64%),
    radial-gradient(680px 360px at 88% -80px, rgba(54,217,196,.04), transparent 60%),
    url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.03'/%3E%3C/svg%3E");
}

/* One card hover treatment — stat + chart cards lift a hair and
   sharpen their border. Deliberately NOT applied to the generic
   .card (large containers lifting on hover feels wrong). */
.stat-card:hover{border-color:var(--border-strong);transform:translateY(-1px);box-shadow:var(--shadow-md)}
.chart-card{transition:border-color var(--dur) var(--ease),box-shadow var(--dur) var(--ease)}
.chart-card:hover{border-color:var(--border-strong)}

/* Button press feedback — the one micro-interaction worth having. */
.btn{transition:background var(--dur) var(--ease),border-color var(--dur) var(--ease),color var(--dur) var(--ease),transform 80ms ease}
.btn:active{transform:translateY(1px)}

@media (prefers-reduced-motion: reduce){
  .stat-card:hover{transform:none}
  .btn:active{transform:none}
}

/* Landing hero atmosphere — anchors the glow to the headline instead
   of a flat black void behind it. */
.hero{position:relative}
.hero::before{content:'';position:absolute;inset:-140px -30% 0;background:
  radial-gradient(820px 420px at 38% 0, rgba(255,82,114,.08), transparent 62%),
  radial-gradient(620px 320px at 82% 60px, rgba(54,217,196,.05), transparent 60%);
  pointer-events:none;z-index:-1}

/* Viral Videos category chips — .filter-chip had no styling on this
   page (it's scoped to the products filter card), so the category row
   rendered as bare accent links. Proper pill chips, selected state in
   accent. */
.vv-chip{display:inline-flex;align-items:center;padding:5px 12px;border-radius:999px;border:1px solid var(--border);background:var(--bg-surface);color:var(--text-secondary);font-size:12px;font-weight:600;text-decoration:none;white-space:nowrap;transition:border-color var(--dur) var(--ease),color var(--dur) var(--ease),background var(--dur) var(--ease)}
.vv-chip:hover{border-color:var(--border-strong);color:var(--text-primary)}
.vv-chip--on,.vv-chip--on:hover{background:var(--accent);border-color:var(--accent);color:#fff}

/* ============================================================================
   v2 SIGNATURE LAYER — Opportunity Canvas + Vantage Lens (2026-07-13)
   The signature product-card system from the visual-differentiation v2 audit
   (docs/reconcile-visual-baseline). Tokens alias the existing Terminal palette
   (accent/teal/win already match the v2 spec values exactly); --creative is
   the only genuinely new semantic color (creation/scripts/editor).
   ========================================================================== */
/* redesign-v3 2026-07-14: --verified is now a real color in the top theme
   blocks (--teal aliases INTO it — redefining it here as var(--teal) again
   would create a var() cycle and invalidate both). --creative folds into
   --modelled: the v2 spec values (#7657D6 / #A78BFA) are byte-identical to
   the v3 modelled violet. */
:root {
  --creative: var(--modelled);
  --creative-dim: rgba(118, 87, 214, 0.09);
  --action: var(--accent);
  --opportunity: var(--win);
}
[data-theme="dark"] {
  --creative-dim: rgba(167, 139, 250, 0.12);
}

/* --- the canvas ----------------------------------------------------------
   Signature shape (v2 §3.3): clipped upper-right corner + left evidence rail
   + small coral action notch. Rail color = decision state. */
.oc {
  position: relative;
  display: flex;
  gap: 14px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 14px 16px 14px 18px;
  clip-path: polygon(0 0, calc(100% - 16px) 0, 100% 16px, 100% 100%, 0 100%);
}
.oc::before { /* evidence rail */
  content: '';
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 3px;
  background: var(--oc-rail, var(--border-strong));
}
.oc__notch { /* coral action notch at the clipped corner */
  position: absolute;
  top: 0; right: 0;
  width: 16px; height: 16px;
  background: var(--action);
  clip-path: polygon(0 0, 100% 100%, 100% 0);
  opacity: .85;
}
.oc--PROMOTE  { --oc-rail: var(--opportunity); }
.oc--TEST     { --oc-rail: var(--verified); }
.oc--RESEARCH { --oc-rail: var(--gold); }
.oc--SKIP     { --oc-rail: var(--text-faint); }

.oc__media-link { flex-shrink: 0; }
.oc__media {
  width: 96px; height: 96px;
  border-radius: 10px;
  object-fit: cover;
  background: var(--bg-elevated);
  display: block;
}
.oc__media--empty {
  display: flex; align-items: center; justify-content: center;
  color: var(--text-faint);
}
.oc__media--dead .oc__media { visibility: hidden; }

.oc__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.oc__top { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

.oc__decision {
  font-size: 11px; font-weight: 800; letter-spacing: .07em;
  padding: 3px 9px; border-radius: 6px;
  font-family: var(--font-mono), monospace;
}
.oc__decision--PROMOTE  { background: var(--win-bg); color: var(--win); border: 1px solid var(--win-line); }
.oc__decision--TEST     { background: var(--teal-dim); color: var(--verified); border: 1px solid var(--teal-dim); }
.oc__decision--RESEARCH { background: var(--gold-dim); color: var(--gold); border: 1px solid var(--gold-dim); }
.oc__decision--SKIP     { background: var(--bg-elevated); color: var(--text-muted); border: 1px solid var(--border); }

.oc__time  { font-size: 11px; color: var(--text-secondary); font-weight: 600; }
.oc__fresh { font-size: 11px; color: var(--text-faint); margin-left: auto; white-space: nowrap; }
.oc__fresh--stale { color: var(--gold); }

.oc__name {
  font-size: 15px; font-weight: 700; color: var(--text-primary);
  text-decoration: none;
  overflow: hidden; text-overflow: ellipsis;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.oc__name:hover { color: var(--action); }
.oc__ident { font-size: 12px; color: var(--text-muted); }

.oc__whynow { margin: 2px 0 0; padding: 0 0 0 16px; }
.oc__whynow li { font-size: 12.5px; color: var(--text-secondary); padding: 1px 0; }
.oc__reason { font-size: 12.5px; color: var(--text-secondary); }

.oc__metrics {
  display: flex; gap: 16px; flex-wrap: wrap;
  font-size: 12px; color: var(--text-muted);
  font-family: var(--font-mono), monospace;
}
.oc__metrics strong { color: var(--text-primary); font-weight: 700; }

.oc__lens {
  font-size: 11px; color: var(--text-muted);
  border-top: 1px solid var(--border);
  padding-top: 6px; margin-top: 2px;
}
.oc__lens-tag {
  font-weight: 800; letter-spacing: .08em; font-size: 10px;
  color: var(--text-faint); margin-right: 6px;
  font-family: var(--font-mono), monospace;
}
.oc__signal { color: var(--text-secondary); border-bottom: 1px dotted var(--text-faint); cursor: help; }

.oc__actions { display: flex; gap: 8px; margin-top: 4px; flex-wrap: wrap; }
.oc__actions .btn-sm { font-size: 12px; }

/* compact mode — dashboard strips + dense lists */
.oc--compact { padding: 10px 12px 10px 14px; gap: 10px; }
.oc--compact .oc__media { width: 56px; height: 56px; }
.oc--compact .oc__name { font-size: 13px; -webkit-line-clamp: 1; }
.oc--compact .oc__metrics { gap: 10px; font-size: 11px; }
.oc--compact .oc__lens { display: none; }
.oc--compact .oc__actions .btn-sm { font-size: 11px; padding: 4px 10px; height: auto; }

@media (max-width: 640px) {
  .oc { flex-direction: row; }
  .oc:not(.oc--compact) .oc__media { width: 72px; height: 72px; }
  .oc__metrics { gap: 10px; }
  .oc__fresh { flex-basis: 100%; margin-left: 0; order: 9; }
}

/* --- v2 decision feed + quick intent filters (2026-07-13, item 3) -------- */
.oc-feed {
  display: grid;
  gap: 12px;
  max-width: 980px;
}
.qf-row {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  min-width: 0;
}
.qf-label {
  font-size: 11px; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--text-muted);
  margin-right: 2px; white-space: nowrap;
}
.qf-pill {
  font-size: 12px; font-weight: 600;
  padding: 5px 12px; border-radius: var(--radius-full);
  border: 1px solid var(--border);
  color: var(--text-secondary); text-decoration: none;
  background: var(--bg-surface);
  white-space: nowrap;
  transition: border-color .12s, color .12s;
}
.qf-pill:hover { border-color: var(--border-strong); color: var(--text-primary); }
.qf-pill--on {
  border-color: var(--action); color: var(--action);
  background: var(--accent-glow);
}
@media (max-width: 768px) {
  .qf-row { overflow-x: auto; flex-wrap: nowrap; -webkit-overflow-scrolling: touch;
            padding-bottom: 4px; }
}

/* --- v2 decision room (2026-07-13, item 4) -------------------------------- */
.decision-hero {
  position: relative;
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; flex-wrap: wrap;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 14px 18px;
  margin-bottom: 12px;
  clip-path: polygon(0 0, calc(100% - 16px) 0, 100% 16px, 100% 100%, 0 100%);
}
.decision-hero::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: var(--oc-rail, var(--border-strong));
}
.decision-hero--PROMOTE  { --oc-rail: var(--opportunity); }
.decision-hero--TEST     { --oc-rail: var(--verified); }
.decision-hero--RESEARCH { --oc-rail: var(--gold); }
.decision-hero--SKIP     { --oc-rail: var(--text-faint); }
.decision-hero__main { flex: 1; min-width: 260px; }
.decision-hero__row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.decision-hero__conf { font-size: 12px; color: var(--text-secondary); font-weight: 600; }
.decision-hero__time { font-size: 12px; color: var(--text-muted); }
.decision-hero__fresh { font-size: 11px; color: var(--text-faint); margin-left: auto; }
.decision-hero__fresh--stale { color: var(--gold); }
.decision-hero__why { margin: 8px 0 0; padding-left: 18px; }
.decision-hero__why li { font-size: 13px; color: var(--text-secondary); padding: 1px 0; }
.decision-hero__lens { margin-top: 8px; font-size: 12px; color: var(--text-muted); }
.decision-hero__actions { display: flex; gap: 8px; align-items: center; }

.room-tabs {
  position: sticky; top: 0; z-index: 20;
  display: flex; gap: 4px;
  background: var(--bg-base);
  padding: 8px 0; margin-bottom: 12px;
  border-bottom: 1px solid var(--border);
  overflow-x: auto; -webkit-overflow-scrolling: touch;
}
.room-tab {
  font-size: 13px; font-weight: 600;
  padding: 6px 14px; border-radius: var(--radius-full);
  border: 1px solid transparent; background: none;
  color: var(--text-muted); cursor: pointer; white-space: nowrap;
}
.room-tab:hover { color: var(--text-primary); }
.room-tab--active {
  color: var(--action); border-color: var(--action);
  background: var(--accent-glow);
}
.room-hidden { display: none !important; }

.detail-sticky-actions { display: none; }
@media (max-width: 768px) {
  .detail-sticky-actions {
    display: flex; align-items: center; gap: 10px;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
    padding: 10px 14px calc(10px + var(--safe-bottom));
    background: var(--bg-surface);
    border-top: 1px solid var(--border-strong);
  }
  /* keep page content clear of the fixed bar */
  .detail-layout { padding-bottom: 72px; }
}

/* signal trail */
.strail { display: flex; flex-direction: column; gap: 8px; }
.strail__row {
  display: flex; align-items: baseline; gap: 10px;
  font-size: 13px; color: var(--text-secondary);
}
.strail__dot {
  width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0;
  background: var(--text-faint); align-self: center;
}
.strail__dot--up    { background: var(--rising); }
.strail__dot--down  { background: var(--cooling); }
.strail__dot--price { background: var(--action); }
.strail__dot--obs   { background: var(--verified); }
.strail__when {
  font-family: var(--font-mono), monospace; font-size: 11px;
  color: var(--text-faint); min-width: 52px;
}

/* --- v2 project board + storyboard + dialogs (2026-07-13, item 5) -------- */
.pb-board {
  display: grid;
  grid-template-columns: repeat(5, minmax(200px, 1fr));
  gap: 12px;
  align-items: start;
  overflow-x: auto;
}
@media (max-width: 1100px) { .pb-board { grid-template-columns: repeat(5, 220px); } }
.pb-col { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.pb-col__head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 4px 2px;
}
.pb-col__label {
  font-size: 11px; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--text-muted);
}
.pb-col__count {
  font-size: 11px; font-weight: 700; color: var(--text-faint);
  font-family: var(--font-mono), monospace;
}
.pb-col__empty {
  font-size: 12px; color: var(--text-faint);
  border: 1px dashed var(--border); border-radius: 10px;
  padding: 14px 10px; text-align: center;
}
.pb-card {
  display: flex; gap: 10px;
  background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: 10px; padding: 10px;
  text-decoration: none; color: inherit;
}
.pb-card:hover { border-color: var(--border-strong); }
.pb-card--muted { opacity: .65; }
.pb-card__img {
  width: 44px; height: 44px; border-radius: 8px; object-fit: cover;
  background: var(--bg-elevated); flex-shrink: 0;
}
.pb-card__body { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.pb-card__title {
  font-size: 13px; font-weight: 700; color: var(--text-primary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pb-card__angle { font-size: 11px; color: var(--text-secondary); }
.pb-card__next { font-size: 11px; color: var(--action); font-weight: 600; }
.pb-card__meta { font-size: 10px; color: var(--text-faint); }
.pb-closed { margin-top: 16px; }
.pb-closed summary { font-size: 12px; color: var(--text-muted); cursor: pointer; }
.pb-closed__grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 10px; margin-top: 10px;
}
.pb-stagechip {
  font-size: 11px; font-weight: 800; letter-spacing: .05em;
  padding: 3px 10px; border-radius: 99px;
  background: var(--creative-dim); color: var(--creative);
}

.sb-next {
  display: flex; align-items: center; gap: 8px;
  font-size: 13px; color: var(--text-secondary);
  background: var(--win-bg); border: 1px solid var(--win-line);
  border-radius: 10px; padding: 8px 12px; margin-bottom: 14px;
}
.sb-next i { color: var(--win); }
.sb-strip {
  display: flex; align-items: center; gap: 6px;
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  padding: 4px 0 12px; margin-bottom: 14px;
}
.sb-block {
  display: flex; align-items: center; gap: 6px;
  font-size: 12px; font-weight: 600; white-space: nowrap;
  color: var(--text-faint);
  border: 1px solid var(--border); border-radius: 10px;
  padding: 7px 12px; background: var(--bg-surface);
}
.sb-block--done { color: var(--text-primary); border-color: var(--teal-dim); }
.sb-block--done i { color: var(--verified); }
.sb-strip__link { width: 14px; height: 1px; background: var(--border-strong); flex-shrink: 0; }
@media (max-width: 900px) { .sb-cols { grid-template-columns: 1fr !important; } }

.v-dialog {
  border: 1px solid var(--border-strong);
  border-radius: 14px;
  background: var(--bg-surface); color: var(--text-primary);
  padding: 0; max-width: 480px; width: calc(100vw - 32px);
  box-shadow: var(--shadow-lg);
}
.v-dialog::backdrop { background: rgba(0, 0, 0, .55); }
.v-dialog__body { display: flex; flex-direction: column; gap: 8px; padding: 20px; }
.v-dialog__title { margin: 0 0 4px; font-size: 17px; font-weight: 800; }
.v-dialog__label {
  font-size: 11px; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--text-muted); margin-top: 6px;
}
.v-dialog__actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; }
.v-dialog__error { font-size: 12px; color: var(--danger); }

/* --- v2 method library (2026-07-13, item 6) ------------------------------- */
.ml-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 12px;
}
.ml-card {
  position: relative;
  display: flex; flex-direction: column; gap: 8px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 14px;
  clip-path: polygon(0 0, calc(100% - 14px) 0, 100% 14px, 100% 100%, 0 100%);
}
.ml-card::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: var(--creative);
}
.ml-card--saved::before { background: var(--verified); }
.ml-card__type {
  font-size: 12px; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--creative);
  font-family: var(--font-mono), monospace;
}
.ml-card--saved .ml-card__type { color: var(--verified); }
.ml-card__desc { font-size: 13px; color: var(--text-secondary); }
.ml-card__adapt {
  font-size: 11.5px; color: var(--text-muted);
  border-top: 1px solid var(--border); padding-top: 8px;
}
.ml-card__struct { font-size: 12px; color: var(--text-secondary); display: flex; flex-direction: column; gap: 3px; }
.ml-card__struct span { color: var(--text-faint); text-transform: capitalize; margin-right: 4px; }
.ml-card__use { margin-top: auto; align-self: flex-start; }
.ml-card__meta {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-top: auto; font-size: 11px; color: var(--text-faint);
}
.ml-card__meta a { color: var(--text-muted); text-decoration: none; }
.ml-card__meta a:hover { color: var(--action); }
.ml-sat { font-weight: 700; padding: 1px 8px; border-radius: 99px; border: 1px solid var(--border); }
.ml-sat--early   { color: var(--verified); }
.ml-sat--growing { color: var(--gold); }
.ml-sat--crowded { color: var(--danger); }

/* --- v2 compare tray + page (2026-07-13, completion batch) ---------------- */
.cmp-tray {
  position: fixed; left: 50%; bottom: 18px; transform: translateX(-50%);
  z-index: 60;
  display: flex; align-items: center; gap: 10px;
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-full);
  padding: 8px 10px 8px 16px;
  box-shadow: var(--shadow-lg);
}
.cmp-tray__label { font-size: 12px; font-weight: 700; color: var(--text-secondary); white-space: nowrap; }
.oc__cmp.cmp-on { border-color: var(--verified); color: var(--verified); }
.cmp-grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); align-items: start; }
.cmp-winners { display: grid; gap: 10px; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); margin-bottom: 16px; }
.cmp-winner {
  background: var(--bg-surface); border: 1px solid var(--win-line);
  border-radius: 12px; padding: 12px 14px;
}
.cmp-winner__label {
  font-size: 10px; font-weight: 800; letter-spacing: .07em; text-transform: uppercase;
  color: var(--win); font-family: var(--font-mono), monospace; margin-bottom: 4px;
}
.cmp-winner__name { font-size: 13px; font-weight: 700; color: var(--text-primary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cmp-winner__value { font-size: 12px; color: var(--text-secondary); }

/* ============================================================================
   REDESIGN v3 — shared partial components (2026-07-14)
   Single source of styling for templates/_partials/{verdict_chip, trust_label,
   range_lock, signal_score, momentum_panel, stat_band, tab_bar}.html.
   Partials carry NO inline <style>; everything lives here so the light/dark
   token swap themes them for free. Color rules: pink is actions only, data
   never renders in pink; every numeral is mono + tabular.
   ========================================================================== */

/* --- verdict chip (§2): PROMOTE win / RESEARCH caution / SKIP risk-quiet --- */
.vchip{display:inline-flex;align-items:center;font-family:var(--font-mono);font-weight:700;font-size:11px;letter-spacing:.10em;text-transform:uppercase;line-height:1.4;padding:3px 9px;border-radius:var(--radius-chip);border:1px solid transparent;white-space:nowrap}
.vchip--promote{color:var(--win);background:var(--win-bg);border-color:var(--win-line)}
.vchip--research{color:var(--caution);background:var(--caution-bg);border-color:var(--caution-line)}
/* SKIP is deliberately LOW emphasis (§2): risk ink, no fill, faint line —
   a skip should recede, not shout. */
.vchip--skip{color:var(--risk);background:transparent;border-color:var(--risk-line);opacity:.8}
.vchip--sm{font-size:9.5px;padding:2px 7px;letter-spacing:.08em}
.vchip--lg{font-size:15px;padding:5px 13px;letter-spacing:.09em}

/* --- trust label (§0.4/§2): dot + mono pill, always links /methodology --- */
.trust{display:inline-flex;align-items:center;gap:6px;font-family:var(--font-mono);font-size:10px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--text-muted);padding:2px 9px;border-radius:var(--radius-full);border:1px solid var(--border);background:var(--bg-elevated);text-decoration:none;white-space:nowrap;transition:border-color var(--dur-fast)}
.trust:hover{border-color:var(--border-strong)}
.trust__dot{width:6px;height:6px;border-radius:50%;background:currentColor;flex-shrink:0}
.trust--observed{color:var(--verified);background:var(--verified-bg);border-color:var(--verified-line)}
/* PROVIDER AGGREGATE keeps a verified-teal dot but muted text: real data,
   one step removed from our own observation. */
.trust--provider .trust__dot{background:var(--verified)}
.trust--modelled{color:var(--modelled);background:var(--modelled-bg);border-color:var(--modelled-line)}
.trust--stale{color:var(--caution);background:var(--caution-bg);border-color:var(--caution-line)}
.trust--range{color:var(--text-muted)}

/* --- range lock (§0.5): the ONLY free-tier gate pattern. Dashed chip with
   a bucketed value + one mono unlock link. Replaces blurred-data. --- */
.range-lock{display:inline-flex;align-items:center;gap:9px;padding:4px 10px;border:1px dashed var(--border-strong);border-radius:var(--radius-chip);background:transparent;white-space:nowrap}
.range-lock__val{font-family:var(--font-mono);font-variant-numeric:tabular-nums;font-size:13px;font-weight:600;letter-spacing:.04em;color:var(--text-primary)}
.range-lock__unlock{font-family:var(--font-mono);font-size:9.5px;font-weight:600;letter-spacing:.10em;text-transform:uppercase;color:var(--accent)}
.range-lock__unlock:hover{color:var(--accent-hover)}

/* --- signal score (§2): big mono 0-100 + labeled component bars --- */
.sigscore{display:flex;flex-direction:column;gap:12px}
.sigscore__head{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}
.sigscore__score{font-family:var(--font-mono);font-variant-numeric:tabular-nums;font-size:40px;font-weight:700;line-height:1;color:var(--text-primary)}
.sigscore__denom{font-family:var(--font-mono);font-size:13px;color:var(--text-faint)}
.sigscore__head .trust{margin-left:auto;align-self:center}
.sigscore__bars{display:flex;flex-direction:column;gap:8px}
.sigscore__bar{display:grid;grid-template-columns:104px 1fr 62px;align-items:center;gap:10px}
.sigscore__bar-label{font-family:var(--font-mono);font-size:10px;font-weight:600;letter-spacing:.10em;text-transform:uppercase;color:var(--text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sigscore__bar-track{height:6px;border-radius:3px;background:var(--bg-elevated);border:1px solid var(--border);overflow:hidden}
.sigscore__bar-fill{height:100%;border-radius:3px}
.sigscore__bar-val{font-family:var(--font-mono);font-variant-numeric:tabular-nums;font-size:11px;color:var(--text-secondary);text-align:right}
.sigscore__reading{font-size:13px;color:var(--text-secondary);line-height:1.5;margin:0}
@media(max-width:640px){.sigscore__bar{grid-template-columns:88px 1fr 56px}}

/* --- momentum panel (§2): tier pill + ratio + six window bars --- */
.mom{display:flex;flex-direction:column;gap:14px}
.mom__head{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.mom__tier{display:inline-flex;align-items:center;font-family:var(--font-mono);font-weight:700;font-size:11px;letter-spacing:.10em;text-transform:uppercase;padding:3px 10px;border-radius:var(--radius-full);border:1px solid var(--border-strong);color:var(--text-secondary)}
.mom__tier--surging,.mom__tier--rising{color:var(--rising);background:var(--rising-bg);border-color:var(--rising-line)}
.mom__tier--cooling,.mom__tier--declining{color:var(--cooling);background:var(--cooling-bg);border-color:var(--cooling-line)}
.mom__ratio{font-family:var(--font-mono);font-variant-numeric:tabular-nums;font-size:12px;font-weight:600;color:var(--text-secondary)}
.mom__bars{display:flex;gap:10px;align-items:stretch;height:96px}
.mom__win{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;gap:4px;min-width:0}
.mom__win-val{font-family:var(--font-mono);font-variant-numeric:tabular-nums;font-size:10px;color:var(--text-secondary);white-space:nowrap}
.mom__win-bar{width:100%;max-width:34px;min-height:3px;border-radius:3px 3px 0 0;background:var(--verified)}
.mom__win-label{font-family:var(--font-mono);font-size:9px;font-weight:600;letter-spacing:.08em;color:var(--text-muted)}

/* --- stat band (§2): ruled columns, NOT per-stat mini cards --- */
.stat-band{display:grid;grid-auto-flow:column;grid-auto-columns:1fr;border-top:1px solid var(--border)}
.stat-band__col{padding:12px 16px;border-right:1px solid var(--border);min-width:0}
.stat-band__col:last-child{border-right:none}
.stat-band__val{display:flex;align-items:baseline;gap:6px;font-family:var(--font-mono);font-variant-numeric:tabular-nums;font-size:21px;font-weight:600;color:var(--text-primary);white-space:nowrap}
.stat-band__delta{font-family:var(--font-mono);font-size:11px;font-weight:600}
.stat-band__delta--up{color:var(--rising)}
.stat-band__delta--down{color:var(--cooling)}
.stat-band__label{margin-top:2px;font-family:var(--font-mono);font-size:10px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--text-muted)}
/* Phones: 2-up rows instead of a horizontal squeeze (no sideways scroll,
   rule §0.8). The row separator keeps the ruled look. */
@media(max-width:640px){
  .stat-band{grid-auto-flow:row;grid-template-columns:1fr 1fr}
  .stat-band__col{border-bottom:1px solid var(--border)}
  .stat-band__col:nth-child(2n){border-right:none}
  .stat-band__col:last-child,.stat-band__col:nth-last-child(2):nth-child(odd){border-bottom:none}
  .stat-band__val{font-size:18px}
}

/* --- mobile bottom tab bar (§3 Mobile) ---
   64px + safe-area, translucent blurred bg, mono 8.5px labels, active pink.
   Desktop never sees it; detail pages suppress it by putting
   class="has-action-dock" on <body> (the contextual action dock owns the
   bottom edge there, per 1c). Replaces .mob-bar once base.html swaps the
   include in. */
.tabbar{display:none}
@media(max-width:900px){
  .tabbar{display:flex;position:fixed;bottom:0;left:0;right:0;z-index:1000;align-items:stretch;height:calc(64px + var(--safe-bottom));padding-bottom:var(--safe-bottom);border-top:1px solid var(--border);background:rgba(250,249,246,.94);-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px)}
  [data-theme="dark"] .tabbar{background:rgba(10,11,14,.94)}
  body.has-action-dock .tabbar{display:none}
  /* redesign-v3 2026-07-14: manual_editor's studio mode hid the old .mob-bar
     (its dock owns the bottom edge, see manual_editor.html "studio chrome");
     carry that suppression over to the replacement tab bar so the dock's
     rightmost tools aren't painted over on phones. */
  body.me-studio .tabbar{display:none}
  .tabbar__item{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;min-height:44px;min-width:44px;color:var(--text-faint);text-decoration:none;background:none;border:none;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:color 150ms;touch-action:manipulation}
  .tabbar__item i,.tabbar__item svg{width:22px;height:22px;color:inherit;stroke:currentColor;fill:none}
  .tabbar__item span{font-family:var(--font-mono);font-size:8.5px;font-weight:600;letter-spacing:.12em;line-height:1}
  .tabbar__item--active{color:var(--accent)}
  .tabbar__item:active{opacity:.7}
}

/* iOS announcement bar - signed-in shell (2026-07-28, owner request) ========
   Sits directly under the topbar on every /app page, above the campaign and
   trial banners. Teal, not accent: coral is THE action colour in this system
   and is spoken for by the upgrade button two rows up, so an announcement in
   coral would compete with the thing it sits next to.

   FLAT tint, no gradient. Every surface in this shell is a flat fill separated
   by a hairline, and a decorative gradient here would be the one place the
   dashboard reads as generic. --verified-bg / --verified-line are the same
   token pair the trust pills use, so the bar is theme-correct in light and
   dark without a hardcoded rgba. */
.app-ios-bar{position:relative;display:flex;align-items:center;justify-content:center;gap:var(--space-3);flex-wrap:wrap;padding:var(--space-3) 56px;background:var(--verified-bg);border-bottom:1px solid var(--verified-line)}
.app-ios-bar[hidden]{display:none}
.app-ios-bar__text{margin:0;color:var(--text-secondary);font-size:var(--text-sm);line-height:1.45;text-align:center}
.app-ios-bar__tag{display:inline-block;margin-right:var(--space-2);padding:2px 8px;border:1px solid var(--verified-line);border-radius:var(--radius-full);color:var(--teal);font-family:var(--font-mono);font-size:10.5px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;white-space:nowrap}
/* 44px minimum target: a dismissible banner whose close button needs a precise
   tap is how it becomes a permanent one on a phone. */
/* The beta link (2026-07-31). Same 44px minimum as the close button below and
   for the same reason: it sits next to a dismiss target, and two small controls
   side by side is how a reader taps the wrong one. */
.app-ios-bar__cta{display:inline-flex;align-items:center;min-height:44px;padding:0 var(--space-3);border:1px solid var(--teal);border-radius:var(--radius-full);color:var(--teal);font-size:var(--text-sm);font-weight:600;text-decoration:none;white-space:nowrap;transition:background var(--dur-fast),color var(--dur-fast)}
.app-ios-bar__cta:hover{background:var(--teal);color:var(--verified-bg)}
.app-ios-bar__cta:focus-visible{outline:2px solid var(--teal);outline-offset:2px}
.app-ios-bar__close{position:absolute;right:var(--space-2);top:50%;transform:translateY(-50%);display:flex;align-items:center;justify-content:center;min-width:44px;min-height:44px;background:none;border:0;color:var(--text-faint);cursor:pointer;-webkit-tap-highlight-color:transparent;transition:color var(--dur-fast)}
.app-ios-bar__close:hover{color:var(--text-primary)}
.app-ios-bar__close:focus-visible{outline:2px solid var(--teal);outline-offset:2px}
@media(max-width:640px){
  /* Centred text under an absolutely-positioned close button only works while
     the line does not wrap. Below the wrap point the content stops pretending
     it is one centred line, and the second sentence (the nice-to-have) goes:
     on a phone it pushed the bar to three lines before the page even starts. */
  .app-ios-bar{justify-content:flex-start;text-align:left;padding-left:var(--space-4)}
  .app-ios-bar__text{text-align:left}
  .app-ios-bar__more{display:none}
}
