/* Opt2In report — design-system stylesheet.
   Source of truth: dev/design-handoff/approved/.../report-proto.css (banked
   Claude Design export, 2026-06-12). Adapted only for host-page scoping.
   Loaded by report/instagram, report/facebook, /me/, /admin/view. */
/* ── Opt2In report prototype — shared styles ───────────────────── */
/* Reset is wrapped in :where() so it carries ZERO specificity — the
   prototype's bare `*` reset worked because every class rule beat it.
   The earlier `#report-output *` form carried ID specificity and
   silently crushed every padding/margin in this file (the 2026-06-12
   "design not transferred" bug). Never put an ID outside :where() here. */
:where(#report-output, .r2-app) *, :where(#report-output, .r2-app) *::before, :where(#report-output, .r2-app) *::after { margin:0; padding:0; box-sizing:border-box; }
/* Host pages style bare h1 for their upload/marketing chrome; inside the
   app subtree roll those back to UA defaults (this file must be linked
   AFTER the host's inline <style> for the tie-break to land here). */
:where(#report-output, .r2-app) h1 { font:revert; letter-spacing:revert; margin:0; }
:root {
  --bg-0:#0f0e0c; --bg-deep:#0c0b09; --bg-1:#15130f; --bg-2:#1c1914; --bg-3:#242019;
  --fg-1:#f2f0ec; --fg-2:#c9c4bc; --fg-3:#9a9590; --fg-4:#6f6a64;
  --line-1:rgba(242,240,236,0.08); --line-2:rgba(242,240,236,0.16);
  --purple:#6b6af0; --purple-light:#8a89f5; --mint:#3dddaa; --amber:#e8b923; --caution:#f0913e; --red:#f87171;
  --grad:linear-gradient(135deg,#6b6af0,#3dddaa);
  --serif:"Cormorant Infant",Georgia,serif; --sans:"Inter",system-ui,sans-serif; --mono:"JetBrains Mono",ui-monospace,monospace;
}
/* scoped: host pages keep their own body styles.
   -webkit-tap-highlight-color:transparent kills iOS's default grey tap flash
   (we supply our own :active feedback below); touch-action:manipulation drops
   the 350ms double-tap-zoom delay on the app's many div-as-button controls. */
#report-output, .r2-app { background:var(--bg-0); color:var(--fg-1); font-family:var(--sans); -webkit-font-smoothing:antialiased; -webkit-tap-highlight-color:transparent; touch-action:manipulation; }
/* Press feedback for the tap-highlight we just removed — a quick scale so
   buttons feel physical on touch (and on desktop mousedown). */
:where(#report-output, .r2-app) .nav-item:active,
:where(#report-output, .r2-app) .qchip:active,
:where(#report-output, .r2-app) .pg:active,
:where(#report-output, .r2-app) .act:active,
:where(#report-output, .r2-app) .cta:active,
:where(#report-output, .r2-app) .chat-send:active { transform:scale(0.975); }
.app { display:grid; grid-template-columns:256px 1fr; min-height:100vh; }

/* ── sidebar ─────────────────────────────────────── */
/* overflow-y:auto (was hidden) so the whole sidebar can scroll on short
   screens — Arthur 2026-06-28 lost the lower nav + "Take action" below the
   fold at his resolution. The inner nav scrolls first (Take action stays
   pinned); this is the fallback when even the pinned chrome can't fit. */
.side { background:var(--bg-deep); border-right:1px solid var(--line-1); padding:18px 14px; display:flex; flex-direction:column; gap:12px; position:sticky; top:0; height:100vh; overflow-y:auto; }
.brand { display:flex; align-items:center; gap:9px; padding:2px 6px; }
.brand-ring { width:24px; height:24px; flex-shrink:0; }
.brand-name { font:600 23px/1.2 var(--serif); /* descender-safe (the p in Opt2In) */ letter-spacing:-0.5px; color:var(--fg-1); }
.brand-name .two { color:var(--mint); }
.userpill { display:flex; align-items:center; gap:10px; background:var(--bg-1); border:1px solid var(--line-1); border-radius:12px; padding:9px 11px; }
.ava { width:32px; height:32px; border-radius:50%; background:var(--grad); color:#0f0e0c; font:700 13px/1 var(--sans); display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.userpill .who { display:flex; flex-direction:column; gap:2px; min-width:0; }
.userpill .who b { font:600 13px/1.1 var(--sans); color:var(--fg-1); }
.userpill .who span { font:500 10px/1.2 var(--mono); color:var(--fg-3); letter-spacing:0.02em; }
.glance { background:linear-gradient(135deg, rgba(107,106,240,0.12), rgba(61,221,170,0.10)); border:1px solid var(--line-1); border-radius:12px; padding:10px 12px; display:flex; flex-direction:column; gap:6px; }
.glance-l { font:500 9px/1 var(--mono); letter-spacing:0.12em; text-transform:uppercase; color:var(--fg-3); }
.glance-row { display:flex; align-items:baseline; justify-content:space-between; gap:8px; }
.glance-row b { font:600 15px/1 var(--sans); color:var(--fg-1); font-variant-numeric:tabular-nums; }
.glance-row span { font:500 11px/1 var(--sans); color:var(--fg-3); }
/* Scoped off the global site nav (Robert 2026-06-15): this rule styles the
   report's INTERNAL sidebar section-nav (.nav-group/.nav-item). As a bare
   `nav{}` it also matched <nav id="site-nav">, forcing it flex-direction:
   column on report + /me/ pages, which stacked the bar's content at the top
   ("closer to the top border"). :not(#site-nav) keeps the sidebar nav styled
   while leaving the shared top bar alone. */
/* min-height:0 + overflow-y:auto let the nav list scroll inside the flex
   column instead of overflowing the sidebar — keeps the header + .side-foot
   ("Take action") pinned and reachable at any viewport height. */
nav:not(#site-nav) { display:flex; flex-direction:column; gap:2px; overflow-y:auto; flex:1; min-height:0; margin:0 -4px; padding:0 4px; }
/* Section headers carry the brand gradient (Robert 2026-06-15) — clipped
   gradient text so the IA groups read as anchors, not muted dividers. */
.nav-group { font:600 9.5px/1 var(--mono); letter-spacing:0.14em; text-transform:uppercase; padding:11px 10px 5px; background:var(--grad); -webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent; color:transparent; }
.nav-item { display:flex; align-items:center; gap:10px; padding:6px 10px; border-radius:8px; font:500 13px/1 var(--sans); color:var(--fg-2); cursor:pointer; transition:background 140ms, color 140ms; }
.nav-item:hover { background:var(--bg-1); color:var(--fg-1); }
.nav-item.active { background:var(--bg-2); color:var(--fg-1); font-weight:600; box-shadow:inset 2px 0 0 var(--mint); }
.nav-item svg { width:15px; height:15px; flex-shrink:0; stroke:currentColor; stroke-width:1.6; fill:none; color:var(--fg-3); }
.nav-item.active svg { color:var(--mint); }
.nav-item .badge { margin-left:auto; font:600 9.5px/1 var(--mono); color:var(--fg-3); background:var(--bg-2); border:1px solid var(--line-1); border-radius:999px; padding:3px 6px; }
/* Desktop shows the full tab label; the short phone label (P8) is hidden until
   the <=760px strip turns it on. */
.nav-short { display:none; }
.nav-item.disabled { color:var(--fg-4); cursor:pointer; }
.nav-item.disabled svg { color:var(--fg-4); }
.nav-item.disabled .badge { color:var(--fg-4); background:transparent; border-color:var(--line-1); }
.side-foot { display:flex; flex-direction:column; gap:8px; padding-top:6px; border-top:1px solid var(--line-1); }
.act { display:flex; align-items:center; justify-content:center; gap:8px; padding:11px; border-radius:10px; font:600 13px/1 var(--sans); cursor:pointer; text-decoration:none; }
.act.primary { background:var(--grad); color:#0f0e0c; }
.act.ghost { background:transparent; border:1px solid var(--line-2); color:var(--fg-2); }

/* ── content shell ───────────────────────────────── */
.main { padding:48px 48px 124px; position:relative; } /* 124px bottom reserves clearance for the fixed chat FAB (.v2-chat-fab, bottom:78px, ~38px tall) so the in-flow tab pager isn't covered/unclickable at scroll-end (code-review 2026-07-22) */
.panel { display:none; }
.panel.active { display:block; }
/* Fluid tab transition (2026-07-17): the incoming tab pane fades + rises a few
   px as it mounts, so switching sections reads as a move, not a hard cut.
   key={tab} in v2-discovery replays it on every switch. Animation only (no
   resting transform) so position:fixed content inside a tab is unaffected once
   it ends. Shared IG + FB report. */
.v2-tabpane { animation:v2TabIn .24s cubic-bezier(.22,.61,.36,1); }
@keyframes v2TabIn { from { opacity:0; transform:translateY(10px); } }
@media (prefers-reduced-motion:reduce){ .v2-tabpane { animation:none; } }
/* Edge-bump (2026-07-18): a swipe past the first/last tab clamps but nudges the
   tab column a few px in the swipe direction, matching the dashboard. Applied to
   .main (which has no entrance animation) so there's no v2TabIn replay flicker. */
@keyframes v2TabBumpLeft { 45% { transform:translateX(-14px); } }
@keyframes v2TabBumpRight { 45% { transform:translateX(14px); } }
.main.v2-tabbump--left { animation:v2TabBumpLeft .28s ease; }
.main.v2-tabbump--right { animation:v2TabBumpRight .28s ease; }
@media (prefers-reduced-motion:reduce){ .main.v2-tabbump--left, .main.v2-tabbump--right { animation:none; } }

.topbar { display:flex; align-items:center; justify-content:space-between; gap:24px; padding-bottom:18px; margin-bottom:24px; border-bottom:1px solid var(--line-1); container-type:inline-size; }
.topbar-l { display:flex; align-items:baseline; gap:16px; }
.topbar h1 { font:600 38px/1.3 var(--serif); letter-spacing:-0.3px; color:var(--fg-1); }
.topbar h1 em { font-style:italic; color:var(--mint); }
/* Overview title only: fit-to-column single line at every width. cqi sizes the
   title to the .topbar container (not the viewport), so the 256px sidebar band
   never overflows; caps at the design 38px, floors at 17px so it never wraps
   or clips on the narrowest phones. Scoped via .th-fit so other tab titles keep
   their normal 38px/26px + wrapping behavior. */
.topbar h1 .th-fit { display:inline-block; white-space:nowrap; font-size:clamp(17px, 6cqi, 38px); line-height:1.25; }
.winset { display:flex; gap:7px; flex-shrink:0; }

/* chips */
.chip { display:inline-flex; align-items:center; gap:6px; font:500 9.5px/1 var(--mono); letter-spacing:0.06em; text-transform:uppercase; border-radius:999px; padding:5px 9px; white-space:nowrap; }
.chip.win { color:var(--fg-3); background:var(--bg-2); border:1px solid var(--line-1); }
.chip.sourced { color:var(--mint); background:rgba(61,221,170,0.12); border:1px solid rgba(61,221,170,0.26); }
.chip.estimated { color:var(--caution); background:rgba(240,145,62,0.12); border:1px solid rgba(240,145,62,0.26); }
.chip.inferred { color:var(--purple-light); background:rgba(138,137,245,0.14); border:1px solid rgba(138,137,245,0.3); }
/* Parsed = computed from verbatim sourced values (e.g. dwell = session end
   − start). One tier below Sourced; distinct cyan so it never reads as the
   mint Sourced tag or the purple Inferred tag. */
.chip.parsed { color:#5BC9D6; background:rgba(91,201,214,0.12); border:1px solid rgba(91,201,214,0.28); }
/* Per-tab eyebrow now reads as the page anchor — solid brand gradient
   fill + dark text + soft glow so the category pops above the headline. */
.chip.eyebrow { color:#0f0e0c; font-weight:600; background:var(--grad); border:1px solid transparent; box-shadow:0 1px 14px rgba(107,106,240,0.30); }
.chip.sm { font-size:10px; padding:4px 7px; }

/* generic exhibit card */
.ex { background:var(--bg-1); border:1px solid var(--line-1); border-radius:14px; padding:22px 24px; }
.ex-head { display:flex; align-items:center; justify-content:space-between; gap:16px; margin-bottom:18px; }
.ex-title { font:600 22px/1.3 var(--serif); color:var(--fg-1); text-wrap:balance; }
.ex-sub { font:500 12px/1.4 var(--sans); color:var(--fg-3); margin-top:2px; }
/* Card BODY prose (the sentence(s) that ARE the card's content, e.g. the Insights
   cross-signal explanations) — 14px matches the Ads tab body, the AI-summary
   compact size, empty-state and chat-answer copy. Distinct from .ex-sub, which is
   the small subtitle/metadata line under a card title. 2026-07-20. */
.ex-body { font:400 14px/1.6 var(--sans); color:var(--fg-2); }
/* Subsection header — the small uppercase label that divides content inside a
   card ("Top creators by views", "Stories you watched", "What they showed you").
   ONE size everywhere (12px) so the label scale never changes tab to tab; matches
   the stat-strip section labels (Robert 2026-07-20: 12px reads better, keep it
   consistent). Pair with an inline margin (and an inline color for the rare
   accent variant, e.g. the purple stories strip). */
.o2i-eyebrow { font:600 12px/1 var(--sans); text-transform:uppercase; letter-spacing:0.1em; color:var(--fg-3); }
.ex-evi { display:flex; align-items:center; gap:7px; flex-shrink:0; }
.grid2 { display:grid; grid-template-columns:1fr 1fr; gap:16px; }
.grid3 { display:grid; grid-template-columns:repeat(3,1fr); gap:16px; }
.grid4 { display:grid; grid-template-columns:repeat(4,1fr); gap:16px; }
.stack { display:flex; flex-direction:column; gap:16px; }
.row { display:flex; gap:16px; }

/* numbers */
.fig { font:600 44px/1 var(--sans); letter-spacing:-0.025em; color:var(--fg-1); font-variant-numeric:lining-nums proportional-nums; }
.fig.mint { color:var(--mint); }
.fig.sm { font-size:30px; }
.statline { display:flex; align-items:baseline; gap:10px; }
.statline .u { font:500 12px/1.3 var(--sans); color:var(--fg-3); }

/* horizontal bar list */
.bars { display:flex; flex-direction:column; gap:13px; }
.bar { display:grid; grid-template-columns:190px 1fr 56px; align-items:center; gap:14px; }
.bar-l { font:500 13px/1.3 var(--sans); color:var(--fg-2); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.bar-track { height:8px; border-radius:4px; background:var(--bg-3); overflow:hidden; }
.bar-track i { display:block; height:100%; border-radius:4px; background:var(--accent,var(--purple)); }
.bar-v { font:600 13px/1 var(--sans); color:var(--fg-1); text-align:right; font-variant-numeric:tabular-nums; }

/* Two-sided compare rows — "What you watch vs what they pitch you" (R8,
   2026-07-20): one topic bucket per row, organic-watch value next to ad-pitch
   value, joined on the shared cluster key. */
.cmp2 { display:flex; flex-direction:column; }
.cmp2-row { display:grid; grid-template-columns:1fr minmax(72px,auto) minmax(72px,auto); gap:8px 16px; align-items:baseline; padding:11px 0; border-top:1px solid var(--line-1); }
.cmp2-row:first-child { border-top:none; }
.cmp2-head { padding:0 0 8px; }
.cmp2-head .cmp2-v { font:600 10px/1 var(--sans); letter-spacing:0.08em; text-transform:uppercase; color:var(--fg-4); }
.cmp2-lab { font:600 14px/1.3 var(--sans); color:var(--fg-1); min-width:0; text-wrap:balance; overflow-wrap:anywhere; }
.cmp2-tap { color:var(--mint); font:500 11px/1 var(--sans); }
.cmp2-v { font:600 13px/1 var(--sans); color:var(--fg-2); text-align:right; min-width:62px; font-variant-numeric:tabular-nums; }
.cmp2-v.cmp2-pitch { color:var(--amber); }
.cmp2-none { color:var(--fg-4); }

/* tables */
.tbl { width:100%; border-collapse:collapse; }
.tbl th { font:600 10px/1 var(--mono); letter-spacing:0.08em; text-transform:uppercase; color:var(--fg-3); text-align:left; padding:0 14px 12px; border-bottom:1px solid var(--line-1); }
.tbl th.r, .tbl td.r { text-align:right; }
.tbl td { font:400 13.5px/1.4 var(--sans); color:var(--fg-2); padding:13px 14px; border-bottom:1px solid var(--line-1); }
.tbl tr:last-child td { border-bottom:none; }
.tbl td b { color:var(--fg-1); font-weight:600; font-variant-numeric:tabular-nums; }
.tbl td .nm { color:var(--fg-1); font-weight:500; }

/* brand chip / logo tile */
.logo-tile { width:30px; height:30px; border-radius:8px; background:var(--bg-2); border:1px solid var(--line-1); object-fit:contain; display:inline-block; vertical-align:middle; }
.brandcell { display:flex; align-items:center; gap:11px; }
.tagwrap { display:flex; gap:5px; justify-content:flex-end; flex-wrap:wrap; max-width:220px; margin-left:auto; }

/* badge tags (advertisers) */
.tag { display:inline-flex; align-items:center; font:600 8.5px/1 var(--mono); letter-spacing:0.05em; text-transform:uppercase; border-radius:5px; padding:4px 6px; border:1px solid var(--line-1); color:var(--fg-3); background:var(--bg-2); }
.tag.track { color:var(--red); border-color:rgba(248,113,113,0.3); background:rgba(248,113,113,0.1); }
.tag.visit { color:var(--amber); border-color:rgba(232,185,35,0.3); background:rgba(232,185,35,0.1); }
.tag.rel { color:var(--purple-light); border-color:rgba(138,137,245,0.3); background:rgba(138,137,245,0.12); }
.tag.match { color:var(--mint); border-color:rgba(61,221,170,0.3); background:rgba(61,221,170,0.1); }

/* callouts / notes */
.note { border-radius:12px; padding:14px 16px; font:400 13px/1.55 var(--sans); color:var(--fg-2); border:1px solid var(--line-1); background:var(--bg-2); display:flex; gap:11px; }
.note .lead { font:600 10px/1.6 var(--mono); letter-spacing:0.08em; text-transform:uppercase; flex-shrink:0; }
.note.infer { border-color:rgba(138,137,245,0.28); }
.note.infer .lead { color:var(--purple-light); }
.note.caution { border-color:rgba(240,145,62,0.28); }
.note.caution .lead { color:var(--caution); }
.note b { color:var(--fg-1); font-weight:600; }

/* mono key/value rows */
.kv { display:flex; flex-direction:column; }
.kv-row { display:flex; align-items:baseline; justify-content:space-between; gap:16px; padding:12px 0; border-bottom:1px solid var(--line-1); }
.kv-row:last-child { border-bottom:none; }
.kv-k { font:500 12px/1.3 var(--mono); letter-spacing:0.02em; color:var(--fg-3); text-transform:uppercase; }
.kv-v { font:500 14px/1.3 var(--sans); color:var(--fg-1); text-align:right; }
.kv-v.mask { font-family:var(--mono); letter-spacing:0.02em; color:var(--fg-2); }

/* donut wrap — the SVG donuts (Attention feed/engagement) render inside */
.donut-wrap { display:flex; align-items:center; gap:24px; }
.legend { display:flex; flex-direction:column; gap:10px; }
.legend-row { display:flex; align-items:center; gap:10px; font:500 13px/1 var(--sans); color:var(--fg-2); }
.legend-row .sw { width:10px; height:10px; border-radius:3px; flex-shrink:0; }
.legend-row .lv { margin-left:auto; font-weight:600; color:var(--fg-1); font-variant-numeric:tabular-nums; }

/* spark / daypart bars */
.spark { display:flex; align-items:flex-end; gap:3px; height:64px; }
.spark i { flex:1; background:var(--purple); border-radius:2px 2px 0 0; opacity:0.55; min-height:3px; }
.spark i.peak { background:var(--mint); opacity:1; }

/* empty state */
.empty { border:1px dashed var(--line-2); border-radius:16px; padding:48px 40px; text-align:center; display:flex; flex-direction:column; align-items:center; gap:12px; }
.empty h3 { font:600 26px/1.3 var(--serif); color:var(--fg-2); }
.empty p { font:400 14px/1.6 var(--sans); color:var(--fg-3); max-width:46ch; text-wrap:pretty; }
.empty .why { font:500 11px/1.5 var(--mono); letter-spacing:0.04em; color:var(--fg-4); background:var(--bg-1); border:1px solid var(--line-1); border-radius:8px; padding:8px 12px; }

/* search input (advertisers) */
.search { display:flex; align-items:center; gap:10px; background:var(--bg-2); border:1px solid var(--line-1); border-radius:10px; padding:11px 14px; width:300px; }
.search input { background:none; border:none; outline:none; color:var(--fg-1); font:400 13px/1 var(--sans); width:100%; }
.search input::placeholder { color:var(--fg-4); }
.pager { display:flex; align-items:center; gap:6px; }
.pager span { font:500 12px/1 var(--mono); color:var(--fg-3); }
.pg { width:30px; height:30px; border-radius:8px; border:1px solid var(--line-1); background:var(--bg-2); color:var(--fg-2); font:500 12px/1 var(--sans); display:flex; align-items:center; justify-content:center; cursor:pointer; }
.pg.on { background:var(--bg-3); color:var(--fg-1); border-color:var(--line-2); }

/* misc */
.math-link { font:600 12px/1 var(--sans); color:var(--purple-light); text-decoration:none; border-bottom:1px solid rgba(138,137,245,0.4); padding-bottom:2px; }
.sec-label { display:flex; align-items:baseline; gap:12px; margin:30px 0 14px; }
.sec-label h3 { font:600 20px/1.3 var(--serif); color:var(--fg-1); }
.sec-label span { font:500 11px/1 var(--sans); color:var(--fg-3); }
/* Removed-interest rows: dimmed, but NO strikethrough (Robert 2026-07-18 — the
   line-through read as noisy). The "Removed (N)" group label already frames them. */
.strike { color:var(--fg-4); }
.cta { display:inline-flex; align-items:center; gap:8px; background:var(--grad); color:#0f0e0c; border:none; cursor:pointer; border-radius:10px; padding:13px 20px; font:600 14px/1 var(--sans); }
.cta.amt { font-weight:700; }
.pillbtn { display:inline-flex; align-items:center; gap:7px; background:var(--bg-2); border:1px solid var(--line-2); color:var(--fg-2); border-radius:10px; padding:11px 16px; font:600 13px/1 var(--sans); cursor:pointer; }
.tracklist { display:flex; flex-wrap:wrap; gap:8px; }
.trackword { font:500 11px/1 var(--mono); color:var(--fg-3); background:var(--bg-2); border:1px solid var(--line-1); border-radius:6px; padding:7px 10px; letter-spacing:0.02em; }

/* KPI tiles */
/* Overview tiles are inert stat cards (2026-07-22): they no longer jump to tabs
   (Leslie - the instant jump confused the read), so no pointer cursor, no hover
   affordance, no :active press-scale. Navigation lives in the sidebar/tab strip. */
.kpi { position:relative; background:var(--bg-1); border:1px solid var(--line-1); border-radius:12px; padding:16px 18px; display:flex; flex-direction:column; gap:9px; transition:border-color 150ms, background 150ms; border-left:2px solid var(--accent); }
.kpi-top { display:flex; align-items:center; justify-content:space-between; gap:8px; }
/* Kept compact (10.5px): the Overview KPI grid is a dense 10-tile component, and
   12px labels wrap to 2 lines on phones. This is a tile-grid label, not a
   subsection divider. 2026-07-20. */
.kpi-label { font:600 10.5px/1.2 var(--sans); letter-spacing:0.05em; text-transform:uppercase; color:var(--fg-3); }
.kpi-num { font:600 30px/1 var(--sans); letter-spacing:-0.02em; color:var(--fg-1); font-variant-numeric:lining-nums proportional-nums; }
.kpi-bar { height:3px; border-radius:2px; background:var(--bg-3); overflow:hidden; }
.kpi-bar i { display:block; height:100%; border-radius:2px; background:var(--accent); }
.kpi-foot { display:flex; align-items:center; justify-content:space-between; gap:8px; }
/* .kpi-go removed 2026-07-22 with the Overview tile tab-jump (see .kpi above). */

/* ── $0 reads as loss — red alert (mint reserved for money toward you) ── */
.fig.loss { color:var(--red); }

/* ── AI summary + live chat card (signature mint-blue) ── */
.summary { position:relative; overflow:hidden; border-radius:14px; padding:20px 24px 18px; margin-bottom:24px;
  background:linear-gradient(135deg, rgba(61,221,170,0.10), rgba(107,106,240,0.10)); border:1px solid rgba(61,221,170,0.30); }
.summary::before { content:""; position:absolute; left:0; top:0; bottom:0; width:3px; background:var(--grad); }
.summary.compact { padding:16px 22px; margin-bottom:22px; }
.summary-head { display:flex; align-items:center; gap:12px; margin-bottom:14px; }
.summary-badge { font:600 11px/1 var(--sans); color:#0f0e0c; background:var(--grad); border-radius:999px; padding:6px 11px; }
.summary-sub { font:600 10px/1 var(--mono); letter-spacing:0.12em; text-transform:uppercase; color:var(--fg-3); }
.summary-text { font:400 15px/1.65 var(--sans); color:var(--fg-1); text-wrap:pretty; }
.summary-text p { margin:0 0 11px; }
.summary-text p:last-child { margin-bottom:0; }
.summary.compact .summary-text { font-size:14px; line-height:1.6; }
.summary-text b { font-weight:600; }
/* Collapsed teaser: clamp every tab's first prose block to a consistent 3-line
   height so the pre-expand Opt2In Summary is the same size on every tab, and no
   tab shows a long wall of text before you tap (Robert 2026-07-20). Clamp the
   text-holding child (a <p> on every tab) so the wrapper stays a tight block;
   the full read + AI chat live in .digest-body, unclamped. */
.summary-teaser { overflow:hidden; }
.summary-teaser > p, .summary-teaser > div {
  display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:3; line-clamp:3; overflow:hidden;
  /* Fixed 3-line box: -webkit-line-clamp caps long intros, min-height fills
     short ones, so every collapsed teaser is the SAME height on every tab
     (3 lines at the compact 14px/1.6 = 4.8em). Robert 2026-07-20. */
  min-height:4.8em;
  /* !important beats each tab's inline paragraph margin (14px on one, 12px on
     another, 0 on others) so the box height stays uniform. */
  margin:0 !important;
}

/* ── Digest treatment (2026-07-16): the summary card expands and collapses
   smoothly (grid-rows 0fr→1fr; visibility gates keyboard focus out of the
   hidden body), mono eyebrows section the prose, and the chat is labeled as
   the one AI-generated region. Replaces the 07-08 mobile line-clamp stopgap. */
.digest-body { display:grid; grid-template-rows:0fr; opacity:0; visibility:hidden;
  transition:grid-template-rows .32s ease, opacity .24s ease, visibility 0s linear .32s; }
.digest-body.open { grid-template-rows:1fr; opacity:1; visibility:visible; transition-delay:0s; }
.digest-body-inner { overflow:hidden; min-height:0; }
/* Head-to-body gap lives on the first child's margin, not inner padding —
   padding would hold the collapsed 0fr track open by its own height. */
.digest-body-inner > :first-child { margin-top:14px; }
.digest-eyebrow { display:flex; align-items:center; gap:6px; font:600 10px/1 var(--mono);
  letter-spacing:0.12em; text-transform:uppercase; color:var(--fg-3); margin:0 0 9px; }
.digest-eyebrow svg { width:13px; height:13px; stroke:var(--mint); fill:none; stroke-width:1.6;
  stroke-linejoin:round; flex-shrink:0; }
.chat .digest-eyebrow { margin:0 0 10px; }
/* .digest-foot rule deleted 2026-07-18 — the honesty footer it styled was
   removed from both digest cards, so the class had no remaining users. */
@media (prefers-reduced-motion:reduce){ .digest-body { transition:none; } }
.chips-try { font:500 11px/1 var(--mono); letter-spacing:0.04em; color:var(--fg-4); align-self:center; margin-right:4px; }
.chat { margin-top:15px; }
.chat-bar { display:flex; gap:10px; align-items:stretch; }
.chat-input { flex:1; display:flex; align-items:center; gap:10px; background:var(--bg-0); border:1px solid var(--line-2); border-radius:10px; padding:0 14px; }
.chat-input svg { width:15px; height:15px; stroke:var(--fg-4); fill:none; stroke-width:1.6; flex-shrink:0; }
.chat-input input { flex:1; background:none; border:none; outline:none; color:var(--fg-1); font:400 13.5px/1 var(--sans); padding:13px 0; }
.chat-input input::placeholder { color:var(--fg-4); }
.chat-send { display:inline-flex; align-items:center; gap:7px; background:var(--grad); color:#0f0e0c; border:none; border-radius:10px; padding:0 18px; font:600 13px/1 var(--sans); cursor:pointer; white-space:nowrap; }
.chat-send:disabled { opacity:0.55; cursor:default; }
.chips { display:flex; flex-wrap:wrap; gap:8px; margin-top:12px; }
.qchip { font:500 12px/1 var(--sans); color:var(--fg-2); background:var(--bg-0); border:1px solid var(--line-1); border-radius:999px; padding:9px 13px; cursor:pointer; transition:border-color 140ms, color 140ms; }
.qchip:hover { border-color:var(--mint); color:var(--fg-1); }
.answer { margin-top:14px; border-top:1px solid rgba(61,221,170,0.22); padding-top:13px; }
.answer-q { font:600 12.5px/1.4 var(--sans); color:var(--fg-2); margin-bottom:7px; display:none; }
.answer-q.show { display:block; }
.answer-a { font:400 14px/1.65 var(--sans); color:var(--fg-1); text-wrap:pretty; min-height:44px; }
.answer-a .hint { color:var(--fg-4); }
.answer-a .typing { color:var(--mint); }

/* ── browsable category pills (interests) ── */
.catgroup { margin-bottom:22px; }
.catgroup:last-child { margin-bottom:0; }
.catgroup-head { display:flex; align-items:baseline; gap:10px; margin-bottom:12px; }
.catgroup-head h4 { font:600 13px/1 var(--sans); color:var(--fg-1); }
.catgroup-head .ct { font:600 10px/1 var(--mono); letter-spacing:0.04em; color:var(--fg-3); }
.catpills { display:flex; flex-wrap:wrap; gap:8px; }
.catpill { font:500 12.5px/1 var(--sans); color:var(--fg-2); background:var(--bg-2); border:1px solid var(--line-1); border-radius:999px; padding:8px 13px; }
.catpill.ai { color:var(--purple-light); border-color:rgba(138,137,245,0.28); }
.catpill.hidden { display:none; }
.showall { font:600 12px/1 var(--sans); color:var(--purple-light); background:none; border:none; cursor:pointer; padding:8px 2px; }
.catgroup.empty { display:none; }
.act-link { font:600 13px/1 var(--sans); color:var(--mint); text-decoration:none; border-bottom:1px solid rgba(61,221,170,0.4); padding-bottom:2px; }

/* worked-ledger steppers */
.stepper { display:inline-flex; align-items:center; gap:0; vertical-align:middle; margin:0 2px; }
.stepper button { width:22px; height:22px; border:1px solid var(--line-2); background:var(--bg-2); color:var(--fg-2); font:600 13px/1 var(--sans); cursor:pointer; display:flex; align-items:center; justify-content:center; }
.stepper button:first-child { border-radius:6px 0 0 6px; }
.stepper button:last-child { border-radius:0 6px 6px 0; border-left:none; }
.stepper button:hover { background:var(--bg-3); color:var(--fg-1); }

/* till receipt */
.till { background:#f4f1e8; color:#1a1712; width:380px; margin:0 auto; padding:40px 32px; font-family:var(--mono); box-shadow:0 18px 50px rgba(0,0,0,0.5);
  -webkit-mask:
    conic-gradient(from -45deg at bottom, #0000, #000 1deg 89deg, #0000 90deg) bottom/18px 9px repeat-x,
    conic-gradient(from 135deg at top, #0000, #000 1deg 89deg, #0000 90deg) top/18px 9px repeat-x,
    linear-gradient(#000 0 0) center/100% calc(100% - 18px) no-repeat;
  mask:
    conic-gradient(from -45deg at bottom, #0000, #000 1deg 89deg, #0000 90deg) bottom/18px 9px repeat-x,
    conic-gradient(from 135deg at top, #0000, #000 1deg 89deg, #0000 90deg) top/18px 9px repeat-x,
    linear-gradient(#000 0 0) center/100% calc(100% - 18px) no-repeat;
}
.till .t-bar { text-align:center; font:600 17px/1.2 var(--sans); letter-spacing:0.28em; color:#1a1712; }
.till .t-sub { text-align:center; font:600 10px/1.4 var(--mono); letter-spacing:0.18em; color:#1f8a5b; }
.till .t-meta { text-align:center; font:500 10.5px/1.5 var(--mono); letter-spacing:0.08em; color:#8a8275; }
.till .t-div { border-top:1px dashed #b8b0a0; margin:14px 0; }
.till .t-div.solid { border-top:1px solid #1a1712; }
.till .t-div.dbl { border-top:3px double #1a1712; }
.till .t-row { display:flex; justify-content:space-between; align-items:baseline; gap:12px; font:500 12px/1.7 var(--mono); color:#3a342a; }
.till .t-row .amt { color:#1a1712; font-weight:600; font-variant-numeric:tabular-nums; }
.till .t-row.sub2 { font-size:11px; color:#8a8275; padding-left:14px; }
.till .t-total { display:flex; justify-content:space-between; align-items:baseline; gap:12px; }
.till .t-total .lab { font:600 24px/1.2 var(--serif); font-style:italic; color:#1a1712; }
.till .t-total .amt { font:700 22px/1 var(--mono); color:#1a1712; font-variant-numeric:tabular-nums; }
.till .t-paid { display:flex; justify-content:space-between; align-items:baseline; gap:8px; font:600 13px/1.8 var(--mono); color:#1a1712; }
.till .t-foot { text-align:center; font:500 9.5px/1.6 var(--mono); letter-spacing:0.08em; color:#8a8275; margin-top:6px; }
.till .t-code { text-align:center; font:400 18px/1 var(--mono); letter-spacing:1px; color:#1a1712; margin-top:12px; }

/* ── followers: gender split bar + age-by-gender ── */
.splitbar { display:flex; align-items:center; gap:20px; }
.splitbar .end { display:flex; flex-direction:column; gap:4px; }
.splitbar .end.r { align-items:flex-end; text-align:right; }
.splitbar .end b { font:600 30px/1 var(--sans); letter-spacing:-0.02em; font-variant-numeric:lining-nums; }
.splitbar .end span { font:500 11px/1 var(--sans); color:var(--fg-3); text-transform:uppercase; letter-spacing:0.06em; }
.split-track { flex:1; height:13px; border-radius:7px; overflow:hidden; display:flex; }
.split-track i { height:100%; }
.agegrid { display:grid; grid-template-columns:repeat(3,1fr); gap:30px; }
.agecol h5 { font:600 12px/1 var(--sans); letter-spacing:0.1em; text-transform:uppercase; color:var(--fg-3); margin:0 0 14px; }
.agerow { display:grid; grid-template-columns:42px 1fr 42px; align-items:center; gap:10px; margin-bottom:10px; }
.agerow:last-child { margin-bottom:0; }
.agerow .al { font:500 11px/1 var(--sans); color:var(--fg-3); }
.agerow .at { height:6px; border-radius:3px; background:var(--bg-3); overflow:hidden; }
.agerow .at i { display:block; height:100%; border-radius:3px; }
.agerow .av { font:600 11px/1 var(--sans); color:var(--fg-2); text-align:right; font-variant-numeric:tabular-nums; }

/* Scoped-CSS tabs (sponsored/threads/export) render inside the padded .main
   shell now - neutralize their own page padding (2026-06-12). */
.main .sa-wrap, .main .th-wrap, .main .es-wrap { padding: 0 0 40px; }

/* Report app owns the viewport while mounted - hide the host's restore banner
   (the upload-view chrome). Host pages add html.r2-active at V2 mount time. */
html.r2-active #restore-banner { display:none !important; }

/* Robert 2026-06-17: bring the global top nav BACK on the report. It used to
   be hidden here (full-screen dashboard), but Robert wants the familiar site
   nav present + a Sign-in path from inside the report. #site-nav is
   position:fixed (64px tall; 96px when the $5 bounty ribbon shows), so push
   the dashboard grid down and pin the sticky sidebar below it, and drop the
   sidebar's own wordmark — the top nav carries the brand + home link now, so
   two stacked "Opt2In" marks would read as a bug. Scoped to .r2-app so only
   the mounted report shell shifts (not /me/'s embedded snapshot, which has no
   r2-active and keeps its own chrome). Applies to BOTH IG + FB reports — they
   share this stylesheet and both mount V2_DiscoveryDash (Universal shell). */
html.r2-active .r2-app { padding-top:64px; }
html.r2-active .r2-app .side { top:64px; height:calc(100vh - 64px); }
html.r2-active .r2-app .side .brand { display:none; }
html.r2-active body.bounty-active .r2-app { padding-top:96px; }
html.r2-active body.bounty-active .r2-app .side { top:96px; height:calc(100vh - 96px); }
/* P7 2026-07-20: the injected "Get my report" nav CTA is a dead end while a
   report is already open (the viewer IS on their report). Hide just that
   button in-report; the rest of the global nav (home, sign-in) stays. */
html.r2-active #site-nav .nav-cta { display:none !important; }

/* The report lives only in memory (leaving/refresh means re-uploading the ZIP).
   overscroll-behavior-y:none disables the browser pull-to-refresh gesture while
   the report is mounted, so a stray downward flick at the top of a tab can no
   longer reload the page and wipe the whole report. Only scoped to r2-active so
   normal pages keep native pull-to-refresh. 2026-07-11. */
html.r2-active, html.r2-active body { overscroll-behavior-y:none; }

/* Save-report nudge sticks just BELOW the fixed 64px site nav (96px with the
   bounty ribbon), not at top:0 where the nav's z-100 covered its z-30 and hid
   the Save CTA + Dismiss the moment the user scrolled. Positioning lives here
   (not inline) so it can key off body.bounty-active. 2026-07-11. */
.v2-save-banner { position:sticky; top:64px; z-index:30; }
body.bounty-active .v2-save-banner { top:96px; }

/* ── (i) glossary tooltip (V2_InfoTip, defined in v2-discovery.jsx) ──────────
   Hover or keyboard-focus the dot next to a term (CPM, CPC, etc.) for a
   one-line definition. Defs live in V2_GLOSSARY so a term reads identically
   wherever it appears. Shared IG + FB report. Opens below the dot, centred,
   and right-aligns on narrow viewports so it can't run off-screen. */
.o2i-tip{position:relative;display:inline-flex;align-items:center;vertical-align:middle;margin-left:5px;cursor:help;outline:none}
.o2i-tip__i{width:14px;height:14px;border-radius:50%;border:1px solid var(--fg-3,#9a9590);color:var(--fg-3,#9a9590);font:italic 600 9px/1 'Inter',sans-serif;display:flex;align-items:center;justify-content:center;user-select:none}
.o2i-tip:hover .o2i-tip__i,.o2i-tip:focus .o2i-tip__i{border-color:var(--mint,#3DDDAA);color:var(--mint,#3DDDAA)}
.o2i-tip__pop{position:absolute;top:calc(100% + 7px);left:50%;transform:translateX(-50%);width:max-content;max-width:260px;padding:10px 12px;border-radius:8px;background:#1b1813;border:1px solid rgba(242,240,236,0.14);box-shadow:0 12px 32px rgba(0,0,0,0.5);font:400 12px/1.5 'Inter',sans-serif;color:#d8d4ce;text-align:left;white-space:normal;z-index:60;opacity:0;visibility:hidden;transition:opacity .12s,visibility .12s;pointer-events:none}
.o2i-tip__pop b{color:#fff;font-weight:600}
.o2i-tip:hover .o2i-tip__pop,.o2i-tip:focus .o2i-tip__pop,.o2i-tip:focus-within .o2i-tip__pop{opacity:1;visibility:visible}
@media(max-width:680px){.o2i-tip__pop{max-width:210px;left:auto;right:0;transform:none}}

/* Shared quote card (V2_QuoteCard) — used by Sponsored ads (ad copy) and
   Interests/creators (creator captions). One definition so they can't drift. */
.v2-quote-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:14px;margin-top:14px}
.v2-quote-card{background:#15130f;border:1px solid rgba(255,255,255,0.06);border-radius:12px;padding:16px 18px;display:flex;flex-direction:column;gap:10px;transition:border-color .2s ease}
.v2-quote-card:hover{border-color:rgba(61,221,170,0.3)}
.v2-qh{display:flex;align-items:center;gap:10px}
.v2-qf{width:30px;height:30px;border-radius:8px;background:rgba(255,255,255,0.05);object-fit:cover;flex-shrink:0}
.v2-qn{font-family:'Cormorant Infant',serif;font-size:1.1rem;font-weight:600;color:#fff;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.v2-qhandle{font:500 11px/1 'Inter',sans-serif;color:#6b665e;margin-top:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.v2-qtext{margin:4px 0 0;color:#d8d4ce;font:italic 400 14px/1.55 'Inter',sans-serif;border-left:2px solid #3DDDAA;padding-left:12px;display:-webkit-box;-webkit-line-clamp:4;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere}
.v2-qlinks{display:flex;gap:16px;margin-top:auto;padding-top:6px}
.v2-qlink{font:600 12px/1 'Inter',sans-serif;color:#3DDDAA;text-decoration:none}
.v2-qlink:hover{text-decoration:underline}

/* Inline disclosure used to collapse long tables/lists behind a toggle. */
.v2-collapse{margin-top:12px}
.v2-collapse>summary{cursor:pointer;list-style:none;display:inline-flex;align-items:center;gap:7px;font:600 12px/1 'Inter',sans-serif;color:var(--mint,#3DDDAA);padding:8px 0;user-select:none}
.v2-collapse>summary::-webkit-details-marker{display:none}
.v2-collapse>summary::before{content:"+";font-weight:700;font-size:13px}
.v2-collapse[open]>summary::before{content:"\2013"}
.v2-collapse[open]>summary{margin-bottom:8px}

/* ── Tablet: 3-up KPI/stat grids drop to 2 columns ─────────────────────── */
@media (min-width:761px) and (max-width:1100px){
  .grid3 { grid-template-columns:1fr 1fr !important; }
  .grid4 { grid-template-columns:1fr 1fr !important; }
}

/* (M2 mobile section-bar + bottom-sheet CSS removed 2026-07-18 with the
   feature — the report's phone nav is the horizontal tab strip again.) */
/* Save-banner sub-line (the browser-only "back/refresh means re-upload" warning):
   visible on ALL widths (2026-07-18) so the amber Overview notice folds on every
   width, not just phones — otherwise a signed-in desktop user saw both bars. */
.v2-save-banner-sub { display:block; margin-top:4px; font:400 11.5px/1.5 'Inter',sans-serif; color:var(--fg-3); }

/* M3 helpers: phone/desktop-only wrappers for chart shape swaps. Wrappers
   only — never put an inline display style on an element carrying these. */
.v2-phone-only { display:none; }

/* Floating "Create shareable card" pill (2026-07-18, Robert). A pop-up like the
   offers pill: take-action colored (the brand gradient + dark ink, matching the
   sidebar "Take action" CTA), a label button that builds the card + an × to
   dismiss. Fixed top-right on desktop (in the gap above the content header);
   moved DOWN on phones so it clears the stacked sidebar/nav strip. Rendered at
   the report shell level so it floats over every tab; the offers pill
   (report/instagram/index.html) stacks just under it. */
/* Bottom-anchored (Robert 2026-07-18: "move the pop ups to the bottom of the
   page" — the top placement kept landing mid-screen once the save banner pushed
   it down). Sits above the offers pill (index.html, bottom:20px); safe-area
   inset keeps it clear of the iPhone home bar in the installed app. No banner
   coupling needed down here — the save banner lives at the top. */
.v2-share-pill{ position:fixed; bottom:calc(80px + env(safe-area-inset-bottom, 0px)); right:20px; z-index:9997;
  display:inline-flex; align-items:center; gap:3px; padding:4px 6px 4px 4px; border-radius:999px;
  background:var(--grad); box-shadow:0 14px 34px -12px rgba(0,0,0,0.8); }
.v2-share-pill__act{ display:inline-flex; align-items:center; gap:8px; padding:8px 12px; border:none;
  background:none; color:#0f0e0c; font:700 13px/1 'Inter',system-ui,sans-serif; cursor:pointer;
  border-radius:999px; white-space:nowrap; }
.v2-share-pill__act:disabled{ opacity:0.65; cursor:default; }
.v2-share-pill__act svg{ stroke:#0f0e0c; }
.v2-share-pill__x{ position:relative; display:inline-flex; align-items:center; justify-content:center; width:24px; height:24px;
  border:none; border-radius:50%; background:rgba(15,14,12,0.16); color:#0f0e0c;
  font:700 17px/1 'Inter',system-ui,sans-serif; cursor:pointer; flex:none; }
/* Expand the tap zone toward the screen edge to ~44px (Apple HIG) without
   growing the 24px visual chip, so a missed dismiss doesn't fire the action
   next to it (2026-07-18). Left inset 0 so it never overlaps the act button. */
.v2-share-pill__x::before{ content:""; position:absolute; inset:-10px -10px -10px 0; }
.v2-share-pill__x:hover{ background:rgba(15,14,12,0.28); }
/* No bounty-ribbon / mobile top overrides anymore: the ribbon and the save
   banner are both TOP chrome, and the pills are bottom-anchored on every width. */
/* Both floating pills hide while a save/share modal is open (2026-07-18): the
   report shell + V2_ShareReceipt toggle body.o2i-modal-open, so neither pill
   floats, still-clickable, over the dimmed scrim during the $5 flow. */
body.o2i-modal-open .v2-share-pill,
body.o2i-modal-open #opt2in-offers-pill { display:none !important; }
/* Scroll-away (F1, 2026-07-20): both pills tuck down + fade while the reader is
   scrolling DOWN, and slide back on scroll-up or once scrolling stops, so they
   stop covering list rows the whole way down the report. The report shell
   (V2_DiscoveryDash) toggles body.o2i-pills-down from a capture-phase scroll
   listener; the offers pill (index.html) sets no inline transform/opacity, so
   these rules reach it too. */
.v2-share-pill,
#opt2in-offers-pill { transition: transform .28s ease, opacity .28s ease; }
body.o2i-pills-down .v2-share-pill,
body.o2i-pills-down #opt2in-offers-pill { transform:translateY(16px); opacity:0; pointer-events:none; }

/* ── Mobile / phone layout (2026-06-29): the V2 report dashboard was built
   desktop-first (fixed 256px sidebar + multi-column grids, no media queries).
   Below 760px the shell collapses to a single column, the sidebar becomes a
   compact top section with a horizontal-scrolling nav, and every multi-column
   grid / wide table reflows. !important is required where tabs set
   grid-template-columns via inline style (which a stylesheet can't otherwise
   override). Scoped mostly to .r2-app so /me/'s embedded snapshot is untouched. */
@media (max-width:760px){
  /* Shell: sidebar leaves the grid and stacks above the content. */
  .app { grid-template-columns:1fr; }
  .side { position:static; height:auto; width:auto; flex-direction:column; gap:10px;
          padding:12px 14px; border-right:none; border-bottom:1px solid var(--line-1); }
  /* Clear the fixed 64px #site-nav (96px with the bounty ribbon). Mobile
     previously zeroed the nav offset, which hid the tab strip UNDER the fixed
     nav (2026-07-08 fix). */
  html.r2-active .r2-app { padding-top:64px; }
  html.r2-active body.bounty-active .r2-app { padding-top:96px; }
  html.r2-active .r2-app .side,
  html.r2-active body.bounty-active .r2-app .side { top:auto; height:auto; }
  /* Trim secondary chrome so the mobile header stays short. */
  .side .userpill, .side .glance { display:none; }
  /* Horizontal tab strip on phones (restored 2026-07-18, Robert: "bar on top,
     not a dropdown"). The M2 section-bar + bottom-sheet that replaced it is
     gone; the strip's row styling is just below. */
  .nav-group { display:none; }
  /* 44px min tap height (Apple HIG floor) for any surface still showing pills
     (e.g. the /me/ snapshot embed keeps its in-flow nav). */
  .side nav:not(#site-nav) { flex-direction:row; flex-wrap:nowrap; overflow-x:auto;
              gap:6px; flex:0 0 auto; min-height:0; min-width:0; padding-bottom:4px; -webkit-overflow-scrolling:touch;
              overscroll-behavior-x:contain;
              /* padding-right lets the LAST tab scroll clear of the right-edge fade
                 mask so it isn't permanently ghosted/cut off (2026-07-18). */
              padding-right:36px;
              -webkit-mask-image:linear-gradient(to right,#000 90%,transparent);
              mask-image:linear-gradient(to right,#000 90%,transparent); }
  .nav-item { white-space:nowrap; padding:11px 13px; font-size:14px; flex:0 0 auto; min-height:44px; }
  /* P8 2026-07-20: on phones show the short label for multi-word tabs so the
     strip stays compact; desktop keeps the full label (.nav-short hidden by
     the base rule below). */
  .nav-item.has-short .nav-full { display:none; }
  .nav-item.has-short .nav-short { display:inline; }
  /* P1 2026-07-20: right-edge fade + trailing space on the horizontally-
     scrolling By Industry table so the off-screen Share column reads as
     "swipe for more" instead of a hard clip (mirrors the tab strip). */
  .tbl-scroll { padding-right:32px;
                -webkit-mask-image:linear-gradient(to right,#000 92%,transparent);
                mask-image:linear-gradient(to right,#000 92%,transparent); }
  .side-foot { border-top:none; padding-top:2px; }
  /* (.v2-save-banner-sub is now a base rule, visible on all widths — 2026-07-18.) */
  /* M2 Overview de-tower: KPI tiles go 2-up with clean faces — number, label,
     and the tab link stay; evidence/window chips + the decorative bar live on
     the tab itself. Double class beats the later `.grid2,.grid3 { 1fr }`
     collapse rule (same specificity would lose on source order). */
  .grid3.v2-kpi-grid { grid-template-columns:1fr 1fr !important; gap:10px; }
  .v2-kpi-grid .kpi { padding:12px 13px; gap:7px; }
  .v2-kpi-grid .kpi .chip { display:none; }
  .v2-kpi-grid .kpi-bar { display:none; }
  .v2-kpi-grid .kpi-num { font-size:26px; }
  /* Foot's only child (the win chip) is hidden on phones, so hide the empty foot
     too - otherwise it stays a flex item and leaves ~7px of dead gap below each
     tile now that .kpi-go is gone (QA 2026-07-22). */
  .v2-kpi-grid .kpi-foot { display:none; }
  /* M3 shape-swap helpers (phones). */
  .v2-phone-only { display:block; }
  .v2-desk-only { display:none; }

  /* Content padding scales down; clip any residual horizontal overflow so the
     page never scrolls sideways (wide tables keep their own scroll wrappers). */
  .main { padding:18px 16px 120px; overflow-x:hidden; } /* bottom pad = chat-FAB (bottom:72px) clearance so the tab pager isn't covered at scroll-end (code-review 2026-07-22) */
  /* The sticky save nudge can't stick on phones: overflow-x:hidden above makes
     .main the banner's sticky scrollport, and .main itself never scrolls, so
     the top:64px offset just shoved the banner ~48px DOWN over whatever sits
     first on every tab (on Overview, the Back/refresh notice — the 2026-07-17
     overlapping-banners bug). Static keeps it in normal flow; on a phone a
     permanently pinned ~140px banner would eat a third of the screen anyway. */
  .v2-save-banner { position:static; }
  /* 16px is the floor that stops iOS Safari auto-zooming the page on focus.
     Covers the Overview chat, every per-tab chat (shared .chat-input), and the
     advertiser search. Desktop keeps its compact sizes. 2026-07-11. */
  .chat-input input { min-width:0; font-size:16px; }
  .search input { font-size:16px; }
  /* Pagination arrows/numbers were 30px; give them a real tap size and let the
     pager wrap instead of clipping a long advertiser page list. */
  .pager { flex-wrap:wrap; }
  .pg { width:40px; height:40px; }
  /* minmax(0,…) so a long unbroken caption token can't force the single
     column past the viewport (paired with overflow-wrap on .v2-qtext). B3. */
  .v2-quote-grid { grid-template-columns:minmax(0,1fr) !important; }

  /* Tab header stacks (title over window chips). */
  .topbar { flex-direction:column; align-items:flex-start; gap:10px; }
  .topbar h1 { font-size:26px; line-height:1.2; }
  .winset { flex-wrap:wrap; }
  /* Card/section heads do the same — otherwise an unshrinkable window chip
     ("17 AD TAPS · LAST 17 DAYS") crushes the serif title into 3 lines. B5. */
  .ex-head { flex-direction:column; align-items:flex-start; gap:10px; }

  /* Card + stat grids collapse to one column. */
  .grid2, .grid3, .grid4 { grid-template-columns:1fr !important; }
  .agegrid { grid-template-columns:1fr !important; gap:16px; }

  /* Bar lists: label on its own row above a full-width track. */
  .bar { grid-template-columns:1fr 56px; }
  .bar .bar-track { grid-column:1 / -1; }
  /* Pin the value beside the label on row 1; without this the auto-placement
     cursor orphans it onto its own row below the track. B4. */
  .bar .bar-v { grid-row:1; grid-column:2; }

  /* Wide tables pan horizontally instead of crushing/overflowing the page. */
  .main .tbl td, .main .tbl th { padding-left:9px; padding-right:9px; }

  /* Donut + gender-split layouts wrap. */
  .donut-wrap, .splitbar { flex-wrap:wrap; }

  /* Fixed-width widgets fit the viewport. */
  .till { width:100%; max-width:380px; }
  .search { width:100%; max-width:100%; }

  /* Inline-grid surfaces given classes in the JSX so they can collapse here. */
  .ribbon { grid-template-columns:1fr !important; }
  .v2-2col { grid-template-columns:1fr !important; }
  .att-scroll-grid { grid-template-columns:1fr !important; gap:20px !important; }
  .math-receipt-grid { grid-template-columns:1fr !important; gap:20px !important; }
  /* (.v2-score-band mobile override removed 2026-07-20: the score card is now
     an inline flex column spaced by its children's own margins — the old
     grid-template-columns + gap:18px !important was left over from the deleted
     two-column gauge layout and stacked ~18px onto every seam on phones.) */
}

/* ── L13: tab pager + one-time flip-through coach-mark (2026-07-22) ─────────── */
.v2-pager { margin:30px 0 6px; }
.v2-pager-row { display:flex; align-items:center; justify-content:space-between; gap:12px; padding-top:18px; border-top:1px solid var(--line-1); }
.v2-pager-spacer { flex:0 0 auto; }
.v2-pager-count { font:600 11px/1 var(--sans); letter-spacing:.08em; text-transform:uppercase; color:var(--fg-3); white-space:nowrap; }
.v2-pager-btn { font:600 13px/1 var(--sans); border-radius:9px; padding:11px 15px; cursor:pointer; text-decoration:none; border:1px solid var(--line-2); background:var(--bg-1); color:var(--fg-1); transition:border-color 150ms, background 150ms, transform 150ms; white-space:nowrap; }
.v2-pager-btn:hover { border-color:var(--purple); background:var(--bg-2); }
.v2-pager-prev { color:var(--fg-2); }
.v2-pager-cta { background:var(--brand-gradient, linear-gradient(120deg, var(--purple), var(--mint))); border:none; color:#0f0e0c; font-weight:700; }
.v2-pager-pulse { animation:v2PagerPulse 1.4s ease-in-out 2; }
@keyframes v2PagerPulse { 0%,100%{transform:none} 50%{transform:translateY(-2px)} }
.v2-coach { display:flex; align-items:center; gap:10px; justify-content:center; margin:0 auto 4px; max-width:540px; padding:10px 14px; border-radius:10px; background:rgba(107,106,240,0.10); border:1px solid rgba(107,106,240,0.30); animation:v2CoachIn .4s ease; }
.v2-coach-txt { font:500 13px/1.4 var(--sans); color:var(--fg-1); }
.v2-coach-arrow { color:var(--purple); font-weight:700; animation:v2CoachBounce 1.2s ease-in-out infinite; }
.v2-coach-x { background:none; border:none; color:var(--fg-3); cursor:pointer; font-size:16px; line-height:1; padding:2px 5px; margin-left:auto; }
@keyframes v2CoachBounce { 0%,100%{transform:translateY(0)} 50%{transform:translateY(3px)} }
@keyframes v2CoachIn { from{opacity:0; transform:translateY(-4px)} to{opacity:1; transform:none} }

/* ── L14: "Ask the analyst" button (replaces the inline summary chat) ──────── */
.v2-ask-analyst { display:inline-flex; align-items:center; gap:4px; margin-top:14px; padding:9px 15px; border-radius:9px; border:1px solid var(--line-2); background:var(--bg-2); color:var(--fg-1); font:600 13px/1 var(--sans); cursor:pointer; transition:border-color 150ms, background 150ms; }
.v2-ask-analyst:hover { border-color:var(--purple); background:rgba(107,106,240,0.08); }

/* ── L14: floating AI-analyst chat dock (launcher FAB + expandable panel) ──── */
.v2-chat-fab { position:fixed; right:20px; bottom:calc(78px + env(safe-area-inset-bottom)); z-index:9996; display:inline-flex; align-items:center; gap:8px; padding:11px 16px; border-radius:999px; border:1px solid var(--purple); background:var(--bg-1); color:var(--fg-1); font:600 13px/1 var(--sans); cursor:pointer; box-shadow:0 6px 22px rgba(0,0,0,0.45); transition:transform 150ms, background 150ms; }
.v2-chat-fab:hover { transform:translateY(-2px); background:var(--bg-2); }
.v2-chat-fab svg { width:16px; height:16px; stroke:var(--purple); fill:none; stroke-width:2; }
.v2-chat-dock { position:fixed; right:20px; bottom:calc(20px + env(safe-area-inset-bottom)); z-index:9999; width:min(400px, calc(100vw - 40px)); height:min(560px, calc(100vh - 110px)); display:flex; flex-direction:column; background:var(--bg-1); border:1px solid var(--line-2); border-radius:16px; box-shadow:0 14px 44px rgba(0,0,0,0.55); overflow:hidden; }
.v2-chat-head { display:flex; align-items:center; justify-content:space-between; padding:14px 16px; border-bottom:1px solid var(--line-1); flex:0 0 auto; }
.v2-chat-title { font:600 16px/1 var(--serif); color:var(--fg-1); }
.v2-chat-close { background:none; border:none; color:var(--fg-3); font-size:22px; line-height:1; cursor:pointer; padding:0 4px; }
.v2-chat-scroll { flex:1 1 auto; overflow-y:auto; overscroll-behavior:contain; padding:16px; display:flex; flex-direction:column; gap:12px; }
.v2-chat-intro p { font:400 13px/1.6 var(--sans); color:var(--fg-2); margin:0 0 12px; }
.v2-chat-msg { font:400 13.5px/1.6 var(--sans); padding:10px 13px; border-radius:12px; max-width:88%; white-space:pre-wrap; word-break:break-word; }
.v2-chat-msg--user { align-self:flex-end; background:var(--purple); color:#fff; border-bottom-right-radius:4px; }
.v2-chat-msg--assistant { align-self:flex-start; background:var(--bg-2); color:var(--fg-1); border:1px solid var(--line-1); border-bottom-left-radius:4px; }
.v2-chat-msg--error { align-self:flex-start; background:rgba(248,113,113,0.10); color:var(--red); border:1px solid rgba(248,113,113,0.30); border-radius:12px; }
.v2-chat-foot { flex:0 0 auto; padding:12px; border-top:1px solid var(--line-1); }
body.o2i-modal-open .v2-chat-fab, body.o2i-modal-open .v2-chat-dock { display:none !important; }
@media (max-width:760px) {
  .v2-chat-dock { right:8px; left:8px; width:auto; bottom:calc(8px + env(safe-area-inset-bottom)); height:min(72vh, 540px); }
  .v2-chat-fab { right:14px; bottom:calc(72px + env(safe-area-inset-bottom)); padding:12px; }
  .v2-chat-fab-txt { display:none; }
}
