/* ─────────────────────────────────────────────────────────────────────────
   newdesign1 - "Airy editorial"
   Plausible-clean redesign of the client analytics dashboard. Generous
   whitespace, one hero chart, a calm Top-N grid, a single orange accent.
   Self-contained: reuses the Maileroo tokens verbatim, self-hosts the brand
   font from /fonts (zero external requests), light-only.
   ───────────────────────────────────────────────────────────────────────── */

/* The sitewide default is Plus Jakarta Sans (self-hosted, @font-face below), with the native system-UI
   stack kept as the fallback so text still renders before the woff2 loads (font-display: swap) or if the
   fetch fails. The other @font-face rules are lazy alternates for the hidden "dev" panel; none fetch
   until --font-sans names them. */

/* The default face + the alternate typefaces offered by the hidden "dev" panel (type d-e-v): Plus
   Jakarta Sans (the default) plus Figtree/Sora/Gabarito/Onest/Nacelle. All self-hosted, all OFL 1.1,
   zero external requests. Figtree/Sora/Gabarito/Onest and Plus Jakarta ship one variable face each;
   Nacelle only publishes static weights, so it takes two @font-face rules. */
@font-face { font-family: 'Figtree';  font-style: normal; font-weight: 300 900; font-display: swap; src: url('fonts/figtree-var.woff2') format('woff2'); }
@font-face { font-family: 'Plus Jakarta Sans'; font-style: normal; font-weight: 200 800; font-display: swap; src: url('fonts/PlusJakartaSans-Variable.woff2') format('woff2'); }
@font-face { font-family: 'Sora';     font-style: normal; font-weight: 100 800; font-display: swap; src: url('fonts/sora-var.woff2') format('woff2'); }
@font-face { font-family: 'Gabarito'; font-style: normal; font-weight: 400 900; font-display: swap; src: url('fonts/gabarito-var.woff2') format('woff2'); }
@font-face { font-family: 'Onest';    font-style: normal; font-weight: 100 900; font-display: swap; src: url('fonts/onest-var.woff2') format('woff2'); }
@font-face { font-family: 'Nacelle';  font-style: normal; font-weight: 400; font-display: swap; src: url('fonts/nacelle-400.woff2') format('woff2'); }
@font-face { font-family: 'Nacelle';  font-style: normal; font-weight: 700; font-display: swap; src: url('fonts/nacelle-700.woff2') format('woff2'); }

:root {
  /* Plus Jakarta Sans (self-hosted variable face) leads; the native system-UI stack follows as the
     pre-load / no-fetch fallback so nothing renders unstyled. */
  --font-sans: 'Plus Jakarta Sans', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
  /* Neutrals + semantics from Apple HIG system colours; the two blues are palette 3598. */
  --ink: #1c1c1e;
  --ink-2: #3c3c43;
  --muted: #6c6c70;
  --muted-2: #8e8e93;
  --line: #e5e5ea;
  --line-soft: #f2f2f7;
  --canvas: #f2f2f7;
  --surface: #ffffff;
  --primary: #0070ff;
  --primary-soft: #e6f1ff;
  --primary-tint: #cfe4ff;
  --good: #248a3d;
  --good-soft: #e3f9e9;
  --bad: #d70015;
  --bad-soft: #fde7e9;
  --c1: #0070ff;
  --c2: #00a4ff;
  --c3: #5856d6;
  --c4: #ff2d55;
  --c5: #8e8e93;
  --c6: #34c759;
  --shadow: 0 4px 24px -14px rgba(60, 60, 67, 0.28);
  --radius: 14px;
}

/* ── Dark mode (Appearance -> Dark mode; the class rides on <html>, persisted as
   ws_theme, re-applied pre-paint by hydrate.js) ──
   This used to be a hand-picked navy-slate palette: ~60 token overrides plus a tail of
   per-component fixes for every rule that hardcoded a raw colour. It is now the same
   mechanism the admin console uses, so the two panels agree.

   Stripe's dashboard, which this app is skinned from, has no dark mode to copy: every
   saved page pins `color-scheme: light` and carries no prefers-color-scheme block, no
   data-theme hook and no dark-named custom property. The only Stripe dark mode that
   exists is the Stripe Dark Mode browser extension (ddjigdjplfeajnddnggjgnaelaadcclg),
   and it ships no colour values either - its whole mechanism is `filter: invert(1)` on
   the root with anything that carries its own colour inverted a second time back. That
   is what is below, so dark mode introduces no colours of its own: every token stays
   exactly as it is in light and is simply viewed inverted, and no per-component fix is
   needed because a hardcoded colour inverts the same as a token does.

   html.dark (0,1,1) outranks :root (0,1,0), so it wins regardless of source order. */
/* Carbon: a hand-built dark palette, NOT the light theme inverted. Every colour token below is
   redefined to a Carbon value and the components read the tokens, so the whole app follows without
   per-component rewrites. html.dark (0,1,1) outranks the :root skin blocks (0,1,0), so these win
   regardless of source order. color-scheme: dark so native controls (scrollbars, form widgets,
   autofill, date pickers) paint dark too, and images/charts keep their own real colours. */
html.dark {
  color-scheme: dark;

  /* grounds - Dim: a softer blue-grey slate, one step up from Carbon's near-black */
  --bg:#1b1f27; --canvas:#1b1f27; --card:#232833; --surface:#232833;
  --sand:#2b313d; --tint:#2b313d; --primary-soft:#2b313d;

  /* text - eased off pure white so the lifted ground stays low-glare */
  --ink:#c6cdd9; --ink-h1:#e2e7ef; --ink-2:#a6adbc; --body-ink:#a6adbc;
  --label:#9aa2b1; --muted:#7e8796; --muted-2:#656e7d; --ink-button:#c6cdd9;

  /* lines / rings (white at low alpha reads as a hairline on any dark surface) */
  --line:rgba(255,255,255,0.06); --line-soft:rgba(255,255,255,0.04);
  --line-hover:rgba(255,255,255,0.11); --primary-tint:rgba(255,255,255,0.11);
  --ring-light:rgba(255,255,255,0.06) 0 0 0 1px;
  --ring-mid:rgba(255,255,255,0.11) 0 0 0 1px;
  --ring-brand:rgb(143,134,240) 0 0 0 1.5px;
  --shadow:rgba(255,255,255,0.06) 0 0 0 1px;
  --shadow-pop:rgba(0,0,0,0.5) 0 1px 2px 0,rgba(255,255,255,0.08) 0 0 0 1px,rgba(0,0,0,0.45) 0 8px 24px -8px;

  /* accent - Dim lavender, softened for the lighter ground */
  --accent:#8f86f0; --accent-rgb:143,134,240; --accent-soft:rgba(143,134,240,0.15);
  --accent-ico:#a9a1f5; --violet:#a9a1f5; --violet-rgb:169,161,245; --icon-idle:#7e8796;
  --primary:#8f86f0;

  /* semantics */
  --good:#5cc98d; --good-soft:rgba(92,201,141,0.14); --ok:#5cc98d; --ok-rgb:92,201,141;
  --green:#5cc98d; --green-rgb:92,201,141;
  --bad:#ef8a90; --bad-soft:rgba(239,138,144,0.14); --coral:#ef8a90;
  --amber:#e0a13a; --amber-bg:rgba(224,161,58,0.16); --wait:#9a9da6; --wait-rgb:154,157,166;

  /* nav / sidebar - a shade deeper than the canvas so the rail reads as a distinct plane */
  --nav:#191d24; --nav-2:#232833; --nav-ink:#c6cdd9; --nav-muted:#7e8796; --nav-line:rgba(255,255,255,0.06);

  /* chart categoricals - eased to sit with the softer Dim ground while still separating */
  --c1:#7c9dff; --c2:#4fc3e8; --c3:#a99cf0; --c4:#f07f95; --c5:#9aa2b1; --c6:#5cc98d;
}

/* Per-component dark fixes: the few rules that hardcode a light colour instead of reading a token,
   so they cannot follow the palette above. Warnings keep an amber identity; the danger/hover tints
   move to a dark wash. Everything else flips through the tokens. */
html.dark .help-banner:not(.hb-bad){background:#241f14;color:#e6b45a}
html.dark .h-warn{background:rgba(239,138,144,0.14);color:#ef8a90}
html.dark .tag.warn{background:rgba(224,161,58,0.16);color:#e6b45a}
html.dark .btn.btn-danger:hover{background:rgba(239,138,144,0.12)}
html.dark .nav-b:hover::before{background:rgba(143,134,240,0.14)}

* , *::before, *::after { box-sizing: border-box; }

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

body {
  margin: 0;
  font-family: var(--font-sans);
  background: var(--canvas);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  line-height: 1.5;
}

a { color: inherit; text-decoration: none; }

/* ── Impersonation banner ──
   Operator chrome, JS-injected at the top of the body when /api/auth/me reports impersonatedBy. It
   deliberately sits outside the app's own visual language (dark bar, no orange) so it can never be
   read as part of the client's UI. Plain document flow: the shell is not fixed, so the bar pushes the
   page down without a body offset.

   Its own tokens, NOT --ink: --ink is the text colour, so in dark mode the bar painted itself
   #c6cdd9 and kept white text on it. Dark mode gets a ground below the canvas plus a hairline, which
   keeps the strip unmistakable without the glare of a light bar in a low-glare theme. */
.imp-banner {
  --imp-bg: #1c1c1e;
  --imp-ink: #fff;
  --imp-line: rgba(255, 255, 255, 0.35);
  --imp-hover: rgba(255, 255, 255, 0.12);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 14px;
  padding: 9px 20px;
  background: var(--imp-bg);
  color: var(--imp-ink);
  font-size: 13px;
  font-weight: 600;
}
html.dark .imp-banner {
  --imp-bg: #10131a;
  --imp-ink: #e2e7ef;
  --imp-line: rgba(255, 255, 255, 0.28);
  --imp-hover: rgba(255, 255, 255, 0.1);
  box-shadow: inset 0 -1px 0 rgba(255, 255, 255, 0.14);
}
.imp-exit {
  border: 1px solid var(--imp-line);
  border-radius: 8px;
  background: transparent;
  color: inherit;
  font: inherit;
  padding: 3px 10px;
  cursor: pointer;
}
.imp-exit:hover { background: var(--imp-hover); }
/* Collapse control: shrinks the bar to a slim strip (Exit stays reachable). State persists in
   ws_imp_collapsed so it survives SPA hops and reloads within the impersonated session. */
.imp-mini { display: none; font-weight: 600; opacity: 0.85; }
.imp-collapse {
  display: inline-flex;
  align-items: center;
  border: 0;
  background: transparent;
  color: inherit;
  cursor: pointer;
  padding: 2px 6px;
  border-radius: 6px;
  opacity: 0.8;
}
.imp-collapse:hover { background: var(--imp-hover); opacity: 1; }
.imp-collapse svg { transition: transform 0.15s ease; }
.imp-banner.collapsed { padding: 4px 16px; gap: 10px; }
.imp-banner.collapsed .imp-who { display: none; }
.imp-banner.collapsed .imp-mini { display: inline; }
.imp-banner.collapsed .imp-collapse svg { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) { .imp-collapse svg { transition: none; } }

/* ── Shell ──
   The redesign owns `.wrap` and `.top` (sidebar shell, further down this file). The pre-redesign
   `.container`-style `.wrap` and the old tab-bar `.top` are gone: they only leaked stray properties
   (margin:0 auto mis-centred the column, justify-content:space-between fought the topbar layout). */
.brand {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}
/* The logo (mark + wordmark) is a link back to the client root. Inline so it sits inside .brand
   next to the site switcher without changing the row layout; only the logo itself is clickable. */
.brand-home {
  display: inline-flex; align-items: center; gap: 12px; min-width: 0;
  border-radius: 8px; color: inherit;
}
.brand-home:hover .brand-name { color: var(--primary); }
.brand-home:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--primary-soft); }
.brand-mark {
  width: 30px; height: 30px;
  border-radius: 8px;
  background: var(--primary);
  display: grid; place-items: center;
  flex: none;
}
.brand-name { font-weight: 700; font-size: 1.02rem; letter-spacing: -0.01em; }
/* site switcher (the slim property pill, made pickable) */
.site { min-width: 0; }
/* The site switcher = the artifact's .switcher pill: same white-pill/shadow as the
   .dd-btn triggers (inherited), a touch lighter weight, and a green live dot. */
.site .site-btn {
  gap: 9px; font-size: 0.88rem; font-weight: 600; padding: 8px 14px; max-width: 230px;
}
.site .site-btn:hover { border-color: var(--line-hover); }
.site-btn .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--green); flex: none; }
.site-host { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ── Dropdown primitive (site / country / range / account) ── */
.controls { display: inline-flex; align-items: center; gap: 8px; }
/* syncNav sets [hidden] on #filters for the Settings/Visitor surfaces, where the
   country/range/answers cluster is meaningless. The class display above beats the UA
   [hidden] rule, so honour the intent explicitly (same idiom as .cc-row/.rng-custom). */
.controls[hidden] { display: none; }
.dd { position: relative; }
/* Trigger pills - copied verbatim from the artifact's .ctrl-b so the site / country /
   range selectors read as one uniform white pill with a soft shadow (they were a flat,
   mismatched set: 10px rounded-rect, borderless range, slim site). The open state draws
   the artifact's blue accent ring. */
.dd-btn {
  display: inline-flex; align-items: center; gap: 9px;
  font: inherit; font-size: 0.86rem; font-weight: 700; color: var(--ink);
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius-pill); padding: 8px 13px; cursor: pointer; white-space: nowrap;
  box-shadow: var(--shadow);
}
.dd-btn:hover { border-color: var(--line-hover); }
.dd.open > .dd-btn { border-color: var(--accent); box-shadow: 0 0 0 4px var(--accent-soft); }
.dd-caret { color: var(--muted); font-size: 0.6rem; flex: none; transition: transform .18s ease; }
.dd.open > .dd-btn .dd-caret { transform: rotate(180deg); }
.dd-ico { display: inline-flex; color: var(--label); }
.dd-menu {
  position: absolute; left: 0; top: calc(100% + 6px);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 12px; box-shadow: var(--shadow);
  padding: 6px; min-width: 190px; z-index: 40; display: none;
}
.dd-menu-r { left: auto; right: 0; }
.dd.open .dd-menu { display: block; }
.dd-menu button, .dd-link {
  display: block; width: 100%; text-align: left;
  font: inherit; font-size: 0.83rem; font-weight: 600; color: var(--ink-2);
  background: none; border: 0; border-radius: 8px; padding: 8px 10px; cursor: pointer;
}
.dd-menu button:hover, .dd-link:hover { background: var(--line-soft); }
.dd-menu button[aria-current="true"] { color: var(--accent); background: var(--accent-soft); }
.dd-fields { display: flex; flex-direction: column; gap: 8px; padding: 8px 6px 4px; }
.dd-fields[hidden] { display: none; }
.dd-fields label { display: flex; justify-content: space-between; align-items: center; gap: 10px; font-size: 0.76rem; font-weight: 700; color: var(--muted); }
.dd-fields input {
  font: inherit; font-size: 0.8rem; font-weight: 600; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line); border-radius: 8px; padding: 6px 8px;
}
.dd-apply { text-align: center !important; color: #fff !important; background: var(--accent) !important; font-weight: 700 !important; }
.dd-apply:hover { filter: brightness(0.96); }

/* ── Time-range picker (Plausible's grouped period sheet in the artifact's pill) ──
   The trigger is now the artifact's .ctrl-b pill (white, bordered, soft shadow, blue
   open ring) so it matches the site + country selectors; the prev/next period arrows
   for the day/month/year presets sit inside the pill on the right. The grouped 224px
   sheet keeps its single-key hints; the active row reads in the blue accent (matching
   the country dropdown) rather than the old orange. Scoped under .range-trigger /
   .range-menu so they out-specify the shared .dd-btn / .dd-menu primitives. */
.range-trigger {
  display: inline-flex; align-items: center;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius-pill); box-shadow: var(--shadow); padding-right: 4px;
}
.range-trigger:hover { border-color: var(--line-hover); }
.range-dd.open .range-trigger { border-color: var(--accent); box-shadow: 0 0 0 4px var(--accent-soft); }
.range-btn {
  display: inline-flex; align-items: center; gap: 9px;
  font: inherit; font-size: 0.86rem; font-weight: 700; color: var(--ink);
  background: none; border: 0; border-radius: var(--radius-pill); padding: 8px 4px 8px 13px; cursor: pointer; white-space: nowrap;
}
.range-dd.open .range-btn .dd-caret { transform: rotate(180deg); }
.range-cal { display: inline-flex; color: var(--label); }
.range-cur { line-height: 1.2; }
.range-arrows { display: inline-flex; align-items: center; padding-right: 4px; }
.range-arrow {
  display: inline-flex; align-items: center; justify-content: center;
  height: 24px; width: 20px; padding: 0; border: 0; background: none; border-radius: 6px;
  color: var(--ink-2); cursor: pointer;
}
.range-arrow:hover:not(:disabled) { background: var(--line); }
.range-arrow:disabled { color: var(--muted-2); cursor: not-allowed; }
.range-menu { min-width: 224px; padding: 4px; border-radius: 8px; }
.dd.open .range-menu { display: flex; flex-direction: column; gap: 2px; }
.range-menu .range-item {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  width: 100%; text-align: left;
  font: inherit; font-size: 0.83rem; font-weight: 500; line-height: 1.2; color: var(--ink-2);
  background: none; border: 0; border-radius: 7px; padding: 9px 14px; cursor: pointer;
}
.range-menu .range-item:hover { background: var(--line-soft); color: var(--ink); }
.range-menu .range-item.on { color: var(--accent); font-weight: 700; }
.range-menu .range-item.on .range-kbd { color: var(--accent); border-color: rgba(var(--accent-rgb), 0.5); }
.range-kbd {
  flex: none; font: inherit; font-size: 0.7rem; font-weight: 600; line-height: 1.4;
  color: var(--muted-2); background: var(--surface);
  border: 1px solid var(--line); border-radius: 5px; padding: 0 6px;
}
.range-sep { height: 1px; background: var(--line); margin: 3px -2px; }
.range-fields { border-top: 1px solid var(--line-soft); margin-top: 3px; }

/* ── Country multi-select ──────────────────────────────────────────────────
   The trigger shows a single selection as a removable chip and any larger
   selection as an "N selected" count; the menu is a search box over a
   scrollable, checkable list whose selected rows float to the top. */
.cc-btn { max-width: 340px; }
.cc-chips { display: inline-flex; align-items: center; gap: 5px; min-width: 0; }
.cc-chip {
  display: inline-flex; align-items: center; gap: 4px;
  background: var(--accent-soft); color: var(--accent);
  border-radius: 999px; padding: 2px 4px 2px 7px; font-size: 0.76rem; font-weight: 700;
}
.cc-chip .flag-ico { width: 16px; height: 11px; }
.cc-chip-name { max-width: 92px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cc-x {
  display: inline-flex; align-items: center; justify-content: center;
  width: 15px; height: 15px; border-radius: 999px; color: var(--accent);
  opacity: 0.66; cursor: pointer;
}
.cc-x:hover { opacity: 1; background: rgba(var(--accent-rgb), 0.16); }
.cc-count { font-weight: 700; color: var(--ink-2); }
.cc-menu { min-width: 248px; padding-top: 8px; }
.cc-search { padding: 0 4px 8px; }
.cc-search input {
  width: 100%; font: inherit; font-size: 0.82rem; font-weight: 600; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 8px; padding: 7px 10px;
}
.cc-search input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.cc-all { border-bottom: 1px solid var(--line-soft); border-radius: 0 !important; margin-bottom: 4px; color: var(--muted); }
.cc-all.on { color: var(--accent); }
/* overflow-y:auto alone computes overflow-x to auto, which puts a horizontal scrollbar in the menu
   the moment a row is a pixel too wide. Rows never need to scroll sideways - they ellipsis. */
.cc-list { max-height: 244px; overflow-y: auto; overflow-x: hidden; }
.cc-row { display: flex !important; align-items: center; gap: 8px; }
.cc-row[hidden] { display: none !important; }
.cc-row.on { color: var(--accent); font-weight: 700; }
.cc-check { width: 15px; height: 15px; flex: none; display: inline-flex; align-items: center; justify-content: center; color: var(--accent); }
.cc-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acct-menu { min-width: 210px; }
.acct-head { display: flex; align-items: center; gap: 10px; padding: 6px 8px 10px; border-bottom: 1px solid var(--line-soft); margin-bottom: 4px; }
.acct-id { display: flex; flex-direction: column; line-height: 1.3; }
.acct-id small { color: var(--muted); font-weight: 600; }
.dd-sep { border-top: 1px solid var(--line-soft); margin-top: 4px; }
.avatar {
  width: 30px; height: 30px; border-radius: 9px; flex: none;
  background: linear-gradient(135deg, var(--primary), #66b2ff);
  display: inline-grid; place-items: center; color: #fff; font-weight: 700; font-size: 0.72rem;
}
.avatar-lg { width: 38px; height: 38px; font-size: 0.85rem; border-radius: 11px; }
/* The avatar chip: a circular ring that hugs the avatar (tighter padding than a text dd-btn). Hover
   and the open state both draw a clean accent ring (no grey fill) and give the avatar a subtle lift,
   so the trigger reads as interactive and, when open, active. */
.acct-btn {
  padding: 3px; border-radius: 999px; border-color: var(--line); box-shadow: none;
  transition: border-color .15s ease, background-color .15s ease, box-shadow .15s ease;
}
.acct-btn .avatar { transition: transform .15s ease; }
.acct-btn:hover { background: var(--line-soft); border-color: var(--muted-2); }
.acct-dd.open .acct-btn { border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }
.acct-dd.open .avatar { transform: scale(1.04); }
@media (prefers-reduced-motion: reduce) {
  .acct-dd.open .avatar { transform: none; }
}
/* Running build, footer of the account menu: quiet, centered, its own hairline above. The only place
   the version is shown. --muted-2 is 3.3:1 on the menu surface, below AA, so use --muted (5.2:1). */
.acct-ver {
  margin-top: 4px; padding: 7px 10px 3px; border-top: 1px solid var(--line-soft);
  font-size: 0.72rem; font-weight: 600; color: var(--muted);
  font-variant-numeric: tabular-nums; text-align: center;
}
/* Fixed build timestamp under the version: a quieter second line so the version still leads. */
.acct-build { display: block; margin-top: 2px; font-size: 0.68rem; font-weight: 500; color: var(--muted); opacity: 0.85; }
/* Account block in the sidebar footer (redesign). A full-width row - avatar + identity + caret -
   that opens the shared account menu upward, out of the dark nav. Overrides the white .dd-btn pill. */
.acct-dd-side { width: 100%; }
.acct-side-btn {
  width: 100%; justify-content: flex-start; gap: 10px;
  background: var(--nav-2); border: 1px solid var(--nav-line); box-shadow: none;
  border-radius: var(--radius-sm); padding: 8px 10px; color: var(--nav-ink);
}
.acct-side-btn:hover { background: var(--nav-2); border-color: rgba(255, 255, 255, 0.18); }
.acct-dd-side.open .acct-side-btn { border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }
.acct-side-id { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; text-align: left; }
.acct-side-id b { font-size: 0.85rem; font-weight: 700; color: var(--nav-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acct-side-id small { font-size: 0.72rem; font-weight: 600; color: var(--nav-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acct-side-btn .dd-caret { margin-left: auto; color: var(--nav-muted); font-size: 0.62rem; }
/* Opens upward from the footer (vertical sidebar), left-aligned within the rail. */
.acct-menu-up { top: auto; bottom: calc(100% + 8px); left: 0; right: auto; }
/* Stripe-style: the account bar is the top item, so the menu drops downward, left-aligned. */
.acct-menu-down { top: calc(100% + 8px); bottom: auto; left: 0; right: auto; }
/* Console layout: the footer sits at the top-right of the horizontal bar, so the menu opens
   downward + right-aligned and the trigger collapses to just the avatar chip. */
.spinner {
  width: 16px; height: 16px; flex: none; border-radius: 50%;
  border: 2px solid var(--primary-tint); border-top-color: var(--primary);
  animation: dd-spin 0.6s linear infinite;
}
.spinner[hidden] { display: none; }
.spinner-sm { width: 10px; height: 10px; border-width: 2px; }
@keyframes dd-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .spinner { animation-duration: 1.5s; } }

/* ── Mobile filter drawer ──────────────────────────────────────────────────
   The country / range controls are wrapped in .filter-cluster, each in a
   labelled .fgroup. On desktop those wrappers are transparent (display:contents), so
   the controls sit inline in the bar exactly as before and the drawer chrome (sheet
   header, group labels, the toggle button, the backdrop) is hidden.
   The 640px block at the foot of the file turns .filter-cluster into a right-side sheet
   and reveals the chrome. Open state is `body.filters-open`, scoped there too. */
.filter-cluster, .fgroup { display: contents; }
.fdrawer-h, .fgroup-lab, .filters-toggle, .fdrawer-backdrop { display: none; }
/* The site picker sits inline in the bar (.top .site) on desktop; its in-sheet twin is
   hidden here and only appears once the cluster becomes the mobile sheet (see 640px block).
   Scoped to .top so it out-specifies the reskin's later `.fgroup{display:inline-flex}` rule, which
   would otherwise un-hide this twin on desktop and show "All pixels" twice. */
.top .fgroup-site { display: none; }

main { padding: 34px 0 80px; }

/* ── Answer-first summary ── */
.answer {
  font-size: 1.42rem;
  line-height: 1.44;
  font-weight: 500;
  letter-spacing: -0.015em;
  color: var(--ink-2);
  max-width: 46ch;
  margin: 0 0 34px;
  text-wrap: pretty;
}
.answer b { color: var(--ink); font-weight: 700; }
.answer .up { color: var(--good); font-weight: 700; }

/* ── Top stats: the KPI row and the hero chart share one card ── */
/* The card chrome the .kpis and .hero blocks each used to carry themselves now lives here once, so
   the tiles read as controls on the chart below them rather than as a separate panel. */
.topstats {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
  margin-bottom: 24px;
}

/* ── KPI row ── */
.kpis {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
  margin-bottom: 24px;
}
/* Inside .topstats the card chrome belongs to the wrapper, so the .kpis row drops its own border,
   radius and bottom margin (earnings.html/video.html use a standalone .kpis and keep theirs). The
   per-tile dividers stay: Plausible's top-stats keeps a vertical rule between tiles, and they are the
   "borders on the KPI buttons" the overview row should carry. */
.topstats .kpis {
  border: 0;
  border-radius: 0;
  margin-bottom: 0;
}
.kpi {
  padding: 18px 20px;
  border-left: 1px solid var(--line);
}
.kpi:first-child { border-left: 0; }
/* The pickable tiles are <button>, so the UA's font/align/background are reset back to the tile look.
   Only the three metrics with a real per-day series are buttons; the rest stay plain divs. The
   pick/active affordance lives on the label, Plausible-style: a 2px underline under the metric name
   only, grey on hover, accent when it is the plotted metric. The tile box itself never changes. */
.kpi-pick {
  font: inherit;
  color: inherit;
  text-align: left;
  background: none;
  /* Reset the UA button border but keep the tile's own left divider: border:0 alone wiped the rule
     the plain .kpi tiles carry, so the three pickable metrics read as one seam-less block. The
     .kpi:first-child rule (higher specificity) still drops the leading edge. */
  border: 0;
  border-left: 1px solid var(--line);
  cursor: pointer;
  width: 100%;
}
.kpi-pick .kpi-label { border-bottom: 2px solid transparent; transition: border-color .15s, color .15s; }
.kpi-pick:hover .kpi-label { border-bottom-color: var(--muted-2); color: var(--ink-2); }
.kpi-pick.active .kpi-label { border-bottom-color: var(--primary); color: var(--ink-2); }
.kpi-pick:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; border-radius: 8px; }
.kpi-label {
  display: inline-block;
  font-size: 0.75rem; font-weight: 700; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.04em;
  padding-bottom: 2px;
}
/* Value and delta share a baseline row, Plausible-style, rather than stacking. */
.kpi-vrow { display: flex; align-items: baseline; gap: 9px; margin-top: 5px; }
.kpi-vrow .kpi-val { margin: 0; }
.kpi-val { font-size: 1.5rem; font-weight: 700; letter-spacing: -0.02em; margin: 7px 0 5px; font-variant-numeric: tabular-nums; }
.kpi-delta { font-size: 0.75rem; font-weight: 700; display: inline-flex; align-items: center; gap: 3px; }
.kpi-delta.up { color: var(--good); }
.kpi-delta.down { color: var(--bad); }

/* ── Section headings ── */
.sec-h {
  display: flex; align-items: baseline; justify-content: space-between;
  margin: 0 0 10px;
}
.sec-h h2 { font-size: 1.02rem; font-weight: 700; letter-spacing: -0.01em; margin: 0; }
.sec-h .note { font-size: 0.8rem; color: var(--muted); font-weight: 600; }
/* Pane-header "view details" trigger: icon-only (Plausible's maximize glyph). A quiet muted icon with
   a soft rounded hover chip so it reads as a control without scattering accent orange across the panes.
   align-self centers it against the h2 despite the header's baseline alignment. */
.pane-details {
  display: inline-flex; align-items: center; justify-content: center; align-self: center;
  width: 26px; height: 26px; margin: -4px -5px -4px 0;
  color: var(--muted-2); background: none; border: 0; border-radius: 8px; cursor: pointer;
  transition: color .15s ease, background-color .15s ease;
}
.pane-details:hover { color: var(--ink-2); background: var(--line-soft); }
.pane-details:focus-visible { outline: none; color: var(--ink-2); box-shadow: 0 0 0 3px var(--primary-soft); }

/* ── Hero chart ── */
.hero {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 22px 24px 14px;
  margin-bottom: 24px;
}
/* Inside .topstats the card chrome belongs to the wrapper, so the hero drops its own. Scoped rather
   than stripped from .hero itself: earnings.html and video.html use a standalone .hero and must keep
   their border. */
.topstats .hero {
  border: 0;
  border-radius: 0;
  margin-bottom: 0;
  /* The KPI row above already carries 18px of bottom padding; the standalone .hero's 22px top would
     stack into a wide gap over the chart title, so tighten it inside the shared card. */
  padding-top: 6px;
}
.chart-wrap { position: relative; }
.chart-wrap svg { display: block; width: 100%; height: auto; }
.chart-tip {
  position: absolute; transform: translate(-50%, -100%);
  background: var(--ink); color: #fff;
  font-size: 0.74rem; font-weight: 700;
  padding: 5px 9px; border-radius: 7px;
  pointer-events: none; white-space: nowrap; opacity: 0;
  transition: opacity .12s; z-index: 5;
}
.chart-tip b { color: var(--primary-tint); }
/* uPlot parents the .chart-tip to its overlay, which clips by default. */
.u-over { overflow: visible; }

/* ── Top-N grid ── */
.grid4 {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  margin-bottom: 24px;
}
.panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 18px 20px 20px;
}
.rows { display: flex; flex-direction: column; gap: 3px; margin-top: 4px; }
.row {
  position: relative;
  display: flex; align-items: center; justify-content: space-between;
  padding: 8px 10px;
  border-radius: 8px;
  font-size: 0.875rem;
  overflow: hidden;
  isolation: isolate;
}
.row-bar {
  position: absolute; left: 0; top: 0; bottom: 0;
  background: var(--primary-soft);
  border-radius: 8px;
  z-index: -1;
}
.row:hover .row-bar { background: var(--primary-tint); }
.row-name { font-weight: 600; color: var(--ink-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: flex; align-items: center; gap: 8px; }
.row-val { font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ink); flex: none; padding-left: 12px; }
.row-val small { color: var(--muted); font-weight: 600; margin-left: 6px; }
.flag { font-size: 0.95rem; line-height: 1; }

/* ── Compact strips (video + earnings) ── */
.strips { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.strip {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 20px 22px;
}
.mini-metrics { display: flex; flex-wrap: wrap; gap: 22px 30px; margin: 6px 0 16px; }
.mini { }
.mini-v { font-size: 1.28rem; font-weight: 700; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.mini-l { font-size: 0.74rem; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: 0.03em; margin-top: 2px; }

/* quartile mini bars */
.quart { display: flex; align-items: flex-end; gap: 8px; height: 62px; }
.quart-col { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px; height: 100%; justify-content: flex-end; }
.quart-fill { width: 100%; max-width: 34px; background: var(--primary); border-radius: 5px 5px 0 0; min-height: 3px; }
.quart-lab { font-size: 0.68rem; font-weight: 700; color: var(--muted); }
.quart-empty { margin: auto; color: var(--muted); font-weight: 600; font-size: 0.78rem; }

/* earnings sparkline */
.spark { display: block; width: 100%; height: auto; margin-top: 4px; }
.earn-foot { display: flex; justify-content: space-between; font-size: 0.78rem; color: var(--muted); font-weight: 600; margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line-soft); }
.earn-foot b { color: var(--ink); }

/* Used as both <a> and <button>; the resets below are what make the two render identically. */
.btn-ghost {
  display: inline-flex; align-items: center; gap: 6px;
  font: inherit; font-size: 0.8rem; font-weight: 700; color: var(--primary);
  background: none; border: 0; padding: 0; cursor: pointer;
}
.btn-ghost:hover { text-decoration: underline; }

/* ── Details modal ── */
/* One dialog serves all four overview panes. Native <dialog>, so Esc, focus trapping and the
   top layer come from the browser rather than a scroll-lock and keydown handler of our own. */
.dmodal {
  width: min(560px, calc(100vw - 32px));
  max-height: min(70vh, 640px);
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  overflow: hidden;
}
/* Scope the flex column to the open dialog so the UA `dialog:not([open]){display:none}` still hides
   it when closed. Header + search stay pinned; the body is the one part that scrolls. */
.dmodal[open] { display: flex; flex-direction: column; }
.dmodal::backdrop { background: rgba(24, 18, 16, 0.42); }
.dmodal-h {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 16px 20px; border-bottom: 1px solid var(--line);
}
.dmodal-h h2 { font-size: 1.02rem; font-weight: 700; letter-spacing: -0.01em; margin: 0; }
.dmodal-x {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border: 0; border-radius: 50%;
  background: none; color: var(--muted); font-size: 0.9rem; cursor: pointer;
}
.dmodal-x:hover { background: var(--line-soft); color: var(--ink); }
/* Search sits between the header and the scrolling body; it filters the already-fetched list in
   place (app.js renderDetailsBody), matching Plausible's breakdown modal. */
.dmodal-search { padding: 12px 20px 0; }
.dmodal-search input {
  width: 100%; font: inherit; font-size: 0.85rem; font-weight: 600; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line); border-radius: 8px; padding: 8px 11px;
}
.dmodal-search input::placeholder { color: var(--muted-2); font-weight: 600; }
.dmodal-search input:focus {
  outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft);
}
.dmodal-body { padding: 8px 20px 16px; overflow-y: auto; flex: 1 1 auto; min-height: 0; }
.dmodal-f { padding: 12px 20px; border-top: 1px solid var(--line); }

/* ── Hidden "dev" panel (type d-e-v anywhere) : sitewide typeface + dark-mode switch ──
   Reuses the details-modal shell (.dmodal). Each font row previews itself, so the picker
   reads as the change it makes; the active row carries the orange check. The dark switch is
   a plain <button> acting as an ARIA toggle (aria-checked drives the visual). */
.dev-sec { padding: 4px 0 6px; }
.dev-sec + .dev-sec { border-top: 1px solid var(--line); margin-top: 10px; padding-top: 14px; }
.dev-lbl {
  font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--muted); margin: 0 0 8px;
}
.dev-fonts { display: flex; flex-direction: column; gap: 4px; }
.dev-font {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%; text-align: left; cursor: pointer;
  font-size: 1.02rem; font-weight: 600; color: var(--ink);
  background: none; border: 1px solid transparent; border-radius: 9px; padding: 9px 12px;
}
.dev-font:hover { background: var(--line-soft); }
.dev-font.active { border-color: var(--primary); background: var(--primary-soft); }
.dev-check { color: var(--primary); font-weight: 700; opacity: 0; }
.dev-font.active .dev-check { opacity: 1; }
.dev-switch-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  width: 100%; cursor: pointer; font: inherit; font-size: 0.92rem; font-weight: 700; color: var(--ink);
  background: none; border: 0; padding: 4px 2px;
}
.dev-switch {
  position: relative; flex: none; width: 40px; height: 23px;
  border-radius: 999px; background: var(--line); transition: background .18s ease;
}
.dev-knob {
  position: absolute; top: 2px; left: 2px; width: 19px; height: 19px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25); transition: transform .18s ease;
}
.dev-switch-row[aria-checked="true"] .dev-switch { background: var(--primary); }
.dev-switch-row[aria-checked="true"] .dev-knob { transform: translateX(17px); }
@media (prefers-reduced-motion: reduce) { .dev-switch, .dev-knob { transition: none; } }

/* Three-way appearance control (System / Light / Dark), shared by the dev panel and the Appearance
   page. A pill-segmented control on the measured Stripe tokens; the whole dark look is filter: invert
   on the root, so these light tokens simply invert with everything else - no dark override needed. */
.theme-seg {
  display: inline-flex; gap: 2px; padding: 3px;
  background: var(--sand); border: 1px solid var(--line); border-radius: var(--radius-sm);
}
.theme-seg-b {
  appearance: none; border: 0; background: none; font: inherit; font-size: 0.85rem; font-weight: 600;
  color: var(--muted); padding: 6px 16px; border-radius: calc(var(--radius-sm) - 3px);
  cursor: pointer; transition: background .15s ease, color .15s ease, box-shadow .15s ease;
}
.theme-seg-b:hover { color: var(--ink); }
.theme-seg-b.active {
  background: var(--card); color: var(--ink); box-shadow: 0 1px 2px rgba(16, 20, 40, 0.10);
}
.theme-seg-b:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .theme-seg-b { transition: none; } }


/* ── Video page ── */
.two-col { display: grid; grid-template-columns: 1.5fr 1fr; gap: 18px; margin-bottom: 18px; }
.qoe-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-top: 4px; }
.qoe {
  border: 1px solid var(--line); border-radius: 10px; padding: 13px 14px;
}
.qoe-v { font-size: 1.3rem; font-weight: 700; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.qoe-l { font-size: 0.72rem; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: 0.03em; margin-top: 2px; }
.tag { display: inline-block; font-size: 0.68rem; font-weight: 700; padding: 2px 7px; border-radius: 999px; margin-top: 8px; }
.tag.good { color: var(--good); background: var(--good-soft); }
.tag.warn { color: #92400e; background: #fef3c7; }

/* ── Data table ── */
table.tbl { width: 100%; border-collapse: collapse; font-size: 0.875rem; }
table.tbl th {
  text-align: left; font-size: 0.72rem; font-weight: 700; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.03em;
  padding: 8px 10px; border-bottom: 1px solid var(--line);
}
table.tbl th.num, table.tbl td.num { text-align: right; font-variant-numeric: tabular-nums; }
table.tbl td { padding: 11px 10px; border-bottom: 1px solid var(--line-soft); font-weight: 600; color: var(--ink-2); }
table.tbl tr:last-child td { border-bottom: 0; }
table.tbl td.name { color: var(--ink); font-weight: 700; }
table.tbl td.ct-cell { color: var(--muted); font-weight: 600; white-space: nowrap; }
.bar-cell { position: relative; }
.bar-cell .mini-track { height: 6px; background: var(--line-soft); border-radius: 4px; overflow: hidden; margin-top: 5px; }
.bar-cell .mini-track > span { display: block; height: 100%; background: var(--primary); border-radius: 4px; }
/* Wide tables (the enriched video list) scroll inside their own box so the page never scrolls sideways. */
.tbl-scroll { overflow-x: auto; }
.tbl-scroll table.tbl { min-width: 640px; }

/* ── Per-play event waterfall ──────────────────────────────────────────────
   A play is two clocks at once: wall time (when they hit pause) and video
   position (where they were). The lanes run on wall time the way a browser's
   devtools network panel does; the position trace above them is what makes a
   seek legible as a jump rather than as one more row in a list. */
.vw { border: 1px solid var(--line); border-radius: var(--radius-sm); overflow: hidden; background: var(--card); }
/* Stats strip: one shared box with hairline dividers, not a grid of bordered tiles. */
.vw-qoe { display: grid; grid-template-columns: repeat(auto-fit, minmax(104px, 1fr)); gap: 1px; background: var(--line); border-bottom: 1px solid var(--line); }
.vw-qoe > div { background: var(--card); padding: 9px 13px; }
.vw-qoe .k { font-size: 0.68rem; color: var(--muted); font-weight: 600; letter-spacing: 0.02em; }
.vw-qoe .v { font-size: 0.92rem; font-weight: 700; margin-top: 2px; font-variant-numeric: tabular-nums; }
.vw-qoe .v.bad { color: var(--coral); }
.vw-qoe .v.ok { color: var(--green); }

.vw-chart { padding: 14px 14px 6px; overflow-x: auto; }
.vw-inner { min-width: 520px; }
/* Position trace: vertical jumps are seeks, flat runs are pauses. */
.vw-trace { position: relative; height: 70px; border: 1px solid var(--line); border-radius: 8px; background: var(--sand); overflow: hidden; }
.vw-trace svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.vw-trace .lbl { position: absolute; top: 5px; left: 8px; font-size: 0.65rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); }
.vw-trace .maxlbl { position: absolute; top: 5px; right: 8px; font-size: 0.67rem; font-weight: 700; color: var(--muted); font-variant-numeric: tabular-nums; }

.vw-lanes { margin-top: 12px; display: grid; grid-template-columns: 108px minmax(0, 1fr); gap: 1px 12px; align-items: center; }
.vw-lane-k { font-size: 0.71rem; font-weight: 700; color: var(--muted); text-align: right; padding: 5px 0; display: flex; align-items: center; justify-content: flex-end; gap: 6px; }
.vw-lane-k .fa { width: 11px; height: 11px; flex: none; opacity: 0.75; }
.vw-lane { position: relative; height: 22px; border-radius: 5px; background: var(--sand); border: 1px solid var(--line); }
.vw-bar { position: absolute; top: 2px; bottom: 2px; border-radius: 3px; min-width: 2px; }
.vw-bar.play { background: var(--accent); }
.vw-bar.buf { background: var(--coral); }
.vw-bar.q { background: var(--violet); opacity: 0.28; border-right: 1px solid var(--violet); }
.vw-bar.on { background: var(--green); opacity: 0.5; }
.vw-bar .t { position: absolute; inset: 0; display: flex; align-items: center; padding-left: 6px; font-size: 0.65rem; font-weight: 700; color: var(--ink-2); white-space: nowrap; overflow: hidden; }
/* Marker lane: the discrete events as icon pins on the same wall-clock axis. */
.vw-mlane { position: relative; height: 30px; }
.vw-pin { position: absolute; top: 2px; transform: translateX(-50%); width: 24px; height: 24px; border-radius: 50%; display: grid; place-items: center; background: var(--card); border: 1px solid var(--line); color: var(--muted); cursor: default; transition: transform 120ms cubic-bezier(0.22, 1, 0.36, 1); }
.vw-pin .fa { width: 11px; height: 11px; }
.vw-pin:hover { transform: translateX(-50%) scale(1.18); z-index: 3; border-color: var(--line-hover); color: var(--ink); }
.vw-pin.warn { border-color: var(--coral); color: var(--coral); }
.vw-pin.acc { border-color: var(--accent); color: var(--accent); }
.vw-axis { position: relative; height: 16px; margin-top: 4px; border-top: 1px solid var(--line); }
.vw-axis span { position: absolute; top: 3px; transform: translateX(-50%); font-size: 0.65rem; color: var(--muted); font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.vw-axis span:first-child { transform: none; left: 0; }
.vw-axis span:last-child { transform: none; right: 0; left: auto; }

.vw-tbl { border-top: 1px solid var(--line); overflow-x: auto; }
.vw-tbl table { width: 100%; min-width: 520px; border-collapse: collapse; table-layout: fixed; }
.vw-tbl th, .vw-tbl td { padding: 7px 12px; text-align: left; font-size: 0.79rem; border-bottom: 1px solid var(--line); }
.vw-tbl th { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); font-weight: 700; }
.vw-tbl td { color: var(--label); overflow: hidden; }
.vw-tbl tr:last-child td { border-bottom: 0; }
.vw-tbl .r { text-align: right; }
.vw-tbl .tnum { font-variant-numeric: tabular-nums; color: var(--muted); white-space: nowrap; }
/* Drag-to-resize handles on the header cells (Detail is the flex remainder, so it has none). The
   sticky thead th (below) is the positioned containing block for these absolute handles. */
.vw-rz { position: absolute; top: 0; right: 0; width: 8px; height: 100%; cursor: col-resize; z-index: 3; touch-action: none; user-select: none; }
.vw-rz::after { content: ""; position: absolute; top: 6px; bottom: 6px; right: 3px; width: 2px; border-radius: 2px; background: transparent; }
.vw-rz:hover::after { background: var(--line); }
body.vw-resizing { cursor: col-resize; user-select: none; }
body.vw-resizing .vw-rz::after { background: var(--accent); }
.vw-act { display: inline-flex; align-items: center; gap: 7px; font-weight: 700; font-size: 0.79rem; white-space: nowrap; color: var(--ink); }
.vw-act .ico { width: 22px; height: 22px; border-radius: 6px; display: grid; place-items: center; flex: none; background: var(--sand); border: 1px solid var(--line); color: var(--muted); }
.vw-act .ico .fa { width: 10px; height: 10px; }
.vw-act.warn .ico { border-color: var(--coral); color: var(--coral); background: transparent; }
.vw-act.acc .ico { border-color: transparent; color: var(--accent); background: var(--accent-soft); }
.vw-hb { display: flex; align-items: center; gap: 8px; padding: 9px 14px; border-top: 1px solid var(--line); font-size: 0.75rem; color: var(--muted); font-weight: 600; }
.vw-hb .fa { width: 12px; height: 12px; flex: none; }
/* Content drill stacks one waterfall per play, each under its own heading. */
.vw-play + .vw-play { margin-top: 14px; }
.vw-play-h { display: flex; align-items: baseline; gap: 8px; margin: 0 0 6px; font-size: 0.82rem; font-weight: 700; color: var(--ink); }
.vw-play-h span { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.7rem; color: var(--muted); font-weight: 600; }

/* ── Visitor profile: sticky identity rail + scrolling sections ──
   Layout + rail chrome only; the per-play breakdown reuses the .vw-* waterfall above.
   Existing tokens only; long values wrap (overflow-wrap:anywhere) and wide tables scroll
   inside .vp-scroll so a long URL never widens the page. */
.vp { display: grid; grid-template-columns: 288px minmax(0, 1fr); gap: 24px; align-items: start; }
@media (max-width: 1023px) { .vp { grid-template-columns: 1fr; gap: 16px; } }
.vp-rail { position: sticky; top: 20px; display: flex; flex-direction: column; gap: 14px; min-width: 0; }
@media (max-width: 1023px) { .vp-rail { position: static; } }
.vp-rail .card { padding: 18px; }
.vp-who { display: flex; align-items: center; gap: 13px; }
.vp-who .avatar { width: 46px; height: 46px; font-size: 1rem; flex: none; }
.vp-who h2 { font-size: 1.06rem; font-weight: 700; line-height: 1.25; margin: 0; min-width: 0; overflow-wrap: anywhere; }
.vp-who .pill { margin-top: 5px; }
.vp-vid { display: flex; align-items: center; gap: 6px; margin-top: 14px; background: var(--sand); border: 1px solid var(--line); border-radius: 8px; padding: 7px 6px 7px 10px; }
.vp-vid code { flex: 1; min-width: 0; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.74rem; font-weight: 600; color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vp-vid button { background: none; border: 0; color: var(--muted); cursor: pointer; display: inline-grid; place-items: center; padding: 4px; border-radius: 6px; line-height: 0; }
.vp-vid button:hover { color: var(--ink); background: var(--card); }
.vp-vid .fa { width: 12px; height: 12px; }
.vp-nums { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: var(--radius-sm); overflow: hidden; margin-top: 14px; }
.vp-nums > div { background: var(--card); padding: 11px 13px; }
.vp-nums .k { font-size: 0.7rem; color: var(--muted); font-weight: 600; }
.vp-nums .v { font-size: 1.06rem; font-weight: 700; margin-top: 2px; font-variant-numeric: tabular-nums; }
.vp-line { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--line-soft); font-size: 0.84rem; font-weight: 600; }
.vp-line:last-child { border-bottom: 0; }
.vp-line .k { color: var(--muted); font-weight: 600; flex: none; }
.vp-line .v { text-align: right; min-width: 0; overflow-wrap: anywhere; }
.vp-rail-h { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase; color: var(--muted); margin: 0 0 6px; }
.vp-body { display: flex; flex-direction: column; min-width: 0; }
.vp-body .sec-h:first-child { margin-top: 0; }
@media (min-width: 1200px) { .vp-body .dfacts { grid-template-columns: repeat(3, 1fr); } }
.vp-body .dfacts .v { font-size: 0.92rem; font-weight: 600; overflow-wrap: anywhere; }
.vp-body .dfacts .v.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.78rem; }
.vp-body .dfacts .v.dash { color: var(--muted); font-weight: 600; }
.vp-scroll { overflow-x: auto; }
.vp-scroll table { min-width: 640px; }
.vp-ev { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.76rem; color: var(--muted); white-space: nowrap; }
.vp-tag { display: inline-block; font-size: 0.7rem; font-weight: 700; padding: 2px 8px; border-radius: 999px; background: var(--sand); border: 1px solid var(--line); color: var(--ink-2); }
.vp-tag.pv { background: var(--accent-soft); border-color: transparent; color: var(--accent); }
.vp-props { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.74rem; color: var(--muted); overflow-wrap: anywhere; }
.vp-declared { display: inline-block; margin-left: 6px; padding: 1px 6px; border-radius: 4px; font-size: 0.68rem; font-weight: 600; letter-spacing: 0.02em; text-transform: uppercase; color: var(--muted); background: var(--surface-sunken, rgba(127, 127, 127, 0.12)); vertical-align: middle; }
.vp-note { margin: 0; font-size: 0.82rem; color: var(--muted); font-weight: 500; line-height: 1.65; }
.vp-note b { color: var(--ink-2); font-weight: 700; }
.vp-prow { cursor: pointer; }
.vp-prow:hover td { background: var(--sand); }
.vp-prow.on td { background: var(--accent-soft); }
.vp-prow:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.vp-caret { display: inline-block; width: 0; height: 0; margin-right: 8px; vertical-align: 1px; border-left: 5px solid currentColor; border-top: 4px solid transparent; border-bottom: 4px solid transparent; transition: transform 140ms cubic-bezier(0.22, 1, 0.36, 1); transform-origin: 2px 50%; }
.vp-prow.on .vp-caret { transform: rotate(90deg); }
@media (prefers-reduced-motion: reduce) { .vp-caret { transition: none; } }
/* Serial-title grouping in the Video table: a series banner row, then a season sub-header, then the
   episode rows (indented, with the episode tag + name in the Video cell). Header rows are non-interactive. */
.vp-grp td { background: var(--surface); }
.vp-grp-series td { padding-top: 14px; border-top: 1px solid var(--line); }
.vp-grp-series:first-child td { border-top: 0; padding-top: 6px; }
.vp-grp-name { font-weight: 700; color: var(--ink); font-size: 0.9rem; }
.vp-grp-count { margin-left: 8px; font-size: 0.72rem; font-weight: 600; color: var(--muted); }
.vp-grp-season td { padding-top: 4px; padding-bottom: 4px; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); }
.vp-prow-ep .vp-props { padding-left: 18px; }
.vp-eptitle { display: flex; flex-direction: column; gap: 2px; }
.vp-ep-series { font-weight: 600; color: var(--ink-2); }
.vp-ep-meta { display: flex; align-items: baseline; gap: 8px; }
.vp-ep-tag { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.72rem; font-weight: 700; color: var(--accent); }
.vp-ep-name { font-size: 0.82rem; color: var(--ink); overflow-wrap: anywhere; }
.vp-pd { margin-top: 12px; }
.vp-pd .card { padding: 0; overflow: hidden; }
.vp-pd-h { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; padding: 14px 16px; border-bottom: 1px solid var(--line); }
.vp-pd-h h3 { margin: 0; font-size: 0.98rem; font-weight: 700; }
.vp-pd-h .id { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.74rem; color: var(--muted); font-weight: 600; }
.vp-pd-h .when { margin-left: auto; font-size: 0.78rem; color: var(--muted); font-weight: 600; }
/* Little link icon in the timeline event header that opens the full title page. */
.vp-title-open { display: inline-grid; place-items: center; width: 26px; height: 26px; padding: 0; border: 1px solid var(--line); border-radius: 7px; background: var(--card); color: var(--muted); cursor: pointer; transition: color .14s ease, border-color .14s ease, background .14s ease; }
.vp-title-open:hover { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
.vp-title-open svg { width: 14px; height: 14px; }
.vp-title-open:focus-visible { outline: none; box-shadow: var(--ring-brand); }
/* General overflow hardening: long URLs / paths in fact grids and drill tables wrap instead of
   widening the page. Applies everywhere .dfacts is used (visitor, drill drawers, detail pages). */
.dfacts .v { overflow-wrap: anywhere; min-width: 0; }

/* Segmented dimension toggle lives in the redesign chrome below as `.seg` + `.seg-b` (pill style).
   The old `.seg button` rules were removed: `.seg button.on` (0,2,1) outranked the redesign's
   `.seg-b.on` (0,2,0), so the active tab kept the pre-redesign square-fill skin. */

/* ── Visitor page ── */
.vhead {
  display: flex; align-items: center; gap: 16px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 20px 24px; margin-bottom: 18px;
}
.vavatar {
  width: 52px; height: 52px; border-radius: 14px; flex: none;
  background: linear-gradient(135deg, var(--primary), #66b2ff);
  display: grid; place-items: center; color: #fff; font-weight: 700; font-size: 1.15rem;
}
.vhead h1 { font-size: 1.15rem; font-weight: 700; margin: 0 0 3px; letter-spacing: -0.01em; }
.vhead .vsub { font-size: 0.83rem; color: var(--muted); font-weight: 600; }
.vhead .vseen { margin-left: auto; text-align: right; font-size: 0.8rem; color: var(--muted); font-weight: 600; flex: none; }
.vhead .vseen b { color: var(--ink); display: block; font-size: 0.92rem; }

.vstat-strip {
  display: grid; grid-template-columns: repeat(5, 1fr);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden; margin-bottom: 18px;
}
.vstat { padding: 15px 18px; border-left: 1px solid var(--line); }
.vstat:first-child { border-left: 0; }

/* smaller tiles for Location / Device / Tech */
.tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin-bottom: 18px; }
.tile { background: var(--surface); border: 1px solid var(--line); border-radius: 12px; padding: 15px 17px; }
.tile h3 { font-size: 0.72rem; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; margin: 0 0 10px; }
.kv { display: flex; justify-content: space-between; gap: 10px; font-size: 0.83rem; padding: 4px 0; }
.kv span:first-child { color: var(--muted); font-weight: 600; }
.kv span:last-child { color: var(--ink); font-weight: 700; }

.chips { display: flex; flex-wrap: wrap; gap: 7px; }
.chip {
  font-size: 0.78rem; font-weight: 700; color: var(--ink-2);
  background: var(--line-soft); border-radius: 999px; padding: 5px 12px;
}
.chip b { color: var(--primary); margin-left: 5px; }

.timeline { display: flex; flex-direction: column; }
.tl-row {
  display: grid; grid-template-columns: 120px 1fr auto; gap: 14px; align-items: center;
  padding: 12px 4px; border-bottom: 1px solid var(--line-soft); font-size: 0.85rem;
}
.tl-row:last-child { border-bottom: 0; }
.tl-when { color: var(--muted); font-weight: 700; }
.tl-entry { color: var(--ink); font-weight: 700; }
.tl-meta { color: var(--muted); font-weight: 600; font-variant-numeric: tabular-nums; }

/* full-bleed panel helper */
.block { margin-bottom: 24px; }

/* ── Loading skeletons (shown ~360ms on filter change) ── */
.sk { background: var(--line-soft); border-radius: 8px; position: relative; overflow: hidden; }
.sk::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.65), transparent);
  transform: translateX(-100%); animation: sk-shimmer 1.1s infinite;
}
/* width: the chart skeleton is a flex item inside the Overview's map tile, where a content-sized box
   would come out zero-wide and the tile would read as empty rather than loading. */
.sk-chart { height: 260px; width: 100%; }
.sk-line { height: 14px; margin: 10px 0; }
.sk-row { height: 34px; margin: 6px 0; max-width: 100%; }
/* A figure that has not arrived yet. Sized in em off the number it replaces so a KPI card, a sidebar
   line and a small tile each get a bar in proportion to their own type, and the card keeps its height
   so nothing jumps when the real value lands. */
.sk-num { display: inline-block; min-width: 2.6em; height: 0.78em; vertical-align: -0.04em; border-radius: 5px; }
/* A form value that has not arrived yet (see [data-live-fill]). An <input> takes no ::after, so the
   shimmer rides its own background rather than the .sk overlay. */
input.sk-field {
  color: transparent;
  background: linear-gradient(90deg, var(--line-soft) 25%, var(--line) 37%, var(--line-soft) 63%);
  background-size: 400% 100%;
  animation: tk-shimmer 1.3s ease infinite;
}
@media (prefers-reduced-motion: reduce) { input.sk-field { animation: none; } }
@keyframes sk-shimmer { 100% { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) { .sk::after { animation: none; } }
main.is-loading .kpi-val, main.is-loading [data-metric], main.is-loading .mini-v, main.is-loading .qoe-v { color: var(--muted-2); transition: color .2s; }
/* Live only: hold the demo scaffold baked into the static HTML off screen for the sliver between
   hydrate.js (which sets this in <head>, pre-paint) and app.js booting. withLoading() clears it the
   moment it has painted the skeletons over that scaffold, so what a live client waits in front of is
   a skeleton page rather than a blank one. visibility keeps the page's height, so revealing it does
   not shift the chrome above. */
.live-hydrating main { visibility: hidden; }

/* ── Split bar (new vs returning) ── */
.split-track { display: flex; height: 12px; border-radius: 999px; overflow: hidden; background: var(--line-soft); }
.split-seg { height: 100%; }
.split-legend { display: flex; flex-wrap: wrap; gap: 8px 22px; margin-top: 14px; }
.split-key { display: inline-flex; align-items: center; gap: 7px; font-size: 0.82rem; font-weight: 600; color: var(--ink-2); }
.split-dot { width: 9px; height: 9px; border-radius: 50%; }
.split-key b { color: var(--ink); font-weight: 700; font-variant-numeric: tabular-nums; }

/* ── People overview (Visitors) ── */
.people-toolbar {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  margin-bottom: 14px; flex-wrap: wrap;
}
.people-count { font-size: 0.85rem; color: var(--muted); font-weight: 600; }
.people-count b { color: var(--ink); font-weight: 700; }
.search {
  position: relative; display: inline-flex; align-items: center;
}
.search svg { position: absolute; left: 12px; color: var(--muted-2); pointer-events: none; }
.search input {
  font: inherit; font-size: 0.86rem; font-weight: 600; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line); border-radius: 10px;
  padding: 9px 14px 9px 36px; width: 260px; max-width: 100%;
}
.search input::placeholder { color: var(--muted-2); font-weight: 500; }
.search input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }

/* Active filter chips beside the search: country pills clear one country, the
   window pill resets to the default 30 days (static, no ×, at 30d). */
.people-toolbar-lead { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; min-width: 0; }
.people-filters { display: inline-flex; align-items: center; flex-wrap: wrap; gap: 6px; }
.people-filters-lab { font-size: 0.7rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted-2); }
/* Base reset only: font/cursor so the <button> chip behaves. Fill, colour, radius and padding come
   from the redesign `.fchip` (accent-soft pill, borderless) further down; the old surface bg + 1px
   border used to leak through and box the accent chip. */
.fchip {
  display: inline-flex; align-items: center; gap: 5px;
  font: inherit; line-height: 1; cursor: pointer;
}
.fchip:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--primary-soft); }
.fchip .flag-ico { width: 16px; height: 11px; border-radius: 2px; }
.fchip .cal-ico { color: var(--muted); }
.fchip-name { max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fchip-x {
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; border-radius: 999px; color: var(--muted); opacity: 0.72;
}
.fchip-x svg { width: 9px; height: 9px; }
.fchip:hover .fchip-x { opacity: 1; color: var(--bad); background: rgba(185, 28, 28, 0.1); }
.fchip-static { cursor: default; background: var(--canvas); }
.fchip-static:hover { border-color: var(--line); }

.table-scroll { overflow-x: auto; }
.tbl.people { min-width: 720px; }
.tbl.people th.sortable { cursor: pointer; user-select: none; white-space: nowrap; }
.tbl.people th.sortable:hover { color: var(--ink-2); }
.tbl.people th.on { color: var(--primary); }
.tbl.people tr.prow { cursor: pointer; }
.tbl.people tr.prow:hover td { background: var(--line-soft); }
.tbl.people td { border-bottom: 1px solid var(--line-soft); }
.tbl.people td.nowrap { white-space: nowrap; }
.pcell { display: flex; align-items: center; gap: 11px; }
.pav {
  position: relative;
  width: 34px; height: 34px; border-radius: 9px; flex: none;
  background: linear-gradient(135deg, var(--primary), #66b2ff);
  display: grid; place-items: center; color: #fff; font-weight: 700; font-size: 0.78rem;
}
/* Country flag riding the avatar's bottom-right corner. The avatar text is the country code for an
   anonymous visitor, so the flag makes it legible at a glance and its title carries the country name. */
.pav-flag {
  position: absolute; right: -4px; bottom: -3px;
  width: 15px; height: 11px; line-height: 0;
  border-radius: 2px; overflow: hidden;
  box-shadow: 0 0 0 1.5px var(--surface);
}
.pav-flag .flag-ico, .pav-flag .flag-code {
  width: 15px; height: 11px; border-radius: 2px; vertical-align: top; box-shadow: none;
}
.pav-flag .flag-code { font-size: 0.44rem; }
.pmeta { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.pname { font-weight: 700; color: var(--ink); }
.psub { font-size: 0.76rem; color: var(--muted); font-weight: 600; }
.empty { text-align: center; color: var(--muted); font-weight: 600; padding: 26px 10px; }

/* ── Audience visitor-table controls: Recent/Loyal tabs + Per-page select ── */
.ppl-tabs { display: inline-flex; gap: 4px; padding: 4px 4px 12px; }
.ppl-tab {
  font: inherit; font-size: 0.82rem; font-weight: 700; color: var(--muted);
  background: transparent; border: 0; border-radius: var(--radius-pill, 999px);
  padding: 6px 15px; cursor: pointer; transition: background .14s ease, color .14s ease;
}
.ppl-tab:hover:not(.on) { color: var(--ink-2); }
.ppl-tab.on { background: var(--primary-soft); color: var(--primary); }
.ppl-foot { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; }
.ppl-foot .pager { flex: 1 1 auto; margin-top: 0; }
.ppl-size {
  display: inline-flex; align-items: center; gap: 8px; flex: none;
  font-size: 0.8rem; font-weight: 600; color: var(--muted); margin: 12px 2px 6px;
}
.ppl-size select {
  font: inherit; font-size: 0.82rem; font-weight: 700; color: var(--ink-2);
  background: var(--surface); border: 1px solid var(--line); border-radius: 8px;
  padding: 6px 26px 6px 10px; cursor: pointer; appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='7' viewBox='0 0 10 7'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23888' stroke-width='1.6' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 9px center;
}
.ppl-size select:hover { border-color: var(--muted-2); color: var(--ink); }

/* ── Person detail (drill-down) ── */
.back {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 0.83rem; font-weight: 700; color: var(--muted); margin-bottom: 16px;
}
.back:hover { color: var(--primary); }
.vtags { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 8px; }
.chip-plain {
  font-size: 0.76rem; font-weight: 700; color: var(--ink-2);
  background: var(--line-soft); border-radius: 999px; padding: 4px 11px;
}
.vseen b + * { }
.vstat-strip.vstat-6 { grid-template-columns: repeat(6, 1fr); }

/* journey path */
.journey { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.jstep {
  font-size: 0.8rem; font-weight: 700; color: var(--ink-2);
  background: var(--primary-soft); border: 1px solid var(--primary-tint);
  border-radius: 8px; padding: 6px 11px; white-space: nowrap;
}
.jarrow { color: var(--muted-2); font-weight: 700; }

/* event stream */
.ev-list { display: flex; flex-direction: column; }
.ev-row {
  display: grid; grid-template-columns: 12px 78px auto 1fr; gap: 10px; align-items: center;
  padding: 9px 2px; border-bottom: 1px solid var(--line-soft); font-size: 0.83rem;
}
.ev-row:last-child { border-bottom: 0; }
.ev-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--muted-2); }
.ev-play { background: var(--primary); }
.ev-complete { background: var(--good); }
.ev-pause { background: var(--muted); }
.ev-share { background: var(--c4); }
.ev-subscribe { background: var(--c3); }
.ev-pageview { background: var(--c5); }
.ev-when { color: var(--muted); font-weight: 700; font-variant-numeric: tabular-nums; }
.ev-type { color: var(--ink); font-weight: 700; text-transform: capitalize; }
.ev-detail { color: var(--muted); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ── Flag icons (self-hosted SVG files in /flags; emoji flags don't render on Win/Chrome) ── */
.flag-ico {
  display: inline-block;
  width: 20px; height: 14px; flex: none;
  border-radius: 2px; vertical-align: -2px;
  object-fit: cover;
  box-shadow: inset 0 0 0 1px rgba(22, 20, 18, 0.09);
}
/* Fallback for a country without a drawn flag: a compact code chip the same 20x14 footprint so mixed
   rows stay aligned. Monospace-ish tabular digits, muted so real flags still lead the eye. */
.flag-code {
  display: inline-grid; place-items: center;
  width: 20px; height: 14px; flex: none;
  border-radius: 2px; vertical-align: -2px;
  font-size: 0.56rem; font-weight: 700; letter-spacing: 0.02em;
  color: var(--muted); background: var(--sand);
  box-shadow: inset 0 0 0 1px rgba(22, 20, 18, 0.09);
}

/* Clickable Top-N row (opens that entity's detail drawer) */
.row-link { cursor: pointer; }
.row-link .row-name { transition: color .12s; }
.row-link:hover .row-name { color: var(--primary); }

/* ── Shared pager (visitors table + breakdown table) ── */
.pager {
  display: flex; align-items: center; justify-content: space-between;
  gap: 14px; flex-wrap: wrap; margin-top: 16px;
}
.pager:empty { display: none; }
.pg-info { font-size: 0.8rem; color: var(--muted); font-weight: 600; font-variant-numeric: tabular-nums; }
.pg-btns { display: inline-flex; align-items: center; gap: 3px; }
.pg-num, .pg-nav {
  font: inherit; font-size: 0.82rem; font-weight: 700; color: var(--ink-2);
  background: var(--surface); border: 1px solid var(--line); border-radius: 8px;
  min-width: 32px; height: 32px; padding: 0 8px; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  font-variant-numeric: tabular-nums;
}
.pg-num:hover, .pg-nav:hover:not([disabled]) { border-color: var(--muted-2); color: var(--ink); }
.pg-num.on { color: #fff; background: var(--primary); border-color: var(--primary); }
.pg-nav[disabled] { color: var(--muted-2); opacity: 0.5; cursor: default; }
.pg-gap { color: var(--muted-2); font-weight: 700; padding: 0 2px; }

/* ── Breakdown table (generic ranked drill-down) ── */
.tbl.breakdown { min-width: 520px; }
.tbl.breakdown th.rank, .tbl.breakdown td.rank { width: 42px; color: var(--muted); font-weight: 700; }
.tbl.breakdown td.name { padding-top: 9px; padding-bottom: 9px; }
.bd-name { display: inline-flex; align-items: center; gap: 8px; color: var(--ink); font-weight: 700; }
.bd-bar { display: block; height: 5px; margin-top: 6px; max-width: 320px; background: var(--line-soft); border-radius: 4px; overflow: hidden; }
.bd-bar > span { display: block; height: 100%; background: var(--primary-tint); border-radius: 4px; }
.tbl td.muted { color: var(--muted); }
.bd-trend { font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.bd-trend.up { color: var(--good); }
.bd-trend.down { color: var(--bad); }
.bd-trend.flat { color: var(--muted); }

/* Back link rendered as a CSP-clean button (breakdown page) */
button.back { font: inherit; background: none; border: 0; cursor: pointer; }

/* ── Collapsible lead paragraph (item 15) ──────────────────────────────────
   The lead is HIDDEN by default and revealed from the account menu's
   "Show/Hide answers" item (state kept in sessionStorage). Height animates via
   the grid 0fr/1fr trick (no JS measuring); the bottom margin only applies when
   open so a collapsed lead leaves no gap above the section below. */
.answer-block { margin: 0; transition: margin .32s cubic-bezier(.4, 0, .2, 1); }
.answer-block:not(.is-collapsed) { margin-bottom: 34px; }
.answer-grid {
  display: grid; grid-template-rows: 1fr;
  transition: grid-template-rows .32s cubic-bezier(.4, 0, .2, 1);
}
.answer-clip { overflow: hidden; min-height: 0; }
.answer-clip .answer { margin: 0; }
.answer-block.is-collapsed .answer-grid { grid-template-rows: 0fr; }
@media (prefers-reduced-motion: reduce) {
  .answer-block, .answer-grid { transition: none; }
}

/* ── Live page (real-time board) ──────────────────────────────────────────── */
/* Header "Live" pill (stands in for the range selector on live.html) */
.live-pill {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 0.83rem; font-weight: 700; color: var(--ink-2);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 999px; padding: 7px 13px; white-space: nowrap;
}
.live-hero {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: 22px 24px 14px; margin-bottom: 18px;
}
.live-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.live-now { display: inline-flex; align-items: center; gap: 8px; }
.live-now-label { font-size: 0.72rem; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: 0.06em; }
.live-rate-wrap { display: inline-flex; align-items: center; gap: 6px; font-size: 0.72rem; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: 0.06em; }
.live-rate {
  font: inherit; font-size: 0.8rem; font-weight: 600; text-transform: none; letter-spacing: 0;
  color: var(--ink-2); background: var(--surface); border: 1px solid var(--line);
  border-radius: 8px; padding: 3px 8px; cursor: pointer;
}
.live-rate:hover { border-color: var(--muted-2); }
.live-rate:focus-visible { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }
.live-big { font-size: 3.2rem; font-weight: 700; letter-spacing: -0.03em; line-height: 1.04; margin: 6px 0 2px; font-variant-numeric: tabular-nums; color: var(--ink); }
.live-chart-wrap { margin-top: 10px; }
.live-area { display: block; width: 100%; height: 140px; }
.live-axis { display: flex; justify-content: space-between; font-size: 0.72rem; font-weight: 600; color: var(--muted-2); margin-top: 2px; }

.live-kpis {
  display: grid; grid-template-columns: repeat(4, 1fr);
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); overflow: hidden; margin-bottom: 18px;
}
.live-kpi { padding: 16px 20px; border-left: 1px solid var(--line); }
.live-kpi:first-child { border-left: 0; }
.live-kpi-v { font-size: 1.5rem; font-weight: 700; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; color: var(--ink); }
.live-kpi-l { font-size: 0.72rem; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: 0.03em; margin-top: 3px; }

.grid4-live { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 22px; }

/* Live event feed */
.lf-list { display: flex; flex-direction: column; }
.lf-row {
  display: grid; grid-template-columns: 10px 108px minmax(0, 1fr) auto auto; gap: 12px; align-items: center;
  padding: 9px 2px; border-bottom: 1px solid var(--line-soft); font-size: 0.84rem;
}
.lf-row:last-child { border-bottom: 0; }
.lf-row:first-child { animation: lf-in 0.42s cubic-bezier(0.4, 0, 0.2, 1); }
.lf-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.lf-act { font-weight: 700; color: var(--ink); text-transform: capitalize; white-space: nowrap; }
.lf-page { color: var(--ink-2); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lf-loc { display: inline-flex; align-items: center; gap: 6px; color: var(--muted); font-weight: 600; white-space: nowrap; }
.lf-ago { color: var(--muted-2); font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
@keyframes lf-in { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .lf-row:first-child { animation: none; }
}

/* ── Responsive ── */
@media (max-width: 860px) {
  .kpis { grid-template-columns: repeat(2, 1fr); }
  .kpi:nth-child(odd) { border-left: 0; }
  .kpi:nth-child(n+3) { border-top: 1px solid var(--line); }
  .vstat-strip, .vstat-strip.vstat-6 { grid-template-columns: repeat(2, 1fr); }
  .vstat:nth-child(odd) { border-left: 0; }
  .vstat:nth-child(n+3) { border-top: 1px solid var(--line); }
  .grid4, .strips, .two-col, .tiles { grid-template-columns: 1fr; }
  .qoe-grid { grid-template-columns: 1fr 1fr; }
  .live-kpis { grid-template-columns: repeat(2, 1fr); }
  .live-kpi:nth-child(odd) { border-left: 0; }
  .live-kpi:nth-child(n+3) { border-top: 1px solid var(--line); }
  .grid4-live { grid-template-columns: 1fr; }
}
@media (max-width: 640px) {
  /* Header reflows to two rows: the title line, then the analytics controls line.
     Below ~640px the title plus the controls no longer fit on one row: .top is
     nowrap and .top-r is flex:none, so the title (min-width:0) collapsed to
     zero width while the account menu slid off the right edge and the page
     scrolled sideways. Wrapping .top-r to its own full-width row restores the
     title, and a wrap-enabled .controls guarantees nothing overflows at any width.
     Covers both the analytics chrome and the account chrome (both mount .controls). */
  .top { flex-wrap: wrap; row-gap: 12px; }
  .top-r { flex: 1 1 100%; }
  .controls { width: 100%; flex-wrap: wrap; row-gap: 8px; }
  /* Account menu hugs the right edge; the country/range/answers controls sit left. */
  .controls .acct-dd { margin-left: auto; }
}
@media (max-width: 560px) {
  .ev-row { grid-template-columns: 10px 64px 1fr; }
  .ev-row .ev-detail { display: none; }
  .lf-row { grid-template-columns: 10px auto minmax(0, 1fr) auto; }
  .lf-page, .lf-cc { display: none; }
}
@media (max-width: 560px) {
  .answer { font-size: 1.18rem; }
  .controls { gap: 6px; }
  .dd-btn { padding: 8px 9px; }
  .kpis { grid-template-columns: 1fr 1fr; }
}

/* ── Mobile: collapse the filter controls into a right-side sheet ────────────
   The account chrome keeps its two-row reflow (the block above); only the analytics
   chrome (the one carrying .filter-cluster / .filters-toggle) is retargeted here, via
   :has() so it out-specifies that block without touching it. The bar stays a single
   row - just the filter toggle and the avatar on the right - and the country / range /
   answers controls move into an off-canvas sheet that slides in from the right, matching
   the details modal's surface, backdrop and close affordance. */
@media (max-width: 640px) {
  .top-r:has(.filter-cluster) { flex: none; }
  .controls:has(.filters-toggle) { width: auto; flex-wrap: nowrap; row-gap: 0; }
  .controls:has(.filters-toggle) .acct-dd { margin-left: 0; }

  /* Drop the header's backdrop-filter on mobile. It made .top a containing block for its
     position:fixed descendants, so the sheet's scrim (emitted inside .controls, a sibling of
     the sheet) resolved inset:0 against the ~98px header strip instead of the viewport and only
     dimmed the header. Keeping the scrim inside .controls (not on <body>) is what lets the sheet
     -- also inside .top's stacking context -- paint above it; a body scrim wins the root cascade
     over the sheet and swallows every tap. Solid header background stands in for the blur. */
  .top { backdrop-filter: none; -webkit-backdrop-filter: none; background: var(--surface); }

  /* The sheet itself: .filter-cluster stops being transparent and becomes the panel. It uses
     vh/dvh for height so its size never depended on the (now removed) containing block; top:0
     right:0 anchors it to the viewport corner. dvh tracks the mobile URL bar. */
  .filter-cluster {
    display: flex; flex-direction: column; gap: 18px;
    position: fixed; top: 0; right: 0; height: 100vh; height: 100dvh;
    width: min(320px, 86vw);
    padding: 16px 18px calc(20px + env(safe-area-inset-bottom));
    background: var(--surface); border-left: 1px solid var(--line);
    box-shadow: -18px 0 40px rgba(24, 18, 16, 0.16);
    z-index: 310; overflow-y: auto; overscroll-behavior: contain;
    transform: translateX(100%);
    transition: transform .26s cubic-bezier(0.22, 1, 0.36, 1);
  }
  body.filters-open .filter-cluster { transform: translateX(0); }
  body.filters-open { overflow: hidden; }
  /* The closed sheet is position:fixed at translateX(100%), so it sits one panel-width off the
     right edge and would extend the document's horizontal scroll region. Clip it on .app (the
     full-width app wrapper), not on html: overflow set on the root element propagates to the
     viewport and leaves documentElement.scrollWidth unclipped, so the phone would still scroll
     sideways. clip (not hidden) adds no scroll container, so vertical scroll and position:sticky
     stay intact; the open sheet sits on-screen (x within the viewport) so it is never clipped.
     Internal scrollers carry their own overflow-x:auto. */
  .app { overflow-x: clip; }

  .fdrawer-backdrop {
    display: block; position: fixed; inset: 0;
    background: rgba(24, 18, 16, 0.42);
    opacity: 0; pointer-events: none;
    transition: opacity .26s ease; z-index: 300;
  }
  body.filters-open .fdrawer-backdrop { opacity: 1; pointer-events: auto; }

  /* Sheet header (mirrors .dmodal-h / .dmodal-x). */
  .fdrawer-h {
    display: flex; align-items: center; justify-content: space-between;
    padding-bottom: 14px; border-bottom: 1px solid var(--line);
  }
  .fdrawer-title { font-size: 1.02rem; font-weight: 700; letter-spacing: -0.01em; }
  .fdrawer-x {
    display: inline-flex; align-items: center; justify-content: center;
    width: 28px; height: 28px; border: 0; border-radius: 50%;
    background: none; color: var(--muted); cursor: pointer;
  }
  .fdrawer-x:hover { background: var(--line-soft); color: var(--ink); }
  .fdrawer-x:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--primary-soft); }

  /* Each control stacks under an uppercase micro-label, full width. */
  .fgroup { display: flex; flex-direction: column; gap: 8px; }
  /* The site picker moves off the cramped bar into the sheet: hide the inline switcher
     and reveal its in-sheet twin (the .fgroup rule above already lays it out). On pages
     with no filter sheet (settings, visitor - .controls is [hidden]) the inline switcher
     stays put, so the site picker is never stranded. */
  .top:has(.controls:not([hidden])) .site { display: none; }
  .top .fgroup-site { display: flex; }
  .fgroup-lab {
    display: block;
    font-size: 0.7rem; font-weight: 700; letter-spacing: 0.05em;
    text-transform: uppercase; color: var(--muted-2);
  }
  .filter-cluster .dd { width: 100%; }
  .filter-cluster .dd-btn,
  .filter-cluster .cc-btn,
  .filter-cluster .range-trigger { width: 100%; max-width: none; }
  .filter-cluster .cc-btn { justify-content: space-between; }
  .filter-cluster .range-trigger { justify-content: space-between; height: auto; padding: 8px 4px 8px 2px; }

  /* Dropdown menus open in-flow inside the sheet (not as floating popovers) so they
     can't be clipped by the sheet's own scroll or spill off the viewport edge. */
  .filter-cluster .dd-menu {
    position: static; width: 100%; min-width: 0;
    border: 0; border-top: 1px solid var(--line-soft); border-radius: 0;
    box-shadow: none; padding: 8px 0 0; margin-top: 8px;
  }
  .filter-cluster .cc-list { max-height: 40vh; }

  /* The trigger button, next to the avatar. */
  .filters-toggle {
    display: inline-flex; align-items: center; justify-content: center;
    position: relative; width: 38px; height: 38px; flex: none;
    border: 1px solid var(--line); border-radius: 10px;
    background: var(--surface); color: var(--ink-2); cursor: pointer;
  }
  .filters-toggle:hover { border-color: var(--muted-2); color: var(--ink); }
  .filters-toggle:focus-visible { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
  .fdot {
    display: none; position: absolute; top: 6px; right: 6px;
    width: 7px; height: 7px; border-radius: 50%;
    background: var(--primary); box-shadow: 0 0 0 2px var(--surface);
  }
  .filters-toggle.has-active .fdot { display: block; }
}
@media (max-width: 640px) and (prefers-reduced-motion: reduce) {
  .filter-cluster, .fdrawer-backdrop { transition: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   Account area (settings / billing / plan / domains)
   Account-scoped pages sit behind the avatar menu. They drop the analytics
   chrome (site switcher, tabs, range/country/answers controls) for a "Back to
   dashboard" control + a left settings rail. Everything here is layered on the
   primitives above (.panel/.sec-h/.tbl/.tag/.tile/.pav/.pcell); only the deltas
   below are new. No side-stripe accents; active states are soft fills.
   ══════════════════════════════════════════════════════════════════════════ */

/* "Back to dashboard" control in the account header (mirrors .dd-btn geometry) */
.back-link {
  display: inline-flex; align-items: center; gap: 6px;
  font: inherit; font-size: 0.83rem; font-weight: 600; color: var(--ink-2);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 10px; padding: 8px 12px 8px 10px; cursor: pointer; white-space: nowrap;
}
.back-link svg { color: var(--muted); }
.back-link:hover { border-color: var(--muted-2); color: var(--ink); }
.back-link:hover svg { color: var(--ink-2); }
.back-link:focus-visible { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }

/* Current-page marker inside the avatar menu (ink on soft fill, not primary-on-soft) */
.dd-link[aria-current="page"] { color: var(--ink); background: var(--primary-soft); font-weight: 700; }

/* Forms */
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 15px 18px; margin-top: 4px; }
.field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.field.col-2 { grid-column: 1 / -1; }
.field label { font-size: 0.78rem; font-weight: 700; color: var(--ink-2); }
.hint { font-size: 0.78rem; color: var(--muted); font-weight: 600; }
.input, .select {
  font: inherit; font-size: 0.875rem; font-weight: 600; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line); border-radius: 9px;
  padding: 9px 12px; width: 100%;
}
.input::placeholder { color: var(--muted); font-weight: 500; }
.input:focus, .select:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }
.input:disabled, .select:disabled { background: var(--line-soft); color: var(--muted); cursor: not-allowed; }
.input.mono, .mono { font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; letter-spacing: -0.01em; }
.mono { font-size: 0.82rem; }
.form-actions { display: flex; align-items: center; gap: 10px; margin-top: 16px; flex-wrap: wrap; }

/* Crypto payout field: live offline format + checksum feedback (see app.js validatePayoutAddress).
   Reused by the onboarding wizard and the Settings add-method form to recolor the field border. */
.input.is-ok { border-color: var(--good); }
.input.is-ok:focus { box-shadow: 0 0 0 3px var(--good-soft); }
.input.is-bad { border-color: var(--bad); }
.input.is-bad:focus { box-shadow: 0 0 0 3px color-mix(in srgb, var(--bad) 18%, transparent); }

/* Buttons (primary / bordered neutral / destructive) */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  font: inherit; font-size: 0.85rem; font-weight: 700; color: #fff;
  background: var(--primary); border: 1px solid var(--primary); border-radius: 10px;
  padding: 9px 16px; cursor: pointer; white-space: nowrap;
}
.btn:hover { filter: brightness(0.96); }
.btn:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.btn-secondary { color: var(--ink-2); background: var(--surface); border-color: var(--line); }
.btn-secondary:hover { color: var(--ink); border-color: var(--muted-2); filter: none; }
.btn.btn-danger { color: var(--bad); background: var(--surface); border-color: var(--line); }
.btn.btn-danger:hover { border-color: var(--bad); background: #fef2f2; filter: none; }
.btn.btn-danger:focus-visible { outline-color: var(--bad); }
.btn-sm { padding: 6px 12px; font-size: 0.8rem; }

/* Badge extensions (reset the .tag top margin inside tables + inline runs) */
.tag.muted { color: var(--ink-2); background: var(--line-soft); }
.tag.primary { color: var(--ink); background: var(--primary-tint); }
.tag.inline, table.tbl .tag { margin-top: 0; vertical-align: middle; }

/* Balance tiles (reuse .tiles/.tile grid) */
.tile-v { font-size: 1.5rem; font-weight: 700; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; color: var(--ink); }
.tile-sub { font-size: 0.76rem; color: var(--muted); font-weight: 600; margin-top: 4px; }

/* Panel wrapping a full-bleed table: header bar + edge-aligned rows */
.panel.flush { padding: 0; overflow: hidden; }
.panel-h { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 16px 20px; border-bottom: 1px solid var(--line); }
.panel-h h2 { font-size: 1.02rem; font-weight: 700; letter-spacing: -0.01em; margin: 0; }
.panel-h .note { font-size: 0.8rem; color: var(--muted); font-weight: 600; }
.panel.flush table.tbl th:first-child, .panel.flush table.tbl td:first-child { padding-left: 20px; }
.panel.flush table.tbl th:last-child, .panel.flush table.tbl td:last-child { padding-right: 20px; }

/* Method / plan rows (payout method, current plan) */
.method { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; margin-top: 4px; }
.method-name { font-size: 0.95rem; font-weight: 700; color: var(--ink); display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.method-main { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.acct-note { font-size: 0.82rem; color: var(--muted); font-weight: 600; margin: 12px 0 0; }
/* Stacked methods (the plan page's feature list) read as one list rather than three loose rows. */
.method + .method { border-top: 1px solid var(--line-soft); margin-top: 14px; padding-top: 14px; }

/* Inline input + control row (invite teammate, create key) */
.row-inline { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 4px; }
.row-inline .input { flex: 1; min-width: 220px; }
.row-inline .select { flex: none; width: auto; }

/* Install-snippet reveal (shown once a domain is added); full border, not a side stripe */
.reveal {
  display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  background: var(--primary-soft); border: 1px solid var(--primary-tint);
  border-radius: 10px; padding: 12px 14px; margin-top: 4px;
}
.reveal-main { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.reveal-key { font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; font-size: 0.85rem; font-weight: 700; color: var(--ink); letter-spacing: -0.01em; word-break: break-all; }
.reveal p { margin: 0; font-size: 0.78rem; color: var(--ink-2); font-weight: 600; }

/* Inline validation line under the add-domain row (a taken/invalid domain), and the paired
   action buttons in a domains-table cell. */
.row-err { color: var(--bad); font-size: 0.8rem; font-weight: 600; margin: 8px 0 0; }
/* Live wallet-address feedback under the onboarding payout row (crypto only); ok/bad recolor it, the
   field border is recolored by .is-ok/.is-bad above. Hidden until there is something to say. */
.row-help { color: var(--ink-2); font-size: 0.8rem; font-weight: 600; margin: 8px 0 0; }
.row-help[data-state="ok"] { color: var(--good); }
.row-help[data-state="bad"] { color: var(--bad); }
.row-actions { display: inline-flex; gap: 4px; align-items: center; white-space: nowrap; }

.field-block { display: flex; flex-direction: column; gap: 8px; }
/* A .field-block sets display:flex, which outranks the UA [hidden]{display:none}; honour hidden
   explicitly so the PayPal/Crypto add-method toggle (and the wizard asset picker) actually swap. */
.field-block[hidden] { display: none; }


/* Install-snippet box: the one-line <script> a client pastes into their site. Shared by the first-run
   Done step and the Sites list. Monospace, wraps, quiet raised fill. */
.pixel-code {
  margin: 0; padding: 11px 13px; background: var(--sand); border: 1px solid var(--line);
  border-radius: 10px; overflow-x: auto;
}
.pixel-code code {
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 0.8rem; color: var(--ink); white-space: pre-wrap; word-break: break-all; line-height: 1.5;
}


/* Destructive zone: top hairline, no side stripe. Sits inside a flush panel, so
   it carries its own 20px side padding to line up with the table cells above. */
.danger { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; border-top: 1px solid var(--line); padding: 16px 20px 18px; }
.danger .danger-main { min-width: 0; }
.danger strong { display: block; color: var(--ink); font-weight: 700; font-size: 0.9rem; margin-bottom: 2px; }
.danger p { margin: 0; font-size: 0.82rem; color: var(--muted); font-weight: 600; }

@media (max-width: 860px) {
  .form-grid { grid-template-columns: 1fr; }
}

/* ==========================================================================
   Publisher-area redesign ("vidstats publisher area") - ported design system.
   Left-sidebar IA, Quicksand display face, bento/segmented/drawer primitives.
   Supersedes the airy-editorial skin above; old skin removed at cutover.
   The artifact's Classic/Console variants and the ui-only toggles are dropped.
   ========================================================================== */
@font-face{font-family:'Quicksand';font-style:normal;font-weight:300 700;font-display:swap;src:url('fonts/quicksand-var.woff2') format('woff2')}
:root{
  --accent:#0070ff;--accent-rgb:0,112,255;--accent-soft:rgba(0,112,255,0.1);--line-hover:#d1d1d6;
  --green:#34c759;--green-rgb:52,199,89;--coral:#ff3b30;--violet:#5856d6;--violet-rgb:88,86,214;
  --amber:#6c6c70;--amber-bg:rgba(108,108,112,0.12);
  --ok:#248a3d;--ok-rgb:36,138,61;--wait:#6c6c70;--wait-rgb:108,108,112;
  --ink:#1c1c1e;--muted:#6c6c70;--label:#3c3c43;--line:#e5e5ea;
  --bg:#f2f2f7;--card:#ffffff;--sand:#e9e9ee;
  --nav:#12162a;--nav-2:#191e37;--nav-ink:#eef1fb;--nav-muted:#98a1bd;--nav-line:rgba(255,255,255,0.08);
  --radius:22px;--radius-sm:14px;--radius-pill:999px;
  --shadow:0 22px 46px -36px rgba(20,30,60,0.5);
  --z-sticky:100;--z-nav:120;--z-pop:200;--z-backdrop:300;--z-drawer:310;
  /* Typeface for the redesign layer. The Stripe skin below is a later :root at the same
     specificity, so it wins and the sitewide default is its measured stack, not this one;
     Quicksand stays a picker option. Either way the Appearance picker overrides both by setting
     --font-sans on <html> (devApplyFont), which beats any :root value. */
  --font-sans:'Quicksand',system-ui,-apple-system,'Segoe UI',sans-serif;
}
html,body{margin:0}
body{
  font-family:var(--font-sans);
  color:var(--ink);background:var(--bg);
  font-weight:500;font-size:15px;line-height:1.5;
  -webkit-font-smoothing:antialiased;
}
.fa{height:1em;width:auto;vertical-align:-0.125em;display:inline-block}
h1,h2,h3,h4{margin:0;letter-spacing:-0.01em;text-wrap:balance}
a{color:inherit;text-decoration:none}
button{font-family:inherit;cursor:pointer}
input,select{font-family:inherit}

/* shell */
.app{display:grid;grid-template-columns:210px 1fr;min-height:100vh}

/* sidebar (slimmer: 210px column, tighter nav rows) */
.side{background:var(--nav);color:var(--nav-ink);display:flex;flex-direction:column;position:sticky;top:0;height:100vh;padding:20px 12px}
.brand{display:flex;align-items:center;gap:10px;padding:6px 8px 20px}
.brand-mark{width:34px;height:34px;border-radius:11px;background:linear-gradient(140deg,var(--accent),#4d9bff);display:grid;place-items:center;color:#fff;font-size:0.95rem}
.brand-mark .fa{font-size:1rem}
.brand-name{font-weight:700;font-size:1.12rem;letter-spacing:-0.02em}
.brand-tag{font-size:0.66rem;font-weight:700;letter-spacing:0.1em;text-transform:uppercase;color:var(--nav-muted);margin-top:1px}
.nav{display:flex;flex-direction:column;gap:3px;margin-top:6px}
.nav-b{display:flex;align-items:center;gap:10px;padding:9px 11px;border-radius:var(--radius-sm);color:var(--nav-muted);background:none;border:0;text-align:left;font-size:0.9rem;font-weight:600;width:100%;transition:background .16s ease,color .16s ease}
.nav-b .fa{font-size:0.95rem;width:18px;text-align:center;opacity:.9}
.nav-b:hover{background:rgba(255,255,255,0.06);color:var(--nav-ink)}
.nav-b.on{background:rgba(var(--accent-rgb),0.2);color:#fff}
.nav-b.on .fa{color:#ffb088;opacity:1}
.side-foot{margin-top:auto;padding-top:16px;border-top:1px solid var(--nav-line);display:flex;flex-direction:column;gap:10px}
.payout-mini{background:var(--nav-2);border-radius:var(--radius-sm);padding:14px}
.payout-mini .pm-l{font-size:0.7rem;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:var(--nav-muted);display:flex;align-items:center;gap:7px}
.payout-mini .pm-v{font-size:1.28rem;font-weight:700;margin-top:4px}
.payout-mini .pm-s{font-size:0.78rem;color:var(--nav-muted);margin-top:1px}
.help-b{display:flex;align-items:center;gap:10px;color:var(--nav-muted);font-size:0.84rem;font-weight:600;padding:4px 8px}
.help-b:hover{color:var(--nav-ink)}
/* Inline, lighter-weight scope note next to a klabel. */
.tip-lite{font-weight:500;color:var(--muted);font-size:0.72rem;margin-left:4px}

/* topbar */
.main{display:flex;flex-direction:column;min-width:0}
.top{display:flex;align-items:center;gap:18px;padding:20px 34px;background:rgba(244,246,251,0.82);backdrop-filter:saturate(1.2) blur(6px);position:sticky;top:0;z-index:var(--z-sticky);border-bottom:1px solid var(--line);flex-wrap:wrap;row-gap:12px}
.top-h{min-width:0}
.top-h h1{font-size:1.32rem;font-weight:700}
.top-h p{margin:2px 0 0;color:var(--muted);font-size:0.86rem;font-weight:500}
.top-r{margin-left:auto;display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.switcher{display:inline-flex;align-items:center;gap:9px;background:var(--card);border:1px solid var(--line);border-radius:var(--radius-pill);padding:8px 14px;font-weight:600;font-size:0.88rem;box-shadow:var(--shadow)}
.switcher .dot{width:7px;height:7px;border-radius:50%;background:var(--green)}
.switcher .fa{color:var(--muted);font-size:0.7rem}
.iconbtn{width:40px;height:40px;border-radius:50%;background:var(--card);border:1px solid var(--line);color:var(--label);display:grid;place-items:center;position:relative}
.iconbtn:hover{color:var(--ink);border-color:var(--line-hover)}
.iconbtn .fa{font-size:0.95rem}
.iconbtn .bump{position:absolute;top:9px;right:10px;width:7px;height:7px;border-radius:50%;background:var(--bad);border:2px solid var(--card)}
.avatar{width:40px;height:40px;border-radius:50%;background:linear-gradient(140deg,var(--primary),#ff9560);color:#fff;display:grid;place-items:center;font-weight:700;font-size:0.9rem}
/* Mobile nav: hamburger (top bar) + dimming scrim for the off-canvas sidebar drawer. Both are inert on
   desktop (hamburger display:none here, scrim display:none) and only come alive in the <=820px block. */
.nav-toggle{display:none;width:42px;height:42px;flex:none;border-radius:12px;border:1px solid var(--line);background:var(--card);color:var(--label);place-items:center;transition:color .16s ease,border-color .16s ease}
.nav-toggle:hover{color:var(--ink);border-color:var(--line-hover)}
.nav-toggle .fa{font-size:1.05rem}
.nav-toggle:focus-visible{outline:none;box-shadow:0 0 0 3px var(--primary-soft)}
.side-scrim{display:none}

/* Console layout (Appearance -> Layout -> Console): folds the sidebar into a top menu bar and tightens
   the grid, for people who want a denser, data-forward dashboard. Toggled by data-theme="console" on
   <html> (persisted as ws_layout, re-applied pre-paint by hydrate.js). Composes with dark mode, which
   rides on html.dark. Every rule outranks the base shell (attribute+class > class), so order is free.

   The bar itself is desktop-only. It holds the brand plus seven nowrap nav items plus the payout pill
   plus the avatar in a fixed 64px row, which stops fitting around 1080px; and the rules below override
   .side's position/width/height without overriding its transform, so under the <=820px drawer block the
   bar inherited translateX(-100%) and console users had no navigation at all. Gating the bar to
   >=1081px makes console fall back to the default shell (sidebar drawer + hamburger) below it, with no
   duplicated rules. The density rules after this block are width-agnostic and stay ungated. */
@media (min-width:1081px){
/* overflow visible (not scroll) so the Behaviour dropdown below isn't clipped; align-items:center keeps
   the flat nav buttons at their natural height even though the group stretches to the full bar. */
/* Behaviour group can't stack vertically inside the horizontal bar, so on click its children drop as a
   panel anchored under the header (dark, matching the bar -- not the light card popovers). The group is
   stretched to the full 64px bar height so the panel anchors cleanly at the bar's bottom edge, and the
   bar's overflow is visible (above) so it isn't clipped. Click-toggled, not hover, so there's no gap to
   bridge; app.js suppresses the sidebar's auto-open here and dismisses it on an outside click. */
/* The hamburger is meaningless while the bar is up. Below 1081px the bar is gone and the default
   <=820px drawer block owns navigation, so this must stay inside the gate. */
}
/* The sidebar folds into a full-width top nav, so drop the sidebar layout's 1180px content
   cap: without this the content stays left-aligned under the edge-to-edge bar, leaving a
   lopsided right gutter (huge on a wide monitor). Console is the denser, full-width layout. */

/* content */
.wrap{padding:30px 34px 56px;max-width:1180px;width:100%}
.screen{display:none;animation:fade .22s ease}
.screen.on{display:block}
@keyframes fade{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}

/* generic bits */
.card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:22px;box-shadow:var(--shadow)}
.sec-h{display:flex;align-items:center;justify-content:space-between;margin:34px 0 15px}
.sec-h:first-child{margin-top:4px}
.sec-h h2{font-size:1.06rem;font-weight:700}
.sec-h .link{color:var(--accent);font-weight:700;font-size:0.85rem;display:inline-flex;align-items:center;gap:7px}
.klabel{font-size:0.72rem;font-weight:700;letter-spacing:0.07em;text-transform:uppercase;color:var(--muted)}
.btn{display:inline-flex;align-items:center;gap:9px;border-radius:var(--radius-pill);padding:11px 20px;font-weight:700;font-size:0.9rem;border:1px solid transparent;transition:transform .12s ease,box-shadow .16s ease,background .16s ease}
.btn:active{transform:translateY(1px)}
.btn-primary{background:var(--accent);color:#fff;box-shadow:0 12px 24px -14px rgba(var(--accent-rgb),0.9)}
.btn-primary:hover{background:#005fe6}
.btn-ghost{background:var(--card);color:var(--ink);border-color:var(--line)}
.btn-ghost:hover{border-color:var(--line-hover)}
.pill{display:inline-flex;align-items:center;gap:7px;border-radius:var(--radius-pill);padding:4px 11px;font-size:0.76rem;font-weight:700}
.pill .fa{font-size:0.7rem}
.pill-live{background:rgba(var(--ok-rgb),0.1);color:var(--ok)}
.pill-pend{background:rgba(var(--wait-rgb),0.11);color:var(--wait)}
.pill-up{background:rgba(var(--ok-rgb),0.1);color:var(--ok)}
.pill-neutral{background:var(--sand);color:var(--label)}

/* earnings monetization pipeline (ADR-066) as a rail funnel: one continuous connector threads through
   three nodes a publisher reads top to bottom - your plays -> content matches -> estimated earnings -
   with a dot flowing down it that MORPHS INTO A "$" once it passes the content-match node (only matched
   plays turn into money). The broker line and the refresh/updated controls live in the footnote, not
   their own stages. Tokens carry dark mode; the payoff node takes the accent. */
/* Each step is one block: its background is a gradient that is TRANSPARENT on the left (so the connector
   rail and the flowing dot stay visible behind the icon station) and blends into a per-step TINT on the
   right behind the label + value. The tint deepens down the funnel (play -> match -> payoff). The rail
   line + dot sit BEHIND the blocks (z 0/1 < node z 2); the dot passes behind each icon and morphs into a
   "$" once it clears the content-match step. The rail is centred on the icons: icon centre =
   railflow padding-left (16) + node padding-left (6) + icon width/2 (22) = 44. */
.railflow{position:relative;background:var(--sand);border-radius:14px;padding:16px}
.rf-nodes{position:relative}
/* Flat nodes (no tint, no gradient). Contiguous (no inter-node margin) so the per-node rail joins into one
   continuous line; vertical rhythm comes from the node padding. */
.rf-node{position:relative;display:flex;align-items:center;gap:16px;padding:16px 4px 16px 6px}
/* the connector rail, drawn per node behind the icon column. Because each icon is vertically centred, the
   first node draws only from its 50% down and the last only to its 50% - so the rail lands exactly on the
   first and last icon centres regardless of body height (never past the payoff icon). */
.rf-node::before{content:"";position:absolute;z-index:0;left:26.5px;top:0;bottom:0;width:3px;border-radius:3px;background:rgba(var(--accent-rgb),.28)}
/* Clamp the rail to the first/last icon centres. Target by class, NOT :first-child/:last-child: the
   animated .rf-flow marker is the first child of .rf-nodes, so :first-child never matches a node and the
   top of the rail was drawing from the top of the first node (above the play icon) - the overshoot. */
.rf-node.play::before{top:50%}
.rf-node.payoff::before{bottom:50%}
/* the flowing marker: a dot until it clears the middle (content-match) node, then a "$". Travels the whole
   .rf-nodes box in percentage terms and fades at both ends, so it never needs pixel-exact endpoints. */
/* --rf-a / --rf-b are the travel endpoints (marker-box top at the first and last icon centres), set in JS
   by positionRailFlow so the marker never runs past the icons regardless of node height. The percentages
   are only a pre-measure fallback. */
.rf-flow{position:absolute;z-index:1;left:19px;width:18px;height:18px;display:grid;place-items:center;
  --rf-a:8%;--rf-b:86%;animation:rfDrop 3.4s cubic-bezier(.5,0,.5,1) infinite}
.rf-dot{grid-area:1/1;width:11px;height:11px;border-radius:50%;background:var(--accent);
  box-shadow:0 0 0 5px rgba(var(--accent-rgb),.16),0 0 12px 3px rgba(var(--accent-rgb),.55);
  animation:rfDotFade 3.4s cubic-bezier(.5,0,.5,1) infinite}
/* the "$" is deliberately a touch larger than the 11px dot so the morph reads as the value growing once
   the play is matched; it keeps the dot's z-index (1) so it threads behind the icons like the dot does. */
.rf-coin{grid-area:1/1;font-size:19px;font-weight:700;line-height:1;color:var(--accent);opacity:0;transform:scale(.3);
  text-shadow:0 0 10px rgba(var(--accent-rgb),.7);animation:rfCoin 3.4s cubic-bezier(.5,0,.5,1) infinite}
@keyframes rfDrop{0%{top:var(--rf-a);opacity:0}12%{opacity:1}88%{opacity:1}100%{top:var(--rf-b);opacity:0}}
/* the dot->$ swap happens at ~57%, just after the marker clears the middle (content-match) node's icon */
@keyframes rfDotFade{0%,55%{opacity:1}61%,100%{opacity:0}}
@keyframes rfCoin{0%,55%{opacity:0;transform:scale(.3)}62%{opacity:1;transform:scale(1.18)}70%,100%{opacity:1;transform:scale(1)}}
.rf-ico{position:relative;z-index:2;flex:none;width:44px;height:44px;display:grid;place-items:center;border-radius:12px;
  background:var(--accent-soft);color:var(--accent);border:3px solid var(--sand)}
.rf-ico svg{width:21px;height:21px}
.rf-body{flex:1;min-width:0}
.rf-k{font-size:0.95rem;font-weight:700;color:var(--ink)}
.rf-sub{font-size:0.82rem;font-weight:500;color:var(--muted);margin-top:2px}
/* the values: plain bold numbers for plays and matches, a solid accent pill only for the payoff */
.rf-v{flex:none;font-size:1.3rem;font-weight:700;letter-spacing:-.02em;line-height:1;color:var(--ink)}
.rf-node.match .rf-v{color:var(--accent)}
.rf-node.payoff .rf-v{padding:9px 15px;border-radius:999px;background:var(--accent);color:#fff;font-size:1.42rem}
.rf-node.payoff .rf-ico{background:var(--accent);color:#fff;border-color:var(--sand)}
.rf-node.payoff .rf-k{color:var(--accent)}
/* "What counts as a validated play?" disclosure under the first node */
.rf-crit{margin-top:8px}
.rf-crit summary{display:inline-flex;align-items:center;gap:6px;font-size:0.78rem;font-weight:700;color:var(--accent);cursor:pointer;list-style:none}
.rf-crit summary::-webkit-details-marker{display:none}
.rf-crit summary::before{content:"";width:6px;height:6px;border-right:1.6px solid currentColor;border-bottom:1.6px solid currentColor;transform:rotate(-45deg);transition:transform .15s ease}
.rf-crit[open] summary::before{transform:rotate(45deg)}
.rf-crit ul{margin:8px 0 2px;padding-left:16px;display:flex;flex-direction:column;gap:3px}
.rf-crit li{font-size:0.8rem;font-weight:500;color:var(--muted)}
/* low content-match: the node turns red and shows the padded fix block under the subtitle */
.rf-node.match.is-lowmatch .rf-ico{background:var(--card);color:var(--bad);border-color:var(--sand)}
.rf-node.match.is-lowmatch .rf-k{color:var(--bad)}
.rf-node.match.is-lowmatch .rf-v{color:var(--bad)}
.rf-alert{margin-top:11px;display:flex;gap:11px;align-items:flex-start;color:var(--bad);
  background:var(--bad-soft);border:1px solid var(--bad-soft);border-radius:10px;padding:13px 15px;max-width:520px}
.rf-alert-ic{flex:none;margin-top:1px}
.rf-alert-ic svg{width:18px;height:18px;display:block}
.rf-alert strong{display:block;font-size:0.9rem;font-weight:700;margin-bottom:4px}
.rf-alert p{margin:0;font-size:0.83rem;line-height:1.55}
.rf-alert-link{display:inline-flex;align-items:center;gap:6px;margin-top:10px;color:inherit;font-size:0.82rem;font-weight:700;text-decoration:underline;text-underline-offset:2px}
/* foot: the earn rule on the left, last-run stamp + recompute control on the right */
.rf-foot{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;
  margin:14px 4px 2px;font-size:0.74rem;font-weight:600;color:var(--muted)}
.rf-foot-rule{color:var(--ink)}
.rf-foot-meta{display:flex;align-items:center;gap:8px;margin-left:auto}
.mstage-refresh{flex:none;width:30px;height:30px;display:grid;place-items:center;border:1px solid var(--line);border-radius:9px;background:var(--card);color:var(--muted);cursor:pointer;transition:color .14s ease,border-color .14s ease,background .14s ease}
.mstage-refresh svg{width:15px;height:15px}
.mstage-refresh:hover{color:var(--accent);border-color:var(--accent);background:var(--accent-soft)}
.mstage-refresh:disabled{cursor:default;opacity:.6}
.mstage-refresh.is-spin svg{animation:mspin .8s linear infinite}
@keyframes mspin{to{transform:rotate(360deg)}}

/* "Your payouts" summary (earnings page): the plain-language balance a first-time publisher reads first,
   paired with a receipt that shows exactly how it is derived (lifetime earned - paid = balance) so the
   number is never a mystery. Two columns on desktop - what/when on the left, the reconciling receipt on
   the right - collapsing to a single stacked column on narrow screens. Named areas keep how-it-works under
   the headline and the month figure under the receipt in both layouts. */
.pay-sum{display:grid;grid-template-columns:1.1fr 1fr;gap:22px 30px;align-items:start;
  grid-template-areas:"head receipt" "how period"}
.pay-head{grid-area:head;min-width:0}
.pay-big{font-size:2.9rem;font-weight:700;letter-spacing:-0.03em;line-height:1;margin:6px 0 0;
  color:var(--primary);font-variant-numeric:tabular-nums}
.pay-clear .pay-big{color:var(--ink-h1)}
.pay-ahead .pay-big{color:var(--amber)}
.pay-lead{margin:14px 0 0;font-size:0.95rem;line-height:1.55;color:var(--ink-2);max-width:42ch;text-wrap:pretty}
.pay-when{margin:14px 0 0;display:flex;align-items:center;gap:8px;font-size:0.82rem;font-weight:600;color:var(--muted)}
.pay-when .fa{width:14px;height:14px;flex:none;color:var(--muted)}
/* receipt: a bank-statement style ledger. Row divider is a hairline; the balance total sits over a heavier
   rule so the "sum" reads at a glance. Inset onto --sand so it rhymes with the funnel panel above. */
.pay-receipt{grid-area:receipt;background:var(--sand);border:1px solid var(--line);border-radius:14px;padding:4px 18px}
.pay-line{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:13px 0;
  font-size:0.92rem;font-weight:600;color:var(--ink-2)}
.pay-line + .pay-line{border-top:1px solid var(--line)}
.pay-line-v{font-variant-numeric:tabular-nums;letter-spacing:-0.01em}
.pay-line-total{border-top:2px solid var(--line-hover,#d4d4dc);margin-top:1px;padding-top:15px}
.pay-line-total .pay-line-l{font-weight:700;color:var(--ink)}
.pay-line-total .pay-line-v{font-size:1.4rem;font-weight:700;color:var(--primary)}
.pay-clear .pay-line-total .pay-line-v{color:var(--ink-h1)}
.pay-ahead .pay-line-total .pay-line-v{color:var(--amber)}
.pay-period{grid-area:period;display:flex;align-items:baseline;gap:8px 10px;flex-wrap:wrap;margin:2px 2px 0;font-size:0.85rem}
.pay-period-l{font-weight:700;color:var(--ink-2)}
.pay-period-v{font-weight:700;font-variant-numeric:tabular-nums;color:var(--ink)}
.pay-period-s{color:var(--muted);font-weight:500}
/* how-it-works: a quiet accent disclosure, same affordance as the funnel's "what counts" details. */
.pay-how{grid-area:how;align-self:start;margin-top:2px}
.pay-how summary{display:inline-flex;align-items:center;gap:6px;font-size:0.8rem;font-weight:700;color:var(--accent);cursor:pointer;list-style:none}
.pay-how summary::-webkit-details-marker{display:none}
.pay-how summary::before{content:"";width:6px;height:6px;border-right:1.6px solid currentColor;border-bottom:1.6px solid currentColor;transform:rotate(-45deg);transition:transform .15s ease}
.pay-how[open] summary::before{transform:rotate(45deg)}
.pay-how ul{margin:10px 0 2px;padding-left:16px;display:flex;flex-direction:column;gap:5px}
.pay-how li{font-size:0.82rem;font-weight:500;line-height:1.5;color:var(--muted)}
/* "Payments to you": the ledger table under the balance card. Type reads as plain text for the ordinary
   settlement (only a bonus earns a chip), and a long operator reference truncates rather than forcing
   the whole table wider. */
.pay-kind{color:var(--muted);font-weight:600}
td.ref{color:var(--muted);max-width:22ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
@media (max-width:720px){
  .pay-sum{grid-template-columns:1fr;grid-template-areas:"head" "receipt" "period" "how";gap:18px}
  .pay-big{font-size:2.4rem}
}
@media (max-width:560px){
  .rf-node{gap:12px;padding:14px 8px 14px 4px}
  .rf-ico{width:38px;height:38px}
  .rf-node::before{left:21.5px}
  .rf-flow{left:14px}
  .rf-v{font-size:1.15rem}
  .rf-node.payoff .rf-v{font-size:1.3rem;padding:8px 13px}
}
@media (prefers-reduced-motion:reduce){
  .mstage-refresh.is-spin svg{animation:none}
  /* The earnings funnel marker keeps animating even under reduced-motion (owner decision): it is the
     point of the card, so it is not gated behind the OS animation preference. */
}

/* overview bento */
/* Overview onboarding strip: sits above the bento, dismissed for good via ws_help_banner_quiet. Tinted
   with the accent rather than filled, so it reads as guidance and never competes with the earnings card.
   [hidden] must win over display:flex - app.js reveals it after reading the stored choice. */
.help-banner{display:flex;align-items:center;gap:14px;margin-bottom:16px;padding:14px 16px;border:1px solid rgba(var(--accent-rgb),0.22);border-radius:var(--radius);background:rgba(var(--accent-rgb),0.06)}
.help-banner[hidden]{display:none}
.hb-ico{flex:none;width:34px;height:34px;display:grid;place-items:center;border-radius:50%;background:rgba(var(--accent-rgb),0.14);color:var(--accent)}
.hb-ico .fa{width:16px;height:16px}
/* flex:1 so the text block fills the row and pushes the close X (and CTA, when present) to the right
   edge. Without it a banner that carries no CTA (the pixel-attention alert) left the X butting against
   the text with dead space beside it. */
.hb-t{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.hb-t strong{font-size:0.92rem;font-weight:700;color:var(--ink)}
.hb-t span{font-size:0.82rem;font-weight:500;color:var(--muted)}
.hb-link{color:var(--accent);font-weight:700;text-decoration:underline;text-underline-offset:2px}
.hb-link:hover{text-decoration-thickness:2px}
.hb-cta{margin-left:auto;flex:none;display:inline-flex;align-items:center;gap:7px;border-radius:var(--radius-pill);padding:9px 17px;background:var(--accent);color:#fff;font-weight:700;font-size:0.85rem;white-space:nowrap;transition:filter .14s ease}
.hb-cta:hover{filter:brightness(1.08)}
.hb-x{flex:none;width:28px;height:28px;display:grid;place-items:center;border:0;border-radius:8px;background:none;color:var(--muted);cursor:pointer;transition:background .14s ease,color .14s ease}
.hb-x:hover{background:rgba(var(--accent-rgb),0.12);color:var(--ink)}
@media (max-width:640px){
  /* Flex-wrap reflowed the four items into an unreadable order (the close X landed mid-row, the CTA
     hung off a stray left margin). On a phone the banner reads as a small card: icon and close on the
     top line, the message spanning below them, the CTA on its own line under the message. */
  .help-banner{display:grid;grid-template-columns:auto 1fr auto;align-items:start;column-gap:12px;row-gap:10px}
  .hb-ico{grid-column:1;grid-row:1}
  .hb-t{grid-column:2;grid-row:1}
  .hb-x{grid-column:3;grid-row:1;margin:0}
  .hb-cta{grid-column:2 / 4;grid-row:2;margin:0;justify-self:start}
}
.bento{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}
.b-earn{grid-row:span 2;display:flex;flex-direction:column}
.b-earn .klabel{display:flex;align-items:center;justify-content:space-between}
.big-num{font-size:2.9rem;font-weight:700;letter-spacing:-0.03em;margin:10px 0 2px;line-height:1}
.sub{color:var(--muted);font-size:0.86rem}
.chart{margin-top:auto;padding-top:26px}
.bars{display:flex;align-items:flex-end;gap:9px;height:118px}
.bars .col{flex:1;display:flex;flex-direction:column;align-items:center;gap:8px;height:100%;justify-content:flex-end}
.bars i{width:100%;max-width:26px;border-radius:7px 7px 3px 3px;background:linear-gradient(var(--accent),rgba(var(--accent-rgb),0.45));display:block}
.bars .col.now i{background:linear-gradient(var(--accent),#ff9560)}
.bars small{font-size:0.66rem;color:var(--muted);font-weight:600}

/* Overview "Live watching now" tile */
.b-live{position:relative;overflow:hidden;display:flex;flex-direction:column}
.klabel-live{color:#7fd39a}
.livenow-v{font-size:1.9rem;font-weight:700;letter-spacing:-0.02em;margin:6px 0 0;line-height:1;font-variant-numeric:tabular-nums}
.livenow-l{color:var(--muted);font-size:0.8rem;margin-top:2px}
.livenow-legs{display:flex;gap:16px;margin-top:9px;font-size:0.76rem;color:var(--muted)}
.livenow-legs b{color:var(--ink-2);font-weight:700}
.livenow-spark{margin-top:auto;padding-top:12px;height:56px}
.livenow-spark .area{height:44px}

/* Slim Latest-statement tile (was the tall b-earn hero card) */
.earn-tile{display:flex;flex-direction:column}
.earn-tile .klabel{display:flex;align-items:center;justify-content:space-between}
.earn-tile .big-num{font-size:1.9rem;margin:6px 0 1px}
.earn-tile .sub{font-size:0.78rem}
.earn-tile .earn-row{display:flex;align-items:flex-end;justify-content:space-between;gap:12px;margin-top:auto;padding-top:10px}
.earn-tile .bars{height:42px;gap:3px;align-items:flex-end}
.earn-tile .bars i{width:6px;max-width:6px;border-radius:2px}
.earn-tile .bars small{display:none}

/* Overview KPI tile: lead Visits number + a Pageviews/Plays row, so all three hero-chart series have a
   matching headline number in one bento cell. */
.ov-kpis{display:flex;flex-direction:column}
.ov-kpi-lead .v{font-size:1.9rem}
.ov-kpi-row{display:flex;gap:18px;margin-top:auto;padding-top:12px;flex-wrap:wrap}
.ov-kpi-row>div{min-width:0}
.ov-kpi-row .v2{font-size:1.15rem;font-weight:700;letter-spacing:-0.01em;line-height:1.1}
.ov-kpi-row .l{font-size:0.76rem;color:var(--muted)}

/* Overview visitor chart (slim sibling of the Audience chart) */
.ov-chart{margin-top:14px}
.ov-chart-top{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:10px;flex-wrap:wrap}
.ov-chart-top h2{font-size:1rem;margin:0;letter-spacing:-0.01em}
.ov-title-wrap{display:flex;align-items:center;gap:6px;min-width:0}
/* Stripe-style help affordance: a small muted "?" that reveals a popover tooltip on hover/focus. */
.help-q{position:relative;display:inline-grid;place-items:center;width:16px;height:16px;border-radius:50%;
  background:var(--sand);color:var(--muted);font-size:0.68rem;font-weight:700;line-height:1;cursor:help;
  border:1px solid var(--line);flex:none;user-select:none;transition:background .15s ease,color .15s ease}
.help-q:hover,.help-q:focus-visible{background:var(--accent);color:#fff;border-color:var(--accent);outline:none}
.help-q::after{content:attr(data-tip);position:absolute;top:calc(100% + 8px);left:50%;transform:translateX(-50%) translateY(4px);
  width:260px;max-width:70vw;background:var(--ink);color:#fff;font-size:0.74rem;font-weight:500;line-height:1.4;
  letter-spacing:0;text-transform:none;padding:8px 10px;border-radius:8px;box-shadow:0 6px 20px rgba(0,0,0,.18);
  opacity:0;pointer-events:none;transition:opacity .15s ease,transform .15s ease;z-index:40}
.help-q::before{content:"";position:absolute;top:calc(100% + 3px);left:50%;transform:translateX(-50%);
  border:5px solid transparent;border-bottom-color:var(--ink);opacity:0;transition:opacity .15s ease;z-index:41}
.help-q:hover::after,.help-q:focus-visible::after{opacity:1;transform:translateX(-50%) translateY(0)}
.help-q:hover::before,.help-q:focus-visible::before{opacity:1}
.ov-chart-hint{font-size:0.72rem;color:var(--muted-2);margin-top:6px}
.kpi-seg{display:inline-flex;background:var(--sand);border:1px solid var(--line);border-radius:var(--radius-pill);padding:3px}
.kpi-b{border:0;background:transparent;color:var(--muted);font:inherit;font-size:0.78rem;font-weight:700;padding:6px 13px;border-radius:var(--radius-pill);cursor:pointer;transition:background .14s ease,color .14s ease}
.kpi-b.active{background:var(--card);color:var(--ink);box-shadow:0 1px 0 rgba(0,0,0,.28)}
.kpi-b:hover:not(.active){color:var(--ink-2)}

.stat{display:flex;flex-direction:column;gap:12px}
.stat .top-row{display:flex;align-items:flex-start;justify-content:space-between;gap:10px}
.stat .v{font-size:1.7rem;font-weight:700;letter-spacing:-0.02em}
.stat .l{color:var(--muted);font-size:0.85rem;font-weight:600}
.stat .meta{font-size:0.8rem;color:var(--muted);display:flex;align-items:center;gap:6px}

/* two-col */
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.grid2.wide{grid-template-columns:1.4fr 1fr}
/* Grid tracks default to min-width:auto, so a child with fixed-width inner columns (the Live barlists:
   face + value + flag) forces the track to its min-content and blows the whole page wider than the
   viewport. Letting the tracks shrink to their fr share confines each card and kills the h-scroll. */
.grid2>*{min-width:0}

/* table */
.wrap table,.drawer-b table{width:100%;border-collapse:collapse}
.wrap th,.drawer-b th{text-align:left;font-size:0.72rem;font-weight:700;letter-spacing:0.05em;text-transform:uppercase;color:var(--muted);padding:0 14px 12px;border-bottom:1px solid var(--line)}
.wrap td,.drawer-b td{padding:14px;border-bottom:1px solid var(--line);font-size:0.9rem;font-weight:600}
.wrap tr:last-child td,.drawer-b tr:last-child td{border-bottom:0}
.wrap th.r,.wrap td.r,.drawer-b th.r,.drawer-b td.r{text-align:right}
/* Drill drawer tables (e.g. Events on this page) must fit the 580px drawer: fix the layout so the
   right-aligned value column never lays out past the panel edge (was overflowing, clipping the count). */
.drawer-b .tbl-scroll{max-width:100%;overflow-x:auto}
.drawer-b table.tbl{width:100%;min-width:0;table-layout:fixed}
.drawer-b table.tbl td.r{white-space:nowrap}
.drawer-b table.tbl td:first-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tnum{font-variant-numeric:tabular-nums}
.money{font-weight:700}

/* donut + legend */
.donut-wrap{display:flex;align-items:center;gap:26px}
.donut{width:150px;height:150px;border-radius:50%;flex:none;
  background:conic-gradient(var(--accent) 0 63%,var(--violet) 63% 92%,var(--coral) 92% 98%,#a8a09a 98% 100%);
  display:grid;place-items:center;position:relative}
.donut::after{content:"";width:96px;height:96px;border-radius:50%;background:var(--card)}
.donut .dc{position:absolute;text-align:center}
.donut .dc b{font-size:1.5rem;font-weight:700;display:block;letter-spacing:-0.02em}
.donut .dc span{font-size:0.72rem;color:var(--muted);font-weight:600}
.legend{display:flex;flex-direction:column;gap:12px;flex:1}
.legend .lr{display:flex;align-items:center;gap:11px;font-size:0.88rem;font-weight:600}
.legend .sw{width:12px;height:12px;border-radius:4px;flex:none}

/* World map (Countries + Live). Land + dots are styled entirely through theme tokens: land is the
   muted ink at low opacity (so it follows light/dark), dots are the accent. Live dots fade over ~10s
   via wm-fade and remove themselves on animationend (see spawnLiveDot). */
.worldmap{position:relative;width:100%;touch-action:none}
/* overflow:hidden so a zoomed/panned viewBox clips to the map rectangle instead of bleeding over the
   card; the tip is a sibling of the svg so it still shows above the top edge. */
.wm-svg{display:block;width:100%;height:auto;color:var(--muted);overflow:hidden;cursor:grab}
.worldmap.wm-panning .wm-svg{cursor:grabbing}
/* Zoom controls (Google-Maps style): a small +/-/reset stack pinned bottom-right of the map. */
.wm-ctrl{position:absolute;right:10px;bottom:10px;display:flex;flex-direction:column;gap:6px;z-index:6}
.wm-btn{width:30px;height:30px;padding:0;display:grid;place-items:center;border:1px solid var(--line);background:var(--card);color:var(--ink);border-radius:8px;cursor:pointer;box-shadow:0 1px 2px rgba(0,0,0,0.06);transition:background .16s ease,border-color .16s ease,color .16s ease}
.wm-btn:hover{background:var(--sand);border-color:var(--line-hover);color:var(--ink)}
.wm-btn:focus-visible{outline:none;box-shadow:0 0 0 3px var(--primary-soft)}
.wm-btn svg{width:15px;height:15px;display:block}
.wm-land{fill:currentColor;fill-opacity:0.15;stroke:currentColor;stroke-opacity:0.10;stroke-width:0.5;stroke-linejoin:round}
.wm-dot{fill:var(--accent);fill-opacity:0.8}
.wm-dot:hover{fill-opacity:1}
/* Heat layer: one soft radial blob per country from the same per-country field the dots use, sized and
   brightened by that country's share of visits. Off by default; the map's flame button toggles it (state
   persisted in localStorage, shared across every map). Blended MULTIPLY on the pale land so busy regions
   deepen and overlaps saturate; SCREEN in dark mode so they glow instead. When heat is on the dots dim
   back so the density field reads clearly. Purely a re-presentation of the dots' data. */
.wm-heat-s0{stop-color:#ff3b30;stop-opacity:0.95}
.wm-heat-s1{stop-color:#ff9f0a;stop-opacity:0.5}
.wm-heat-s2{stop-color:#ffd60a;stop-opacity:0}
html.dark .wm-heat-s0{stop-color:#ff5a4d}
html.dark .wm-heat-s1{stop-color:#ffb43a}
.wm-heat{opacity:0;transition:opacity .28s ease;pointer-events:none;mix-blend-mode:multiply}
html.dark .wm-heat{mix-blend-mode:screen}
.wm-heat-blob{fill:url(#wm-heat-grad)}
.worldmap.wm-heat-on .wm-heat{opacity:1}
.worldmap.wm-heat-on .wm-dots{opacity:0.22;transition:opacity .28s ease}
.wm-btn-heat[aria-pressed="true"]{background:var(--accent);border-color:var(--accent);color:#fff}
.wm-btn-heat[aria-pressed="true"]:hover{background:var(--accent);border-color:var(--accent);color:#fff}
@media (prefers-reduced-motion:reduce){.wm-heat,.worldmap.wm-heat-on .wm-dots{transition:none}}
.wm-live{transform-box:fill-box;transform-origin:center;animation:wm-fade 10s linear forwards}
@keyframes wm-fade{
  0%{opacity:0;transform:scale(0.3)}
  4%{opacity:1;transform:scale(1.25)}
  10%{transform:scale(1)}
  72%{opacity:0.55}
  100%{opacity:0;transform:scale(1)}
}
/* Reduced motion: keep the fade (it conveys recency, and animationend must still fire so the dot is
   cleaned up) but drop the scale "pop". */
@media (prefers-reduced-motion:reduce){
  .wm-live{animation:wm-fade-rm 10s linear forwards}
}
@keyframes wm-fade-rm{0%{opacity:0}5%{opacity:1}72%{opacity:0.55}100%{opacity:0}}
.wm-tip{position:absolute;z-index:5;pointer-events:none;transform:translate(-50%,calc(-100% - 12px));
  background:var(--ink);color:var(--card);font-size:0.74rem;font-weight:600;line-height:1.35;
  padding:7px 10px;border-radius:8px;box-shadow:var(--shadow-pop);max-width:220px}
.wm-tip b{display:flex;align-items:center;gap:6px;font-weight:700}
.wm-tip .flag-ico{margin:0}
.wm-tip .wm-tip-sub{display:block;opacity:0.82;font-weight:500;margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.wm-tip[hidden]{display:none}
.legend .lr .n{margin-left:auto;color:var(--muted);font-weight:700}

/* metric strip */
.mstrip{display:grid;grid-template-columns:repeat(4,1fr);gap:16px}
/* When-they-watch play-time heatmap: a 25-col grid (day-label gutter + 24 hour cells). Cell alpha is set
   inline from the play count, and each cell prints that count compactly (80k / 1.2k / 999). Fitting four
   digits is what sets the 30px cell floor and the 840px grid width; below that the card scrolls
   horizontally rather than crushing the labels out of legibility. */
.hm-scroll{overflow-x:auto}
.hm-grid{display:grid;grid-template-columns:auto repeat(24,minmax(30px,1fr));gap:3px;align-items:center;min-width:840px}
.hm-h{font-size:10px;color:var(--muted);text-align:center;font-variant-numeric:tabular-nums;line-height:1}
.hm-day{font-size:11px;color:var(--muted);padding-right:10px;text-align:right;white-space:nowrap}
.hm-cell{height:24px;border-radius:3px;background:rgba(var(--accent-rgb),0.05);transition:transform .08s ease-out;
  display:flex;align-items:center;justify-content:center;overflow:hidden;
  font-size:10px;line-height:1;font-variant-numeric:tabular-nums;color:var(--ink)}
/* Past ~half opacity the accent fill is dark enough that ink text drops under 4.5:1, so the label flips. */
.hm-cell.hm-on{color:#fff}
.hm-cell:hover{transform:scale(1.16);outline:2px solid var(--card);outline-offset:-1px}
.hm-legend{display:flex;align-items:center;justify-content:flex-end;gap:5px;margin-top:14px;font-size:11px;color:var(--muted)}
.hm-legend i{width:16px;height:12px;border-radius:3px;display:inline-block}
@media (prefers-reduced-motion: reduce){.hm-cell{transition:none}}
/* Live "Now playing" wall: a poster grid of the titles being watched this minute. We hold no poster
   art (the CSP blocks external image hosts), so each tile is a GENERATED poster - a hue seeded from
   the title (--pw-h, set inline) drives its gradient and monogram, and the busiest title leads with an
   accent ring. Uniform portrait tiles, like a streaming wall; the live count and order carry the rank. */
.pwall{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:14px}
.pw-tile{position:relative;aspect-ratio:2/3;border-radius:14px;overflow:hidden;isolation:isolate;box-shadow:0 1px 3px rgba(0,0,0,.14);transition:transform .28s cubic-bezier(.2,.7,.2,1),box-shadow .28s ease;animation:pwIn .4s cubic-bezier(.2,.7,.2,1) both}
.pw-tile:hover{transform:translateY(-4px);box-shadow:0 12px 28px rgba(0,0,0,.24)}
.pw-tile.pw-open{cursor:pointer}
.pw-tile.pw-open:focus-visible{outline:2px solid var(--primary);outline-offset:2px}
.pw-art{position:absolute;inset:0;z-index:-1;display:flex;align-items:center;justify-content:center;background:radial-gradient(120% 85% at 18% 8%,rgba(255,255,255,.24),transparent 58%),linear-gradient(155deg,hsl(var(--pw-h,220) 64% 54%),hsl(calc(var(--pw-h,220) + 46) 56% 34%))}
.pw-mono{font-size:3.4rem;font-weight:700;letter-spacing:-.03em;color:rgba(255,255,255,.30);line-height:1;user-select:none}
.pw-type{position:absolute;top:9px;right:9px;font-size:10px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:#fff;background:rgba(0,0,0,.42);padding:3px 8px;border-radius:999px}
.pw-info{position:absolute;left:0;right:0;bottom:0;padding:34px 12px 12px;background:linear-gradient(to top,rgba(0,0,0,.82),rgba(0,0,0,.42) 55%,transparent);color:#fff}
.pw-title{font-size:14px;font-weight:700;line-height:1.25;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;text-shadow:0 1px 2px rgba(0,0,0,.5)}
.pw-live{display:flex;align-items:center;gap:6px;margin-top:7px;font-size:12px;color:rgba(255,255,255,.88)}
.pw-live .pulse{background:#fff}
.pw-live b{color:#fff;font-weight:700}
.pw-lead{outline:2px solid var(--accent);outline-offset:2px}
.pw-lead::before{content:"Top now";position:absolute;top:9px;left:9px;z-index:1;font-size:10px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:#fff;background:var(--accent);padding:3px 8px;border-radius:999px}
.pw-empty{padding:40px 20px;text-align:center;color:var(--muted);font-size:14px}
@keyframes pwIn{from{opacity:0;transform:scale(.96) translateY(6px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion: reduce){.pw-tile{animation:none;transition:none}.pw-tile:hover{transform:none}}
@media(max-width:560px){.pwall{grid-template-columns:repeat(auto-fill,minmax(118px,1fr))}.pw-mono{font-size:2.6rem}}

/* ── Errors page ─────────────────────────────────────────────────────────────
   One colour per issue kind (playback / buffering / slow start / autoplay blocked), themed for light and
   dark, plus a good/warn/bad severity ramp for the rate cells. */
:root{
  --err-play:#d70015; --err-buf:#b26b00; --err-slow:#5856d6; --err-block:#0091cf;
  --err-play-soft:#fde7e9; --err-buf-soft:#fff3e0; --err-slow-soft:#ecebfb; --err-block-soft:#e2f4fb;
}
html.dark{
  --err-play:#ef8a90; --err-buf:#e0a13a; --err-slow:#a9a1f5; --err-block:#4fc3e8;
  --err-play-soft:rgba(239,138,144,.15); --err-buf-soft:rgba(224,161,58,.16);
  --err-slow-soft:rgba(169,161,245,.15); --err-block-soft:rgba(79,195,232,.15);
}
.err-cols{display:grid;grid-template-columns:1fr 1fr;gap:16px;align-items:start}
.err-empty{padding:26px;text-align:center;color:var(--muted);font-size:13px}
.err-dot{width:7px;height:7px;border-radius:50%;display:inline-block;margin-left:2px}
.err-dot.err-good{background:var(--good)}.err-dot.err-warn{background:var(--err-buf)}.err-dot.err-bad{background:var(--bad)}
/* Issue chip (top issue + feed) */
.echip{display:inline-flex;align-items:center;gap:6px;font-size:11.5px;font-weight:650;padding:2px 9px;border-radius:7px;white-space:nowrap}
.echip i{width:7px;height:7px;border-radius:50%;background:currentColor;flex:none}
.echip.et-play{color:var(--err-play);background:var(--err-play-soft)}
.echip.et-buf{color:var(--err-buf);background:var(--err-buf-soft)}
.echip.et-slow{color:var(--err-slow);background:var(--err-slow-soft)}
.echip.et-block{color:var(--err-block);background:var(--err-block-soft)}
/* Trend (stacked bars by day) */
.err-thead{display:flex;align-items:center;gap:14px;margin-bottom:14px;flex-wrap:wrap}
.err-tsum b{font-size:22px;font-weight:750;letter-spacing:-.02em}
.err-tsum span{color:var(--muted);font-size:12px;font-weight:600;margin-left:6px}
.err-legend{display:flex;flex-wrap:wrap;gap:12px;margin-left:auto}
.err-legend span{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--muted);font-weight:600}
.err-legend i{width:9px;height:9px;border-radius:3px;flex:none}
.err-legend i.et-play{background:var(--err-play)}.err-legend i.et-buf{background:var(--err-buf)}
.err-legend i.et-slow{background:var(--err-slow)}.err-legend i.et-block{background:var(--err-block)}
.et-bars{display:flex;align-items:flex-end;gap:6px;height:186px;padding-bottom:24px}
.et-col{flex:1;display:flex;flex-direction:column-reverse;height:100%;gap:2px;position:relative;cursor:default}
.et-col:hover{opacity:.82}
.et-seg{width:100%;border-radius:2px}
.et-seg.et-play{background:var(--err-play)}.et-seg.et-buf{background:var(--err-buf)}
.et-seg.et-slow{background:var(--err-slow)}.et-seg.et-block{background:var(--err-block)}
.et-lb{position:absolute;bottom:-20px;left:0;right:0;text-align:center;font-size:10px;color:var(--muted)}
/* Pages/videos table */
.err-tblwrap{overflow-x:auto}
.err-tbl{width:100%;border-collapse:collapse}
.err-tbl th{text-align:left;font-size:11px;font-weight:700;letter-spacing:.03em;text-transform:uppercase;color:var(--muted);padding:0 12px 10px;white-space:nowrap}
.err-tbl th.r,.err-tbl td.r{text-align:right}
.err-tbl td{padding:11px 12px;border-top:1px solid var(--line);font-size:13.5px;vertical-align:middle}
.err-pcell{display:flex;align-items:center;gap:11px;min-width:0}
/* Identified rows wrap their poster+title in a button that opens the title page; reset the native
   chrome so it reads as the cell it replaced, and hint the link on hover/focus. */
.err-open{display:flex;align-items:center;gap:11px;min-width:0;width:100%;text-align:left;font:inherit;color:inherit;background:none;border:0;padding:0;cursor:pointer}
.err-open:hover .err-pt,.err-open:focus-visible .err-pt{color:var(--primary)}
.err-open:focus-visible{outline:2px solid var(--primary);outline-offset:2px;border-radius:6px}
.err-ptxt{display:flex;flex-direction:column;min-width:0}
.err-pt{font-weight:650;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:340px}
.err-rate{display:inline-flex;align-items:center;gap:9px;justify-content:flex-end}
.err-rate .err-mini{width:52px;height:6px;border-radius:4px;background:var(--line-soft);overflow:hidden}
.err-rate .err-mini i{display:block;height:100%;border-radius:4px;background:currentColor}
.err-rate b{font-weight:700;min-width:44px;text-align:right}
.err-rate.err-good{color:var(--good)}.err-rate.err-warn{color:var(--err-buf)}.err-rate.err-bad{color:var(--bad)}
.err-rate.err-na{color:var(--muted)}
/* Country / device breakdown list */
.err-blist{display:flex;flex-direction:column}
.err-brow{display:flex;align-items:center;gap:12px;padding:10px 2px;border-top:1px solid var(--line)}
.err-brow:first-child{border-top:0}
.err-bl{display:flex;align-items:center;gap:8px;flex:1;min-width:0;font-weight:600}
.err-bn{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.err-bsub{color:var(--muted);font-weight:500;font-size:12px;white-space:nowrap}
.err-bt{width:84px;height:7px;border-radius:5px;background:var(--line-soft);overflow:hidden;flex:none}
.err-bt i{display:block;height:100%;border-radius:5px}
.err-bt i.err-good{background:var(--good)}.err-bt i.err-warn{background:var(--err-buf)}.err-bt i.err-bad{background:var(--bad)}
.err-bp{width:48px;text-align:right;font-weight:700}
.err-bp.err-good{color:var(--good)}.err-bp.err-warn{color:var(--err-buf)}.err-bp.err-bad{color:var(--bad)}
/* Recent feed */
.err-feed{display:flex;flex-direction:column}
.efrow{display:grid;grid-template-columns:72px 160px 1fr auto;gap:14px;align-items:center;padding:10px 2px;border-top:1px solid var(--line);font-size:13px}
.efrow:first-child{border-top:0}
.ef-ts{color:var(--muted);font-size:12px}
.ef-where{color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ef-env{display:flex;align-items:center;gap:8px;color:var(--muted);font-size:12px;white-space:nowrap;justify-content:flex-end}
@media(max-width:900px){.err-cols{grid-template-columns:1fr}}
@media(max-width:640px){.efrow{grid-template-columns:64px 1fr auto;gap:10px}.efrow .ef-where{display:none}.err-legend{margin-left:0}}
.metric .mk{color:var(--muted);font-size:0.82rem;font-weight:600;display:flex;align-items:center;gap:8px}
.metric .mk .fa{color:var(--label);font-size:0.85rem}
.metric .mv{font-size:1.85rem;font-weight:700;letter-spacing:-0.02em;margin-top:8px}
/* Text-valued KPI (a country/place name, not a figure): smaller and lighter than the numeric size so
   it reads as a label, and it stays on one line with the flag aligned to the text. */
.metric .mv-txt{font-size:1.25rem;font-weight:600;letter-spacing:-0.01em}
.metric .mv-txt .mv-place{display:inline-flex;align-items:center;gap:8px;min-width:0;max-width:100%}
.metric .mv-txt .mv-cn{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.metric .mv-txt .flag-ico,.metric .mv-txt .flag-code{flex:none}
.metric .ms{font-size:0.8rem;color:var(--muted);margin-top:2px}

/* sites */
.siterow{display:flex;align-items:center;gap:16px;padding:18px;border:1px solid var(--line);border-radius:var(--radius);background:var(--card);box-shadow:var(--shadow)}
.siterow+.siterow{margin-top:12px}
.site-ic{width:42px;height:42px;border-radius:12px;background:var(--sand);display:grid;place-items:center;color:var(--label);flex:none}
.site-main{min-width:0}
.site-main b{font-size:0.98rem;display:block}
.site-main span{font-size:0.82rem;color:var(--muted);font-weight:500}
.site-num{text-align:right;flex:none}
.site-num b{font-size:1.02rem}
.site-num span{font-size:0.76rem;color:var(--muted);display:block;font-weight:600}
/* Pixels page: every pixel is a self-contained pane down the page (.pxpanes > .pxpane.card), carrying its
   identity + health chip, the health callout, Site type, Install (tags + snippets) and Domains, in place.
   The pane body reuses the .px-setup vocabulary; the panes stack with the standard card gap. */
.pxpanes{display:flex;flex-direction:column;gap:16px}
.pxpane{padding:20px 22px}
/* Pixel pane body: identity header (name + guid + health chip on the left, rename/remove on the right),
   Install (tags + snippets), Domains + suggestions, and the Site type picker. */
.px-setup{min-width:0}
.pxs-id{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;flex-wrap:wrap}
.pxs-idmain{display:flex;align-items:center;gap:9px;min-width:0;flex-wrap:wrap}
.pxs-idacts{display:flex;gap:8px;flex:none}
.px-name{font-size:1.02rem;font-weight:700;line-height:1.25;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:0 1 auto}
/* Pixel guid, mono, next to the name (its data lands under this id). */
.px-guid{flex:none;display:inline-flex;align-items:center;gap:2px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:0.78rem;font-weight:500;color:var(--muted)}
.px-rename{display:flex;gap:8px;margin-top:12px;flex-wrap:wrap}
.px-rename[hidden]{display:none}
.px-rename .pixel-rename{max-width:280px;margin:0}
.px-setup .idchip-copy{background:none;border:0;color:var(--muted);cursor:pointer;display:inline-grid;place-items:center;padding:2px;border-radius:6px;line-height:0;margin-left:1px}
.px-setup .idchip-copy:hover{color:var(--ink);background:var(--sand)}
.px-setup .idchip-copy .fa{width:11px;height:11px}
/* Per-pixel "Site type" as a pill in the actions row (next to Rename). Clicking opens a menu to change it.
   The dot is neutral for a dwell type, accent for a video one, so the counting mode reads at a glance. */
.px-type-dd{position:relative}
/* Match the Rename / Remove ghost buttons next to it: same radius, ring border, height and type. */
.px-type-pill{display:inline-flex;align-items:center;gap:7px;padding:2px 8px;border:0;border-radius:var(--radius-xs);background:var(--card);box-shadow:var(--ring-mid);color:var(--ink-button,var(--ink));font-size:13px;line-height:18px;font-weight:600;cursor:pointer;white-space:nowrap}
.px-type-pill:hover{background:var(--sand)}
.px-type-dd.open>.px-type-pill{background:var(--sand)}
.px-type-caret{width:9px;height:9px;color:var(--muted);flex:none;transition:transform .15s ease}
.px-type-dd.open>.px-type-pill .px-type-caret{transform:rotate(180deg)}
.px-type-dot{width:8px;height:8px;border-radius:50%;flex:none;background:var(--muted-2,var(--muted))}
.px-type-dot.is-video{background:var(--accent)}
.px-type-menu{min-width:216px;padding:6px}
.px-type-menuh{margin:4px 8px 6px;font-size:0.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--muted)}
.px-type-menuf{display:flex;align-items:center;gap:5px;margin:6px 4px 2px;padding:8px 4px 2px;border-top:1px solid var(--line-soft);font-size:0.78rem;color:var(--accent);font-weight:600;line-height:1.4;text-decoration:none}
.px-type-menuf:hover{text-decoration:underline}
.px-type-menuf-ic{width:10px;height:10px;flex:none}
.px-type-opt{display:flex;align-items:center;gap:9px}
.px-type-opt.is-cur{color:var(--accent)}
.px-type-opt .px-type-dot{margin-left:1px}
/* Axis-titled chart wrapper (Validated plays over time): Y title rotated on the left, X title centred
   below. A grid so the chart keeps its full width and the labels sit in their own tracks. */
.axchart{display:grid;grid-template-columns:auto 1fr;grid-template-rows:1fr auto;gap:2px 6px}
.axchart-main{grid-column:2;grid-row:1;min-width:0}
.axchart-y{grid-column:1;grid-row:1;align-self:center;writing-mode:vertical-rl;transform:rotate(180deg);font-size:0.7rem;font-weight:700;letter-spacing:.03em;text-transform:uppercase;color:var(--muted)}
.axchart-x{grid-column:2;grid-row:2;text-align:center;font-size:0.7rem;font-weight:700;letter-spacing:.03em;text-transform:uppercase;color:var(--muted);margin-top:2px}
/* The per-tag kebab reuses the .dd dropdown machinery (Rename reveals the inline input row; Retire
   revokes). Reset the .dd-btn pill box down to a bare icon button; qualify with `.dd-btn` so the later
   Stripe-reskin `.dd-btn` rule (equal specificity, later in the file) does not re-add a bordered pill. */
.dd-btn.px-kebab{width:32px;height:32px;padding:0;border-radius:8px;border:1px solid transparent;background:none;box-shadow:none;color:var(--muted);display:grid;place-items:center;cursor:pointer}
.dd-btn.px-kebab:hover{color:var(--ink);background:var(--sand);border-color:transparent}
.tag-menu.dd.open>.px-kebab{background:var(--sand);color:var(--ink);border-color:transparent;box-shadow:none}
.px-kebab .fa{width:15px;height:15px}
.px-danger{color:var(--bad)}
.px-danger:hover{background:var(--bad-soft,var(--line-soft))}
/* --- Per-pixel Health signal. The chip rides the list row and the drawer identity; the callout (green
   when a site's plays are identified, red when it is landing validated plays it cannot match to a title)
   leads the setup drawer. 'Waiting for data' is neutral, never an alarm. */
.health-chip{display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 11px;border-radius:var(--radius-pill);font-size:0.76rem;font-weight:700;white-space:nowrap}
.health-chip .hdot{width:7px;height:7px;border-radius:50%;flex:none}
.health-chip .fa{width:11px;height:11px;flex:none}
.h-ok{background:rgba(var(--ok-rgb),0.1);color:var(--ok)}
.h-ok .hdot{background:var(--ok)}
.h-warn{background:var(--bad-soft);color:var(--bad)}
.h-warn .hdot{background:var(--bad)}
/* The "Needs attention" X gives a gentle periodic wiggle so a pixel that needs a fix catches the eye
   without nagging. Motion is suppressed under prefers-reduced-motion. */
.h-warn .fa{animation:attn-wiggle 5s ease-in-out infinite;transform-origin:50% 50%}
/* One quick wiggle roughly every 5s: rest for the first 80% of the cycle, wiggle through the last fifth,
   so the X nudges for attention without nagging. Motion is suppressed under prefers-reduced-motion. */
@keyframes attn-wiggle{0%,80%,100%{transform:rotate(0)}83%{transform:rotate(-16deg)}87%{transform:rotate(13deg)}91%{transform:rotate(-9deg)}95%{transform:rotate(5deg)}}
@media (prefers-reduced-motion:reduce){.h-warn .fa{animation:none}}
.h-new{background:var(--sand);color:var(--muted)}
.h-new .hdot{background:var(--muted)}
.health-note{border-radius:var(--radius-sm);background:var(--bad-soft);color:var(--bad);padding:14px 16px;display:flex;gap:12px;align-items:flex-start}
.px-setup .health-note{margin-top:18px}
.health-note.h-good{background:rgba(var(--ok-rgb),0.1);color:var(--ok)}
.health-note .hn-ic{flex:none;margin-top:1px}
.health-note .hn-ic svg{width:18px;height:18px;display:block}
.health-note strong{display:block;font-size:0.9rem;font-weight:700;margin-bottom:3px}
.health-note p{margin:0;font-size:0.83rem;line-height:1.5}
.health-note .hn-acts{display:flex;gap:10px;margin-top:10px;flex-wrap:wrap}
.hn-link{display:inline-flex;align-items:center;gap:6px;border:0;background:none;color:inherit;font:inherit;font-size:0.82rem;font-weight:700;text-decoration:underline;text-underline-offset:2px;cursor:pointer;padding:0}
.hn-metric{font-variant-numeric:tabular-nums;font-weight:700}
.hn-code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:0.85em;background:rgba(215,0,21,0.12);border-radius:5px;padding:1px 5px}
/* Section headers inside the setup drawer (Install, Domains), with an "Optional" tag on Domains. */
.pxs-opt{font-size:0.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--muted)}
/* One line of guidance under a section header (paste before </body>; domains are an optional filter). */
.px-sec-hint{margin:11px 0 0;font-size:0.82rem;color:var(--muted);font-weight:500;line-height:1.5}
.px-sec-hint code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:0.86em;background:var(--sand);border-radius:5px;padding:1px 5px;color:var(--ink-2)}
/* "Add install tag" ghost button at the foot of the tag list (mirrors the Domains add control). */
.px-add-tag{margin-top:14px}
.px-add-tag .fa{width:11px;height:11px;margin-right:3px;vertical-align:-1px}
.px-empty{margin:12px 0 0;font-size:0.85rem;color:var(--muted);font-weight:500}
/* Tags + Domains are each a collapsible section (<details>). The summary is the clickable header; a thin
   rounded chevron sits in a soft round hit target and rotates when the section opens. */
.pxs-sec{margin-top:28px;border-top:1px solid var(--line)}
.pxs-sum{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:14px 2px;cursor:pointer;list-style:none;user-select:none;-webkit-tap-highlight-color:transparent}
.pxs-sum::-webkit-details-marker{display:none}
.pxs-sum-t{font-size:1.05rem;font-weight:700;letter-spacing:-0.01em;color:var(--ink)}
.pxs-domsum-opt{font-size:0.82rem;font-weight:500;color:var(--muted);letter-spacing:0}
.pxs-chev{display:inline-grid;place-items:center;width:28px;height:28px;border-radius:50%;color:var(--muted);line-height:0;transition:transform .22s cubic-bezier(.22,1,.36,1),background .15s ease,color .15s ease}
.pxs-chev svg{width:15px;height:15px}
.pxs-sum:hover .pxs-chev{background:var(--sand);color:var(--ink)}
.pxs-sec[open]>.pxs-sum .pxs-chev{transform:rotate(180deg)}
.pxs-secbody{padding-bottom:6px}
@media (prefers-reduced-motion:reduce){.pxs-chev{transition:none}}
/* Domain token field: registered domains are removable pills that sit inside the input box; the bare
   text input shares the box and grows to fill the row. Focus ring on the whole field. */
.dom-field{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:10px;padding:6px 8px;border:1px solid var(--line);border-radius:10px;background:var(--card);cursor:text}
.dom-field:focus-within{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
/* Added domain = a solid neutral pill: hostname + its beacon count + a remove x. */
.dom-token{display:inline-flex;align-items:center;gap:7px;padding:3px 4px 3px 11px;border:1px solid var(--line);border-radius:var(--radius-pill);background:var(--sand);max-width:100%}
.dom-token .dom-host{font-size:0.84rem;font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ink)}
.dom-token .dom-x{background:none;border:0;color:var(--muted);cursor:pointer;font-size:1rem;line-height:1;width:20px;height:20px;border-radius:50%;display:inline-grid;place-items:center;flex:none}
.dom-token .dom-x:hover{color:var(--ink);background:var(--line-soft)}
.dom-token-input{flex:1 1 120px;min-width:110px;border:0;background:none;outline:none;padding:5px 4px;font-size:0.86rem;color:var(--ink)}
.dom-token-input::placeholder{color:var(--muted-2,var(--muted))}
/* Suggested domain = a dashed accent chip (clearly a different colour from the solid added pills): a +,
   the host, and its beacon count. One click registers it, turning it into a .dom-token. Sits in the same
   box as the added pills. */
.dom-chip{display:inline-flex;align-items:center;gap:6px;padding:3px 11px 3px 8px;border:1px dashed var(--accent);border-radius:var(--radius-pill);background:var(--accent-soft);color:var(--accent);font-size:0.84rem;font-weight:600;cursor:pointer;max-width:100%}
.dom-chip:hover{background:var(--accent);color:#fff;border-style:solid}
.dom-chip .chip-plus{display:inline-grid;place-items:center;line-height:0}
.dom-chip .chip-plus .fa{width:10px;height:10px}
.dom-chip .chip-host{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The per-pill stat (beacon count). Muted on the neutral pill, tinted on the accent chip / its hover. */
.tok-stat{font-size:0.74rem;font-weight:600;color:var(--muted);font-variant-numeric:tabular-nums;white-space:nowrap}
.dom-chip .tok-stat{color:var(--accent)}
.dom-chip:hover .tok-stat{color:rgba(255,255,255,0.85)}
/* Tags: divider-separated rows. Each is name + short guid (inline, in brackets, mirroring the pixel
   guid) + a kebab (Rename reveals the inline input row; Retire revokes), snippet directly under. The
   first row sits flush under the hint. */
.taglist{display:flex;flex-direction:column;margin-top:10px}
.tag-item{border-top:1px solid var(--line-soft);padding-top:12px;margin-top:12px}
.tag-item:first-child{border-top:0;padding-top:0;margin-top:0}
.tag-head{display:flex;gap:7px;align-items:baseline;min-width:0}
.tag-name{font-size:0.9rem;font-weight:700;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:0 1 auto}
.tag-name.untitled{font-weight:600;color:var(--muted)}
.tag-guid{flex:none;font-size:0.74rem;font-weight:500;color:var(--muted);font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
.tag-menu{flex:none;margin-left:auto;align-self:center}
.tag-dd{min-width:150px}
/* Rename row, revealed by the kebab -> Rename (tagRenameToggle). Same [hidden] guard as .px-rename. */
.tag-rename{display:flex;gap:8px;margin-top:10px;flex-wrap:wrap}
.tag-rename[hidden]{display:none}
.tag-rename .input{max-width:260px;margin:0}
.tag-item .snippet{margin-top:10px}
/* Overview "Your sites" mini-rows: divider-separated, no per-row card chrome. */
.srow{display:flex;align-items:center;gap:14px;padding:14px 0;border-bottom:1px solid var(--line)}
.srow:last-child{border-bottom:0}
.srow .site-ic{width:38px;height:38px}
.srow .site-main{flex:1;min-width:0}
.srow .site-main b{font-size:0.94rem;display:block}
.srow .site-main span{font-size:0.8rem;color:var(--muted);font-weight:500}
.empty-note{color:var(--muted);font-size:0.86rem;font-weight:500;padding:8px 0}
.snippet{background:#12162a;color:#dbe4f5;border-radius:var(--radius-sm);padding:16px 18px;font-family:'SFMono-Regular',ui-monospace,'Cascadia Code',Consolas,monospace;font-size:0.8rem;line-height:1.6;overflow-x:auto;position:relative;font-weight:500;white-space:pre}
.snippet .tok{color:#ffb27a}.snippet .str{color:#8fdcae}.snippet .cmt{color:#6b7a99;font-style:italic}
.copy{position:absolute;top:11px;right:11px;background:rgba(255,255,255,0.08);color:#cfdaf0;border:0;border-radius:9px;padding:7px 12px;font-size:0.74rem;font-weight:700;display:inline-flex;gap:7px;align-items:center}
.copy:hover{background:rgba(255,255,255,0.16)}

/* settings */
.set-grid{display:grid;grid-template-columns:210px 1fr;gap:16px 34px;align-items:start}
.set-grid .sg-h{font-weight:700;font-size:0.96rem;padding-top:4px}
.set-grid .sg-h p{margin:5px 0 0;font-weight:500;font-size:0.82rem;color:var(--muted);max-width:26ch}
/* Settings > Payout methods: the manage list + add-method form (PayPal | Crypto asset picker). */
.pm-list{display:flex;flex-direction:column}
.pm{display:flex;align-items:center;gap:13px;padding:12px 0}
.pm + .pm{border-top:1px solid var(--line-soft)}
.pm-ic{flex:none;width:38px;height:38px;border-radius:11px;display:grid;place-items:center;background:var(--accent-soft);color:var(--accent)}
.pm-ic .fa{width:20px;height:20px;font-size:20px}
.pm-main{min-width:0;flex:1}
.pm-name{font-size:0.94rem;font-weight:700;color:var(--ink);display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.pm-sub{font-size:0.8rem;color:var(--muted);margin-top:2px}
.pm-sub.mono{font-variant-numeric:tabular-nums}
.pill{font-size:0.66rem;font-weight:700;padding:2px 8px;border-radius:999px;letter-spacing:0.02em}
.pill.def{color:var(--accent);background:var(--accent-soft)}
.pm .linkbtn{background:none;border:0;color:var(--accent);font:inherit;font-size:0.8rem;font-weight:600;cursor:pointer;padding:4px 6px;border-radius:8px}
.pm .linkbtn:hover{background:var(--accent-soft)}
.pm-rm{flex:none;width:30px;height:30px;border-radius:9px;border:1px solid transparent;background:none;color:var(--muted);cursor:pointer;display:grid;place-items:center}
.pm-rm .fa{width:16px;height:16px;font-size:16px}
.pm-rm:hover{background:var(--line-soft);color:var(--coral)}
.pm-empty{font-size:0.85rem;color:var(--muted);margin:8px 0}
.pm-add-btn{margin-top:14px;display:inline-flex;align-items:center;gap:8px;background:none;border:1px dashed var(--line);color:var(--ink);font:inherit;font-weight:600;font-size:0.86rem;padding:10px 15px;border-radius:12px;cursor:pointer}
.pm-add-btn .fa{width:15px;height:15px;font-size:15px}
.pm-add-btn:hover{border-color:var(--accent);color:var(--accent)}
.pm-form{margin-top:16px;border:1px solid var(--line);border-radius:var(--radius-sm);padding:18px;background:rgba(var(--accent-rgb),0.03)}
.pm-seg{display:inline-flex;background:var(--line-soft);border-radius:11px;padding:3px;gap:2px;margin-bottom:16px}
.pm-seg button{border:0;background:none;font:inherit;font-weight:600;font-size:0.85rem;color:var(--muted);padding:7px 16px;border-radius:9px;cursor:pointer}
.pm-seg button.on{background:var(--card);color:var(--ink);box-shadow:0 1px 3px rgba(0,0,0,0.14)}
.pm-form .field-block + .field-block{margin-top:14px}
.pm-form .pm-addr-lab{margin-top:12px}
.pm-help[data-state="ok"]{color:var(--ok)}
.pm-help[data-state="bad"]{color:var(--coral)}
/* Deprecation / memo-tag notice under the asset+network pickers. Full border + background tint (no
   side-stripe): warn = amber for the deprecated rails, info = neutral for the memo/tag caveat. */
.pm-note{margin-top:12px;font-size:0.82rem;line-height:1.45;border:1px solid var(--line);border-radius:var(--radius-sm);padding:9px 11px;color:var(--label)}
.pm-note[data-state="warn"]{border-color:color-mix(in srgb,var(--amber) 55%,var(--line));background:color-mix(in srgb,var(--amber) 10%,transparent);color:var(--ink)}
.pm-note[data-state="info"]{border-color:var(--line);background:var(--line-soft);color:var(--muted)}
.pm-actions{display:flex;gap:10px;margin-top:18px}
select.input{appearance:auto;-webkit-appearance:menulist}
.field{display:flex;flex-direction:column;gap:7px;margin-bottom:16px}
.field label{font-size:0.82rem;font-weight:700;color:var(--label)}
.field input,.field select{border:1px solid var(--line);border-radius:var(--radius-sm);padding:12px 14px;font-size:0.92rem;font-weight:600;color:var(--ink);background:var(--card)}
.field input:focus,.field select:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 4px var(--accent-soft)}
.chips{display:flex;gap:10px;flex-wrap:wrap}
.chip{position:relative;cursor:pointer}
.chip input{position:absolute;inset:0;opacity:0;cursor:pointer}
.chip span{display:inline-flex;align-items:center;gap:9px;border:1px solid var(--line);border-radius:var(--radius-pill);padding:11px 18px;font-weight:700;font-size:0.88rem;color:var(--label);transition:.14s}
.chip input:checked+span{border-color:var(--accent);background:var(--accent-soft);color:var(--accent)}
.chip .fa{font-size:0.95rem}
.toggle-row{display:flex;align-items:center;justify-content:space-between;padding:15px 0;border-bottom:1px solid var(--line)}
.toggle-row:last-of-type{border-bottom:0}
.toggle-row b{font-size:0.9rem}.toggle-row p{margin:2px 0 0;font-size:0.8rem;color:var(--muted);font-weight:500}
.sw-t{width:46px;height:27px;border-radius:var(--radius-pill);background:#d3d9e6;position:relative;flex:none;transition:.18s;cursor:pointer;border:0}
.sw-t::after{content:"";position:absolute;top:3px;left:3px;width:21px;height:21px;border-radius:50%;background:#fff;box-shadow:0 1px 3px rgba(0,0,0,.2);transition:.18s}
.sw-t.on{background:var(--accent)}.sw-t.on::after{left:22px}
.note{font-size:0.82rem;color:var(--muted);margin-top:14px;display:flex;align-items:center;gap:8px}
.note .fa{color:var(--green)}

/* segmented sub-tabs */
.seg{display:inline-flex;gap:3px;background:var(--sand);border-radius:var(--radius-pill);padding:4px;margin:2px 0 18px;max-width:100%;overflow-x:auto;scrollbar-width:none}
.seg::-webkit-scrollbar{display:none}
.seg-b{border:0;background:none;color:var(--muted);font-weight:700;font-size:0.85rem;padding:8px 16px;border-radius:var(--radius-pill);white-space:nowrap;transition:color .14s ease,background .14s ease}
.seg-b:hover{color:var(--ink)}
.seg-b.on{background:var(--card);color:var(--ink);box-shadow:0 2px 8px -5px rgba(20,30,60,0.4)}
.pane{display:none}
.pane.on{display:block;animation:fade .2s ease}

/* ranked bar list */
.barlist{display:flex;flex-direction:column}
.bl-row{display:flex;align-items:center;gap:13px;padding:10px 0;border-bottom:1px solid var(--line)}
.bl-row:last-child{border-bottom:0}
.bl-rank{width:16px;flex:none;color:var(--muted);font-size:0.76rem;font-weight:700;font-variant-numeric:tabular-nums}
.bl-face{display:flex;align-items:center;gap:10px;min-width:0;width:200px;flex:none}
.bl-face b{font-weight:600;font-size:0.89rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* Content-mode bar rows (Top titles/videos in the country and site drills): a mini poster + the whole
   row is a link to the title page. */
.bl-thumb{flex:none;display:inline-flex}
.bl-thumb .poster-img.vt-thumb{width:26px;height:39px;border-radius:5px}
/* Page rows: the title the URL resolved to, between the rank and the path. Fixed width and always
   present (.is-none is the empty slot for a page that never resolved) so every path in the list starts
   on the same left edge. The poster is its own link, so the row still drills to the page. */
.bl-tthumb{flex:none;width:26px;height:39px;display:inline-flex;border-radius:5px}
.bl-tthumb .poster-img.vt-thumb{width:26px;height:39px;border-radius:5px}
.bl-tthumb.is-none{border:1px dashed var(--line);border-radius:5px}
a.bl-tthumb{cursor:pointer}
a.bl-tthumb:hover .poster-img,a.bl-tthumb:focus-visible .poster-img{outline:2px solid var(--accent);outline-offset:1px}
.bl-open{display:flex;align-items:center;gap:13px;text-decoration:none;color:inherit;cursor:pointer;border-radius:8px;padding-left:6px;margin-left:-6px}
.bl-open:hover{background:var(--sand)}
.bl-open:hover .bl-face b{color:var(--accent)}
.bl-open:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* Linked title cell in the visitor Content table: mini poster + title, opens the title page. */
.vp-clink{display:inline-flex;align-items:center;gap:8px;background:none;border:0;padding:0;cursor:pointer;color:inherit;font:inherit;text-align:left}
.vp-clink .poster-img.vt-thumb{width:22px;height:33px;border-radius:4px;flex:none}
.vp-clink span{font-weight:600}
.vp-clink:hover span{color:var(--accent)}
.vp-clink:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}
.bl-track{flex:1;height:8px;border-radius:var(--radius-pill);background:var(--sand);overflow:hidden;min-width:40px}
.bl-track i{display:block;height:100%;border-radius:var(--radius-pill);background:linear-gradient(90deg,rgba(var(--accent-rgb),.5),var(--accent))}
.bl-val{width:118px;flex:none;text-align:right;font-weight:700;font-size:0.87rem;font-variant-numeric:tabular-nums}
.bl-val span{color:var(--muted);font-weight:600;font-size:0.79rem;margin-left:7px}
/* "Open full page report" affordance at the end of a page-URL row. Faint until the row is hovered so it
   does not compete with the bar; enlarges its own hit target without shifting the row layout. */
.bl-golink{flex:none;display:grid;place-items:center;width:26px;height:26px;margin:-4px -6px -4px -2px;border-radius:7px;color:var(--icon-idle);opacity:0;transition:opacity .12s ease,background .12s ease,color .12s ease}
.bl-golink svg{width:13px;height:13px}
.bl-row:hover .bl-golink,.bl-golink:focus-visible{opacity:1}
.bl-golink:hover{background:var(--sand);color:var(--accent)}
@media(hover:none){.bl-golink{opacity:1}}
.cc{width:26px;height:19px;border-radius:4px;flex:none;display:grid;place-items:center;font-size:0.6rem;font-weight:700;background:var(--sand);color:var(--label)}
.gl{width:24px;height:24px;border-radius:7px;background:var(--sand);display:grid;place-items:center;color:var(--label);flex:none;font-weight:700}
.gl .fa{font-size:0.72rem}
.tag-init{display:inline-grid;place-items:center;width:28px;height:28px;border-radius:50%;background:var(--accent-soft);color:var(--accent);font-size:0.68rem;font-weight:700}

/* live */
.live-ctl{display:inline-flex;align-items:center;gap:8px;font-size:0.8rem;color:var(--muted);font-weight:600}
.live-ctl select{border:1px solid var(--line);border-radius:var(--radius-pill);padding:6px 12px;font-weight:700;font-size:0.8rem;background:var(--card);color:var(--ink)}
/* Live hero footer stat strip: the per-minute KPIs, folded into the hero card as a full-width row
   under a hairline (a third flex child - flex-basis 100% forces its own line). Values inherit .card .v. */
.live-substats{flex:1 1 100%;border-top:1px solid var(--line);margin-top:8px;padding-top:16px;display:grid;grid-template-columns:repeat(4,1fr);gap:18px}
.live-substats > div{min-width:0}
.live-substats .klabel{margin-top:3px}
@media(max-width:640px){.live-substats{grid-template-columns:repeat(2,1fr);gap:16px}}
.pulse{width:8px;height:8px;border-radius:50%;background:var(--green);flex:none;animation:pulse 1.8s ease-out infinite}
@keyframes pulse{0%{box-shadow:0 0 0 0 rgba(var(--green-rgb),.45)}70%{box-shadow:0 0 0 8px rgba(var(--green-rgb),0)}100%{box-shadow:0 0 0 0 rgba(var(--green-rgb),0)}}
.feed{display:flex;flex-direction:column}
.feed-row{display:flex;align-items:center;gap:11px;padding:10px 0;border-bottom:1px solid var(--line)}
.feed-row:last-child{border-bottom:0}
.feed-dot{width:9px;height:9px;border-radius:50%;flex:none}
.feed-act{font-weight:700;font-size:0.88rem;flex:none}
.feed-pg{flex:1;min-width:0;color:var(--muted);font-weight:600;font-size:0.85rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.feed-time{width:42px;flex:none;text-align:right;color:var(--muted);font-weight:600;font-size:0.77rem;font-variant-numeric:tabular-nums}
/* Live cards sit in the narrow 1fr grid column; the barlist's fixed 200px face + 118px value can be
   wider than that column and spill past the card (and the page). Let the face/value flex down so the
   name ellipses instead of overflowing. Scoped to the live mounts so the wide breakdown lists keep
   their fixed alignment. */
[data-live-countries] .bl-face,[data-live-pages] .bl-face{width:auto;flex:1 1 0;min-width:0}
[data-live-countries] .bl-val,[data-live-pages] .bl-val{width:auto;min-width:52px}
.splitbar{display:flex;height:14px;border-radius:var(--radius-pill);overflow:hidden;background:var(--sand)}
.splitbar i{height:100%}

/* visitor unified timeline: sessions grouping pageviews + custom events + plays (ADR-054) */
.pt{display:flex;flex-direction:column;gap:16px}
.pt-sess{border:1px solid var(--line);border-radius:var(--radius-sm);overflow:hidden}
.pt-sh{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:11px 15px;background:var(--sand);border-bottom:1px solid var(--line)}
.pt-when{font-weight:700;font-size:0.85rem;color:var(--ink)}
.pt-sep{color:var(--muted)}
.pt-smeta{color:var(--muted);font-weight:600;font-size:0.8rem;font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:220px}
.pt-list{display:flex;flex-direction:column;padding:2px 15px}
.pt-row{display:flex;align-items:center;gap:11px;padding:11px 0;border-bottom:1px solid var(--line);width:100%;text-align:left}
.pt-row:last-child{border-bottom:0}
button.pt-row{appearance:none;-webkit-appearance:none;background:none;font:inherit;color:inherit;cursor:pointer}
button.pt-row:hover{background:var(--sand)}
button.pt-row:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--primary);border-radius:8px}
.pt-dot{width:9px;height:9px;border-radius:50%;flex:none}
.pt-kind{font-weight:700;font-size:0.85rem;flex:none;width:72px}
.pt-desc{flex:1;min-width:0;color:var(--muted);font-weight:600;font-size:0.85rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pt-badge{flex:none;font-weight:700;font-size:0.78rem;color:var(--muted);font-variant-numeric:tabular-nums;min-width:44px;text-align:right}

/* charts */
.area{width:100%;height:150px;display:block}
.area .fill{fill:rgba(var(--accent-rgb),.12)}
.area .line{fill:none;stroke:var(--accent);stroke-width:2.5;stroke-linejoin:round;stroke-linecap:round}
.area .grid{stroke:var(--line);stroke-width:1}
.area-tall{height:172px}

/* video: retention + heatmap + qoe */
.mstrip.five{grid-template-columns:repeat(5,1fr)}
.retn{display:flex;gap:14px;height:176px}
.rq{flex:1;display:flex;flex-direction:column;align-items:center}
.rq .rv{font-size:0.9rem;font-weight:700}
.rqb{flex:1;width:100%;display:flex;align-items:flex-end;margin:8px 0}
.rq i{width:100%;border-radius:9px 9px 4px 4px;background:linear-gradient(var(--accent),rgba(var(--accent-rgb),.38));display:block}
.rq .rl{font-size:0.73rem;color:var(--muted);font-weight:700}
.heat{display:grid;grid-auto-flow:column;grid-auto-columns:1fr;gap:3px;height:58px}
.heat .hc{border-radius:4px;background:rgba(var(--accent-rgb),var(--i,.3))}
.heat .hc.rw{box-shadow:inset 0 0 0 2px var(--violet)}
.heat-axis{display:flex;justify-content:space-between;font-size:0.68rem;color:var(--muted);font-weight:700;margin-top:8px}
.heat-legend{display:flex;gap:20px;margin-top:14px;font-size:0.78rem;color:var(--muted);font-weight:600}

/* ── Behaviour submenu (collapsible nav group) ─────────────────────────────── */
.nav-group{display:flex;flex-direction:column}
.nav-ghead{cursor:pointer}
.nav-ghead .nav-caret{margin-left:auto;display:inline-flex;align-items:center;opacity:.75;transition:transform .18s ease}
.nav-group.open .nav-ghead .nav-caret{transform:rotate(180deg)}
.nav-kids{display:none;flex-direction:column;gap:3px;margin-top:3px}
.nav-group.open .nav-kids{display:flex}
.nav-b.nav-sub{position:relative;padding:9px 12px 9px 46px;font-size:0.9rem}
.nav-b.nav-sub::before{content:"";position:absolute;left:27px;top:50%;transform:translateY(-50%);width:5px;height:5px;border-radius:50%;background:var(--nav-muted);opacity:.55}
.nav-b.nav-sub.on::before{background:#fff;opacity:1}

/* ── Cohort retention grid ─────────────────────────────────────────────────── */
.coh-scroll{overflow-x:auto}
.coh-grid{display:flex;flex-direction:column;gap:5px;min-width:560px}
.coh-row{display:grid;grid-template-columns:104px 62px repeat(var(--wk,8),minmax(38px,1fr));gap:5px;align-items:center}
.coh-lab{font-size:0.8rem;font-weight:700;color:var(--ink);white-space:nowrap}
.coh-size{font-size:0.78rem;color:var(--muted);text-align:right;justify-self:end}
.coh-head .coh-lab,.coh-head .coh-size,.coh-hcell{font-size:0.66rem;font-weight:700;letter-spacing:0.05em;text-transform:uppercase;color:var(--muted)}
.coh-cell{height:36px;border-radius:7px;display:flex;align-items:center;justify-content:center;background:rgba(var(--accent-rgb),var(--i,0))}
.coh-cell span{font-size:0.72rem;font-weight:700;color:var(--ink)}
.coh-hcell{background:none;text-align:center}
.coh-empty{background:transparent}

/* ── Visitor lifecycle (stacked weekly bars) ───────────────────────────────── */
.lc-chart{display:flex;gap:10px;height:190px;align-items:flex-end}
.lc-col{flex:1;display:flex;flex-direction:column;align-items:center;height:100%;justify-content:flex-end}
.lc-bar{width:100%;max-width:44px;flex:1;display:flex;flex-direction:column;justify-content:flex-end;border-radius:8px 8px 3px 3px;overflow:hidden}
.lc-bar i{display:block;width:100%}
.lc-new{background:var(--accent)}
.lc-ret{background:rgba(var(--accent-rgb),0.55)}
.lc-res{background:rgba(var(--accent-rgb),0.28)}
.lc-lab{font-size:0.68rem;color:var(--muted);font-weight:700;margin-top:8px}
.lc-legend{display:flex;gap:18px;margin-top:16px;font-size:0.78rem;color:var(--muted);font-weight:600}
.lc-legend span{display:inline-flex;align-items:center;gap:7px}
.lc-sw{width:12px;height:12px;border-radius:3px;display:inline-block}

/* ── Session-length / stickiness histogram (reuses .retn columns) ──────────── */
.retn.hist{height:196px}
.retn.hist .rv{font-size:0.78rem}
.retn.hist .rl{font-size:0.66rem;text-align:center}

/* ── Engagement funnel ─────────────────────────────────────────────────────── */
.funnel{display:flex;flex-direction:column;gap:15px}
.fnl-row{display:flex;flex-direction:column;gap:6px}
.fnl-h{display:flex;justify-content:space-between;align-items:baseline;font-size:0.9rem;font-weight:700;color:var(--ink)}
.fnl-track{position:relative;height:34px;border-radius:9px;background:rgba(var(--accent-rgb),0.1);overflow:hidden}
.fnl-track i{position:absolute;left:0;top:0;bottom:0;border-radius:9px;background:linear-gradient(90deg,var(--accent),rgba(var(--accent-rgb),0.6))}
.fnl-pct{position:absolute;right:12px;top:50%;transform:translateY(-50%);font-size:0.8rem;font-weight:700;color:var(--ink)}
.fnl-drop{font-size:0.72rem;color:var(--muted);font-weight:600}

/* ── User-flow Sankey ──────────────────────────────────────────────────────── */
.sankey-scroll{overflow-x:auto}
.sankey{width:100%;min-width:560px;height:auto;display:block}
.sank-node{fill:var(--accent)}
.sank-link{fill:var(--accent)}
.sank-lab{fill:var(--muted);font-size:11px;font-weight:600}
.hleg{display:flex;align-items:center;gap:8px}
.hleg-hi{width:13px;height:13px;border-radius:3px;background:rgba(var(--accent-rgb),.8)}
.hleg-rw{width:13px;height:13px;border-radius:3px;box-shadow:inset 0 0 0 2px var(--violet)}
.table-card{padding:6px 8px 2px;overflow-x:auto}
.qgrid{display:grid;grid-template-columns:repeat(2,1fr);gap:13px}
.qcell{border:1px solid var(--line);border-radius:var(--radius-sm);padding:14px}
.qcell .qk{font-size:0.8rem;color:var(--muted);font-weight:600;display:flex;align-items:center;gap:8px}
.qcell .qk .fa{color:var(--label);font-size:0.82rem}
.qcell .qv{font-size:1.42rem;font-weight:700;margin-top:6px;font-variant-numeric:tabular-nums}
.qcell .qs{font-size:0.76rem;color:var(--muted);margin-top:2px}
@media(max-width:1000px){.mstrip.five{grid-template-columns:repeat(2,1fr)}}
@media(max-width:560px){.mstrip.five{grid-template-columns:1fr 1fr}.bl-face{width:132px}.bl-val{width:74px}}

/* topbar filters: time range + country */
.subline{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:3px}
.subline p{margin:0}
.fchip{display:inline-flex;align-items:center;gap:7px;background:var(--accent-soft);color:var(--accent);border-radius:var(--radius-pill);padding:4px 5px 4px 9px;font-size:0.78rem;font-weight:700}
.fchip .cc{width:22px;height:16px;background:rgba(var(--accent-rgb),0.16);color:var(--accent)}
.fchip-x{border:0;background:rgba(var(--accent-rgb),0.16);color:var(--accent);width:19px;height:19px;border-radius:50%;display:grid;place-items:center}
.fchip-x .fa{font-size:0.6rem}
.fchip-x:hover{background:rgba(var(--accent-rgb),0.3)}
.ctrl{position:relative}
.ctrl-b{display:inline-flex;align-items:center;gap:9px;background:var(--card);border:1px solid var(--line);border-radius:var(--radius-pill);padding:8px 13px;font-weight:700;font-size:0.86rem;color:var(--ink);box-shadow:var(--shadow);white-space:nowrap}
.ctrl-b:hover{border-color:var(--line-hover)}
.ctrl-b .cvi{color:var(--label);font-size:0.86rem;flex:none}
.ctrl-b .chev{color:var(--muted);font-size:0.6rem;transition:transform .18s ease;flex:none}
.ctrl.open .ctrl-b{border-color:var(--accent);box-shadow:0 0 0 4px var(--accent-soft)}
.ctrl.open .ctrl-b .chev{transform:rotate(180deg)}
.ctrl-menu{position:absolute;top:calc(100% + 9px);right:0;min-width:230px;background:var(--card);border:1px solid var(--line);border-radius:var(--radius-sm);box-shadow:0 26px 60px -28px rgba(20,30,60,0.55);padding:6px;z-index:var(--z-pop);display:none}
.ctrl.open .ctrl-menu{display:block;animation:pop .16s ease}
@keyframes pop{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}
.ctrl-opt{display:flex;align-items:center;gap:10px;width:100%;text-align:left;border:0;background:none;padding:9px 11px;border-radius:10px;font-weight:600;font-size:0.88rem;color:var(--ink);cursor:pointer}
.ctrl-opt:hover{background:var(--sand)}
.ctrl-opt.on{color:var(--accent);font-weight:700}
.ctrl-opt .ck{margin-left:auto;opacity:0;color:var(--accent);font-size:0.72rem;flex:none}
.ctrl-opt.on .ck{opacity:1}
.ctrl-opt .cc{flex:none}
.ctrl-sep{height:1px;background:var(--line);margin:5px 4px}
.ctrl-search{display:flex;align-items:center;gap:8px;padding:8px 10px;margin:2px 2px 6px;border:1px solid var(--line);border-radius:10px}
.ctrl-search:focus-within{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
.ctrl-search input{border:0;outline:0;background:none;font-weight:600;font-size:0.86rem;width:100%;color:var(--ink);min-width:0}
.ctrl-search input::placeholder{color:var(--muted)}
.ctrl-search .fa{color:var(--muted);font-size:0.8rem;flex:none}
.ctrl-list{max-height:264px;overflow-y:auto}
.ctrl-empty{padding:12px 11px;color:var(--muted);font-size:0.84rem;font-weight:600}
.filters{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.ctrl[data-ctrl="range"] .ctrl-menu{min-width:240px}
.rng-opt{justify-content:flex-start}
.rng-opt .rng-t{flex:1}
.range-kbd{margin-left:auto;display:inline-grid;place-items:center;min-width:21px;height:21px;padding:0 5px;font-size:0.7rem;font-weight:700;line-height:1;color:var(--muted);border:1px solid var(--line);border-radius:6px;background:var(--card);font-family:inherit;flex:none}
.rng-opt.on{color:var(--accent);font-weight:700}
.rng-opt.on .range-kbd{color:var(--accent);border-color:rgba(var(--accent-rgb),0.5)}
.rng-custom{padding:9px 7px 5px;border-top:1px solid var(--line);margin-top:5px}
.rng-custom[hidden]{display:none}
.rc-fields{display:flex;gap:8px}
.rc-f{flex:1;display:flex;flex-direction:column;gap:4px;font-size:0.66rem;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:0.05em}
.rc-f input{border:1px solid var(--line);border-radius:9px;padding:8px;font-family:inherit;font-weight:600;font-size:0.82rem;color:var(--ink);background:var(--card)}
.rc-f input:focus{outline:0;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
.rc-apply{margin-top:8px;width:100%;background:var(--accent);color:#fff;border:0;border-radius:9px;padding:9px;font-weight:700;font-size:0.84rem}
.rc-apply:hover{background:#005fe6}
.fchips{display:inline-flex;align-items:center;gap:8px;flex-wrap:wrap}
.wrap.refreshing .card{animation:cardRefresh .3s ease}
@keyframes cardRefresh{from{opacity:.55}to{opacity:1}}

/* person detail page */
.backlink{display:inline-flex;align-items:center;gap:9px;background:var(--card);border:1px solid var(--line);border-radius:var(--radius-pill);padding:8px 15px;font-weight:700;font-size:0.85rem;color:var(--label);box-shadow:var(--shadow);margin-bottom:18px}
.backlink:hover{border-color:var(--line-hover);color:var(--ink)}
.backlink .fa{font-size:0.72rem}
.person-hero{display:flex;flex-direction:column;gap:20px}
.ph-id{display:flex;align-items:center;gap:16px}
.ph-av{width:56px;height:56px;font-size:1.2rem;flex:none}
.ph-name{font-size:1.32rem;font-weight:700}
.ph-meta{display:flex;align-items:center;gap:9px;margin-top:4px;color:var(--muted);font-weight:600;font-size:0.86rem;flex-wrap:wrap}
.ph-meta .cc{flex:none}
.ph-meta .pdot{opacity:.5}
.ph-badge{margin-left:auto;flex:none}
.ph-stats{display:grid;grid-template-columns:repeat(5,1fr);gap:1px;background:var(--line);border:1px solid var(--line);border-radius:var(--radius-sm);overflow:hidden}
.ph-stats>div{background:var(--card);padding:14px 16px}
.ph-stats .k{font-size:0.72rem;color:var(--muted);font-weight:600}
.ph-stats .v{font-size:1.16rem;font-weight:700;margin-top:3px;font-variant-numeric:tabular-nums}
@media(max-width:820px){.ph-stats{grid-template-columns:repeat(2,1fr)}.ph-badge{margin-left:0}}

/* drill-down rows */
[data-drill]{cursor:pointer}
[data-drill-rows] tbody tr{cursor:pointer}
[data-drill-rows] tbody tr:hover{background:var(--sand)}
[data-drill-rows] .bl-row{cursor:pointer}
[data-drill-rows] .bl-row:hover{background:var(--sand)}
/* A barlist row rendered as a real button (keyboard-reachable drill), reset to look like a div row. */
button.bl-act{appearance:none;-webkit-appearance:none;background:none;border:0;border-bottom:1px solid var(--line);font:inherit;color:inherit;text-align:left;width:100%;cursor:pointer}
button.bl-act:last-child{border-bottom:0}
button.bl-act:hover{background:var(--sand)}
button.bl-act:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--primary);border-radius:8px}
.siterow[data-drill]:hover{border-color:var(--line-hover);box-shadow:var(--shadow)}

/* drill-down drawer */
.scrim{position:fixed;inset:0;background:rgba(16,20,34,0.44);opacity:0;visibility:hidden;transition:opacity .22s ease,visibility 0s linear .22s;z-index:var(--z-backdrop)}
.scrim.open{opacity:1;visibility:visible;transition:opacity .22s ease}
.drawer{position:fixed;top:0;right:0;height:100vh;height:100dvh;width:min(580px,94vw);background:var(--bg);box-shadow:-32px 0 64px -34px rgba(16,20,40,0.55);transform:translateX(101%);visibility:hidden;transition:transform .3s cubic-bezier(.22,1,.36,1),visibility 0s linear .3s;z-index:var(--z-drawer);display:flex;flex-direction:column}
.drawer.open{transform:none;visibility:visible;transition:transform .3s cubic-bezier(.22,1,.36,1)}
.drawer-h{display:flex;align-items:flex-start;gap:14px;padding:22px 24px;border-bottom:1px solid var(--line);background:var(--card);flex:none}
.dh-t{min-width:0;flex:1}
.drawer-h h3{font-size:1.16rem;font-weight:700;letter-spacing:-0.01em;overflow-wrap:anywhere}
.drawer-h .dsub{margin:4px 0 0;color:var(--muted);font-size:0.85rem;font-weight:600}
.drawer-x{width:38px;height:38px;border-radius:50%;border:1px solid var(--line);background:var(--card);color:var(--label);display:grid;place-items:center;flex:none}
.drawer-x:hover{color:var(--ink);border-color:var(--line-hover)}
.drawer-b{padding:22px 24px 40px;overflow-y:auto;flex:1;min-height:0}
.drawer-b .sec-h{margin:22px 0 12px}
.drawer-b .sec-h:first-child{margin-top:2px}
/* Source-page drill: declared-content list. Near-duplicate declarations are flagged so a stray dash or
   trailing space that splits a title's plays is visible at a glance. */
.dc-lead{margin:0 0 2px;font-weight:600;font-size:0.95rem;color:var(--ink);overflow-wrap:anywhere}
.dc-kpis{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-top:14px}
.dc-kpis>div{display:flex;flex-direction:column;gap:3px;padding:11px 13px;background:var(--sand);border-radius:10px}
.dc-k{font-size:0.68rem;text-transform:uppercase;letter-spacing:0.04em;color:var(--muted);font-weight:700}
.dc-v{font-size:1.16rem;font-weight:700}
.dc-note{margin:0 0 12px;font-size:0.84rem;color:var(--muted);line-height:1.5}
.dc-flag{margin:0 0 12px;padding:11px 13px;border-radius:10px;background:rgba(214,143,10,0.10);border:1px solid rgba(214,143,10,0.30);font-size:0.84rem;color:var(--ink);line-height:1.45}
.dc-flag code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:0.85em;background:var(--card);border-radius:5px;padding:1px 5px}
.dc-list{display:flex;flex-direction:column;border:1px solid var(--line);border-radius:12px;overflow:hidden}
.dc-row{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:11px 14px;border-bottom:1px solid var(--line)}
.dc-row:last-child{border-bottom:0}
.dc-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}
.dc-title{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:0.84rem;color:var(--ink);overflow-wrap:anywhere}
.dc-ep{display:inline-flex;align-items:center;gap:7px;font-size:0.76rem}
.dc-ep-tag{font-weight:700;letter-spacing:0.02em;color:var(--muted);background:var(--sand);border-radius:5px;padding:1px 6px}
.dc-ep-title{color:var(--muted);overflow-wrap:anywhere}
.dc-count{color:var(--muted);font-size:0.84rem;font-weight:700;flex:none}
.dc-ids{display:inline-flex;gap:6px;flex-wrap:wrap;justify-content:flex-end;flex:none}
.dc-id{display:inline-flex;align-items:center;font-size:0.72rem;font-weight:700;letter-spacing:0.02em;color:var(--accent);background:var(--accent-soft);border-radius:6px;padding:2px 7px;white-space:nowrap}
.dc-id.dc-id-type{color:var(--muted);background:var(--sand);text-transform:capitalize}
.dc-ids.dc-ids-none{font-size:0.72rem;color:var(--muted);font-weight:600;font-style:italic}
.dc-row.dc-warn{background:rgba(214,143,10,0.09)}
.dc-row.dc-warn .dc-count{color:#b5710a}
/* Resolved-content banner: the poster + canonical title a page/URL matched in content_map, with a button
   to open its full content page. Leads the source-page and Reports>Pages drills. */
.prh{display:flex;align-items:center;gap:14px;padding:13px 15px;margin:0 0 16px;border:1px solid var(--line);border-radius:12px;background:var(--sand)}
.prh .vt-thumb{width:46px;height:69px;border-radius:6px}
.prh-meta{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}
.prh-k{font-size:0.68rem;text-transform:uppercase;letter-spacing:0.04em;color:var(--muted);font-weight:700}
.prh-title{font-size:1.02rem;font-weight:700;color:var(--ink);letter-spacing:-0.01em;overflow-wrap:anywhere}
.prh-sub{display:inline-flex;flex-wrap:wrap;align-items:center;gap:8px;font-size:0.78rem}
.prh-type{display:inline-flex;align-items:center;font-size:0.72rem;font-weight:700;letter-spacing:0.02em;color:var(--muted);background:var(--card);border:1px solid var(--line);border-radius:6px;padding:1px 7px;text-transform:capitalize}
.prh-ids{color:var(--muted);font-weight:600;font-variant-numeric:tabular-nums}
.prh-open{flex:none;white-space:nowrap}
tr.row-link{cursor:pointer}
tr.row-link:hover{background:var(--sand)}
.dfacts{display:grid;grid-template-columns:1fr 1fr;gap:1px;background:var(--line);border:1px solid var(--line);border-radius:var(--radius-sm);overflow:hidden}
.dfacts>div{background:var(--card);padding:13px 15px}
.dfacts .k{font-size:0.75rem;color:var(--muted);font-weight:600}
.dfacts .v{font-size:1.06rem;font-weight:700;margin-top:3px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
.dhero{display:flex;align-items:center;gap:13px;margin-bottom:16px}
.dhero .amt{font-size:1.9rem;font-weight:700;letter-spacing:-0.02em;font-variant-numeric:tabular-nums}
.dfoot{margin-top:14px;color:var(--muted);font-size:0.82rem;font-weight:600;text-align:center}
.dnote{margin-top:14px;color:var(--label);font-size:0.82rem;font-weight:600;background:var(--sand);border-radius:var(--radius-sm);padding:12px 14px}
/* Fingerprint tile on the person profile + the shared-audience drawer it opens. */
.fp-cell{display:flex;flex-wrap:wrap;align-items:center;gap:8px;font-size:0.9rem;font-weight:600}
.fp-val{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:0.8rem;font-weight:600;background:var(--sand);border:1px solid var(--line);border-radius:6px;padding:2px 6px}
.fp-conf{font-size:0.72rem;color:var(--muted);font-weight:600}
.fp-uniq{font-size:0.72rem;color:var(--muted);font-weight:600}
.fp-share{background:var(--accent-soft);color:var(--accent);border:0;border-radius:999px;padding:4px 11px;font-size:0.74rem;font-weight:700;cursor:pointer}
.fp-share:hover{background:var(--accent);color:#fff}
.fp-rows{display:flex;flex-direction:column;margin-top:4px}
.fp-row{display:flex;flex-wrap:wrap;align-items:center;gap:6px 12px;padding:12px 4px;border-bottom:1px solid var(--line);color:inherit;text-decoration:none}
.fp-row:last-child{border-bottom:0}
.fp-row:hover{background:var(--sand)}
.fp-row-id{display:flex;align-items:center;gap:8px;flex:1;min-width:150px;font-size:0.8rem}
.fp-row-id code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;overflow-wrap:anywhere}
.fp-row-meta{font-size:0.78rem;color:var(--muted);font-weight:600}
.fp-row-seen{font-size:0.75rem;color:var(--muted);font-weight:600;margin-left:auto}
.dtoolbar{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:14px;flex-wrap:wrap}

@media (prefers-reduced-motion:reduce){.screen,.pane{animation:none}.pulse{animation:none}.ctrl.open .ctrl-menu{animation:none}.wrap.refreshing .card{animation:none}.scrim,.drawer{transition:none}.side,.side-scrim{transition:none}}
@media (max-width:1000px){.bento{grid-template-columns:1fr}.b-earn{grid-row:auto}.grid2,.grid2.wide{grid-template-columns:1fr}.mstrip{grid-template-columns:repeat(2,1fr)}}

/* Below 820px the fixed 250px sidebar can't share the row with content on a phone, so it folds into
   an off-canvas drawer: content spans the full width, the hamburger (.nav-toggle, injected into .top by
   app.js) slides .side in over a dimming scrim, and the open state is a body class. The drawer shows the
   full sidebar (labels + footer), not an icon rail, so nav is legible one-handed. Console layout gets this
   too: its top-bar rules are gated to >=1081px, so below that both layouts share this drawer. */
@media (max-width:820px){
  .app{grid-template-columns:1fr}
  .side{
    position:fixed;top:0;left:0;bottom:0;height:100dvh;width:min(290px,84vw);
    z-index:var(--z-drawer);overflow-y:auto;overscroll-behavior:contain;
    transform:translateX(-100%);transition:transform .28s cubic-bezier(.22,1,.36,1);
    padding:20px 16px calc(22px + env(safe-area-inset-bottom));
  }
  body.nav-open .side{transform:none;box-shadow:26px 0 64px -34px rgba(8,12,24,0.7)}
  body.nav-open{overflow:hidden}
  /* The drawer scrolls, so the nav keeps its natural height and the Next payout block flows directly
     under the last nav item with a small gap. Pinning it to the bottom (margin-top:auto) let it overlap
     the last items on short screens. */
  .side .nav{flex:0 0 auto}
  .side .side-foot{margin-top:20px}
  .side-scrim{
    display:block;position:fixed;inset:0;background:rgba(10,14,26,0.5);
    z-index:var(--z-backdrop);opacity:0;visibility:hidden;
    transition:opacity .28s ease,visibility 0s linear .28s;
  }
  body.nav-open .side-scrim{opacity:1;visibility:visible;transition:opacity .28s ease}
  .nav-toggle{display:grid}
  .top{padding-left:16px;padding-right:16px;gap:12px}
  /* the rule is inset to the gutter, so it has to follow the gutter down to 16 or it lands 24px
     inside the content it underlines */
  .top::after{left:16px;right:16px}
  .wrap{padding-left:16px;padding-right:16px}
  .top-h p{display:none}
  /* Dense tables scroll horizontally inside their card instead of being clipped by .app overflow. */
  .tbl-scroll,.table-card{-webkit-overflow-scrolling:touch}
  .tbl.people{min-width:640px}
  .tbl.breakdown{min-width:560px}
  table[data-drill-rows="video"]{min-width:560px}
}
@media (max-width:560px){.mstrip{grid-template-columns:1fr 1fr}.set-grid{grid-template-columns:1fr}.switcher span.hostn{display:none}}

/* ── Content page (ADR-072): title leaderboard + drill-down ──────────────────── */
.mstrip.three{grid-template-columns:repeat(3,1fr)}
/* Toolbar: search on the left, the type + sort segmented controls on the right. Wraps to a stack on
   narrow viewports; the .seg default bottom margin is dropped here since the toolbar owns the spacing. */
.content-bar{display:flex;align-items:center;justify-content:flex-start;gap:10px;flex-wrap:wrap;margin:18px 0 14px}
.content-bar .seg{margin:0}
.content-segs{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
/* Two labelled filter groups separated by a 1px rule (Stripe's divider colour). The label is the
   measured 12/16 muted card label; the rule stands 24px tall, centred between the groups. */
.fgroup{display:inline-flex;align-items:center;gap:8px}
.fgroup-l{font-size:12px;line-height:16px;font-weight:600;color:var(--muted)}
.fgroup-div{width:1px;height:24px;background:var(--line-hover);flex:none}
@media (max-width:560px){.fgroup-div{display:none}.fgroup{flex:1 1 100%}}
.content-search{position:relative;display:flex;align-items:center;flex:1 1 240px;max-width:340px}
.content-search .fa{position:absolute;left:12px;width:14px;height:14px;color:var(--muted-2);pointer-events:none}
.content-q{width:100%;font:inherit;font-size:0.86rem;font-weight:600;color:var(--ink);background:var(--card);border:1px solid var(--line);border-radius:var(--radius-pill);padding:9px 14px 9px 34px}
.content-q::placeholder{color:var(--muted-2);font-weight:600}
.content-q:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}

/* Poster grid: fixed-min responsive columns, no media queries. Cards are buttons (the whole card opens
   the drill-down), so they carry no card chrome of their own beyond the poster + a two-line meta block. */
.poster-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:18px}
.poster-card{display:flex;flex-direction:column;gap:9px;text-align:left;background:none;border:0;padding:0;cursor:pointer;font:inherit;color:inherit}
.poster-img{position:relative;aspect-ratio:2/3;border-radius:12px;overflow:hidden;background:
  linear-gradient(150deg,hsl(var(--h),42%,42%),hsl(var(--h),46%,26%));
  border:1px solid var(--line);display:grid;place-items:center;transition:transform .18s cubic-bezier(.22,1,.36,1),box-shadow .18s ease}
.poster-card:hover .poster-img{transform:translateY(-3px);box-shadow:var(--shadow)}
.poster-card:focus-visible{outline:none}
.poster-card:focus-visible .poster-img{box-shadow:0 0 0 3px var(--accent-soft);border-color:var(--accent)}
.poster-pic{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:1}
/* The typographic fallback sits under the <img>; a failed/absent image (.no-art) leaves it showing. */
.poster-fallback{font-size:1.9rem;font-weight:700;letter-spacing:0.02em;color:rgba(255,255,255,0.9);text-shadow:0 1px 8px rgba(0,0,0,0.3)}
.poster-card.no-art .poster-pic,.poster-pic.poster-err{display:none}
/* Row thumbnail (Video top-videos): a 28x42 2:3 tile reusing the poster component, with the title
   beside it. The fallback initials scale down to fit the small tile. */
.vt-cell{display:inline-flex;align-items:center;gap:10px;min-width:0}
.vt-thumb{width:28px;height:42px;aspect-ratio:auto;border-radius:4px;flex:none}
.vt-thumb .poster-fallback{font-size:0.72rem}
.vt-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Drill hero flag: the same self-hosted flag as the rows, scaled up to sit beside the big figure. */
.dhero-flag{display:inline-flex;flex:none}
.dhero-flag .flag-ico,.dhero-flag .flag-code{width:36px;height:26px;border-radius:4px;vertical-align:0}
.dhero-flag .flag-code{font-size:0.72rem}
.poster-rank{position:absolute;top:8px;left:8px;z-index:2;min-width:22px;height:22px;padding:0 6px;display:inline-flex;align-items:center;justify-content:center;
  font-size:0.74rem;font-weight:700;color:#fff;background:rgba(12,16,23,0.72);border-radius:999px;backdrop-filter:blur(3px)}
.poster-meta{display:flex;flex-direction:column;gap:3px;min-width:0}
.poster-title{font-size:0.9rem;font-weight:700;color:var(--ink);line-height:1.25;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
.poster-sub{display:flex;align-items:baseline;justify-content:space-between;gap:8px}
.poster-type{font-size:0.72rem;font-weight:700;text-transform:uppercase;letter-spacing:0.05em;color:var(--muted-2)}
.poster-num{font-size:0.8rem;font-weight:700;color:var(--accent);white-space:nowrap}
.sk-poster{aspect-ratio:2/3;border-radius:12px;background:var(--sand)}

.content-pager{display:flex;align-items:center;justify-content:center;gap:14px;margin-top:22px}
.content-pager .pg-here{font-size:0.82rem;font-weight:700;color:var(--muted)}
/* Content > Unmatched table: declared pages that resolved to no title. Same table chrome as the rest of
   the app; the lead line explains the fix and the "sent" cell shows the raw declared ids. */
.um-lead{font-size:0.86rem;line-height:1.5;color:var(--muted);margin:0 0 14px;max-width:78ch}
.um-table{width:100%;border-collapse:collapse;font-size:0.88rem}
.um-table th{text-align:left;font-size:0.72rem;font-weight:700;letter-spacing:0.02em;text-transform:uppercase;color:var(--muted);padding:0 12px 8px;border-bottom:1px solid var(--line)}
.um-table th.r,.um-table td.r{text-align:right}
.um-table td{padding:11px 12px;border-bottom:1px solid var(--line-soft);color:var(--ink);vertical-align:middle}
.um-table tr:last-child td{border-bottom:0}
.um-type{display:inline-block;font-size:0.74rem;font-weight:700;color:var(--label);background:var(--sand);border-radius:5px;padding:2px 8px}
.um-ids{font-family:var(--mono,ui-monospace,monospace);font-size:0.8rem;color:var(--ink);background:var(--sand);border-radius:5px;padding:2px 7px;white-space:nowrap}
.um-none{font-size:0.82rem;color:var(--bad);font-weight:600}
.um-path{color:var(--muted);max-width:280px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.empty-card{text-align:center;padding:44px 24px}
.empty-title{font-size:1.02rem;font-weight:700;color:var(--ink);margin:0 0 6px}
.empty-sub{font-size:0.88rem;font-weight:500;color:var(--muted);margin:0 auto;max-width:52ch;line-height:1.5}

/* Drill-down table (inside the shared .dmodal). The "differs" flag marks a source page whose own
   <title> disagrees with the matched catalogue title. */
.content-dd-tbl{min-width:520px}
.dd-path{color:var(--ink);font-weight:700;word-break:break-all}
.dd-flag{display:inline-block;margin-left:6px;font-size:0.68rem;font-weight:700;text-transform:uppercase;letter-spacing:0.04em;
  color:var(--wait,#b06a00);background:rgba(176,106,0,0.14);border-radius:999px;padding:1px 7px;vertical-align:middle}
@media (max-width:900px){.mstrip.three{grid-template-columns:1fr}}
@media (prefers-reduced-motion:reduce){.poster-card:hover .poster-img{transform:none}}
/* end content page */
/* end publisher-area redesign */

/* ==========================================================================
   Stripe skin.

   Every value below was measured off Stripe's own dashboard: SingleFile saves
   rendered headless at 1440x1100, walked with getComputedStyle and
   getBoundingClientRect. The source element is named in the comment above each
   block. Nothing here is invented: if Stripe's element does not carry the
   declaration, it is not here.

   It is a final layer rather than an edit of the redesign above so the two
   skins stay separable and the dark/console variants keep their own rules.
   Class names and DOM are untouched; only the styling changes.
   ========================================================================== */

:root{
  /* type - Plus Jakarta Sans (self-hosted) is the default; the measured system stack is the fallback.
     This is the winning :root (latest at equal specificity), so it sets the effective sitewide face. */
  --font-sans:'Plus Jakarta Sans',-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";

  /* colour - each measured on the element named */
  --ink:rgb(26,44,68);            /* A#primary-nav-item-link-home color */
  --ink-h1:rgb(48,49,61);         /* H1#secondary-nav-title color */
  --body-ink:rgb(84,89,105);      /* BODY color */
  --muted:rgb(60,79,105);         /* DIV#shortcuts-nav color */
  --label:rgb(65,69,82);          /* P body color */
  --ink-button:rgb(39,57,81);     /* A#bento-custom-add-button color */
  --accent:rgb(83,58,253);        /* A#primary-nav-item-link-customers, active */
  --accent-rgb:83,58,253;
  --accent-soft:rgba(83,58,253,0.1);
  --accent-ico:rgb(103,93,255);   /* svg#primary-nav-item-icon fill, active */
  --violet:rgb(98,90,250);        /* A "View" inline link */
  --violet-rgb:98,90,250;
  --icon-idle:rgb(80,97,122);     /* svg#primary-nav-item-icon fill, idle */
  --line:rgb(236,241,246);        /* sidebar divider, TH inset, pagination border */
  --line-hover:rgb(212,222,233);  /* button ring, tab strip border-bottom */
  --bg:rgb(255,255,255);          /* BODY backgroundColor */
  --card:rgb(255,255,255);
  --sand:rgb(244,247,250);        /* search field bg, panel bg */
  --tint:rgb(244,247,250);
  --nav:rgb(255,255,255);
  --nav-2:rgb(244,247,250);
  --nav-ink:rgb(26,44,68);
  --nav-muted:rgb(60,79,105);
  --nav-line:rgb(236,241,246);

  /* geometry - measured rects at 1440px */
  --side-w:240px;                 /* sidebar DIV [0,44,240,1056] */
  --side-pad-x:20px;              /* NAV#primary-nav padding-left/right */
  --content-w:1120px;             /* content column DIV [280,104,1120,*] at 1440 */
  /* MAIN max-width 1600px, margin-inline auto, padding 0 40px 0 280px. That 1600 is measured over
     the 240px sidebar, so the part of it that is ours is 1600-240=1360, gutters included. Past it
     the column stops growing and centres: measured 440->1720 at 1920 and 760->2040 at 2560, which
     is what centring a 1360 box in the track right of the sidebar gives. */
  --shell-w:1360px;
  --radius:12px;                  /* outer panel DIV border-radius */
  --radius-sm:8px;                /* A#account-switcher-trigger border-radius */
  --radius-xs:6px;                /* card DIV, nav item, button border-radius */
  --radius-pill:999px;            /* chip SPAN border-radius */

  /* Stripe draws outlines as box-shadow rings, so a box keeps its size across states */
  --ring-light:rgb(236,241,246) 0 0 0 1px;
  --ring-mid:rgb(212,222,233) 0 0 0 1px;    /* A#bento-custom-add-button */
  --ring-brand:rgb(83,58,253) 0 0 0 1.5px;  /* selected option card */
  --shadow:rgb(236,241,246) 0 0 0 1px;
  /* popover DIV : the elevated stack Stripe puts under every menu */
  --shadow-pop:rgba(0,0,0,0.12) 0 1px 1px 0,rgba(64,68,82,0.16) 0 0 0 1px,rgba(64,68,82,0.08) 0 2px 5px 0;
}

/* BODY : 14px / 400, colour rgb(84,89,105), white ground */
body{font-family:var(--font-sans);font-size:14px;font-weight:400;line-height:20px;color:var(--body-ink);background:var(--bg)}
h1,h2,h3,h4{letter-spacing:normal}

/* ---------- shell ---------- */
.app{grid-template-columns:var(--side-w) 1fr}
/* The cap belongs to the column, not to one band inside it. It used to sit on .wrap alone, so past
   ~1520px the body stopped widening while the header kept going and its rule ran out past the
   content it is meant to underline. Stripe's NAV stays pinned at x=0 while MAIN centres, and auto
   margins on the grid item reproduce that without a 100vw term (100vw counts the scrollbar and
   would shift the column half a scrollbar left of Stripe's). */
.main{max-width:var(--shell-w);margin-inline:auto;width:100%}
/* Console folds the sidebar into a 64px top bar and is deliberately edge to edge - its own .wrap
   drops the cap - so the column cap is not its layout. Its own `.top{top:64px}` in the console
   block parks the header under that bar, which is right again now the header is sticky. */

/* ---------- sidebar ---------- */
/* sidebar DIV [0,44,240,1056] white; its divider is a literal 1px DIV [239,44,1,1056] #ecf1f6 */
.side{background:var(--nav);color:var(--ink);border-right:1px solid var(--line);padding:0}
/* account switcher bar DIV [0,44,240,60] : 60 tall, padding 0 20px.
   The brand block sits in that slot, so it takes that box. */
.brand{height:60px;padding:0 var(--side-pad-x);gap:8px;flex:none}
.brand-mark{width:24px;height:24px;border-radius:var(--radius-xs);background:var(--accent);flex:none}
.brand-mark .fa{font-size:0.8rem}
.brand-name{font-size:14px;line-height:20px;font-weight:600;color:var(--ink);letter-spacing:normal}
.brand-tag{font-size:12px;line-height:16px;font-weight:400;letter-spacing:normal;text-transform:none;color:var(--muted);margin-top:0}
/* Stripe's rail opens with ONE control, not a logo row plus an account row: the account switcher IS
   the header, and it carries the product mark. It occupies the same 60px slot the brand block used
   (12 + 36 + 12), and its menu drops downward (.acct-menu-down).
   Quiet by default - transparent ground, no border - so the top of the rail is the account name and
   not a filled box. Hover/open fill with sand, which is how Stripe's own switcher behaves. */
.side-head{padding:18px var(--side-pad-x) 8px;flex:none}
/* 10px of inset puts the 24px mark at x=30, within a pixel of the nav icons below it, so the rail has
   one left edge rather than two. */
.side-head .acct-side-btn{
  gap:10px;padding:6px 10px;background:none;border-color:transparent;box-shadow:none;
  transition:background-color .16s ease;
}
.side-head .acct-side-btn:hover{background:var(--sand);border-color:transparent}
.side-head .acct-dd-side.open .acct-side-btn{background:var(--sand)}
.side-head .acct-side-id b{font-size:14px;line-height:20px;font-weight:600;color:var(--ink-h1)}
.side-head .acct-side-id small{font-size:12px;line-height:16px;font-weight:400;color:var(--muted)}
/* The switcher glyph sits at the far edge of the row, quiet until the row is hovered. */
.acct-switch{margin-left:auto;flex:none;color:var(--muted-2)}
.side-head .acct-side-btn:hover .acct-switch{color:var(--ink)}
/* Stripe's single 8x8 down caret (db-ChromeHeader-accountSwitcher). Same placement as .acct-switch:
   far edge, quiet until hover. The rail title is the signed-in email, a single ellipsised line. */
.acct-caret{margin-left:auto;flex:none;color:var(--muted-2)}
.side-head .acct-side-btn:hover .acct-caret{color:var(--ink)}
.side-head .acct-side-id .acct-side-title{display:block;font-size:14px;line-height:20px;font-weight:600;color:var(--ink-h1);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Identity skeleton: shown on live boot until /api/auth/me resolves, so the real host never flashes the
   demo email and the row does not jump when it fills. Reuses the shimmer used elsewhere in the shell. */
.acct-side-skel{display:block;width:130px;max-width:100%;height:13px;border-radius:4px;background:linear-gradient(90deg,var(--line-soft) 25%,var(--line) 37%,var(--line-soft) 63%);background-size:400% 100%;animation:tk-shimmer 1.3s ease infinite}
.avatar.avatar-skel{background:linear-gradient(90deg,var(--line-soft) 25%,var(--line) 37%,var(--line-soft) 63%);background-size:400% 100%;animation:tk-shimmer 1.3s ease infinite;color:transparent}
@media (prefers-reduced-motion: reduce){.acct-side-skel,.avatar.avatar-skel{animation:none}}
/* NAV#primary-nav : column, padding 20px 20px 48px. Its 28px gap is the gap BETWEEN sections, not
   between items: SECTION#core-nav [20,124,200,150] holds 5 items of 30 with nothing between them,
   and the next section starts at y=302 (274 + 28). Our ungrouped leaves are direct children of
   .nav, so a flex gap here put the section spacing between every single item - the "big gaps" on
   the items with no parent title. The leaves stack flush and the groups carry the 28 themselves. */
.nav{gap:0;margin-top:0;padding:20px var(--side-pad-x) 48px;flex:1 1 auto;min-height:0}
.nav-group{margin-top:28px}
/* SECTION#shortcuts-nav [20,302,200,176] = a 26px label + 5 items of 30: the label sits directly on
   its items, so the kid list takes neither a gap nor a leading offset. */
.nav-kids{gap:0;margin-top:0}
/* In console the group is a floating dropdown anchored to the bar, not a section in a column. Gated
   like the rest of that layout: below 1081px console falls back to the sidebar drawer, which wants
   the section spacing. */
/* A#primary-nav-item-link-* [20,*,200,30] : h30, radius 6, gap 6, 14px / 400 */
.nav-b{gap:6px;height:30px;min-height:30px;padding:0;position:relative;z-index:0;border-radius:var(--radius-xs);font-size:14px;line-height:20px;font-weight:400;color:var(--ink);transition:none}
/* Stripe paints the hover/active highlight as a pill inset -4px horizontally (it bleeds toward the rail),
   not as a background on the padded row. Match that with a ::before so the item text/icon stay flush to
   the 20px nav edge. */
.nav-b::before{content:"";position:absolute;left:-4px;right:-4px;top:0;bottom:0;border-radius:var(--radius-xs);z-index:-1}
.nav-b .fa{width:16px;height:16px;font-size:0.85rem;margin-left:0;color:var(--icon-idle);opacity:1}
.nav-b:hover{background:none;color:var(--ink)}
.nav-b:hover::before{background:#f7f5fd}
/* active state is colour and weight only: Stripe draws no background pill on the nav */
.nav-b.on{background:none;color:var(--accent);font-weight:600}
.nav-b.on .fa{color:var(--accent-ico);opacity:1}
/* DIV#shortcuts-nav [20,*,200,26] : the group label, 12px / 400, muted */
.nav-group{display:flex;flex-direction:column;gap:0}
.nav-b.nav-ghead{height:26px;min-height:26px;padding-right:4px;font-size:12px;line-height:16px;font-weight:400;color:var(--muted)}
.nav-b.nav-ghead:hover{background:none;color:var(--ink)}
.nav-b.nav-ghead .fa{display:none}
.nav-b.nav-ghead.on{color:var(--muted);font-weight:400}
.nav-caret{margin-left:auto;color:var(--icon-idle);display:inline-flex}
/* Zero the sub-item's inherited right padding (12px from the base .nav-sub) so an attention .nav-dot
   lands at the same x as one on a top-level leaf (e.g. Need help?), not 12px short of it. */
.nav-b.nav-sub{padding:0 0 0 4px}
.nav-b.nav-sub::before{display:none}
/* side footer: no rule above it, and the payout tile is a card on the nav ground */
.side-foot{padding:0 var(--side-pad-x) 20px;border-top:0;gap:8px}
.payout-mini{background:var(--sand);border-radius:var(--radius-xs);padding:16px}
.payout-mini .pm-l{font-size:12px;line-height:16px;font-weight:600;letter-spacing:normal;text-transform:none;color:var(--muted);gap:4px}
.payout-mini .pm-l .fa{font-size:0.75rem}
.payout-mini .pm-v{font-size:20px;line-height:28px;font-weight:700;color:var(--ink-h1);margin-top:4px}
.payout-mini .pm-s{font-size:12px;line-height:16px;color:var(--muted)}
.help-b{font-size:14px;line-height:20px;font-weight:400;color:var(--muted);padding:0 6px;height:30px;border-radius:var(--radius-xs)}
.help-b:hover{background:var(--sand);color:var(--ink)}

/* ---------- collapse toggle ---------- */
/* Stripe's .toggle-nav-collapsed (measured from the saved Home page): a 40x56 button, radius 6,
   background TRANSPARENT, no border, no shadow, opacity 1 (always visible - NOT hover-gated). It lives in
   a full-height strip flexed to the rail's right edge and is align-self:center (box [232,544,40,56] in a
   240 rail at 1100 tall). We reproduce it as a strip absolutely positioned down the rail's right edge
   (.side's sticky position is its containing block), vertically centring a transparent chevron button.
   The chevron rotates 180deg to flip collapse/expand. Rail is 240 open, collapses to a 64px icon rail. */
/* right:-32px straddles the rail's right border exactly as Stripe does: in a 240 rail the 40px button
   spans x=232..272, i.e. 8px inside the rail and the rest overhanging the content gutter. Vertically
   centred. Needs .side stacked above .main (below) so the overhang paints over the content, not under. */
.nav-collapse{position:absolute;top:50%;right:-32px;transform:translateY(-50%);z-index:2;margin:0;padding:0}
.nav-collapse-toggle{width:40px;height:56px;display:grid;place-items:center;border:0;background:transparent;box-shadow:none;
  border-radius:var(--radius-xs);color:var(--nav-muted);cursor:pointer;padding:0}
.nav-collapse-toggle:focus-visible{outline:none;box-shadow:var(--ring-brand)}
.nav-collapse-toggle svg{width:16px;height:16px;transition:transform .18s ease}
/* Default is a plain vertical line (reads as a rail handle); on hover of the rail it morphs into the
   collapse chevron - exactly Stripe's behaviour. No hover background. */
.nav-collapse-toggle svg .nc-line,.nav-collapse-toggle svg .nc-chev{stroke:rgb(125,139,164);stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round;transition:opacity .16s ease}
.nav-collapse-toggle svg .nc-chev{opacity:0}
.side:hover .nav-collapse-toggle svg .nc-line{opacity:0}
.side:hover .nav-collapse-toggle svg .nc-chev{opacity:1}

/* Collapsed rail (html[data-nav="collapsed"]): 64px, icons only. Gated to >=821px: below that the
   sidebar is an off-canvas drawer that needs its full labels, so the collapse (and its toggle) do not apply. */
@media (min-width:821px){
/* Lift the rail above the content column so the collapse toggle's border overhang paints over .main
   (both are grid siblings; without this the later-painted .main would cover the overhang). Desktop only
   so the mobile drawer's own stacking is untouched. */
.side{z-index:5}
html[data-nav="collapsed"]{--side-w:64px}
html[data-nav="collapsed"] .brand{justify-content:center;padding:0}
html[data-nav="collapsed"] .brand>div,
html[data-nav="collapsed"] .nav-b span,
html[data-nav="collapsed"] .nav-caret,
html[data-nav="collapsed"] .nav-b.nav-ghead,
html[data-nav="collapsed"] .payout-mini,
html[data-nav="collapsed"] .help-b span,
html[data-nav="collapsed"] .acct-side-id,
html[data-nav="collapsed"] .acct-switch,
html[data-nav="collapsed"] .acct-caret,
html[data-nav="collapsed"] .acct-side-btn .dd-caret{display:none}
html[data-nav="collapsed"] .nav-b{justify-content:center;padding:0}
/* Keep the hover pill inside the 64px rail rather than bleeding -4px past its edges. */
html[data-nav="collapsed"] .nav-b::before{left:8px;right:8px}
html[data-nav="collapsed"] .nav-b .fa{margin-left:0}
html[data-nav="collapsed"] .nav{padding-left:0;padding-right:0}
html[data-nav="collapsed"] .nav-kids{align-items:center}
html[data-nav="collapsed"] .side-foot{padding-left:0;padding-right:0;align-items:center}
html[data-nav="collapsed"] .side-head{padding-left:0;padding-right:0;display:flex;justify-content:center}
html[data-nav="collapsed"] .side-head .acct-side-btn{justify-content:center;padding:8px 0;background:none;border-color:transparent}
/* Collapsed the row is just the mark, but it is still the button that opens the account menu, so it
   keeps its hover ground (the base hover rule loses to the collapsed selector's higher specificity). */
html[data-nav="collapsed"] .side-head .acct-side-btn:hover{background:var(--sand)}
html[data-nav="collapsed"] .nav-collapse{justify-content:center;padding-left:0;padding-right:0}
html[data-nav="collapsed"] .nav-collapse-toggle svg{transform:rotate(180deg)}
}
/* the toggle is meaningless in the mobile drawer */
@media (max-width:820px){.nav-collapse{display:none}}

/* ---------- topbar ---------- */
/* DIV#secondary-nav [280,120,1120,77] : no fill of its own and no blur, on the white page ground.
   MAIN padding-left 280 / right 40 over a 240 sidebar leaves a 40px gutter.
   Its rule is not a border on the bar: it is SPAN [280,164,1120,1], a 1px element in
   rgb(212,222,233) that spans the content column only and stops at both gutters. Drawn here as a
   pseudo inset to the same 40px padding so it lines up with the content underneath it.

   Two deliberate deviations from the measurement, both asked for:
   1. STICKY. Stripe's own header scrolls away - #secondary-nav measures `position: static` with
      zero sticky or fixed ancestors on all five saved pages, and its title moves 120 -> -380 at
      scrollY 500. Only the left sidebar is pinned. Pinning ours is a choice, not a copy.
   2. PADDING-BOTTOM. Stripe's box is the title and nothing else (36px tall on the pages with no
      tab strip, 77 with one) and carries no rule and no bottom padding, so there is no value to
      lift. 8px is Stripe's own smallest spacing step - the one in its button padding (4px 8px),
      its chip padding (3px 8px 3px 6px) and its nav gaps - so the composition stays on their
      scale rather than inventing a number.
   Sticky forces a paint: transparent was safe while the header scrolled with the page, but a
   pinned transparent box lets the content run visibly underneath it. --bg is the same measured
   white the header already sat on, so occluding costs no new colour. */
.top{background:var(--bg);backdrop-filter:none;border-bottom:0;padding:20px 40px 8px;gap:16px;
  align-items:flex-start;position:sticky;top:0;z-index:var(--z-sticky)}
.top::after{content:"";position:absolute;left:40px;right:40px;bottom:0;height:1px;background:var(--line-hover)}
.top-h h1{font-size:28px;line-height:36px;font-weight:700;color:var(--ink-h1)}
.top-h p{font-size:14px;line-height:20px;font-weight:400;color:var(--muted);margin-top:2px}
.top-r{align-items:center;gap:8px;padding-top:4px}
/* Mobile header (<=640px): one vertically-centred row. The hamburger and the filter toggle are given
   the same 38px bordered box so the left and right controls match and sit on the title's centreline,
   and the title is scaled down so nothing crowds. Overrides the desktop align-items:flex-start (which
   left the small borderless hamburger floating ~8px above the boxed funnel). */
@media (max-width:640px){
  /* Padding is owned by the <=820px rule below (it wins by source order); here we only centre the row. */
  .top{align-items:center;gap:12px}
  .top::after{left:16px;right:16px}
  .top-h h1{font-size:20px;line-height:28px}
  .top-r{padding-top:0;gap:10px}
  /* .top prefix out-specifies the reskin .iconbtn,.nav-toggle 28px rule so the hamburger matches the
     38px bordered filter toggle on its right -- both buttons the same size, on the title's centreline. */
  .top .nav-toggle{width:38px;height:38px;border:1px solid var(--line);border-radius:10px;background:var(--card);color:var(--ink-2)}
  .top .nav-toggle:hover{background:var(--card);border-color:var(--muted-2)}
}
/* content column DIV [280,104,1120,*] : padding-top 8, padding-bottom 48. No cap of its own - the
   column is capped once, on .main above, so the header rule and the content it underlines cannot
   disagree about where the column ends. */
.wrap{max-width:none;padding:16px 40px 48px}

/* ---------- buttons ---------- */
/* A#listview-add-customer-button : brand fill, radius 6, padding 4px 8px, 14/20 w600, 1px ring */
/* The base button is the secondary one: white fill and the 1px ring. .btn-primary fills it. */
.btn{padding:4px 8px;border-radius:var(--radius-xs);font-size:14px;line-height:20px;font-weight:600;gap:6px;
  background:var(--card);color:var(--ink-button);border:0;box-shadow:var(--ring-mid);transition:none}
.btn:hover{background:var(--sand);border-color:transparent}
.btn-primary{background:var(--accent);color:#fff;box-shadow:var(--accent) 0 0 0 1px}
.btn-primary:hover{background:var(--accent)}
/* A#bento-custom-add-button : white fill, 1px rgb(212,222,233) ring, colour rgb(39,57,81) */
.btn-ghost,.copy,.backlink{background:var(--card);color:var(--ink-button);border:0;box-shadow:var(--ring-mid);border-radius:var(--radius-xs);padding:4px 8px;font-size:14px;line-height:20px;font-weight:600}
.btn-ghost:hover,.copy:hover,.backlink:hover{background:var(--sand);border-color:transparent}
/* .btn-sm is declared in the pre-Stripe block above, so the later .btn resets its padding and
   font-size at equal specificity and every btn-sm in the app renders full size. Re-state it as a
   compound so it wins wherever it sits in the cascade. */
.btn.btn-sm{padding:2px 8px;font-size:13px;line-height:18px}
/* icon button DIV [1332,56,28,28] : 28 square, radius 6, idle icon fill */
.iconbtn,.nav-toggle,.drawer-x{width:28px;height:28px;border-radius:var(--radius-xs);background:none;border:0;box-shadow:none;color:var(--icon-idle)}
.iconbtn:hover,.nav-toggle:hover,.drawer-x:hover{background:var(--sand);color:var(--ink-button);border-color:transparent}
.iconbtn .fa,.nav-toggle .fa{font-size:0.9rem}
.avatar{width:24px;height:24px;border-radius:var(--radius-xs);background:var(--sand);color:var(--muted);font-size:12px;line-height:16px;font-weight:600}

/* ---------- dropdown triggers ---------- */
/* filter button DIV "All" [280,177,*,36] : radius 12, 1px #ecf1f6, padding 8px 12px, 14/20 w600 */
.dd-btn,.ctrl-b,.site .site-btn,.range-btn,.switcher{
  background:var(--card);border:1px solid var(--line);border-radius:var(--radius);
  padding:8px 12px;font-size:14px;line-height:20px;font-weight:600;color:var(--ink-button);
  box-shadow:none;gap:8px}
.dd-btn:hover,.ctrl-b:hover,.site .site-btn:hover,.range-btn:hover{background:var(--card);border-color:var(--line-hover)}
.dd-btn .fa,.ctrl-b .fa,.site .site-btn .fa,.range-btn .fa,.switcher .fa{color:var(--icon-idle);font-size:0.7rem}
/* .range-trigger is not itself the control: it wraps .range-btn plus the optional prev/next period
   arrows. Carrying the pill on the wrapper stacked its 8px/12px on top of the button's own padding,
   so the time selector measured 51px tall against 38px for the pills beside it. The pill belongs on
   the button; the wrapper is a bare row. */
.range-trigger{background:none;border:0;border-radius:0;padding:0;box-shadow:none;gap:4px}
.range-trigger:hover{background:none;border-color:transparent}
.range-dd.open .range-trigger{border-color:transparent;box-shadow:none}
.range-dd.open .range-btn{border-color:var(--accent)}
.range-cal{color:var(--icon-idle)}
.range-arrows{padding-right:0;gap:2px}
.range-arrow{width:28px;height:28px;border-radius:var(--radius-xs);color:var(--icon-idle)}
.range-arrow:hover:not(:disabled){background:var(--sand);color:var(--ink-button)}
.ctrl-b.on,.ctrl.open .ctrl-b{border-color:var(--accent);color:var(--accent)}
.switcher .dot{width:6px;height:6px}
/* popover DIV : white, radius 8, elevated ring plus shadow. NOTE: .tag-menu is the kebab
   WRAPPER (a bare .dd holding the icon button + its .dd-menu), not a popover - listing it here boxed the
   kebab in a floating card (the "ugly border" regression). The real popovers are the inner .px-dd / .tag-dd,
   already covered by .dd-menu. Keep the wrappers out of this rule. */
.dd-menu,.ctrl-menu,.rng-custom,.acct-menu{
  background:var(--card);border:0;border-radius:var(--radius-sm);box-shadow:var(--shadow-pop);padding:8px}
.ctrl-opt,.rng-opt,.dd-menu a,.dd-menu button{border-radius:var(--radius-xs);font-size:14px;line-height:20px;font-weight:400;color:var(--ink)}
.ctrl-opt:hover,.rng-opt:hover,.dd-menu a:hover,.dd-menu button:hover,.dd-link:hover{background:var(--sand)}
.ctrl-opt.on,.rng-opt.on{background:var(--sand);color:var(--accent);font-weight:600}
/* global search DIV [280,56,360,36] : bg rgb(244,247,250), radius 8, padding 0 12, 14/20 */
.ctrl-search,.content-q,.field input,.field select,.rc-f input{
  background:var(--sand);border:0;border-radius:var(--radius-sm);padding:8px 12px;
  font-size:14px;line-height:20px;font-weight:400;color:var(--ink)}
.content-q{padding-left:34px}
.content-q:focus{border-color:transparent;box-shadow:var(--ring-brand)}

/* ---------- segmented controls ---------- */
/* DIV "Needs response" [*,172,218,42] : h42, radius 8, 1px ring; selected ring 1.5px brand */
/* The strip keeps its horizontal scroll on narrow screens, so it needs 2px of padding:
   the option ring is a box-shadow and overflow-x:auto would clip it to an arc. */
.seg,.kpi-seg,.pm-seg,.content-segs{background:none;border:0;padding:2px;gap:8px;border-radius:0;margin:0 0 16px}
.content-bar .seg{margin:0}
.seg-b,.kpi-b,.pm-seg button,.content-segs button{
  height:42px;padding:0 12px;border-radius:var(--radius-sm);background:var(--card);
  box-shadow:var(--ring-mid);border:0;
  font-size:14px;line-height:20px;font-weight:400;color:var(--muted)}
.seg-b.on,.kpi-b.active,.pm-seg button.on,.content-segs button.on{
  background:var(--card);color:var(--accent);font-weight:400;box-shadow:var(--ring-brand)}

/* ---------- tabs ---------- */
/* strip DIV [280,160,1120,37] : border-bottom 1px rgb(212,222,233).
   tab A : padding 8px 12px, 14/20 w600, active = brand plus a 2px underline */
.sw-t{border-radius:0;background:none;padding:8px 12px;font-size:14px;line-height:20px;font-weight:600;color:var(--muted);position:relative}
.sw-t.on{background:none;color:var(--accent)}
.sw-t.on::after{content:"";position:absolute;left:12px;right:12px;bottom:-1px;height:2px;background:var(--accent)}

/* ---------- panels and cards ---------- */
/* card DIV [288,715,548,84] : white, radius 6, padding 16, gap 16, no drop shadow.
   Free-standing cards carry the measured 1px #ecf1f6 ring so they read on the white ground. */
.card,.siterow,.qcell,.table-card,.empty-card,.pixel-card,.snippet{
  background:var(--card);border:0;border-radius:var(--radius);box-shadow:var(--ring-light);padding:16px}
.card:hover,.siterow:hover{box-shadow:var(--ring-light)}
.bento,.grid2,.grid2.wide,.mstrip,.qgrid,.set-grid,.poster-grid{gap:16px}
/* SPAN "Disputes" : the card label, 12/16 w600, muted */
.klabel,.card .l,.metric .l,.sub,.poster-sub{
  font-size:12px;line-height:16px;font-weight:600;color:var(--muted);letter-spacing:normal;text-transform:none}
/* SPAN "$0.00" : the card figure, 28/36 w700 */
.big-num,.card .v,.livenow-v,.fp-val{font-size:28px;line-height:36px;font-weight:700;color:var(--ink-h1);letter-spacing:normal}
.card .v2,.money{font-size:20px;line-height:28px;font-weight:700;color:var(--ink-h1)}
.meta,.subline,.note,.empty-sub,.dnote{font-size:14px;line-height:20px;font-weight:400;color:var(--muted)}
/* DIV [280,269,360,28] : the section heading, 20/28 w700 */
.sec-h h2,.card h2,.drawer-h h2,.dh-t h2,.empty-title{font-size:20px;line-height:28px;font-weight:700;color:var(--ink);letter-spacing:normal}
.sec-h{margin:24px 0 8px}
/* A "View" : the inline link, a different purple from the nav brand, w500 */
.link,.px-rename,.tag-rename{color:var(--violet);font-weight:500;font-size:14px;line-height:20px}

/* ---------- chips and pills ---------- */
/* SPAN chip [*,*,*,24] : h24, full pill, padding 3px 8px 3px 6px, 12px w600 */
.pill,.tag,.chip,.fchip,.pt-badge,.ph-badge{
  height:24px;padding:3px 8px 3px 6px;border-radius:var(--radius-pill);
  font-size:12px;line-height:16px;font-weight:600;letter-spacing:normal;text-transform:none}
.pill-neutral,.pill.def,.chip{background:var(--sand);color:var(--muted)}
/* VPN/Datacenter visitor flag: amber attention chip, sits beside the New/Returning badge. */
.pill-vpn{background:var(--amber-bg);color:var(--amber);margin-left:6px}

/* Stripe encodes state and deltas as plain text - weight + colour, sometimes a small leading dot -
   never a filled pill. Measured: Disputes state labels are borderless text (rgb(83,58,253) w600 when
   they need action, rgb(60,79,105) w400 when settled); Payments status is a 6px dot + plain text. The
   pill geometry above stays for Stripe's one real chip (the white 1px-ring badge: .pill.def, and the
   sand .pill-neutral). These four variants - the AI-looking coloured fills - strip back to text. */
.pill.pill-up,.pill.pill-down,.pill.pill-live,.pill.pill-pend{
  height:auto;padding:0;border-radius:0;background:none;box-shadow:none;
  gap:4px;font-size:12px;line-height:16px;font-weight:600}
.pill.pill-up{color:var(--ok)}
.pill.pill-down{color:var(--bad)}
.pill.pill-up .fa,.pill.pill-down .fa{width:10px;height:10px}
/* status: a 6px leading dot + label, Stripe's payments-list idiom (no fill, no ring) */
.pill.pill-live::before,.pill.pill-pend::before{content:"";width:6px;height:6px;border-radius:50%;flex:none}
.pill.pill-live{color:var(--ink)}
.pill.pill-live::before{background:var(--ok)}
.pill.pill-pend{color:var(--muted)}
.pill.pill-pend::before{background:var(--wait)}
.delta-flat{font-size:12px;line-height:16px;color:var(--muted)}

/* ---------- countries page ---------- */
.ct-table tbody tr{cursor:pointer}
.ct-name{display:inline-flex;align-items:center;gap:8px;min-width:0}
.ct-name b{font-weight:600}
.ct-table td{vertical-align:middle}
.ct-spark{width:88px}
.ct-spark svg{width:80px;height:24px;display:block}
.ct-table td .pill{justify-content:flex-end}

/* ---------- title detail page ---------- */
.title-hero{display:flex;gap:20px;align-items:flex-start}
.title-hero-art{flex:none;width:104px}
.title-hero-art .poster-img{width:104px;height:156px;aspect-ratio:auto;border-radius:var(--radius-sm)}
.title-hero-art .vt-thumb{width:104px;height:156px}
.title-hero-body{min-width:0;flex:1}
.title-back{display:inline-block;color:var(--muted);font-size:14px;line-height:20px;font-weight:500;margin-bottom:8px}
.title-back:hover{color:var(--ink)}
.title-hero-name{font-size:28px;line-height:36px;font-weight:700;color:var(--ink-h1)}
.title-hero-meta{display:flex;align-items:center;gap:12px;margin-top:8px;flex-wrap:wrap}
.title-idk{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;color:var(--muted)}
.title-hero-note{margin-top:12px;font-size:14px;line-height:20px;color:var(--muted);max-width:60ch}
@media (max-width:560px){.title-hero{flex-direction:column}.title-hero-art,.title-hero-art .poster-img,.title-hero-art .vt-thumb{width:88px;height:132px}}
/* Country detail hero: a large flag stands in for the poster art. */
.country-hero-art{width:auto}
.title-hero-flag{display:inline-flex}
.title-hero-flag .flag-ico,.title-hero-flag .flag-code{width:96px;height:70px;border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,.18)}
.title-hero-flag .flag-code{font-size:1.4rem}
@media (max-width:560px){.title-hero-flag .flag-ico,.title-hero-flag .flag-code{width:72px;height:52px}}
/* Page detail hero: the path reads as a monospace URL. A very long URL truncates with an ellipsis on one
   line (full value stays in the title tooltip) rather than pushing the hero to several tall lines. */
.page-hero-path{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:22px;line-height:30px;display:block;max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* Drawer -> full detail page jump. */
.drawer-expand{display:inline-flex;align-items:center;gap:6px;margin:0 0 16px;padding:8px 14px;border:1px solid var(--line);border-radius:var(--radius-pill);background:var(--surface);color:var(--accent);font:inherit;font-weight:700;font-size:0.84rem;cursor:pointer}
.drawer-expand:hover{background:var(--sand);border-color:var(--accent)}
.drawer-expand:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* Country-name KPI rendered as a link to its detail page. */
.mv-place-link{background:none;border:0;padding:0;font:inherit;color:inherit;cursor:pointer;text-align:left}
.mv-place-link:hover .mv-cn{color:var(--accent);text-decoration:underline}
.mv-place-link:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}

/* ---------- payout tiers (rate ladder) ---------- */
/* Each tier is a bar sized by its percent of the base rate. Tier 1 = 100% (full bar), lower tiers a
   fraction. No dollar figure is shown; the base rate stays private. */
.tier-wrap{display:flex;align-items:center;gap:22px}
.tier-donut{flex:none;line-height:0;position:relative}
.td-arc{transition:opacity .12s ease}
.tier-donut.is-hov .td-arc{opacity:.35}
.tier-donut.is-hov .td-arc.hot{opacity:1}
/* Combined selector so this wins over the later .mlc-tip{position:absolute}: the donut tip is
   positioned from viewport coords (clientX/Y), so it must stay position:fixed, not fall back to
   absolute inside the position:relative .tier-donut (which threw it ~the donut's offset from the mouse). */
.mlc-tip.td-tip{position:fixed;z-index:60;pointer-events:none}
@media(prefers-reduced-motion:reduce){.td-arc{transition:none}}
.td-num{fill:var(--ink);font-size:26px;font-weight:600;font-variant-numeric:tabular-nums}
.td-lab{fill:var(--muted);font-size:12px;font-weight:400}
.tier-list{flex:1 1 auto;min-width:0;display:flex;flex-direction:column}
.tr-row{display:grid;grid-template-columns:auto 1fr auto auto;align-items:center;gap:10px;padding:7px 0;border-bottom:1px solid var(--line-soft)}
.tr-row:last-child{border-bottom:0}
.tr-sw{width:10px;height:10px;border-radius:3px;flex:none}
.tr-name{font-size:14px;line-height:20px;font-weight:600;color:var(--ink);white-space:nowrap}
.tr-where{font-size:12px;line-height:16px;color:var(--muted);text-align:right;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tr-pct{font-size:14px;line-height:20px;font-weight:600;color:var(--ink);white-space:nowrap;min-width:44px;text-align:right}
/* Caption under the tier card saying whether the tiers actually settle earnings (applied) or are shown
   for reference while a flat rate pays every country the same. */
.tier-cap{margin-top:14px;padding:10px 12px;border-radius:8px;font-size:12.5px;line-height:18px}
.tier-cap-on{background:var(--good-soft);color:var(--good)}
.tier-cap-off{background:var(--sand);color:var(--muted)}
@media(max-width:520px){.tier-wrap{flex-direction:column;align-items:stretch;gap:16px}.tier-donut{align-self:center}}

/* ---------- tables ---------- */
/* TH : h36, 12px / 20 / 600, inset 0 -1px #ecf1f6. TD : h44, 14/20. */
table{font-size:14px;line-height:20px;color:var(--ink);border-collapse:collapse;width:100%}
.wrap th,.drawer-b th,.vw-tbl th,table th{
  height:36px;padding:0 8px;text-align:left;background:var(--card);
  font-size:12px;line-height:20px;font-weight:600;color:var(--ink);letter-spacing:normal;text-transform:none;
  border:0;box-shadow:var(--line) 0 -1px 0 0 inset}
.wrap td,.drawer-b td,table td{
  height:44px;padding:0 8px;background:var(--card);font-size:14px;line-height:20px;color:var(--ink);border:0}
tbody tr:hover td{background:var(--sand)}
/* pagination NAV : border-top 1px #ecf1f6, padding 8px 0, gap 8; pager 24 square, radius 4 */
.content-pager,.feed-pg{border-top:1px solid var(--line);padding:8px 0;gap:8px;background:var(--card)}
.content-pager button,.feed-pg button{width:24px;height:24px;border-radius:4px;background:none;border:0;color:var(--icon-idle)}
.content-pager button:hover,.feed-pg button:hover{background:var(--sand)}

/* ---------- Content: Filters menu + per-page selector + numbered pager ---------- */
/* Filters live in an expandable dropdown so the bar reads clean; the badge counts non-default filters. */
.content-filters{position:relative}
.content-filters .cf-btn{display:inline-flex;align-items:center;gap:8px;font-weight:600}
.content-filters .cf-btn .fa{width:13px;height:13px;color:var(--icon-idle)}
.cf-badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;
  border-radius:9px;background:var(--accent);color:#fff;font-size:11px;font-weight:700;line-height:1}
.cf-badge[hidden]{display:none}
.content-filters-menu{width:308px;padding:14px 16px}
/* Each group is a left-aligned row: a sentence-case label above its own segmented control. The control
   hugs its pills and sits flush left - not a full-width bar of centre-wrapped pills (the old look). */
.content-filters-menu .content-segs{display:flex;flex-direction:column;gap:13px;margin:0}
.content-filters-menu .fgroup{display:flex;flex-direction:column;align-items:flex-start;gap:6px}
.content-filters-menu .fgroup-l{font-size:12px;line-height:1.2;font-weight:600;letter-spacing:0;text-transform:none;color:var(--muted)}
.content-filters-menu .fgroup-div{display:none}
/* A real segmented control: a sand track sized to its pills, left-aligned. Pills hug their own text
   (no stretch, no wrap); the active pill lifts to card. Overrides the page-level .seg pill defaults. */
.content-filters-menu .seg{display:inline-flex;flex-wrap:wrap;justify-content:flex-start;gap:4px;width:max-content;max-width:100%;margin:0;padding:3px;background:var(--sand);border-radius:9px}
.content-filters-menu .seg-b{flex:0 0 auto;justify-content:center;text-align:center;min-height:28px;padding:5px 11px;border-radius:6px;background:none;box-shadow:none;font-size:0.8rem;line-height:1.2;font-weight:600;color:var(--muted);white-space:nowrap;transition:background .12s,color .12s}
.content-filters-menu .seg-b:hover{color:var(--ink)}
.content-filters-menu .seg-b.on{background:var(--card);color:var(--accent);box-shadow:var(--shadow);font-weight:700}
.content-filters-menu .seg-sizes .seg-b{font-variant-numeric:tabular-nums}
/* Pager row: the numbered pager / range sits centred (the per-page control now lives in the Filters menu). */
.content-pager{margin-top:22px}

/* ---------- Title page: Seasons & episodes accordion ---------- */
.se-lead{font-size:0.86rem;line-height:1.5;color:var(--muted);margin:0 0 14px;max-width:80ch}
.se-list{display:flex;flex-direction:column;gap:8px}
.se-season{border:1px solid var(--line);border-radius:var(--radius-sm);overflow:hidden;background:var(--card)}
.se-head{display:flex;align-items:center;gap:12px;width:100%;padding:12px 14px;background:none;border:0;
  cursor:pointer;text-align:left;font:inherit;color:var(--ink)}
.se-head:hover{background:var(--sand)}
.se-chev{display:inline-flex;color:var(--icon-idle);transition:transform 0.15s ease}
.se-season.open .se-chev{transform:rotate(180deg)}
.se-name{font-weight:600;font-size:0.94rem}
.se-meta{margin-left:auto;font-size:0.8rem;color:var(--muted);white-space:nowrap}
.se-body{display:none;border-top:1px solid var(--line)}
.se-season.open .se-body{display:block}
.se-table{width:100%}
.se-table th{text-align:left;font-size:0.72rem;font-weight:700;letter-spacing:0.02em;text-transform:uppercase;
  color:var(--muted);padding:10px 12px;border-bottom:1px solid var(--line)}
.se-table th.r{text-align:right}
.se-epnum{color:var(--muted);font-weight:600;width:56px}
.se-eptitle{color:var(--ink)}
/* Each episode row drills into "which pages declared this episode" - so it reads and behaves as a link. */
.se-eprow{cursor:pointer;transition:background 0.12s ease}
.se-eprow:hover,.se-eprow:focus-visible{background:var(--sand);outline:none}
.se-eprow:focus-visible{box-shadow:inset 0 0 0 2px var(--accent)}
.se-eptitle{display:flex;align-items:center;gap:8px}
.se-eptext{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.se-epgo{display:inline-flex;flex:none;color:var(--icon-idle);opacity:0;transform:translateX(-2px);
  transition:opacity 0.12s ease,transform 0.12s ease}
.se-eprow:hover .se-epgo,.se-eprow:focus-visible .se-epgo{opacity:1;transform:none}
.se-pager{padding:10px 12px;border-top:1px solid var(--line)}
@media (prefers-reduced-motion:reduce){.se-chev,.se-eprow,.se-epgo{transition:none}}

/* ---------- bar lists ---------- */
/* The breakdown rows keep their geometry; only the fill and the type take Stripe's values. */
.bl-row{font-size:14px;line-height:20px;color:var(--ink)}
.bl-track{background:var(--sand);border-radius:4px}
.bl-val{font-size:14px;line-height:20px;font-weight:400;color:var(--ink)}
.bl-rank{font-size:12px;line-height:16px;color:var(--muted)}

/* ---------- banners ---------- */
/* DIV#global-account-status-banner-content : rgb(255,239,201) ground, rgb(177,54,0) ink */
.help-banner{background:rgb(255,239,201);color:rgb(177,54,0);border:0;border-radius:var(--radius);padding:16px}
.help-banner .hb-t strong{font-weight:700;color:rgb(177,54,0)}
.help-banner .hb-t span,.hb-link{color:rgb(177,54,0)}
/* DIV#global-account-status-banner-content > A : a bold inline link, not a filled button */
.hb-cta{background:none;color:rgb(177,54,0);font-weight:700;font-size:14px;line-height:20px;border-radius:4px;padding:0}
.hb-cta:hover{filter:none;text-decoration:underline}
.hb-ico{color:rgb(177,54,0)}
.hb-x{color:rgb(177,54,0)}
/* Red variant: the pixel-needs-attention alert (matches the red Health callout). Higher specificity
   than the amber `.help-banner` base so it wins without !important. */
.help-banner.hb-bad{background:var(--bad-soft);color:var(--bad)}
.help-banner.hb-bad .hb-t strong,.help-banner.hb-bad .hb-t span,.help-banner.hb-bad .hb-link,.help-banner.hb-bad .hb-ico,.help-banner.hb-bad .hb-x{color:var(--bad)}
.help-banner.hb-bad .hb-link{text-decoration:underline;text-underline-offset:2px}

/* Multi-line chart custom legend (countries over time): line swatch + flag + name, no boxed square. */
.mlc-legend{display:flex;flex-wrap:wrap;gap:8px 18px;justify-content:center;padding:4px 4px 2px}
.mlc-key{display:inline-flex;align-items:center;gap:7px;font-size:12px;line-height:16px;color:var(--muted)}
.mlc-line{width:16px;height:3px;border-radius:2px;flex:none}
.mlc-key .flag-ico,.mlc-key .flag-code{flex:none}
.mlc-name{color:var(--ink);font-weight:500}
/* End-of-line country flags: pinned in the chart's right padding at each line's most recent point. */
.mlc-endflags{position:absolute;inset:0;pointer-events:none;z-index:4;overflow:visible}
.mlc-endflag{position:absolute;transform:translateY(-50%);line-height:0}
.mlc-endflag .flag-ico,.mlc-endflag .flag-code{display:block;box-shadow:0 0 0 1px var(--card),0 1px 3px rgba(24,39,75,0.18);border-radius:2px}
/* Floating hover readout for the multi-line chart (replaces uPlot's hidden boxed legend). */
.mlc-tip{position:absolute;pointer-events:none;z-index:5;min-width:150px;padding:8px 10px;background:var(--card);border:1px solid var(--line-hover);border-radius:8px;box-shadow:0 4px 16px rgba(24,39,75,0.12);font-size:12px;line-height:16px}
.mlc-tip-h{font-weight:600;color:var(--ink);margin-bottom:6px}
.mlc-tip-r{display:flex;align-items:center;gap:7px;padding:2px 0}
.mlc-tip-r i{width:9px;height:3px;border-radius:2px;flex:none}
.mlc-tip-n{display:inline-flex;align-items:center;gap:5px;color:var(--muted);flex:1 1 auto;white-space:nowrap}
.mlc-tip-r b{color:var(--ink);font-weight:600}
.mlc-tip-n .flag-ico,.mlc-tip-n .flag-code{flex:none}

/* Behaviours hero tab: day-by-day stacked bars (new + returning). Square legend swatch + a total row
   in the shared hover tooltip. */
.mlc-sw{width:11px;height:11px;border-radius:3px;flex:none}
.mlc-tip-tot{margin-top:4px;padding-top:5px;border-top:1px solid var(--line)}
.mlc-tip-tot .mlc-tip-n{color:var(--ink);font-weight:600}

/* ---------- charts and breakdown bars ---------- */
/* The legacy --primary ramp still drives uPlot, the bar-list fills and the sparklines.
   Point it at the measured brand purple and the two measured neutrals so a chart cannot
   reintroduce a colour Stripe does not use. */
:root{
  --primary:rgb(83,58,253);       /* A#primary-nav-item-link-customers, active */
  --primary-soft:rgb(244,247,250);/* search field bg */
  --primary-tint:rgb(212,222,233);/* button ring */
}

/* ---------- flat fills ---------- */
/* Nothing in the measured dashboard is a gradient: every chart fill, avatar and icon tile is a
   flat colour. The legacy skin used two-stop gradients in a dozen places, so they are flattened
   onto the measured brand purple, the tint or the idle icon grey. */
.bars i,.bars .col.now i,.rq i,.bl-track i,.fnl-track i{background:var(--primary)}
/* A#primary-nav-item-link-home > icon tile : solid brand purple, white glyph. The tile IS
   /favicon.svg, so the sidebar logo and the browser tab can never drift apart; the box only sizes
   it, which is why the skin's own background and radius are dropped here rather than painted
   behind a mark that already draws its own. */
.brand-mark{background:none;border-radius:0}
.brand-mark img{width:100%;height:100%;display:block}
/* DIV account avatar : the tint ground with ink initials, not a coloured disc */
.avatar,.site-ico{background:var(--sand);color:var(--ink-button)}
/* the list-row title, 14/20 w600. .px-name is excluded: the pixel card title is the
   larger 1.04rem/700 heading from the Health mockup (see .px-name above). */
.tag-name,.siterow .s-name{font-size:14px;line-height:20px;font-weight:600;color:var(--ink)}

/* ---------- visitors table ---------- */
/* table.tbl carries its own header rule at a higher specificity than the bare `table th` above,
   so it is named explicitly rather than left to lose the cascade. */
table.tbl th{height:36px;padding:0 8px;font-size:12px;line-height:20px;font-weight:600;color:var(--ink);
  letter-spacing:normal;text-transform:none;border:0;box-shadow:var(--line) 0 -1px 0 0 inset}
table.tbl td{height:44px;padding:0 8px;font-size:14px;line-height:20px;font-weight:400;color:var(--ink);border:0}
table.tbl td.name,.pname{font-weight:600;color:var(--ink)}
.psub{font-size:12px;line-height:16px;font-weight:400;color:var(--muted)}
.tbl.people tr.prow:hover td{background:var(--sand)}
.tbl.people th.on{color:var(--ink)}
.pav,.vavatar{background:var(--sand);color:var(--ink-button);border-radius:var(--radius-xs);
  font-size:12px;line-height:16px;font-weight:600}
/* the fallback donut ramp: brand, link purple, idle icon grey, button ring. No coral. */
.donut{background:conic-gradient(var(--accent) 0 63%,var(--violet) 63% 92%,var(--icon-idle) 92% 98%,var(--primary-tint) 98% 100%)}
.people-filters-lab{font-size:12px;line-height:16px;font-weight:600;letter-spacing:normal;text-transform:none;color:var(--muted)}
.people-count{font-size:14px;line-height:20px;font-weight:400;color:var(--muted)}
.people-count b{font-weight:600}
/* the toolbar search field takes the measured global-search treatment */
.search input{background:var(--sand);border:0;border-radius:var(--radius-sm);padding:8px 12px 8px 36px;
  font-size:14px;line-height:20px;font-weight:400;color:var(--ink)}
.search input:focus{border-color:transparent;box-shadow:var(--ring-brand)}
/* pager buttons match the measured 24 square: no fill, no border, tint on hover */
.pg-num,.pg-nav{min-width:24px;height:24px;padding:0 4px;border:0;border-radius:4px;background:none;
  font-size:12px;line-height:16px;font-weight:600;color:var(--icon-idle)}
.pg-num:hover,.pg-nav:hover:not([disabled]){background:var(--sand);color:var(--ink);border-color:transparent}
.pg-num.on{background:var(--sand);color:var(--ink);border-color:transparent}
.pg-info{font-size:12px;line-height:16px;font-weight:400;color:var(--muted)}

/* ---------- option lists ---------- */
/* Same measured option row as the segmented control: h42, radius 8, 1px ring, 1.5px brand when picked. */
.dev-font{height:42px;padding:0 12px;border:0;border-radius:var(--radius-sm);background:var(--card);
  box-shadow:var(--ring-mid);font-size:14px;line-height:20px;font-weight:400;color:var(--ink)}
.dev-font:hover{background:var(--card);box-shadow:var(--line-hover) 0 0 0 1px}
.dev-font.active{background:var(--card);box-shadow:var(--ring-brand)}
.dev-check{color:var(--accent)}
.dev-fonts{gap:8px}
.dev-lbl,.set-lab,.field label,
.coh-head .coh-lab,.coh-head .coh-size,.coh-hcell,.rc-f,.mini-l,.qoe-l,.live-kpi-l,.tile h3,.vp-rail-h{
  font-size:12px;line-height:16px;font-weight:600;letter-spacing:normal;text-transform:none;color:var(--muted)}

/* ---------- visitor profile ---------- */
/* .vp-line is a label/value pair, not a metric: it must not inherit the 28px card figure above. */
.vp-line .v,.vp-line .k{font-size:14px;line-height:20px;font-weight:400}
.vp-line .k{color:var(--muted)}
.vp-line .v{font-weight:600;color:var(--ink);text-align:right}
.vp-nums .k{font-size:12px;line-height:16px;font-weight:600;color:var(--muted)}
.vp-nums .v{font-size:20px;line-height:28px;font-weight:700;color:var(--ink-h1)}
.vp-rail-h{font-size:12px;line-height:16px;font-weight:600;letter-spacing:normal;text-transform:none;color:var(--muted)}
.vp-who h2{font-size:20px;line-height:28px;font-weight:700;color:var(--ink)}


/* ---------- poster grid ---------- */
/* The poster placeholder was a per-title hue gradient. Nothing in the measured dashboard is a
   gradient and nothing there is hue-randomised, so the placeholder falls back to the same tile the
   avatars use: tint ground, button ink, 1px ring, small radius. Real artwork still covers it. */
.poster-img{background:var(--sand);border:0;box-shadow:var(--ring-mid);border-radius:var(--radius-sm);transition:none}
.poster-card:hover .poster-img{transform:none;box-shadow:var(--line-hover) 0 0 0 1px}
.poster-card:focus-visible .poster-img{border-color:transparent;box-shadow:var(--ring-brand)}
.poster-fallback{font-size:20px;line-height:28px;font-weight:700;color:var(--ink-button);text-shadow:none;letter-spacing:normal}
/* rank chip: a subtle translucent glass pill over the artwork (a thin light hairline keeps it legible
   on pale posters), not an opaque white box that fights the image. */
.poster-rank{min-width:22px;height:22px;padding:0 7px;border-radius:999px;
  background:rgba(17,22,33,0.5);color:#fff;box-shadow:none;
  backdrop-filter:blur(5px) saturate(1.3);-webkit-backdrop-filter:blur(5px) saturate(1.3);
  border:1px solid rgba(255,255,255,0.16);font-size:12px;line-height:16px;font-weight:600}
.poster-title{font-size:14px;line-height:20px;font-weight:600;color:var(--ink)}
.poster-type{font-size:12px;line-height:16px;font-weight:600;letter-spacing:normal;text-transform:none;color:var(--muted)}
.poster-num{font-size:12px;line-height:16px;font-weight:600;color:var(--muted)}
.sk-poster{border-radius:var(--radius-sm)}

/* Bar-chart corners: the measured bars are square-topped with a 4px cap, not a 9-14px pill. */
.bars i,.rq i{border-radius:4px 4px 0 0}
.bl-track i,.fnl-track i{border-radius:var(--radius-pill)}


/* Install snippet : the panel rule above re-grounds every .snippet white; the install block instead
   keeps the dark developer-console ground (Stripe uses the same for API keys / code samples) so the
   pixel tag reads as copy-me code, with two restrained syntax colours. Kept in sync with the pixel
   Health mockup. Monospace family matches the measured KBD stack. */
.snippet{background:#12162a;color:#dbe4f5;box-shadow:none;
  font-family:Menlo,Consolas,monospace;font-size:14px;line-height:20px;font-weight:600}
.snippet .tok{color:#ffb27a}.snippet .str{color:#8fdcae}.snippet .cmt{color:#6b7a99;font-style:italic}
.snippet .copy{background:rgba(255,255,255,0.08);color:#cfdaf0}
.snippet .copy:hover{background:rgba(255,255,255,0.14)}

/* Icon-only control : 28 square, radius 6px, no border, transparent ground, colour rgb(60,79,105).
   Five of them on Home, all identical. The rollup refresh carried a 9px radius and a tinted accent
   hover, neither of which appears on a Stripe control. */
.mstage-refresh{width:28px;height:28px;border:0;border-radius:var(--radius-xs);background:none;color:var(--muted);box-shadow:none}
.mstage-refresh:hover{background:var(--sand);color:var(--muted);border-color:transparent}

/* An expanded play lands as a full-width TR directly under its own row, so its cell has to drop the
   TD's fixed 44px height and the row hover tint - the panel is the surface here, not the row. The
   panel takes the measured panel/search ground rgb(244,247,250) and the 16px panel padding. */
/* padding-left/right 0 so the sand panel fills the row edge to edge - the 8px inset left a strip of
   white gutter down both sides of the panel that read as a misalignment. */
.vp-pdrow td{height:auto;padding:0 0 12px;background:var(--card)}
.vp-pdrow:hover td{background:var(--card)}
.vp-pd{margin-top:0;background:var(--sand);border-radius:var(--radius-xs)}
.vp-pd-h{padding:16px;align-items:center}
/* the poster thumbnail leading the expanded video header */
.vp-pd-h .vt-thumb{width:34px;height:51px}
/* The QoE strip above the timeline: bring the numbers down to Stripe's card scale (they were reading a
   size larger than the panel around them). Label 12/16 muted, value 14/20. */
.vw-qoe .k{font-size:12px;line-height:16px;letter-spacing:normal}
.vw-qoe .v{font-size:14px;line-height:20px;font-weight:600;margin-top:2px}


/* ---------- live replay cue ---------- */
/* The action list runs well past the panel on a busy play, so it scrolls in place with its head
   pinned - the measured TH above already carries the white ground a sticky head needs. The cue rule
   is the one piece here with no Stripe element of its own to copy: it takes the active-nav brand,
   which is what the track's own position trace is already drawn in. A cued pin borrows the state the
   measured selected option uses, brand ring on the brand's own 10% tint. */
.vw-tbl{max-height:280px;overflow-y:auto}
.vw-tbl thead th{position:sticky;top:0;z-index:2}
.vw-lanes{position:relative}
.vw-cue{position:absolute;top:0;bottom:0;width:1px;background:var(--accent);opacity:0;
  transition:opacity .12s;pointer-events:none;z-index:1}
.vw-cue.on{opacity:1}
.vw-tbl tr.is-cued td{background:var(--sand)}
.vw-pin.is-cued{transform:translateX(-50%) scale(1.18);z-index:3;
  border-color:var(--accent);color:var(--accent);background:var(--accent-soft)}

/* ---------- chart pointer readout ---------- */
/* Every Stripe chart reads under the pointer: a vertical rule at the hovered bucket, a dot on the
   series at that point, and a popover with the value. The rule and the dot are the only pieces
   without a Stripe element of their own to copy, so they take the measured tokens for the same job
   elsewhere: the rule is the tab-strip hairline colour, the dot is the active-nav brand with a
   card-coloured ring so it separates from the line it sits on. The popover is the measured menu
   ground, with the card figure/label pair for its two lines.
   The furniture is HTML, not SVG: these charts are preserveAspectRatio="none" on a 600-wide viewBox,
   so an SVG circle would render as an ellipse and a 1px stroke would scale with the box. */
.chart-hov{position:relative}
.chart-hov .chart-xh,.chart-hov .chart-dot{position:absolute;opacity:0;pointer-events:none;transition:opacity .12s}
.chart-hov.is-hov .chart-xh,.chart-hov.is-hov .chart-dot,.chart-hov.is-hov .chart-tip{opacity:1}
.chart-hov .chart-xh{top:0;bottom:0;width:1px;background:var(--line-hover);transform:translateX(-0.5px)}
.chart-hov .chart-dot{width:8px;height:8px;border-radius:var(--radius-pill);background:var(--accent);
  box-shadow:var(--card) 0 0 0 2px;transform:translate(-50%,-50%)}
/* Same popover treatment as .dd-menu and friends; the uPlot hero shares this class and gets it too. */
.chart-tip{background:var(--card);color:var(--ink);border-radius:var(--radius-sm);
  box-shadow:var(--shadow-pop);padding:8px;font-size:14px;line-height:20px;font-weight:400}
.chart-hov .chart-tip{margin-top:-10px}
.chart-tip b{color:var(--ink);font-weight:600}
.chart-tip span{color:var(--muted);font-size:12px;line-height:16px;font-weight:600}

/* ---------- responsive ---------- */
/* This layer is appended after the app's own responsive rules, so every selector it repeats at the
   same specificity wins at all widths - which quietly took the phone layout apart. The worst of it:
   .app kept Stripe's 240px sidebar track while the sidebar below 820px is position:fixed, so the
   content column laid out 240px wide inside a 375px viewport. The drawer machinery below is the
   app's own; this only re-asserts the four declarations this layer had overridden. Stripe's gutters
   are desktop measurements taken at 1440 and do not survive a 375px screen. */
@media (max-width:820px){
  /* No sidebar track: the sidebar is an off-canvas drawer here, not a column. */
  .app{grid-template-columns:1fr}
  /* The drawer keeps the docked sidebar's zero inset - .nav owns the padding in both - and only adds
     room for the home indicator. */
  .side{padding:0 0 env(safe-area-inset-bottom)}
  /* Bottom gap gives the controls room before the underline instead of sitting right on it. */
  .top{padding:16px 16px 14px}
  /* the header rule follows the gutter it is inset to */
  .top::after{left:16px;right:16px}
  .wrap{padding:12px 16px 40px}
}

@media (max-width:560px){
  /* A panel head is a title with its label pushed to the far end. In 311px both halves wrap to two
     lines and interleave, so the label goes under the title it belongs to. */
  .sec-h{flex-direction:column;align-items:flex-start;gap:2px}
}

/* ── Need help?: support chat ────────────────────────────────────────────────
   One ticket thread, message bubbles (client right / support left), a composer, an
   Open/Closed chip and the notifications toggle. Product register: quiet surfaces,
   the accent reserved for the client's own bubbles + the Send button. */
/* Attention dot on a nav leaf (Need help? unread, or a pixel needing attention). Pushed to the far edge
   of the row via margin-left:auto so it sits at the same x on every leaf, top-level or grouped sub-item.
   A quick wiggle roughly every 5s draws the eye without nagging (suppressed under reduced-motion). */
.nav-dot{margin-left:auto;width:8px;height:8px;border-radius:50%;background:var(--coral);box-shadow:0 0 0 3px rgba(255,59,48,0.18);flex:none;transform-origin:50% 50%;animation:dot-wiggle 5s ease-in-out infinite}
@keyframes dot-wiggle{0%,80%,100%{transform:translateX(0) scale(1)}83%{transform:translateX(-2px) scale(1.18)}87%{transform:translateX(2px) scale(1.18)}91%{transform:translateX(-1px) scale(1.1)}95%{transform:translateX(1px) scale(1.05)}}
@media (prefers-reduced-motion:reduce){.nav-dot{animation:none}}
.chat-card{padding:0;overflow:hidden}
.chat{display:flex;flex-direction:column;min-height:min(70vh,640px)}
.chat-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;padding:20px 22px;border-bottom:1px solid var(--line)}
.chat-title{font-size:1.05rem;font-weight:700;margin:0}
.chat-sub{color:var(--muted);font-size:0.85rem;margin:4px 0 0;max-width:52ch}
.chat-head-r{display:flex;align-items:center;gap:10px;flex:none}
.chat-notify{display:inline-flex;align-items:center;gap:7px}
.chat-notify .fa{font-size:0.85rem}
.chat-notify[data-on]{color:var(--ok)}
/* Status chip */
.chip-open{background:var(--good-soft);color:var(--good)}
.chip-closed{background:var(--sand);color:var(--label)}
/* Thread */
.chat-thread{flex:1;display:flex;flex-direction:column;gap:14px;padding:22px;overflow-y:auto}
.chat-row{display:flex}
.chat-row.chat-me{justify-content:flex-end}
.chat-bubble{max-width:min(76%,560px);border-radius:16px;padding:11px 14px;border:1px solid var(--line);background:var(--card)}
.chat-me .chat-bubble{background:rgba(var(--accent-rgb),0.08);border-color:rgba(var(--accent-rgb),0.2);border-bottom-right-radius:5px}
.chat-them .chat-bubble{background:var(--line-soft);border-color:var(--line);border-bottom-left-radius:5px}
.chat-meta{display:flex;align-items:baseline;gap:9px;margin-bottom:3px}
.chat-who{font-size:0.78rem;font-weight:700;color:var(--ink)}
.chat-when{font-size:0.72rem;color:var(--muted-2)}
.chat-body{margin:0;font-size:0.9rem;line-height:1.5;color:var(--ink-2);white-space:pre-wrap;word-break:break-word}
/* Empty teaching state */
.chat-empty{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;gap:8px;padding:56px 24px}
.chat-empty-ico{width:52px;height:52px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;background:rgba(var(--accent-rgb),0.1);color:var(--accent);margin-bottom:4px}
.chat-empty-ico .fa{font-size:1.2rem}
.chat-empty h3{font-size:1rem;font-weight:700;margin:0}
.chat-empty p{color:var(--muted);font-size:0.88rem;margin:0;max-width:40ch}
/* Composer */
.chat-composer{border-top:1px solid var(--line);padding:14px 18px;background:var(--card)}
.chat-input{width:100%;background:var(--sand);border:0;border-radius:var(--radius-sm);padding:9px 12px;font:inherit;font-size:14px;line-height:20px;font-weight:400;color:var(--ink);resize:vertical;min-height:52px;transition:box-shadow .15s ease}
.chat-input:focus{outline:none;box-shadow:var(--ring-brand)}
.chat-composer-foot{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:10px}
.chat-hint{font-size:0.74rem;color:var(--muted-2)}
.chat-send[disabled]{opacity:.6;cursor:progress}
@media (max-width:640px){
  .chat-head{flex-direction:column;gap:12px}
  .chat-bubble{max-width:88%}
  .chat-hint{display:none}
}
@media (prefers-reduced-motion:reduce){
  .chat-input,.nav-b{transition:none}
}

/* ── Support ticket desk ──────────────────────────────────────────────────────
   The "Need help?" leaf carries a friendly teal so the support entry stands apart
   from the data pages without competing with the purple action accent. The desk
   itself (inbox -> new issue -> one thread) reuses the .chat-* thread + composer
   styles for the per-ticket conversation. Product register: quiet rows, status by
   chip + unread dot, teal reserved for the help affordance. */
.nav-help{--help:#0f766e;--help-soft:rgba(15,118,110,0.1)}
.nav-help .fa{color:var(--help)}
.nav-help:not(.on):hover{background:var(--help-soft)}
.nav-help:not(.on):hover span{color:var(--help)}
@media (prefers-color-scheme:dark){
  .nav-help{--help:#2dd4bf;--help-soft:rgba(45,212,191,0.14)}
}
.tk-card{padding:0;overflow:hidden}
.tk{display:flex;flex-direction:column;min-height:min(70vh,640px)}
.tk-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;padding:20px 22px;border-bottom:1px solid var(--line)}
.tk-title{font-size:1.05rem;font-weight:700;margin:0}
.tk-sub{color:var(--muted);font-size:0.85rem;margin:4px 0 0;max-width:54ch}
.tk-head-r{display:flex;align-items:center;gap:10px;flex:none}
.tk-notify{display:inline-flex;align-items:center;gap:7px}
.tk-notify .fa{font-size:0.85rem}
.tk-notify[data-on]{color:var(--ok)}
/* Inbox list */
.tk-list{display:flex;flex-direction:column;padding:8px 10px;gap:2px}
/* The global reset only inherits font-family onto buttons, so a bare <button> row keeps the UA's
   13.333px/normal metrics. font:inherit puts the row back on the document's type. */
.tk-row{display:flex;align-items:center;justify-content:space-between;gap:14px;width:100%;text-align:left;font:inherit;background:none;border:none;border-radius:var(--radius-sm);padding:14px 12px;cursor:pointer;transition:background .15s ease}
.tk-row:hover{background:var(--sand)}
.tk-row-main{min-width:0;flex:1}
.tk-row-top{display:flex;align-items:center;gap:9px;min-width:0}
.tk-no{font-size:0.76rem;font-weight:700;color:var(--muted-2);font-variant-numeric:tabular-nums;flex:none}
.tk-subject{font-size:0.92rem;font-weight:600;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tk-dot{width:8px;height:8px;border-radius:50%;background:var(--bad);box-shadow:0 0 0 3px color-mix(in srgb,var(--bad) 16%,transparent);flex:none}
.tk-preview{margin:4px 0 0;font-size:0.83rem;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:60ch}
.tk-from{color:var(--label);font-weight:600}
.tk-row-side{display:flex;flex-direction:column;align-items:flex-end;gap:6px;flex:none}
.tk-row-chips{display:flex;align-items:center;gap:8px}
.tk-when{font-size:0.74rem;color:var(--muted-2);font-variant-numeric:tabular-nums}
/* Ticket status uses the shared .pill primitives (Open = dot + label, Resolved = the sand chip),
   not a bespoke chip - the desk has no status vocabulary of its own. */
/* Skeleton rows while the inbox loads */
.tk-skel{height:58px;border-radius:var(--radius-sm);background:linear-gradient(90deg,var(--line-soft) 25%,var(--line) 37%,var(--line-soft) 63%);background-size:400% 100%;animation:tk-shimmer 1.3s ease infinite;pointer-events:none}
@keyframes tk-shimmer{0%{background-position:100% 0}100%{background-position:-100% 0}}
/* Empty teaching state */
.tk-empty{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;gap:9px;padding:56px 24px}
.tk-empty-ico{width:52px;height:52px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;background:var(--accent-soft);color:var(--accent);margin-bottom:4px}
.tk-empty-ico .fa{font-size:1.2rem}
.tk-empty h3{font-size:1rem;font-weight:700;margin:0}
.tk-empty p{color:var(--muted);font-size:0.88rem;margin:0 0 6px;max-width:44ch}
/* Back bar + thread header */
.tk-bar{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 16px;border-bottom:1px solid var(--line)}
.tk-bar-r{display:inline-flex;align-items:center;gap:8px}
.tk-close .fa{font-size:0.82rem}
.tk-back{display:inline-flex;align-items:center;gap:7px;background:none;border:none;padding:6px 8px;border-radius:var(--radius-sm);font:inherit;font-size:0.85rem;font-weight:600;color:var(--muted);cursor:pointer;transition:background .15s ease,color .15s ease}
.tk-back:hover{background:var(--sand);color:var(--ink)}
.tk-thread-head{display:flex;align-items:flex-start;justify-content:space-between;gap:14px;padding:18px 22px;border-bottom:1px solid var(--line)}
.tk-thread-no{font-size:0.75rem;font-weight:700;color:var(--muted-2);font-variant-numeric:tabular-nums;margin-bottom:3px}
/* The thread scrolls, the composer stays put. A flex item's min-height defaults to auto, so
   .chat-thread{flex:1;overflow-y:auto} grew the card instead of scrolling and repaintHelp's
   scrollTop=scrollHeight did nothing. Bound the card and let the thread be the scroller. */
.tk-thread{height:min(78vh,720px)}
.tk-thread .chat-thread{min-height:0}
.tk-thread .chat-composer{flex:none}
/* Response-time disclaimer. Sits above the composer on a thread and inside the new-issue form, so
   the expectation is set at the two moments someone is about to wait for a reply. */
.tk-note{display:flex;align-items:flex-start;gap:9px;padding:10px 14px;background:var(--sand);border-radius:var(--radius-sm);color:var(--muted);font-size:0.8rem;line-height:1.45}
.tk-note .fa{flex:none;margin-top:2px;font-size:0.8rem;color:var(--icon-idle)}
.tk-thread .tk-note{margin:0 18px 12px;flex:none}
/* New issue form */
.tk-new{display:flex;flex-direction:column;gap:16px;padding:24px 22px;max-width:620px}
.tk-new .tk-sub{margin:0}
.tk-field{display:flex;flex-direction:column;gap:6px}
.tk-label{font-size:0.82rem;font-weight:600;color:var(--label)}
/* Form fields follow the app's Stripe field idiom (sand fill, no border, 8px radius, 14/20) rather
   than a bordered card field of their own. */
.tk-input{width:100%;background:var(--sand);border:0;border-radius:var(--radius-sm);padding:9px 12px;font:inherit;font-size:14px;line-height:20px;font-weight:400;color:var(--ink);transition:box-shadow .15s ease}
.tk-input:focus{outline:none;box-shadow:var(--ring-brand)}
.tk-input:disabled{color:var(--muted);cursor:not-allowed}
select.tk-input{appearance:auto;-webkit-appearance:menulist}
.tk-textarea{resize:vertical;min-height:120px;line-height:1.5}
/* Category and urgency sit side by side above the details box. */
.tk-pair{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.tk-new-foot{display:flex;align-items:center;justify-content:flex-end;gap:10px;margin-top:2px}
@media (max-width:640px){
  .tk-head{flex-direction:column;gap:12px}
  .tk-row-side{align-items:flex-end}
  .tk-preview{max-width:none}
  .tk-new{padding:18px 16px}
}
@media (prefers-reduced-motion:reduce){
  .tk-row,.tk-back,.tk-input{transition:none}
  .tk-skel{animation:none}
}

/* ── Overview: customisable tile grid ─────────────────────────────────────────
   The Overview below the help banner is a grid of tiles the user arranges. Sizes are
   fixed (s/m/l = 1/2/4 columns of a 4-col grid) and collapse responsively; tiles can be
   dragged, resized, removed and re-added, with the arrangement in localStorage. The grid
   only owns placement - each tile body is a normal RENDER mount, so the data path and the
   tokens (light/dark both follow with no per-component overrides) are unchanged. The one
   Committed module is the earnings tile; everything else stays Restrained. */

/* Header toggle (Overview only): an icon button after the range selector that enters/leaves edit mode.
   Sized to sit level with the .dd-btn range pills beside it. */
.ov-cust-slot{display:inline-flex}
.top-cust{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;color:var(--muted);background:var(--card);border:1px solid var(--line);border-radius:var(--radius-pill);cursor:pointer;box-shadow:var(--shadow);transition:border-color .15s,color .15s,background .15s}
.top-cust:hover{border-color:var(--line-hover);color:var(--ink)}
.top-cust.on{background:var(--accent-soft);border-color:var(--accent);color:var(--accent)}
.top-cust svg{width:16px;height:16px}

/* Arrange bar shown above the grid while editing (Reset / Done). */
.ov-bar{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:2px 0 16px;min-height:34px}
.ov-bar-t{font-size:0.85rem;color:var(--muted)}
.ov-bar-a{display:flex;gap:8px;flex:none}
.ov-bar-btn{font-size:0.85rem;font-weight:600;border-radius:var(--radius-pill);padding:7px 16px;cursor:pointer;border:1px solid transparent}
.ov-bar-btn.ghost{background:var(--card);border-color:var(--line);color:var(--muted)}
.ov-bar-btn.ghost:hover{border-color:var(--line-hover);color:var(--ink)}
.ov-bar-btn.primary{background:var(--accent);color:#fff}
.ov-bar-btn.primary:hover{filter:brightness(1.05)}

/* The 4-column masonry grid. Rows are a fine 8px unit with a 16px gap; each tile is given a grid-row span
   equal to its measured height (ovMasonry in app.js: span = ceil((height + gap) / (unit + gap))), so a
   tile is exactly as tall as its content (no empty space inside it) while `dense` packs tiles up to fill
   the vertical gaps between them (no empty space around them). `align-items:start` lets each tile size to
   its content while it is measured. */
.ov-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));grid-auto-rows:8px;grid-auto-flow:row dense;column-gap:16px;row-gap:16px;align-items:start}
.ov-tile.size-s{grid-column:span 1}
.ov-tile.size-m{grid-column:span 2}
.ov-tile.size-l{grid-column:span 4}

/* Every tile is a card that lays its body out as a column so footers (money foot, live spark) can pin to
   the bottom and the tile fills its grid row cleanly. */
.ov-tile{position:relative;display:flex;flex-direction:column;overflow:hidden}
.ov-tile>.ovt-body{flex:1;min-width:0;display:flex;flex-direction:column}
.ovt-head{margin-bottom:14px}
.ovt-head-t{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap;min-width:0}
.ovt-head-t h2{font-size:1.02rem}

/* Edit affordances: a drag handle, an S/M/L segmented control and a remove button, laid over the tile head. */
.ov-tile.is-edit{outline:1.5px dashed var(--line-hover);outline-offset:-1.5px;cursor:grab}
.ov-tile.is-edit>.ovt-head,.ov-tile.is-edit>.ovt-body{opacity:.5;pointer-events:none}
.ov-tile.dragging{opacity:.4;outline-color:var(--accent)}
.ovt-edit{position:absolute;inset:0 0 auto 0;z-index:3;display:flex;align-items:center;gap:8px;padding:9px 10px;background:linear-gradient(180deg,var(--card) 68%,rgba(0,0,0,0));pointer-events:auto}
.ovt-drag{color:var(--muted);cursor:grab;display:inline-flex}
.ovt-drag svg{width:16px;height:16px}
.ovt-szs{display:inline-flex;gap:2px;background:var(--sand);border-radius:var(--radius-sm);padding:2px}
.ovt-sz{min-width:26px;font-size:0.72rem;font-weight:700;color:var(--muted);background:transparent;border:0;border-radius:var(--radius-xs);padding:3px 6px;cursor:pointer}
.ovt-sz.on{background:var(--card);color:var(--accent);box-shadow:var(--shadow)}
.ovt-x{margin-left:auto;flex:none;width:26px;height:26px;display:grid;place-items:center;color:var(--muted);background:var(--sand);border:0;border-radius:var(--radius-sm);cursor:pointer}
.ovt-x:hover{background:var(--bad-soft);color:var(--bad)}
.ovt-x svg{width:13px;height:13px}

/* Add-a-tile: a dashed full-width button above the grid (edit mode) that opens the catalogue modal. */
.ov-add-row{margin:0 0 16px}
.ov-add-btn{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;font-size:0.9rem;font-weight:600;color:var(--muted);background:var(--sand);border:1px dashed var(--line-hover);border-radius:var(--radius);padding:15px;cursor:pointer;transition:border-color .15s,color .15s}
.ov-add-btn:hover{border-color:var(--accent);color:var(--accent)}
.ov-add-btn svg{width:15px;height:15px}

/* Add-a-tile modal: a native <dialog> so it escapes the grid stacking context. Search + preview cards. */
.ov-modal{width:min(760px,calc(100vw - 32px));max-height:min(84vh,720px);padding:0;border:1px solid var(--line);border-radius:var(--radius);background:var(--card);color:var(--ink);box-shadow:0 24px 60px rgba(10,22,40,0.28);overflow:hidden}
.ov-modal::backdrop{background:rgba(10,22,40,0.42)}
.ov-modal-head{display:flex;align-items:flex-start;justify-content:space-between;gap:14px;padding:20px 22px 14px}
.ov-modal-h h3{font-size:1.12rem;font-weight:700;color:var(--ink);margin:0}
.ov-modal-h p{margin:3px 0 0;font-size:0.85rem;color:var(--muted)}
.ov-modal-x{flex:none;width:30px;height:30px;display:grid;place-items:center;color:var(--muted);background:var(--sand);border:0;border-radius:var(--radius-sm);cursor:pointer}
.ov-modal-x:hover{color:var(--ink)}
.ov-modal-x svg{width:14px;height:14px}
.ov-modal-searchwrap{position:relative;padding:0 22px 4px}
.ov-modal-searchico{position:absolute;left:35px;top:50%;transform:translateY(-50%);color:var(--muted);display:inline-flex;pointer-events:none}
.ov-modal-searchico svg{width:15px;height:15px}
.ov-modal-search{width:100%;font:inherit;font-size:0.9rem;color:var(--ink);background:var(--sand);border:1px solid var(--line);border-radius:var(--radius-sm);padding:10px 12px 10px 34px}
.ov-modal-search:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft);background:var(--card)}
.ov-modal-list{padding:14px 22px 22px;overflow:auto}
.ov-pick-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px}
.ov-pick{display:flex;flex-direction:column;gap:0;text-align:left;background:var(--card);border:1px solid var(--line);border-radius:var(--radius-sm);overflow:hidden;cursor:pointer;transition:border-color .15s,box-shadow .15s;padding:0}
button.ov-pick:hover{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
.ov-pick.is-on .ov-pick-prev{opacity:.72}
.ov-pick.is-on:hover{border-color:var(--bad);box-shadow:0 0 0 3px rgba(215,0,21,0.13)}
html.dark .ov-pick.is-on:hover{box-shadow:0 0 0 3px var(--bad-soft)}
.ov-pick-prev{display:block;background:var(--sand);border-bottom:1px solid var(--line);padding:12px}
.ov-pick-svg{display:block;width:100%;height:auto}
.ov-pick-meta{display:flex;flex-direction:column;gap:2px;padding:11px 13px 4px}
.ov-pick-t{font-size:0.9rem;font-weight:700;color:var(--ink)}
.ov-pick-d{font-size:0.78rem;line-height:1.4;color:var(--muted)}
.ov-pick-foot{padding:8px 13px 12px}
.ov-pick-add{display:inline-flex;align-items:center;gap:5px;font-size:0.8rem;font-weight:700;color:var(--accent)}
.ov-pick-add svg{width:12px;height:12px}
.ov-pick-on{display:inline-flex;align-items:center;gap:5px;font-size:0.76rem;font-weight:600;color:var(--muted)}
.ov-pick-on svg{width:12px;height:12px}
.ov-pick-rm{display:none;align-items:center;gap:5px;font-size:0.8rem;font-weight:700;color:var(--bad)}
.ov-pick-rm svg{width:12px;height:12px}
.ov-pick.is-on:hover .ov-pick-on{display:none}
.ov-pick.is-on:hover .ov-pick-rm{display:inline-flex}
.ov-pick-empty{margin:8px 0;font-size:0.9rem;color:var(--muted)}

/* Errors > buffer-error threshold. Reuses the .ov-modal chrome; one field, so it is narrow and the
   number leads - the unit sits beside it rather than in the label, which keeps the sentence readable. */
.err-buf-modal{width:min(440px,calc(100vw - 32px))}
.err-buf-form{display:flex;flex-direction:column;gap:14px;padding:4px 22px 22px;margin:0}
.err-buf-field{display:flex;flex-direction:column;gap:8px;font-size:0.88rem;font-weight:600;color:var(--ink)}
.err-buf-in{display:flex;align-items:center;gap:9px}
.err-buf-secs{width:104px;font:inherit;font-size:1.1rem;font-weight:700;color:var(--ink);background:var(--sand);border:1px solid var(--line);border-radius:var(--radius-sm);padding:9px 11px}
.err-buf-secs:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft);background:var(--card)}
.err-buf-unit{font-size:0.88rem;font-weight:500;color:var(--muted)}
.err-buf-note{margin:0;font-size:0.82rem;line-height:1.5;color:var(--muted)}
.err-buf-actions{display:flex;justify-content:flex-end;gap:8px}

/* The earnings tile: the page's one accent-tinted module. A whisper of accent on the ground plus the accent
   figure and area chart give it a distinct identity without a heavy fill. */
.ov-tile[data-id="money"]{background:linear-gradient(180deg,rgba(var(--accent-rgb),0.05),rgba(var(--accent-rgb),0) 42%),var(--card)}
.ov-tile[data-id="money"]>.ovt-body{gap:14px}
.mny-head{display:flex;align-items:center;justify-content:space-between;gap:10px}
.mny-k{font-size:0.72rem;font-weight:700;letter-spacing:0.05em;text-transform:uppercase;color:var(--muted)}
.mny-link{display:inline-flex;align-items:center;gap:5px;color:var(--accent);font-weight:600;font-size:0.82rem}
.mny-link svg{width:13px;height:13px}
.mny-figure{display:flex;flex-direction:column;gap:5px}
.mny-v{font-size:2.6rem;font-weight:700;letter-spacing:-0.03em;color:var(--ink);line-height:1}
.mny-cap{display:flex;align-items:center;gap:9px;font-size:0.85rem;font-weight:500;color:var(--muted)}
.mny-pill{display:inline-flex;align-items:center;gap:4px;font-size:0.73rem;font-weight:700;padding:2px 9px;border-radius:var(--radius-pill)}
.mny-pill.up{background:var(--good-soft);color:var(--good)}
.mny-pill.down{background:var(--bad-soft);color:var(--bad)}
.mny-chart{height:82px;margin:2px -6px 0}
.mny-chart .area{height:100%}
.mny-chart .area .fill{fill:rgba(var(--accent-rgb),0.13)}
.mny-chart .area .grid{stroke:var(--line)}
.mny-foot{display:flex;gap:24px;flex-wrap:wrap;padding-top:15px;margin-top:auto;border-top:1px solid var(--line)}
.mny-stat{display:flex;flex-direction:column;gap:2px}
.mny-sl{font-size:0.7rem;font-weight:600;letter-spacing:0.03em;text-transform:uppercase;color:var(--muted)}
.mny-sv{font-size:1.06rem;font-weight:700;color:var(--ink)}

/* KPI tiles: one momentum count with its trend sparkline, pinned so the spark sits at the tile foot. */
.ov-tile[data-id^="kpi-"]>.ovt-body{gap:7px}
.ovk-top{display:flex;align-items:center;justify-content:space-between;gap:8px;min-height:22px}
.ovk-l{font-size:0.82rem;font-weight:600;color:var(--muted)}
.ovk-pill{font-size:0.72rem;font-weight:700}
.ovk-v{font-size:1.9rem;font-weight:700;letter-spacing:-0.02em;color:var(--ink);line-height:1}
.ovk-spark{width:100%;height:34px;display:block;margin-top:auto}
.ovk-sp-fill{fill:rgba(var(--accent-rgb),0.10)}
.ovk-sp-line{fill:none;stroke:var(--accent);stroke-width:1.6}

/* The live tile lets its realtime spark drop to the bottom edge. */
.ov-tile[data-id="live"] .livenow-spark{margin-top:auto}

/* Top-earning content: real posters, wrapping to as many columns as the tile width allows. */
.ovc-row{display:grid;grid-template-columns:repeat(auto-fill,minmax(112px,1fr));gap:15px}
.ovc-card{display:flex;flex-direction:column;gap:9px;min-width:0;font:inherit;color:inherit;text-align:left;background:none;border:0;padding:0;cursor:pointer}
.ovc-card:hover .ovc-poster{box-shadow:0 0 0 2px var(--accent)}
.ovc-card:hover .ovc-title{color:var(--accent)}
.ovc-poster{display:block;position:relative;border-radius:10px;overflow:hidden;transition:box-shadow .15s}
.ovc-card .poster-img,.ovc-card .vt-thumb{width:100%;height:auto;aspect-ratio:2/3;border-radius:10px}
.ovc-card .vt-thumb .poster-fallback{font-size:1.4rem}
.ovc-rank{position:absolute;top:6px;left:6px;min-width:20px;height:20px;padding:0 5px;border-radius:6px;background:rgba(0,0,0,0.62);color:#fff;font-size:0.72rem;font-weight:700;display:grid;place-items:center;z-index:2}
.ovc-meta{display:flex;flex-direction:column;gap:1px;min-width:0}
.ovc-est{font-size:1.05rem;font-weight:700;letter-spacing:-0.01em;color:var(--accent)}
.ovc-title{font-size:0.85rem;font-weight:600;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ovc-sub{font-size:0.76rem;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ovc-empty{color:var(--muted);font-size:0.9rem;margin:4px 0}
.ovc-empty a{color:var(--accent);font-weight:600}

/* Audience map: fills the tile body; hides itself (and the whole tile) if world-map.js is absent. */
.ov-tile[data-id="map"]>.ovt-body,.ova-mapwrap{display:flex;flex-direction:column}
.ova-mapwrap{flex:1;min-height:230px}
.ova-mapwrap[hidden]{display:none}
.ov-tile:has(.ova-mapwrap[hidden]){display:none}
.ova-mapwrap>[data-render="audienceMap"]{flex:1;display:flex}
.ova-mapwrap .worldmap{flex:1;width:100%;margin-top:2px}

/* Pixel health strip. */
.ovph{display:flex;flex-direction:column;gap:1px}
.ovph-row{display:flex;align-items:center;gap:12px;padding:11px 8px;border-radius:var(--radius-sm)}
.ovph-row:hover{background:var(--sand)}
.ovph-ico{flex:none;width:34px;height:34px;border-radius:9px;background:var(--sand);color:var(--muted);display:grid;place-items:center}
.ovph-ico svg{width:15px;height:15px}
.ovph-b{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
.ovph-name{font-size:0.9rem;font-weight:600;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ovph-sub{font-size:0.78rem;color:var(--muted)}

/* The chart tile keeps its metric switcher + zoom hint. */
.ov-chart-top{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:12px}
.ov-chart-hint{margin-top:10px;font-size:0.8rem;color:var(--muted)}

/* Responsive: 4-col -> 2-col on tablets (l and m both span the full 2, s stays half), 1-col on phones. */
@media (max-width:1024px){
  .ov-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .ov-tile.size-l{grid-column:span 2}
  .ov-tile.size-m{grid-column:span 2}
  .ov-tile.size-s{grid-column:span 1}
}
@media (max-width:560px){
  .ov-grid{grid-template-columns:1fr}
  .ov-tile.size-s,.ov-tile.size-m,.ov-tile.size-l{grid-column:span 1}
  .mny-v{font-size:2.3rem}
  .ov-bar.is-edit{flex-wrap:wrap}
  .ov-bar-t{flex:1 1 100%;order:2}
}

/* end Stripe skin */
