:root {
  --bg: #ffffff;
  --bg-muted: #fafafa;
  --bg-hover: #f3f4f6;
  --border: #e5e7eb;
  --border-strong: #d1d5db;
  --text: #111827;
  --text-muted: #6b7280;
  --text-soft: #9ca3af;
  --accent: #2563eb;
  --accent-soft: #eff6ff;
  --app-nudge-bg: var(--accent-soft);            /* light blue, same as the rest of the app — backdrop for every "get the Mac app" nudge */
  --app-nudge-border: rgba(37, 99, 235, 0.15);   /* faint blue edge */
  --danger: #dc2626;
  --success: #16a34a;
  --warning: #d97706;
  --radius: 10px;
  --radius-sm: 6px;
  --shadow-sm: 0 1px 2px rgba(17, 24, 39, 0.04);
  --font-sans: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  /* Authentic Instagram brand gradient — used ONLY by the IG account finder
     so that one feature reads unmistakably as "Instagram, separate tool". */
  --ig-gradient: linear-gradient(45deg, #feda75 0%, #fa7e1e 24%, #d62976 52%, #962fbf 76%, #4f5bd5 100%);
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

.app-shell {
  display: flex;
  /* Viewport-locked shell: each pane (sidebar, content) owns its own
     scroll. Without a fixed height the whole page would scroll and the
     sidebar's single-scroll behaviour below couldn't work. */
  height: 100vh;
  overflow: hidden;
}

/* Sidebar */
.sidebar {
  width: 256px;
  flex-shrink: 0;
  border-right: 1px solid var(--border);
  background: var(--bg-muted);
  display: flex;
  flex-direction: column;
  padding: 20px 14px;
  /* ONE overall scroll for the whole sidebar. Sections inside never
     scroll on their own — they just grow as topics/creators are added
     and the entire sidebar scrolls as a single unit. */
  overflow-y: auto;
  overflow-x: hidden;
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 8px 18px;
  text-decoration: none;
  color: inherit;
}

.brand:hover .brand-name {
  color: var(--accent);
}

.sidebar-section-title {
  text-decoration: none;
  color: var(--text-soft);
}

.sidebar-section-title:hover {
  color: var(--text);
}

.brand-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  /* background: var(--accent);  — V2 blue; V3 brand = the pink→orange icon gradient */
  background: linear-gradient(135deg, #ff0090 0%, #ff4b3c 50%, #ff8200 100%);
  flex-shrink: 0;
}

/* Pulses only while work is in flight. Idle = solid accent (never
   grey — the dot is a "the app is alive" signal, not a job indicator). */
.brand-dot.is-busy {
  animation: brand-dot-pulse 1.2s ease-in-out infinite;
}

@keyframes brand-dot-pulse {
  0%, 100% { transform: scale(1);   opacity: 1; }
  50%      { transform: scale(0.7); opacity: 0.55; }
}

/* Brand name + status sit on a single line, like "Randm.Reels Watching".
   Status borrows the brand-name's size for visual continuity but stays
   regular-weight + muted so the eye lands on the name first. */
.brand-text {
  display: flex;
  flex-direction: row;
  align-items: baseline;
  gap: 6px;
  min-width: 0;
}

.brand-name {
  font-weight: 600;
  font-size: 15px;
  letter-spacing: -0.01em;
  /* Never wrap and never give up width to the status — the channel mark below
     sits on the same line and the status is what ellipsizes. */
  white-space: nowrap;
  flex-shrink: 0;
}

/* In the narrow sidebar the channel label is a superscript in the brand
   gradient, not a pill — a pill wrapped under the name and ate the status. */
.brand-name .badge-channel {
  background: linear-gradient(135deg, #ff0090 0%, #ff4b3c 50%, #ff8200 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  padding: 0;
  margin-left: 2px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.03em;
  vertical-align: 6px;
  display: inline;
}

.brand-status {
  font-size: 15px;
  font-weight: 400;
  color: var(--text-soft);
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.brand-status.is-busy {
  color: var(--accent);
}

.brand-status.is-link {
  cursor: pointer;
}

.sidebar-section {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 14px;
}

.sidebar-section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 12px;
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-soft);
  font-weight: 600;
}

.sidebar-section-title {
  user-select: none;
}

.sidebar-new {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--accent);
  border: 1px solid var(--accent);
  color: #fff;
  text-decoration: none;
  line-height: 1;
  transition: background-color 0.1s ease, transform 0.1s ease, box-shadow 0.1s ease;
  box-shadow: 0 1px 2px rgba(37, 99, 235, 0.25);
}

.sidebar-new svg {
  width: 12px;
  height: 12px;
}

.sidebar-new:hover {
  background: #1d4ed8;
  border-color: #1d4ed8;
  box-shadow: 0 2px 4px rgba(37, 99, 235, 0.35);
}

.sidebar-new:active {
  transform: scale(0.95);
}

/* Topics / Watchlist grow with their content — no internal scroll, no
   space-splitting. A hairline rule + extra top padding splits them away
   from the section above so they read as distinct sections instead of
   one long blurred list. The whole sidebar (.sidebar) is the single
   scroll container; these just get taller as rows are added. */
.sidebar-section-grow {
  border-top: 1px solid var(--border);
  padding-top: 18px;
  margin-top: 6px;
}

.topics-list {
  display: flex;
  flex-direction: column;
  gap: 0;
  padding-right: 2px;
}

/* Hairline divider between consecutive (non-active) topic rows. Looks
   like a subtle group inside the sidebar instead of a wall of text;
   the active row sits on the accent-soft background so it visually
   stands out without needing the divider. */
.topics-list .topic-row + .topic-row {
  border-top: 1px solid var(--border);
}

.topics-list .topic-row.is-active,
.topics-list .topic-row.is-active + .topic-row {
  border-top-color: transparent;
}

.sidebar-empty {
  padding: 8px 12px;
  font-size: 12px;
  color: var(--text-soft);
  font-style: italic;
}

.topic-row {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 9px 10px;
  /* No corner radius: the row-to-row hairline (border-top below) is
     drawn on this box, so a radius made the divider ends curve. Flush
     rows = straight dividers. */
  border-radius: 0;
  border-top: 1px solid transparent;   /* keeps height stable when divider toggles on/off */
  color: var(--text);
  text-decoration: none;
  font-size: 13px;
  transition: background-color 0.1s ease;
}

.topic-row:hover {
  background: var(--bg-hover);
}

.topic-row.is-active {
  background: var(--accent-soft);
  color: var(--accent);
}

.topic-row-title {
  display: flex;
  align-items: center;
  gap: 6px;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.topic-row-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  color: var(--text-soft);
}

.topic-row-icon svg {
  width: 14px;
  height: 14px;
}

.topic-row.is-active .topic-row-icon {
  color: var(--accent);
}

.topic-row-title-text {
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.topic-row-meta {
  font-size: 11px;
  color: var(--text-soft);
  font-weight: 400;
}

.topic-row.is-active .topic-row-meta {
  color: var(--accent);
  opacity: 0.7;
}

.topic-mini-badge {
  display: inline-flex;
  align-items: center;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--bg-hover);
  color: var(--text-muted);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  line-height: 1.5;
}

.topic-mini-badge.is-paused {
  background: #fef3c7;
  color: #92400e;
}

/* "+N new since you last opened this topic" — the most actionable
   signal in the list, so it borrows the accent so the eye lands on
   topics that have fresh videos waiting. */
.topic-mini-badge.is-new {
  background: var(--accent-soft);
  color: var(--accent);
}

/* Schedule line under the query: "every 6h · in 3h" / "· due now" /
   "· paused". Same muted treatment as the query meta line. */
.topic-row-sched {
  font-size: 11px;
  color: var(--text-soft);
  font-weight: 400;
}

.topic-row.is-active .topic-row-sched {
  color: var(--accent);
  opacity: 0.7;
}

/* Drag-to-reorder + live scrape indicator (sidebar). The row becomes a
   [grip | body] flex; the body keeps the original stacked layout. */
.topic-row {
  flex-direction: row;
  align-items: flex-start;
  gap: 7px;
  cursor: grab;
}

.topic-row-grip {
  flex-shrink: 0;
  width: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 1px;
  font-size: 11px;
  line-height: 1;
  letter-spacing: -1px;
  color: var(--text-soft);
  opacity: 0.45;
  cursor: grab;
  user-select: none;
}

.topic-row:hover .topic-row-grip {
  opacity: 0.8;
}

.topic-row-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
  min-width: 0;
}

.topic-row.is-dragging {
  opacity: 0.45;
  cursor: grabbing;
  background: var(--bg-hover);
}

.topic-row-sched.is-scraping {
  display: flex;
  align-items: center;
  gap: 5px;
  color: var(--accent);
  font-weight: 600;
}

.topic-row-sched .scrape-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
  animation: brand-dot-pulse 1s ease-in-out infinite;
}

.nav {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* Tools are explicit buttons: a raised white surface with a border and
   shadow — deliberately unlike the flat, borderless topic rows below,
   so the two sections never read as one continuous list. */
.nav-item {
  display: flex;
  align-items: center;
  padding: 9px 12px;
  border-radius: var(--radius-sm);
  background: var(--bg);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
  color: var(--text);
  text-decoration: none;
  font-weight: 500;
  font-size: 13.5px;
  transition: background-color 0.1s ease, color 0.1s ease,
    border-color 0.1s ease, box-shadow 0.1s ease, transform 0.05s ease;
}

.nav-item:hover {
  background: var(--bg-hover);
  border-color: var(--border-strong);
  color: var(--text);
}

.nav-item:active {
  transform: translateY(1px);
  box-shadow: none;
}

.nav-item.is-active {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
}

.sidebar-footer {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding-top: 8px;
  border-top: 1px solid var(--border);
}

/* Empty in local mode (no account) — don't leave a stray divider line. */
.sidebar-footer:empty {
  display: none;
}

/* Hosted: signed-in identity + sign-out, sitting quietly under the nav. */
.account-email {
  padding: 2px 12px 4px;
  font-size: 11.5px;
  line-height: 1.2;
  color: var(--text-soft);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.account-logout {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 8px 12px;
  border: 0;
  background: none;
  color: var(--text-muted);
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
  border-radius: var(--radius-sm);
  cursor: pointer;
  text-align: left;
  transition: background-color 0.12s ease, color 0.12s ease;
}

.account-logout:hover {
  background: var(--bg-hover);
  color: var(--danger);
}

.account-logout:active {
  transform: translateY(0.5px);
}

.account-logout svg {
  width: 15px;
  height: 15px;
  flex: none;
  opacity: 0.85;
}

/* Main */
.main {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.topbar {
  height: 56px;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 28px;
  background: var(--bg);
}

.topbar-title {
  font-size: 15px;
  font-weight: 600;
}

.topbar-status {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--text-muted);
  font-size: 12.5px;
}

/* (Activity indicator moved to the sidebar brand area — see .brand-dot
   and .brand-status above. The topbar chip is gone.) */

.status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--text-soft);
}

.status-dot.is-ok {
  background: var(--success);
}

.status-dot.is-err {
  background: var(--danger);
}

.topbar-right {
  display: flex;
  align-items: center;
  gap: 14px;
}
/* Global preview-sound toggle — a quiet icon button next to the status. Muted
   (gray) by default; accent when sound is on. */
.sound-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--text-soft);
  cursor: pointer;
  transition: color 0.12s ease, background-color 0.12s ease, border-color 0.12s ease;
}
.sound-toggle:hover { color: var(--text); background: var(--bg-hover); }
.sound-toggle.is-on { color: var(--accent); }

/* Impersonation strip — fixed at the top while an admin is "viewing as" someone;
   the shell shifts down so nothing hides under it. */
#impersonation-banner {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 34px;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  background: var(--accent);
  color: #fff;
  font-size: 13px;
  padding: 0 16px;
}
#impersonation-banner button {
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  padding: 3px 12px;
  border: 1px solid rgba(255, 255, 255, 0.6);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.15);
  color: #fff;
  cursor: pointer;
}
#impersonation-banner button:hover { background: rgba(255, 255, 255, 0.28); }
body.is-impersonating .app-shell { height: calc(100vh - 34px); margin-top: 34px; }

.content {
  flex: 1;
  padding: 28px;
  /* Vertical scroll only. If this were `overflow: auto` the whole
     content pane (topic head, stat tiles, scrape-state) would scroll
     sideways together with the wide videos table on narrow screens —
     "разъезжается". Horizontal overflow is owned exclusively by
     `.vt-scroll` so only the table moves. */
  overflow-y: auto;
  overflow-x: hidden;
  min-width: 0;
}

/* Page header */
.page-header {
  margin-bottom: 24px;
}

.page-title {
  font-size: 22px;
  font-weight: 600;
  margin: 0 0 6px;
  letter-spacing: -0.01em;
}

.page-subtitle {
  color: var(--text-muted);
  font-size: 13.5px;
  margin: 0;
}

/* Empty state */
.empty {
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
  padding: 40px 28px;
  text-align: center;
  color: var(--text-muted);
  background: var(--bg-muted);
}

.empty-title {
  font-weight: 600;
  color: var(--text);
  margin-bottom: 6px;
}

/* Form */
.card {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 22px 24px;
  box-shadow: var(--shadow-sm);
  max-width: 720px;
}

.card + .card {
  margin-top: 16px;
}

.card-title {
  font-size: 14px;
  font-weight: 600;
  margin: 0 0 4px;
}

.card-hint {
  font-size: 12.5px;
  color: var(--text-muted);
  margin: 0 0 18px;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 14px;
}
/* Local-only fields (e.g. the per-user Telegram bot token / chat id) are hidden
   in hosted, where Telegram is a shared bot + per-workspace bind. */
.field-local-only.is-hidden { display: none; }

.field-label {
  font-size: 12.5px;
  font-weight: 500;
  color: var(--text-muted);
}

.field input {
  width: 100%;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: 13.5px;
  font-family: inherit;
  background: var(--bg);
  color: var(--text);
  transition: border-color 0.1s ease, box-shadow 0.1s ease;
}

.field input:focus {
  outline: none;
}

body.input-keyboard .field input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 6px;
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 16px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  font-family: inherit;
  text-decoration: none;
  line-height: 1.2;
  transition: background-color 0.1s ease, border-color 0.1s ease, opacity 0.1s ease, box-shadow 0.1s ease, transform 0.1s ease;
}

.btn-lg {
  padding: 11px 22px;
  font-size: 14px;
  font-weight: 600;
  gap: 8px;
}

.btn-lg svg {
  width: 14px;
  height: 14px;
}

.btn-primary.btn-lg {
  box-shadow: 0 2px 6px rgba(37, 99, 235, 0.25);
}

.btn-primary.btn-lg:hover:not(:disabled) {
  box-shadow: 0 3px 10px rgba(37, 99, 235, 0.32);
  transform: translateY(-1px);
}

.btn:hover:not(:disabled) {
  background: var(--bg-hover);
}

.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

.btn-primary:hover:not(:disabled) {
  background: #1d4ed8;
  border-color: #1d4ed8;
}

/* First-run call-to-action: the topic was just created and has no
   scrape yet, so "Start scraping" gets a soft pulsing accent ring to
   pull the eye. Same brand colour (accent stays reserved for primary
   actions) — only the halo signals "do this next". Drops to a normal
   primary button the moment there's any scrape history. */
.btn-primary.is-cta {
  animation: btn-cta-pulse 1.35s ease-in-out infinite;
}

.btn-primary.is-cta:hover:not(:disabled) {
  animation: none;
}

/* Expanding ring + a small lift/brighten so it actually reads as
   "press me" across the room, not a barely-there halo. */
@keyframes btn-cta-pulse {
  0% {
    box-shadow: 0 0 0 0 rgba(37, 99, 235, 0.55);
    transform: scale(1);
    background: var(--accent);
  }
  50% {
    box-shadow: 0 0 0 12px rgba(37, 99, 235, 0);
    transform: scale(1.045);
    background: #1d4ed8;
  }
  100% {
    box-shadow: 0 0 0 0 rgba(37, 99, 235, 0);
    transform: scale(1);
    background: var(--accent);
  }
}

.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.inline-msg {
  font-size: 12.5px;
  color: var(--text-muted);
}

.inline-msg.is-ok {
  color: var(--success);
}

.inline-msg.is-err {
  color: var(--danger);
}

.text-err {
  color: var(--danger);
}

/* Spending card */
.cost-headline {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-muted);
}

.cost-headline.is-over {
  border-color: var(--danger);
  background: #fef2f2;
}

.cost-headline-label {
  font-size: 12.5px;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 600;
}

.cost-headline-value {
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.cost-headline.is-over .cost-headline-value {
  color: var(--danger);
}

.cost-of {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-soft);
}

/* Daily-spend mini chart (inline SVG, stacked Apify + HikerAPI) */
.cost-chart-wrap {
  margin: 12px 0 4px;
  position: relative;
}
.cost-chart {
  display: block;
  width: 100%;
  height: 84px;
}
.cost-bar-apify { fill: var(--accent); }
.cost-bar-hiker { fill: #f59e0b; }
.cost-bar-sc { fill: #10b981; }
.cost-chart-legend {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 4px;
  font-size: 11.5px;
  color: var(--text-muted);
}
.cost-chart-legend span {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.cost-chart-span {
  margin-left: auto;
  color: var(--text-soft);
}
.cost-dot {
  width: 9px;
  height: 9px;
  border-radius: 2px;
  display: inline-block;
}
.cost-dot-apify { background: var(--accent); }
.cost-dot-hiker { background: #f59e0b; }
.cost-dot-sc { background: #10b981; }
.cost-dot-other { background: var(--text-soft); }

/* Transparent full-height hover targets, one per day column. */
.cost-hit {
  fill: transparent;
  cursor: default;
}
.cost-hit:hover {
  fill: var(--accent);
  fill-opacity: 0.07;
}

/* Custom hover tooltip over the daily-spend chart. */
.cost-tip {
  position: absolute;
  z-index: 6;
  pointer-events: none;
  min-width: 132px;
  padding: 7px 9px;
  border-radius: 8px;
  background: var(--text);
  color: #fff;
  font-size: 11.5px;
  line-height: 1.5;
  box-shadow: 0 6px 18px rgba(17, 24, 39, 0.22);
}
.cost-tip[hidden] { display: none; }
.cost-tip-day {
  font-weight: 700;
  margin-bottom: 3px;
}
.cost-tip-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.cost-tip-row > span {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  opacity: 0.85;
}
.cost-tip-row > b { font-variant-numeric: tabular-nums; }
.cost-tip-total {
  margin-top: 4px;
  padding-top: 4px;
  border-top: 1px solid rgba(255, 255, 255, 0.2);
  opacity: 1;
}

.cost-table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 12px;
  font-size: 13px;
}

.cost-table th,
.cost-table td {
  padding: 7px 10px;
  text-align: left;
  border-bottom: 1px solid var(--border);
}

.cost-table th {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-soft);
  font-weight: 600;
}

.cost-table .num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.cost-table tfoot td {
  font-weight: 700;
  border-bottom: none;
}

.cost-empty {
  color: var(--text-soft);
  font-style: italic;
}

.cost-limit-field {
  margin-top: 16px;
}

.cost-limit-row {
  display: flex;
  align-items: center;
  gap: 10px;
}

.cost-limit-row input {
  width: 120px;
}

.cost-limit-row .btn {
  flex-shrink: 0;
}

.btn-sm {
  padding: 5px 12px;
  font-size: 12px;
}

.field-validate {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 6px;
}

.tg-subtoggles {
  margin: 12px 0 0 34px;
  padding-left: 14px;
  border-left: 2px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 10px;
  transition: opacity 0.1s ease;
}

.tg-subtoggles.is-disabled {
  opacity: 0.45;
}

.danger-card {
  border-color: #fecaca;
}

.danger-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

.danger-row-title {
  font-weight: 600;
  font-size: 13.5px;
}

.danger-row-note {
  font-size: 12px;
  color: var(--text-muted);
  margin-top: 2px;
  max-width: 520px;
}

.danger-row .btn-danger {
  flex-shrink: 0;
}

.danger-divider {
  height: 1px;
  background: var(--border);
  margin: 14px 0;
}

.shutdown-screen {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 100vh;
  text-align: center;
  font-size: 18px;
  font-weight: 600;
  color: var(--text);
  font-family: var(--font-sans);
}

.shutdown-screen span {
  font-size: 13px;
  font-weight: 400;
  color: var(--text-muted);
}

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

/* Settings → Scrapers section */

.scraper-row {
  display: grid;
  grid-template-columns: 44px 1fr;
  gap: 14px;
  align-items: start;
  padding: 12px 0;
  border-bottom: 1px solid var(--border);
}

.scraper-row:last-child {
  border-bottom: none;
}

.scraper-toggle {
  display: inline-flex;
  align-items: center;
  cursor: pointer;
  position: relative;
}

.scraper-toggle input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.scraper-toggle-track {
  display: inline-block;
  width: 36px;
  height: 20px;
  border-radius: 999px;
  background: var(--bg-hover);
  border: 1px solid var(--border);
  position: relative;
  transition: background-color 0.12s ease, border-color 0.12s ease;
}

.scraper-toggle-knob {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
  transition: transform 0.14s ease;
}

.scraper-toggle input[type="checkbox"]:checked + .scraper-toggle-track {
  background: var(--accent);
  border-color: var(--accent);
}

.scraper-toggle input[type="checkbox"]:checked + .scraper-toggle-track .scraper-toggle-knob {
  transform: translateX(16px);
}

body.input-keyboard .scraper-toggle input[type="checkbox"]:focus + .scraper-toggle-track {
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.scraper-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.scraper-title {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text);
}

.scraper-note {
  font-size: 12px;
  color: var(--text-muted);
}

.scraper-status {
  font-size: 11.5px;
  color: var(--text-muted);
  margin-top: 4px;
}

/* Downloads */

.downloads-form-card {
  max-width: none;
}

.downloads-form {
  display: flex;
  gap: 10px;
  align-items: center;
}

.downloads-form input,
.downloads-form select {
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: 13.5px;
  font-family: inherit;
  background: var(--bg);
  color: var(--text);
}

.downloads-form input {
  flex: 1;
  min-width: 0;   /* allow shrink when the form is narrow */
}
/* (.downloads-form select layout lives near the chevron styling
   further down, so flex / width / min-width sit alongside the
   appearance overrides instead of competing across two blocks.) */

.downloads-form input:focus,
.downloads-form select:focus {
  outline: none;
}

body.input-keyboard .downloads-form input:focus,
body.input-keyboard .downloads-form select:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.downloads-list {
  margin-top: 22px;
  display: grid;
  /* Smaller cards — ~5 per row on a normal laptop instead of 3. */
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 14px;
}

.dl-card {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  box-shadow: var(--shadow-sm);
}

.dl-media {
  position: relative;
  display: block;
  width: 100%;
  /* Vertical-first: most content is 9:16 reels. Let the full portrait
     ratio show (the old 360px cap squashed it toward square on wide
     cards). The taller cap only kicks in on unusually wide columns. */
  aspect-ratio: 9 / 16;
  max-height: 460px;
  background: #0f172a;
  overflow: hidden;
  text-decoration: none;
  color: inherit;
}

.dl-media.is-clickable {
  cursor: pointer;
}

.dl-media img,
.dl-media video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.dl-media video {
  opacity: 0;
  transition: opacity 0.15s ease;
}

.dl-media.is-hoverable:hover video {
  opacity: 1;
}

/* Sits under the cover <img> while it loads — and stays visible when there's no
   cover at all (the img removes itself on error), so it must read as "no preview"
   rather than a broken black hole: a faint centred image glyph. */
.thumb-placeholder {
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
}

.thumb-placeholder::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 30px;
  height: 30px;
  opacity: .3;
  background: no-repeat center / contain
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='3' width='18' height='18' rx='2'/%3E%3Ccircle cx='8.5' cy='8.5' r='1.5'/%3E%3Cpath d='M21 15l-5-5L5 21'/%3E%3C/svg%3E");
}

.dl-overlay {
  position: absolute;
  left: 10px;
  top: 10px;
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  max-width: calc(100% - 20px);
}

.dl-status,
.quality-badge,
.platform-badge {
  display: inline-flex;
  align-items: center;
  padding: 3px 9px;
  font-size: 10.5px;
  font-weight: 600;
  border-radius: 999px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  line-height: 1.4;
}

.dl-status {
  background: rgba(15, 23, 42, 0.78);
  color: #fff;
  backdrop-filter: blur(6px);
}

.dl-status.is-done {
  background: rgba(22, 163, 74, 0.95);
  color: #fff;
}

.dl-status.is-failed {
  background: rgba(220, 38, 38, 0.95);
  color: #fff;
}

.dl-status.is-active {
  background: rgba(37, 99, 235, 0.92);
  color: #fff;
}

.quality-badge {
  background: rgba(255, 255, 255, 0.85);
  color: var(--text);
  backdrop-filter: blur(6px);
}

.quality-badge.quality-best {
  background: rgba(15, 23, 42, 0.78);
  color: #fff;
}

/* Static / carousel downloads: a "photo" / "N slides" badge in the
   media overlay, and a clickable slide strip in the card body. */
.dl-kind-badge {
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  background: rgba(15, 23, 42, 0.78);
  color: #fff;
  backdrop-filter: blur(6px);
}

/* Carousel pager — flip slides inside the preview. The slide img/video
   fills the media box via the existing .dl-media img/video rules. */
.dl-car-track {
  position: absolute;
  inset: 0;
}
.dl-car-track > a {
  display: block;
  width: 100%;
  height: 100%;
}
.dl-car-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 30px;
  height: 30px;
  border: none;
  border-radius: 999px;
  background: rgba(15, 23, 42, 0.55);
  color: #fff;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: center;
}
.dl-car-nav:hover {
  background: rgba(15, 23, 42, 0.82);
}
.dl-car-prev { left: 8px; }
.dl-car-next { right: 8px; }
.dl-car-count {
  position: absolute;
  bottom: 8px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 4;
  padding: 2px 10px;
  border-radius: 999px;
  background: rgba(15, 23, 42, 0.72);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
}

.platform-badge {
  gap: 5px;
  padding-left: 8px;
  padding-right: 10px;
  color: #fff;
}

.platform-badge svg {
  width: 12px;
  height: 12px;
  flex-shrink: 0;
}

.platform-badge.platform-youtube {
  background: #ff0000;
}

.platform-badge.platform-tiktok {
  background: #111;
}

.platform-badge.platform-instagram {
  background: linear-gradient(45deg, #f09433 0%, #e6683c 25%, #dc2743 50%, #cc2366 75%, #bc1888 100%);
}

.platform-badge.platform-unknown {
  background: #475569;
}

/* Read-only network tag (topic list cards + detail header) — a quiet
   pill with a brand-colored icon. Not interactive. */
.net-tag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  background: var(--bg-hover);
  color: var(--text-muted);
}
.net-tag svg { width: 12px; height: 12px; flex-shrink: 0; }
.net-tag.platform-tiktok svg { color: #111; }
.net-tag.platform-instagram svg { color: #cc2366; }

/* Icon-only network marker for the sidebar rows. */
.net-mini {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
}
.net-mini svg { width: 13px; height: 13px; }
.net-mini.platform-tiktok svg { color: #111; }
.net-mini.platform-instagram svg { color: #cc2366; }

.dl-body {
  flex: 1;
  padding: 14px 16px 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.dl-title {
  font-size: 16px;
  color: var(--text);
  font-weight: 600;
  line-height: 1.3;
  word-break: break-word;
  /* No max-height / no auto-shrink: long titles wrap onto extra lines.
     Action rows still line up across the row via .dl-card flex layout
     and `.dl-actions { margin-top: auto }`. */
}

/* Captions clamp to 2 lines so chatty posts can't blow up the grid row's
   height. The "See more" toggle (JS) removes this class to reveal the
   full text. */
.dl-title.is-clamped {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}

.dl-caption-toggle {
  align-self: flex-start;
  margin-top: -4px;
  padding: 0;
  border: none;
  background: none;
  color: var(--accent, #2563eb);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.dl-caption-toggle:hover { text-decoration: underline; }

.dl-tags {
  font-size: 11px;
  color: var(--text-muted);
  line-height: 1.35;
  max-height: 30px;
  overflow: hidden;
  overflow-wrap: anywhere;
}

.dl-meta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 5px 12px;
  font-size: 12px;
  color: var(--text-muted);
  align-items: center;
}

.dl-meta-item {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  white-space: nowrap;
  color: inherit;
  text-decoration: none;
}

.dl-meta-item svg {
  width: 13px;
  height: 13px;
  color: var(--text-soft);
  flex-shrink: 0;
}

.dl-meta-item.is-link {
  color: var(--text);
}

.dl-meta-item.is-link:hover {
  color: var(--accent);
}

.dl-meta-item.is-link:hover svg {
  color: var(--accent);
}

.dl-error {
  font-size: 12px;
  color: var(--danger);
  background: #fef2f2;
  border: 1px solid #fecaca;
  border-radius: var(--radius-sm);
  padding: 6px 8px;
}

.dl-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: auto;
  min-height: 30px;
  align-items: flex-end;
}

.dl-actions .btn {
  padding: 6px 12px;
  font-size: 12.5px;
}

.btn-ghost-danger {
  color: var(--danger);
}

.btn-ghost-danger:hover:not(:disabled) {
  background: #fef2f2;
  border-color: #fecaca;
}

.btn-danger {
  background: var(--danger);
  border-color: var(--danger);
  color: #fff;
}

.btn-danger:hover:not(:disabled) {
  background: #b91c1c;
  border-color: #b91c1c;
}

/* Modal */

.app-modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 42, 0.48);
  backdrop-filter: blur(2px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  animation: app-modal-fade 0.12s ease-out;
}

@keyframes app-modal-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

.app-modal {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 24px 48px rgba(15, 23, 42, 0.18);
  width: 100%;
  max-width: 420px;
  margin: 0 24px;
  padding: 22px 24px 18px;
  animation: app-modal-pop 0.14s ease-out;
}

@keyframes app-modal-pop {
  from { transform: translateY(6px) scale(0.98); opacity: 0; }
  to { transform: translateY(0) scale(1); opacity: 1; }
}

.app-modal-title {
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
}

.app-modal-body {
  margin-top: 8px;
  font-size: 13.5px;
  color: var(--text-muted);
  line-height: 1.45;
  /* Honour explicit \n in the message string — used to break the refresh
     confirmation into a few scannable lines instead of a wall of text. */
  white-space: pre-line;
}

/* Optional label/value list used in confirm dialogs that summarise a
   paid action (refresh, etc). Label sits muted, value is bold and
   inherits the body text colour so the value is what your eye lands
   on first. */
.app-modal-details {
  margin: 12px 0 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 13.5px;
  line-height: 1.5;
}

.app-modal-detail {
  display: flex;
  gap: 6px;
  align-items: baseline;
}

.app-modal-detail dt {
  color: var(--text-muted);
  font-weight: 400;
  margin: 0;
}

.app-modal-detail dd {
  margin: 0;
  color: var(--text);
  font-weight: 600;
}

.app-modal-actions {
  margin-top: 20px;
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}

.app-modal-title.is-danger {
  color: var(--danger);
}

/* groupDialog: name input + colour palette. The input matches the standard
   .field input (same border/size/colour) so it doesn't read as a foreign box. */
.app-modal-input {
  width: 100%;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: 13.5px;
  font-family: inherit;
  color: var(--text);
  background: var(--bg);
  margin-bottom: 14px;
}
.app-modal-input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.app-modal-swlabel { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-soft); margin: 0 2px 9px; }
/* Exactly the group-header edit popover's swatches (.wlg-sw): 32px, left-aligned,
   wrapping — 15 of them read as two tidy rows. Don't reinvent the size. */
.app-modal-swatches { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 4px; }
.app-modal-swatch {
  width: 32px; height: 32px; flex: none;
  border-radius: 6px; border: 1px solid rgba(15, 23, 42, 0.1);
  padding: 0; cursor: pointer; transition: transform 0.1s;
}
.app-modal-swatch:hover { transform: scale(1.06); }
.app-modal-swatch.is-active { box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent); }

/* Watchlist groups in the sidebar: collapsible coloured sections. Headers are
   also drag-drop targets ("file this creator here"). */
.wl-group-head {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 4px 8px 3px;
  margin-top: 6px;
  border-radius: 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-muted);
  cursor: pointer;
  user-select: none;
}
.wl-group-head:hover { background: var(--bg-hover); }
.wl-group-head.is-drop { background: var(--accent-soft); outline: 1px dashed var(--accent); }
/* Notion-style twist: a colour dot at rest that crossfades to a rotating
   chevron on row-hover (⌄ open / › collapsed, 0.2s ease-out — same easing as
   Notion). The button folds the list; the rest of the band opens the group. */
.wl-group-tw { position: relative; width: 18px; height: 18px; flex: none;
  border: none; background: none; padding: 0; cursor: pointer; border-radius: 4px;
  display: inline-flex; align-items: center; justify-content: center;
  font-family: inherit; color: var(--text-muted); }
.wl-group-dot { position: absolute; width: 8px; height: 8px; border-radius: 3px;
  background: var(--gc, var(--border-strong)); opacity: 1; transition: opacity .12s ease; }
.wl-group-chev { position: absolute; width: 15px; height: 15px; opacity: 0;
  transform: rotate(0deg); transition: opacity .12s ease, transform .2s ease-out; }
.wl-group-head:hover .wl-group-dot { opacity: 0; }
.wl-group-head:hover .wl-group-chev { opacity: 1; }
.wl-group-head:hover .wl-group-tw { color: var(--text); }
.wl-group-head.is-collapsed .wl-group-chev { transform: rotate(-90deg); }
.wl-group-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wl-group-count { margin-left: auto; font-weight: 500; font-size: 11px; color: var(--text-soft); }

/* Real-group band: colour-tinted, two lines (name + weekly signal). The dot
   carries the group colour; on hover it becomes the fold chevron. --gc = colour. */
.wl-group-band { align-items: center; gap: 7px; border-radius: 8px; padding: 6px 8px 6px 6px; margin: 1px 0;
  cursor: pointer; background: color-mix(in srgb, var(--gc) 13%, transparent);
  transition: background .02s ease-in; }
.wl-group-band:hover { background: color-mix(in srgb, var(--gc) 20%, transparent); }
.wl-group-band:hover .wl-group-tw { background: color-mix(in srgb, var(--gc) 34%, transparent); }
.wl-group-main { flex: 1; min-width: 0; }
.wl-group-band .wl-group-name { font-size: 13.5px; font-weight: 700; color: var(--text); line-height: 1.2; }
.wl-group-sub { font-size: 11px; color: var(--text-muted); margin-top: 1px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wl-group-body.is-collapsed { display: none; }
.wl-group-body .topic-row { margin-left: 8px; }

/* "+" menu: New group / Track a creator */
.sidebar-plus-menu {
  position: absolute;
  right: 4px;
  top: calc(100% + 2px);
  z-index: 40;
  min-width: 176px;
  padding: 4px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(17, 24, 39, 0.12);
}
.sidebar-plus-menu button {
  display: block;
  width: 100%;
  text-align: left;
  padding: 7px 10px;
  border: none;
  background: none;
  border-radius: 6px;
  font: inherit;
  font-size: 13px;
  color: var(--text);
  cursor: pointer;
}
.sidebar-plus-menu button:hover { background: var(--bg-hover); }
.wl-group-name { color: inherit; text-decoration: none; }
a.wl-group-name:hover { color: var(--accent); }

/* Group page (#/watchlist/group/{id}) */
.wlg-page { max-width: none; }   /* full width, like Topics / Watchlist */
.wlg-head { display: flex; align-items: center; gap: 10px; row-gap: 8px; flex-wrap: wrap; margin-bottom: 4px; }
/* full-size header actions — one line each; wrap to a second row when narrow
   rather than squeezing the title into an ellipsis. */
.wlg-head .btn { white-space: nowrap; }
.wlg-chip { width: 12px; height: 12px; border-radius: 4px; }
.wlg-spacer { flex: 1; }
.wlg-stats { display: flex; gap: 24px; color: var(--text-muted); font-size: 12.5px; margin: 4px 0 22px; }
.wlg-stats b { color: var(--text); font-weight: 600; }
/* "Refresh now" progress bar (polls until every creator finishes checking). */
.wlg-refreshbar { margin: 0 0 18px; padding: 8px 14px; border-radius: 8px; font-size: 12.5px;
  font-weight: 600; background: var(--accent-soft); color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent); }
.wlg-refreshbar[hidden] { display: none; }
/* group scrape-cadence picker (overrides each creator's interval when set) —
   lives in the header row next to Refresh now; sized to sit level with the
   buttons (same height / radius family) so the row reads as one control group. */
.wlg-interval-wrap { display: inline-flex; align-items: center; gap: 6px;
  font-size: 13px; color: var(--text-muted); white-space: nowrap; }
/* Same chrome as `.field select` (custom chevron, padding, radius) so it doesn't
   look crooked next to the styled selects elsewhere — just auto-width for the row. */
.wlg-interval { font-family: inherit; font-size: 13px; padding: 8px 30px 8px 12px;
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg);
  color: var(--text); cursor: pointer; appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%236b7280' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><path d='M3 5l3 3 3-3'/></svg>");
  background-repeat: no-repeat; background-position: right 10px center; background-size: 12px; }

/* Group header: inline-editable name + Notion-style colour popover */
/* The name is the SAME control as the Topics title, one-to-one — it reuses
   .topic-head-name / .topic-head-name-edit verbatim (font, padding, border,
   grey editing box, hover, keyboard focus halo) AND the same click-to-swap
   mechanism (the <h1> hides, the full-width <input> replaces it). The name
   area is the flex-grower of the header row, so the edit field stretches to
   full width exactly like on Topics; the action buttons sit past it and don't
   move when editing (the swapped <h1>/<input> share one box height). */
.wlg-namewrap { flex: 1 1 auto; min-width: 0; }
/* display:block (not the inherited inline-block) so the <h1> adds no inline
   line-box strut — otherwise the resting header row is ~5px taller than the
   editing one and the whole page jumps up on rename. */
.wlg-namewrap .wlg-title { display: block; margin: 0; max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wlg-namewrap .wlg-title-edit { display: block; margin: 0; }
.wlg-colorpick { position: relative; display: inline-flex; }
.wlg-swbtn { display: inline-flex; align-items: center; gap: 4px; border: 1px solid var(--border);
  border-radius: 8px; padding: 4px 6px; background: var(--bg); cursor: pointer; }
.wlg-swbtn:hover { background: var(--bg-hover); }
.wlg-swdot { width: 16px; height: 16px; border-radius: 5px; display: block; box-shadow: inset 0 0 0 1px rgba(15,23,42,.12); }
.wlg-swchev { color: var(--text-soft); font-size: 9px; }
.wlg-colorpop { position: absolute; top: calc(100% + 7px); left: 0; z-index: 20; width: 224px;
  background: var(--bg); border: 1px solid var(--border); border-radius: 10px;
  box-shadow: 0 8px 28px rgba(15,23,42,.16); padding: 11px; }
.wlg-colorpop[hidden] { display: none; }
.wlg-colorpop-t { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  color: var(--text-soft); margin: 0 2px 9px; }
.wlg-swgrid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }
.wlg-sw { width: 32px; height: 32px; border-radius: 6px; border: 1px solid rgba(15,23,42,.1); cursor: pointer; padding: 0; transition: transform .1s; }
.wlg-sw:hover { transform: scale(1.06); }
.wlg-sw.is-active { box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent); }

/* Group digest strips (overperformers / posted / picking-up) */
.wlg-sec { margin-bottom: 26px; }
.wlg-sec[hidden] { display: none; }
.wlg-sechead { display: flex; align-items: center; gap: 9px; margin: 0 2px 11px; }
.wlg-seclabel { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .09em; color: var(--text-muted); }
.wlg-seccount { font-size: 11px; font-weight: 700; color: var(--text-soft); background: var(--bg-hover); border-radius: 999px; padding: 2px 8px; }
.wlg-secsub { font-size: 12px; color: var(--text-soft); font-weight: 500; }
.wlg-railshead { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .09em;
  color: var(--text-muted); margin: 30px 2px 6px; }
.wlg-topstrip { margin-bottom: 26px; }
.wlg-striphead { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
.wlg-striplabel { font-size: 11px; font-weight: 700; letter-spacing: 0.05em; color: var(--text-soft); }
.wl-seg { display: inline-flex; background: var(--bg-hover); border-radius: 8px; padding: 2px; gap: 2px; }
.wl-seg[hidden] { display: none; }
.wl-seg-btn { border: none; background: none; padding: 4px 12px; border-radius: 6px; font: inherit; font-size: 12px; font-weight: 500; color: var(--text-muted); cursor: pointer; }
.wl-seg-btn.is-active { background: var(--bg); color: var(--text); font-weight: 600; box-shadow: var(--shadow-sm); }
.wlg-topcards { display: flex; gap: 12px; overflow-x: auto; min-height: 208px; align-items: flex-start; padding-bottom: 4px; }
.wlg-topcard { flex: none; width: 112px; text-decoration: none; color: inherit; }
.wlg-coverwrap { position: relative; display: block; width: 100%; aspect-ratio: 9/16; border-radius: 8px; overflow: hidden; background: var(--bg-hover); }
.wlg-cover { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.wlg-cover.thumb-placeholder { background: var(--bg-hover); }
.wlg-dur { position: absolute; right: 5px; bottom: 5px; background: rgba(17,24,39,.65); color: #fff; font-size: 10px; padding: 0 5px; border-radius: 4px; }
.wlg-topmeta { display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--text-muted); margin-top: 6px; flex-wrap: wrap; }
.wlg-topmeta b { color: var(--text); }
.wlg-delta { color: var(--success); font-weight: 600; }
.wlg-topauthor { width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wlg-rail { border-top: 1px solid var(--border); padding: 16px 0 14px; }
.wlg-railhead { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.wlg-railname { font-weight: 600; font-size: 13.5px; color: inherit; text-decoration: none; }
.wlg-railname:hover { color: var(--accent); }
.wlg-railmeta { color: var(--text-muted); font-size: 12px; }
/* One non-scrolling row of the creator's newest clips — show what fits, no
   horizontal scrollbar (the count is capped in JS to fit typical widths). */
.wlg-railcards { display: flex; gap: 10px; overflow: hidden; min-height: 172px; align-items: flex-start; padding-bottom: 4px; }
.wlg-railcard { flex: none; width: 96px; text-decoration: none; color: inherit; }
.wlg-railviews { display: block; font-size: 11.5px; font-weight: 600; color: var(--text); margin-top: 5px; }

/* Top 100 page */
.top-toolbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }
.top-spacer { flex: 1; }
.top-tabs { display: flex; gap: 22px; border-bottom: 1px solid var(--border); }
.top-tab { display: inline-flex; align-items: center; gap: 7px; border: none; background: none; padding: 9px 2px; font: inherit; font-size: 13.5px; font-weight: 600; color: var(--text-muted); border-bottom: 2px solid transparent; cursor: pointer; }
.top-tab.is-active { color: var(--accent); border-bottom-color: var(--accent); }
.top-tab svg { width: 15px; height: 15px; }
.top-desc { color: var(--text-muted); font-size: 12.5px; margin: 12px 0 14px; min-height: 18px; }
table.top-table { width: 100%; border-collapse: collapse; font-size: 13px; }
table.top-table th { font-size: 11px; font-weight: 700; letter-spacing: 0.05em; color: var(--text-soft); text-align: left; padding: 12px 14px; border-bottom: 1px solid var(--border); white-space: nowrap; }
table.top-table td { padding: 12px 14px; border-bottom: 1px solid var(--border); vertical-align: middle; }
/* Breathing room off the rounded .vt-scroll edge — the rank number (centered in
   its column) and the last column shouldn't hug the container border. Symmetric
   padding on the rank column so the centering isn't skewed. */
table.top-table th:first-child, table.top-table td:first-child { padding-left: 12px; padding-right: 12px; }
table.top-table th:last-child, table.top-table td:last-child { padding-right: 20px; }
table.top-table tr:hover td { background: var(--bg-muted); }
.top-rank { color: var(--text-soft); font-weight: 600; font-size: 12px; width: 44px; text-align: center; }
/* Pull the VIDEO cell flush against the rank column so the rank number reads as
   centred in the gutter from the table border to the preview (no stray padding
   between them) — applies to Top 100 + group digest tables. */
.top-table td:nth-child(2), .top-table th:nth-child(2) { padding-left: 0; }
.top-vid { display: flex; align-items: center; gap: 10px; min-width: 0; text-decoration: none; color: inherit; }
.top-coverwrap { position: relative; flex: none; width: 34px; height: 60px; border-radius: 6px; overflow: hidden; background: var(--bg-hover); }
.top-cover { width: 100%; height: 100%; object-fit: cover; }
.top-cap { min-width: 0; }
.top-cap b { display: block; font-weight: 600; max-width: 360px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* In the full-width group digest, let captions fill the wide column and wrap
   (show in full when the window is wide) instead of the fixed 360px ellipsis —
   matches the Topics video table. */
.wlg-sec .top-cap b { max-width: none; white-space: normal; }
.top-cap span { font-size: 11.5px; color: var(--text-muted); }
.top-herocell { white-space: nowrap; }
.top-hero { display: block; font-size: 15px; font-weight: 700; font-variant-numeric: tabular-nums; }
.top-hero.is-up { color: var(--success); }
.top-hero-sub { display: block; font-size: 11px; color: var(--text-soft); }
/* Overperformer multiple in a grid caption — the signal, so make it pop
   (colour + weight + a touch larger), matching the .top-hero.is-up list treatment. */
.vt-op { color: var(--success); font-weight: 800; font-size: 1.15em; }
.top-actions { text-align: right; white-space: nowrap; }
.chip-downloaded { display: inline-block; padding: 2px 9px; border-radius: 999px; font-size: 11px; font-weight: 600; background: #ecfdf5; color: var(--success); }
.top-selbar { position: sticky; bottom: 12px; display: flex; align-items: center; gap: 12px; background: #111827; color: #fff; border-radius: 12px; padding: 10px 16px; margin-top: 14px; box-shadow: 0 8px 24px rgba(17, 24, 39, 0.25); font-size: 13px; }
.top-selbar[hidden] { display: none; }

/* Top 100 list — same preview (standard .vt-preview, hover-play) and the same
   always-on Download button as the video tables. No bespoke sizing. */
.top-vid { gap: 12px; }
.top-vid .vt-preview { flex: none; }
.top-cap { text-decoration: none; color: inherit; }

/* Top 100 checkbox column — second-to-last, right before Download. */
.top-check-cell { width: 34px; text-align: center; }

/* Top 100 grid view — the SAME cell as the watchlist grid (.vt-grid /
   .vt-grid-cell / .vt-grid-thumb / .vt-grid-cap). The only addition is the
   Download button, revealed on hover over the media wrapper. */
.top-grid-media { position: relative; }
.top-grid-dl { position: absolute; left: 8px; right: 8px; bottom: 8px; z-index: 4; justify-content: center; font-weight: 700; opacity: 0; transition: opacity 0.12s ease; }
.top-grid-cell:hover .top-grid-dl,
.top-grid-dl:focus-visible { opacity: 1; }
/* The active/done pills span the SAME left:8→right:8 box as .top-grid-dl so the
   control never resizes across Download → Downloading… → ✓ Downloaded (no layout
   shift) — see docs/ui-conventions.md. */
.top-grid-done { position: absolute; left: 8px; right: 8px; bottom: 8px; z-index: 4;
  display: flex; align-items: center; justify-content: center; text-align: center; }

.chip { display: inline-block; padding: 2px 9px; border-radius: 999px; font-size: 11px; font-weight: 600; }
.chip-tt { background: #111827; color: #fff; }
.chip-ig { background: linear-gradient(45deg, #fa7e1e, #d62976, #962fbf); color: #fff; }
.chip-soft { background: var(--accent-soft); color: var(--accent); }
.chip-ok { background: #ecfdf5; color: var(--success); }
.chip-mut { background: var(--bg-hover); color: var(--text-muted); }
.chip-warn { background: #fffbeb; color: var(--warning); }
.top-selbar .btn { border-color: transparent; }

.badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border-radius: 999px;
  background: var(--bg-hover);
  color: var(--text-muted);
}

/* Deployment label next to the brand ("BETA" on the V3 beta) — the brand gradient. */
.badge-channel {
  background: linear-gradient(135deg, #ff0090 0%, #ff4b3c 50%, #ff8200 100%);
  color: #fff;
  margin-left: 6px;
  vertical-align: 2px;
}

.field-hint {
  font-size: 11.5px;
  color: var(--text-soft);
}

/* Link-analysis alert — a clear RED box for AI-unavailable / key-not-set
   (only paints when it actually holds a message). */
.la-alert {
  margin-top: 8px;
  font-size: 12.5px;
  color: var(--danger);
}
.la-alert:not(:empty) {
  padding: 8px 11px;
  background: rgba(220, 38, 38, 0.08);
  border: 1px solid rgba(220, 38, 38, 0.25);
  border-radius: var(--radius-sm);
}

/* "No Mac linked" banner — amber (the warning role, #d97706). */
.mac-banner {
  margin: 0 0 16px;
  padding: 12px 16px;
  border: 1px solid var(--app-nudge-border);
  background: var(--app-nudge-bg);
  border-radius: var(--radius);
  font-size: 0.9rem;
}

.field select {
  width: 100%;
  padding: 9px 32px 9px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: 13.5px;
  font-family: inherit;
  background: var(--bg);
  color: var(--text);
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%236b7280' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><path d='M3 5l3 3 3-3'/></svg>");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 12px;
}

/* Same chrome (chevron, padding, etc.) as `.field select` but the
   downloads form is a flex row, not a vertical field, so width must
   stay `auto` — picking it up from the generic rule was outcompeting
   the URL input's `flex: 1` and squashing the input to nothing. */
.downloads-form select {
  width: auto;
  flex: 0 0 auto;
  min-width: 170px;
  padding: 9px 32px 9px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: 13.5px;
  font-family: inherit;
  background: var(--bg);
  color: var(--text);
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%236b7280' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><path d='M3 5l3 3 3-3'/></svg>");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 12px;
}

.field select:focus {
  outline: none;
}

body.input-keyboard .field select:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.form-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 14px;
}

/* Topics list page */

.topics-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 14px;
}

.topic-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 16px 18px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  text-decoration: none;
  color: inherit;
  transition: border-color 0.1s ease, box-shadow 0.1s ease;
}

.topic-card:hover {
  border-color: var(--accent);
  box-shadow: var(--shadow-sm);
}

.topic-card-head {
  display: flex;
  gap: 6px;
  align-items: center;
}

.topic-card-status {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border-radius: 999px;
  background: var(--bg-hover);
  color: var(--text-muted);
}

.topic-card-status.status-active {
  background: rgba(22, 163, 74, 0.12);
  color: #15803d;
}

.topic-card-status.status-paused {
  background: #fef3c7;
  color: #92400e;
}


.topic-card-name {
  font-size: 15px;
  font-weight: 600;
  color: var(--text);
}

.topic-card-query {
  font-size: 12.5px;
  color: var(--text-muted);
}

.topic-card-meta {
  font-size: 11.5px;
  color: var(--text-soft);
  margin-top: 2px;
}

/* Create-tile: always the first card in the topics index grid.
   Dashed outline + centered '+' so it reads as a positive CTA
   rather than placeholder noise. */
.topic-card-create {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 24px 18px;
  border: 1px dashed var(--border-strong);
  background: var(--bg-muted);
  color: var(--text-muted);
  text-align: center;
  min-height: 144px;
  transition: border-color 0.1s ease, color 0.1s ease, background-color 0.1s ease;
}

.topic-card-create:hover {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--accent);
  box-shadow: var(--shadow-sm);
}

.topic-card-create-plus {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--bg);
  border: 1px solid var(--border);
  color: var(--text-muted);
  margin-bottom: 2px;
  transition: background-color 0.1s ease, color 0.1s ease, border-color 0.1s ease;
}

.topic-card-create-plus svg {
  width: 18px;
  height: 18px;
}

.topic-card-create:hover .topic-card-create-plus {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

.topic-card-create-label {
  font-size: 14px;
  font-weight: 600;
}

.topic-card-create-sub {
  font-size: 11.5px;
  color: var(--text-soft);
}

.topic-card-create:hover .topic-card-create-sub {
  color: var(--accent);
  opacity: 0.7;
}

/* Topic detail page */

.topic-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 24px;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--border);
}

.topic-head-main {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

.topic-head-meta {
  display: flex;
  gap: 6px;
  align-items: center;
}

/* Inline-editable text fields (topic name + keyword query).
   The display element and the edit input share the *exact* box model
   (same padding + same 1px border) so swapping them in place doesn't
   shift anything below by even a pixel. The display border is
   transparent; the input border is the neutral --border. Accent halo
   is only shown to keyboard users (input-keyboard body class) because
   for mouse users the click itself already says "I'm editing this". */

.topic-head-name,
.topic-head-name-edit {
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -0.01em;
  font-family: inherit;
  line-height: 1.25;
  margin: 2px 0 0;
  margin-left: -5px;
  padding: 2px 4px;
  border-radius: 6px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--text);
}

.topic-head-name {
  display: inline-block;
  cursor: text;
  transition: background-color 0.1s ease;
}

/* The [hidden] attribute would normally apply `display: none` via UA
   styles, but a class-level `display: inline-block` here has higher
   specificity and was leaving both the <h1> and the edit <input>
   visible at the same time. Re-assert hidden explicitly. */
.topic-head-name[hidden],
.topic-head-name-edit[hidden],
.topic-head-query[hidden],
.topic-head-query-edit[hidden] {
  display: none;
}

.topic-head-name:hover {
  background: var(--bg-muted);
}

.topic-head-name-edit {
  background: var(--bg-muted);
  border-color: var(--border);
  outline: none;
  width: 100%;
  max-width: 540px;
}

body.input-keyboard .topic-head-name-edit:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.18);
}

/* Inline keyword query — same UX as name, smaller font / muted colour
   because it's secondary to the topic title. */

.topic-rules-query {
  display: flex;
  align-items: center;
  gap: 8px;
}

.topic-head-query,
.topic-head-query-edit {
  font-size: 14px;
  font-weight: 500;
  font-family: inherit;
  line-height: 1.25;
  padding: 3px 8px;
  margin-left: -8px;
  border-radius: 6px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--text);
}

.topic-head-query {
  display: inline-block;
  cursor: text;
  transition: background-color 0.1s ease;
}

.topic-head-query:hover {
  background: var(--bg-muted);
}

.topic-head-query-edit {
  background: var(--bg-muted);
  border-color: var(--border);
  outline: none;
  width: 100%;
  max-width: 420px;
}

body.input-keyboard .topic-head-query-edit:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.18);
}

.topic-rules-hint {
  font-size: 11.5px;
  color: var(--text-muted);
  margin-top: 2px;
}

/* Sound topic: the two reference videos (searched / original) as small
   preview cards under the sound — a thumbnail makes the one you
   searched by instantly recognisable. Not rows in the results table. */
/* Sound header: title + sound id on one line (id muted, to the side). */
.sound-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px 14px;
}

.sound-id {
  font-size: 11.5px;
  color: var(--text-soft);
  font-variant-numeric: tabular-nums;
}

.sound-cards {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 10px;
}

.sound-card {
  display: flex;
  align-items: stretch;
  gap: 12px;
  /* Content-sized, not stretched — two compact cards sit side by side
     instead of each ballooning to fill the row. */
  flex: 0 0 auto;
  width: 340px;
  max-width: 100%;
  padding: 10px;
  padding-right: 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
  text-decoration: none;
  transition: border-color 0.1s ease, box-shadow 0.1s ease;
}

.sound-card:hover {
  border-color: var(--accent);
  box-shadow: var(--shadow-sm);
}

.sound-card-thumb {
  position: relative;
  width: 50px;
  aspect-ratio: 9 / 16;
  flex-shrink: 0;
  border-radius: 6px;
  overflow: hidden;
  background: #0f172a;
}

.sound-card-thumb .vt-thumb {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.sound-card-body {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  min-width: 0;
}

.sound-card-tag {
  font-size: 9.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-soft);
}

.sound-card.is-original .sound-card-tag {
  color: var(--accent);
}

.sound-card-who {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sound-card-when {
  font-size: 11px;
  color: var(--text-muted);
}

/* Compact metrics row on the Searched / Original preview cards —
   reuses .vt-growth / .vt-status colours but shrinks them to fit. */
.sound-card-stats {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-top: 3px;
}

.sound-card-stats .sc-stat {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-muted);
  white-space: nowrap;
}

.sound-card-stats .vt-growth,
.sound-card-stats .vt-status {
  padding: 1px 7px;
  font-size: 10.5px;
  font-weight: 700;
  gap: 3px;
  border-radius: 999px;
}

/* Rules panel: networks + search-rule chips with inline add input. */

.topic-rules {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin-top: 10px;
}

/* A scrape is in flight: the search rules are frozen (the running job already
   read them — changing them mid-run breaks the batch). Only opacity changes,
   so nothing on the page moves when the lock comes and goes. */
.topic-rules.is-locked {
  opacity: 0.55;
  pointer-events: none;
}

.topic-rules-section {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.topic-rules-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  font-weight: 600;
}

.topic-rules-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
}

.topic-rules-add {
  display: flex;
  gap: 8px;
  margin-top: 4px;
  max-width: 420px;
}

.topic-rules-add-input {
  flex: 1;
  padding: 7px 12px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 13px;
  font-family: inherit;
  outline: none;
}

.topic-rules-add-input:focus {
  border-color: var(--accent);
}

/* Rule chips. Same Primary-signal metrics as Status/Download pills
   (12.5 px, weight 700, padding 5×13, 1px border) so they sit visually
   on the same "decision-relevant state" tier. The × button is a hover
   affordance; clicking it removes the rule via the API. */

.rule-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 13px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.2;
  white-space: nowrap;
}

/* × button: flex-centered so the icon sits on the same baseline as the
   chip text regardless of font metrics. SVG is 12 px square. Pushed
   a touch to the right within the chip (negative right margin pulls it
   closer to the chip's right edge; small left margin keeps it visually
   separated from the tag text). */
.rule-chip-x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  margin-left: 2px;
  margin-right: -7px;
  border: none;
  background: transparent;
  color: var(--text-soft);
  cursor: pointer;
  padding: 0;
  border-radius: 999px;
}

.rule-chip-x svg {
  width: 10px;
  height: 10px;
  display: block;
}

.rule-chip-x:hover {
  color: var(--danger);
  background: rgba(220, 38, 38, 0.08);
}

/* + add affordance: small accent-filled circle that occupies the same
   slot as the inline-add input after click. Sized to align vertically
   with the hashtag chips (chip height = 1.2*12.5 + 5+5 + 1+1 = ~27 px). */
.rule-chip-add {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 27px;
  height: 27px;
  border-radius: 999px;
  border: none;
  background: var(--accent);
  color: #fff;
  cursor: pointer;
  padding: 0;
  transition: background-color 0.1s ease, transform 0.05s ease;
}

.rule-chip-add:hover {
  background: #1d4ed8;
}

.rule-chip-add:active {
  transform: scale(0.94);
}

.rule-chip-add svg {
  width: 14px;
  height: 14px;
  display: block;
}

.rule-chip-add-input {
  height: 27px;
  padding: 0 12px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg-muted);
  color: var(--text);
  font-size: 12.5px;
  font-weight: 600;
  font-family: inherit;
  line-height: 1.2;
  outline: none;
  min-width: 140px;
}

body.input-keyboard .rule-chip-add-input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.18);
}

/* Brand network chips on topic detail + creation form.
   Active chip uses the saturated brand color (TikTok #111, Instagram
   gradient, YouTube #ff0000). Locked / unselected chips are fully
   neutral — no brand color at all, just a faint grey outline — so
   they read as "available, not picked" rather than "soft-tinted
   version of the real thing". The user adds color back by selecting. */

.platform-badge-selected {
  /* brand colors come from .platform-tiktok / -youtube / -instagram */
}

/* Locked (coming later, e.g. YouTube): neutral + non-interactive. */
.platform-badge-locked {
  background: transparent !important;
  color: var(--text-soft) !important;
  border: 1px solid var(--border);
  cursor: not-allowed;
}
.platform-badge-locked svg {
  opacity: 0.4;
}

/* Available but not selected: clearly clickable — brand-tinted outline +
   brand icon, readable text, pointer and a subtle hover lift. Distinct
   from the greyed-out locked chip. */
.platform-badge-muted:not(.platform-badge-locked) {
  background: transparent !important;
  color: var(--text) !important;
  border: 1px solid var(--border-strong);
  cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease, transform 0.12s ease;
}
.platform-badge-muted:not(.platform-badge-locked):hover {
  background: var(--bg-hover) !important;
  transform: translateY(-1px);
}
/* Brand-colored icon + border per network so the choice reads at a glance. */
.platform-badge-muted:not(.platform-badge-locked).platform-tiktok {
  border-color: #111;
}
.platform-badge-muted:not(.platform-badge-locked).platform-tiktok svg {
  color: #111;
}
.platform-badge-muted:not(.platform-badge-locked).platform-instagram {
  border-color: #cc2366;
}
.platform-badge-muted:not(.platform-badge-locked).platform-instagram svg {
  color: #cc2366;
}

.platform-soon {
  font-size: 9.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-left: 4px;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--bg-hover);
  color: var(--text-soft);
}

/* Network chips on the topic page (and creation form) are larger and
   less uppercase-y than the preview-overlay variant from downloads —
   they're in the main content flow, not overlaid on a thumbnail. */
.topic-rules-chips .platform-badge {
  font-size: 12px;
  padding: 6px 12px 6px 10px;
  gap: 6px;
  letter-spacing: 0.02em;
}

.topic-rules-chips .platform-badge svg {
  width: 14px;
  height: 14px;
}

.topic-head-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}

.topic-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  gap: 14px;
  margin-top: 18px;
}

.stat {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 14px;
  background: var(--bg);
}

.stat-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  font-weight: 600;
}

.stat-value {
  font-size: 14.5px;
  font-weight: 600;
  margin-top: 2px;
  color: var(--text);
}

.stat-input {
  margin-top: 2px;
  width: 100%;
  padding: 4px 18px 4px 6px;
  border: 1px solid transparent;
  background: transparent;
  border-radius: 4px;
  font-size: 14.5px;
  font-weight: 600;
  font-family: inherit;
  color: var(--text);
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%236b7280' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><path d='M3 5l3 3 3-3'/></svg>");
  background-repeat: no-repeat;
  background-position: right 4px center;
  background-size: 10px;
}

.stat-input:hover {
  border-color: var(--border);
  background-color: var(--bg);
}

/* Scrape state under the topic head */

.scrape-state {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 12px 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
}

.scrape-state.is-running {
  background: var(--accent-soft);
  border-color: var(--accent);
}

.scrape-state.is-done {
  background: #f0fdf4;
  border-color: #86efac;
}

.scrape-state.is-partial {
  background: #fef3c7;
  border-color: #fcd34d;
}

.scrape-state.is-failed {
  background: #fef2f2;
  border-color: #fecaca;
}

.scrape-state-head {
  display: flex;
  align-items: center;
  gap: 8px;
}

.scrape-state.has-detail .scrape-state-head {
  cursor: pointer;
}

.scrape-chevron {
  display: inline-flex;
  flex: none;
  color: var(--text-soft);
  transition: transform 0.15s ease;
}

.scrape-state.is-open .scrape-chevron {
  transform: rotate(90deg);
}

.scrape-state-detail {
  display: none;
  flex-direction: column;
  gap: 6px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}

.scrape-state.is-open .scrape-state-detail {
  display: flex;
}

.scrape-state-summary {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.01em;
  text-transform: capitalize;
  color: var(--text);
}

.scrape-state-count {
  font-size: 11.5px;
  color: var(--text-soft);
  font-weight: 500;
  margin-left: auto;
  white-space: nowrap;
}

.scrape-state-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.scrape-run-direction {
  display: inline-flex;
  align-items: center;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--text);
  min-width: 80px;
  padding: 2px 0;
}

.scrape-state-runs {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  flex: 1;
}

.scrape-run {
  display: inline-flex;
  align-items: center;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 11px;
  background: var(--bg);
  border: 1px solid var(--border);
  color: var(--text-muted);
}

.scrape-run-running {
  border-color: var(--accent);
  color: var(--accent);
}

.scrape-run-done {
  border-color: #86efac;
  color: #166534;
}

.scrape-run-failed {
  border-color: #fecaca;
  color: var(--danger);
}

.scrape-run-skipped {
  border-style: dashed;
}

/* Transient Apify hiccup (network/DNS/5xx) — already retried once and
   self-heals next scrape. Soft amber, not the alarming red of a real
   failure. */
.scrape-run-temporary {
  border-color: #fcd9a8;
  color: var(--warning);
}

.scrape-state-err {
  font-size: 12px;
  color: var(--danger);
}

/* Topic videos table (Creative-Center style) */

/* Tabs */

.vt-tabs {
  display: flex;
  gap: 2px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 14px;
}

.vt-tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 9px 14px;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text-muted);
  cursor: pointer;
  font-family: inherit;
}

.vt-tab:hover:not(:disabled) {
  color: var(--text);
}

.vt-tab.is-active {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

.vt-tab:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.vt-tab-count {
  font-size: 11px;
  font-weight: 600;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--bg-hover);
  color: var(--text-muted);
}

.vt-tab.is-active .vt-tab-count {
  background: var(--accent-soft);
  color: var(--accent);
}

.vt-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 12px;
  flex-wrap: wrap;
}

.vt-counts {
  font-size: 12.5px;
  color: var(--text-muted);
}

.vt-counts strong {
  color: var(--text);
  font-weight: 600;
}

.vt-toolbar-hint {
  font-size: 11.5px;
  color: var(--text-soft);
}

.vt-scroll {
  /* The single horizontal scroller for the videos table. max-width
     pins it to the content pane so a 1500px table scrolls *inside*
     this card instead of widening the page. */
  overflow-x: auto;
  overflow-y: hidden;
  max-width: 100%;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
}

.vt-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  /* Fits the consolidated ~10-column table on a normal screen without the
     horizontal scroll clipping the right columns (was 1500px for the old
     14-column layout, which pushed Age/Actions off-pane). */
  min-width: 1180px;
  table-layout: fixed;
}

.vt-table thead th {
  background: var(--bg-muted);
  text-align: left;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  /* Above the pinned body columns (z-index 2) so the header row
     stays on top when scrolling vertically; corners override to 4. */
  z-index: 3;
  white-space: nowrap;
}

.vt-sortable {
  cursor: pointer;
  user-select: none;
  transition: color 0.1s ease;
}

.vt-sortable:hover {
  color: var(--text);
}

.vt-sortable.is-active {
  color: var(--accent);
}

.vt-th-label {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.vt-num .vt-th-label {
  justify-content: flex-end;
}

.vt-arrow {
  display: inline-flex;
  align-items: center;
  width: 10px;
  height: 10px;
  opacity: 0.35;
  transition: opacity 0.1s ease;
}

.vt-sortable:hover .vt-arrow {
  opacity: 0.7;
}

.vt-arrow.is-active {
  opacity: 1;
  color: var(--accent);
}

.vt-sort-arrow {
  width: 10px;
  height: 10px;
  flex-shrink: 0;
}

.vt-table th.vt-num,
.vt-table td.vt-num {
  text-align: right;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.vt-table tbody tr {
  border-bottom: 1px solid var(--border);
  transition: background-color 0.08s ease;
}

.vt-table tbody tr:last-child {
  border-bottom: none;
}

.vt-table tbody tr:hover {
  background: var(--bg-muted);
}

.vt-table td {
  padding: 12px 14px;
  vertical-align: middle;
  color: var(--text);
}

.vt-table td strong {
  font-weight: 700;
  font-size: 14px;
  color: var(--text);
}

/* Preview cell */

.vt-col-preview {
  width: 168px;
}

.vt-preview {
  position: relative;
  display: block;
  width: 144px;
  height: 192px;
  border-radius: 10px;
  overflow: hidden;
  background: #0f172a;
  text-decoration: none;
}

.vt-thumb {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Media-kind badge (image / carousel) — top-left corner of the preview.
   Plain videos render no badge, so this only appears on IG photos and
   carousels. Sits above the cover/video layers. */
.vt-media-badge {
  position: absolute;
  top: 6px;
  left: 6px;
  z-index: 3;
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 6px;
  border-radius: 999px;
  background: rgba(15, 23, 42, 0.78);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
  pointer-events: none;
  backdrop-filter: blur(2px);
}
.vt-media-badge svg {
  width: 12px;
  height: 12px;
}

/* Paid-partnership pill next to the author (IG, from apidojo). */
.vt-paid-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: 999px;
  background: rgba(234, 179, 8, 0.16);
  color: #a16207;
  border: 1px solid rgba(234, 179, 8, 0.4);
  font-size: 11px;
  font-weight: 600;
  vertical-align: middle;
}

.vt-thumb-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.18s ease;
  /* Transparent, not solid dark: if it's ever painted before a frame
     exists it must not blot out the cover underneath. */
  background: transparent;
}

/* Reveal the video only when it actually has footage (`vt-has-frame`,
   set by JS on loadeddata/playing). Hovering alone no longer flips the
   cell to an empty dark <video>; the cover stays, dimmed with the
   spinner, until the real frame fades in over it. */
.vt-preview.has-video.vt-has-frame:hover .vt-thumb-video {
  opacity: 1;
}

/* Loading overlay: while the CDN-direct mp4 is still buffering after
   the user hovers, dim the thumbnail and show a spinner so the cell
   doesn't look frozen. Cleared on `playing` or on play() error. */
.vt-preview.is-loading::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(15, 23, 42, 0.45);
  z-index: 2;
}

.vt-preview.is-loading::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 28px;
  height: 28px;
  margin: -14px 0 0 -14px;
  border: 3px solid rgba(255, 255, 255, 0.35);
  border-top-color: #fff;
  border-radius: 50%;
  animation: vt-spin 0.8s linear infinite;
  z-index: 3;
}

@keyframes vt-spin {
  to { transform: rotate(360deg); }
}

/* ===== Account-search results (Track-a-creator by keyword) ===== */
.wl-sr {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 9px 10px;
  border-top: 1px solid var(--border);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background-color 0.1s ease;
}
.wl-sr:first-child { border-top: none; }
/* Whole row is clickable (opens the IG profile) — hover like a sidebar item. */
.wl-sr:hover { background: var(--bg-hover); }
.wl-sr-av {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  flex: 0 0 auto;
  background: var(--bg-hover);
  overflow: hidden;
  display: inline-flex;
}
.wl-sr-av img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.wl-sr-av-ph { display: inline-flex; }
.wl-sr-body { flex: 1 1 auto; min-width: 0; }
.wl-sr-handle { font-size: 13.5px; font-weight: 600; }
.wl-sr-handle a { color: var(--text); text-decoration: none; }
.wl-sr-handle a:hover { color: var(--accent); }
.wl-sr-verified {
  margin-left: 5px;
  color: var(--accent);
  font-size: 12px;
  font-weight: 700;
}
.wl-sr-sub {
  font-size: 12px;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Instagram account finder — identical to a standard .card in every way
   EXCEPT the border, which is Instagram's brand gradient instead of the grey
   1px. That ring is the ONLY distinctive touch; everything inside uses the
   normal design system (field label, inputs, .btn-primary, .wl-sr rows). */
.ig-finder {
  max-width: 620px;
  margin-top: 16px;
  padding: 22px 24px;
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  /* Gradient border: solid card on padding-box, IG gradient on border-box. */
  border: 2px solid transparent;
  background:
    linear-gradient(var(--bg), var(--bg)) padding-box,
    var(--ig-gradient) border-box;
}
/* The finder's search field fills the row — Settings' .cost-limit-row keeps
   its compact 120px because this is scoped to .ig-finder. */
.ig-finder .cost-limit-row input {
  width: auto;
  flex: 1 1 auto;
  min-width: 0;
}

/* ===== Associated tab — collapsible advertiser-network cards =====
   Each linked account is a card: header (caret · name · why · Track) + body.
   Expanded → the full ads table; collapsed → a tight grid of small previews. */
.assoc-group {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 8px 12px 12px;
  margin-bottom: 12px;
  background: var(--bg);
}
.assoc-group.is-collapsed {
  background: var(--bg-muted);
  padding-bottom: 8px;
}
/* Full collapse: header only, body hidden entirely. Drop the head's
   bottom margin so the single row sits centered between the 8px top/bottom
   padding (otherwise it's 8px above vs 16px below — looks top-heavy). */
.assoc-group.is-collapsed .assoc-body {
  display: none;
}
.assoc-group.is-collapsed .assoc-head {
  margin-bottom: 0;
}
.assoc-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
}
.assoc-toggle {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--text-soft);
  cursor: pointer;
  font-size: 11px;
  line-height: 22px;
  text-align: center;
  border-radius: var(--radius-sm);
}
.assoc-toggle:hover {
  background: var(--bg-hover);
  color: var(--text);
}
.assoc-head-main {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
}
.assoc-head .vt-author {
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
}
.assoc-why {
  font-size: 12px;
  color: var(--text-muted);
}
.assoc-empty {
  font-size: 12px;
  color: var(--text-soft);
  padding: 2px 0 4px;
}

/* ===== Compact grid view (Videos / Ads / Associated) ===== */
/* List ⇄ grid toggle in the toolbar (right side, beside the count/actions). */
.vt-toolbar-right {
  display: flex;
  align-items: center;
  gap: 12px;
}
.vt-view-toggle {
  display: inline-flex;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
  flex: 0 0 auto;
}
.vt-view-btn {
  width: 30px;
  height: 26px;
  padding: 0;
  border: none;
  background: var(--bg);
  color: var(--text-soft);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.vt-view-btn + .vt-view-btn {
  border-left: 1px solid var(--border);
}
.vt-view-btn:hover {
  background: var(--bg-hover);
  color: var(--text);
}
.vt-view-btn.is-active {
  background: var(--accent-soft);
  color: var(--accent);
}
.vt-view-btn svg {
  width: 15px;
  height: 15px;
}

/* The preview wall: each cell is a 9:16 thumb + one caption line. */
.vt-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(124px, 1fr));
  gap: 14px 12px;
}
.vt-grid-cell {
  min-width: 0;
}
.vt-grid-thumb {
  width: 100%;
  height: auto;
  aspect-ratio: 9 / 16;
  border-radius: var(--radius-sm);
}
.vt-grid-cap {
  margin-top: 5px;
  font-size: 12px;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Author + caption column */

.vt-caption-col {
  max-width: 360px;
  min-width: 220px;
}

.vt-author-row {
  margin-bottom: 2px;
}

.vt-author {
  font-size: 12.5px;
  color: var(--text-muted);
  font-weight: 500;
  text-decoration: none;
}

.vt-author:hover {
  color: var(--accent);
}

.vt-author.muted {
  color: var(--text-soft);
}

.vt-title {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text);
  line-height: 1.3;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.vt-title.muted {
  color: var(--text-soft);
  font-weight: 400;
}

.vt-tags {
  font-size: 11px;
  color: var(--text-muted);
  line-height: 1.35;
  margin-top: 2px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.vt-music {
  font-size: 11px;
  color: var(--text-soft);
  margin-top: 4px;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Status pill */

/* Two pill scales across the topic table:
   - "Primary signal" (Status, Quality, Downloaded): 12.5 px, weight
     600, mixed case, ~5×13 padding. Reads as a decision-relevant
     state.
   - "Soft tag" (Match, Age, Scrapers): 11 px, weight 500, soft tint,
     ~2×9 padding. Reads as contextual metadata.
   Keep these two and only these two; new pills go into one bucket. */

.vt-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 13px;
  font-size: 12.5px;
  font-weight: 700;
  line-height: 1.2;          /* match .btn so pill height == button height */
  letter-spacing: 0;
  text-transform: none;
  border-radius: 999px;
  border: 1px solid transparent;   /* invisible — matches .btn's 1px border so heights align */
  white-space: nowrap;
}

.vt-status-icon {
  width: 12px;
  height: 12px;
  flex-shrink: 0;
}

/* Status pill palette — variant A (stock-chart / TikTok-flame).
   The pill answers two orthogonal questions: how hot is the trend
   right now, and have we even seen this video long enough to know.
   Hot/Growing carry the strongest signal (red flame, green growth),
   New is a soft yellow "look at this" while we wait for the second
   scrape to refine it, and the cool/decline states use desaturated
   complements. Pending stays quietly grey. */

.vt-status.is-pending {
  background: var(--bg-hover);
  color: var(--text-soft);
}

.vt-status.is-new {
  background: #fde68a;   /* yellow-200 */
  color: #854d0e;        /* yellow-800 */
}

.vt-status.is-stable {
  background: var(--bg-hover);
  color: var(--text);
}

.vt-status.is-growing {
  background: #16a34a;   /* green-600 */
  color: #fff;
}

.vt-status.is-hot {
  background: #dc2626;   /* red-600 */
  color: #fff;
}

.vt-status.is-cooling {
  background: #dbeafe;   /* blue-100 */
  color: #1e40af;        /* blue-800 */
}

.vt-status.is-declining {
  background: #fecaca;   /* red-200 */
  color: #991b1b;        /* red-800 */
}

.vt-status.is-failed {
  background: var(--bg);
  color: var(--text-soft);
  border: 1px dashed var(--border-strong);
}

/* Freshness pill */

.vt-fresh {
  display: inline-flex;
  align-items: center;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 500;
  background: var(--bg-hover);
  color: var(--text-muted);
  white-space: nowrap;
}

.vt-fresh.is-fresh {
  background: rgba(22, 163, 74, 0.12);
  color: #166534;
}

.vt-fresh.is-warm {
  background: #fef3c7;
  color: #92400e;
}

.vt-fresh.is-old,
.vt-fresh.is-stale {
  background: var(--bg-hover);
  color: var(--text-soft);
}

.vt-scrapers {
  display: inline-flex;
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--bg-hover);
  color: var(--text-muted);
  font-size: 11px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}

/* Scrapers badge moved inline into the author row (its column was merged away). */
.vt-scrapers-inline {
  margin-left: 6px;
  padding: 1px 7px;
  vertical-align: middle;
}

/* Merged engagement cell: likes · comments · rate, stacked compactly. */
.vt-eng-cell {
  display: flex;
  flex-direction: column;
  gap: 1px;
  font-size: 12px;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  line-height: 1.25;
}
.vt-eng-cell .vt-eng-pct {
  color: var(--text);
  font-weight: 600;
}

/* Duration overlay on the preview thumbnail (its column was merged away). */
.vt-dur-badge {
  position: absolute;
  right: 6px;
  bottom: 6px;
  z-index: 3;
  padding: 1px 6px;
  border-radius: 6px;
  background: rgba(15, 23, 42, 0.78);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  line-height: 1.3;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}

.vt-col-actions {
  /* Wide enough for the "✓ Downloaded" pill (~119px), not just the
     narrower "Download" button — at 130px the fixed-layout column
     clipped the pill against the sticky right edge. */
  width: 152px;
  text-align: right;
}

/* Pinned columns. A 14-column table is wider than most screens, so
   the Preview (left) and Actions/Download (right) columns stick to
   the edges of the .vt-scroll viewport — you can always see *which*
   video and reach Download without scrolling the table all the way
   across. Sticky cells need an opaque background or the scrolling
   columns show through; they mirror the row's normal / hover /
   expanded background. */
.vt-table th.vt-col-preview,
.vt-table td.vt-col-preview {
  position: sticky;
  left: 0;
  z-index: 2;
  background: var(--bg);
}

.vt-table th.vt-col-actions,
.vt-table td.vt-col-actions {
  position: sticky;
  right: 0;
  z-index: 2;
  background: var(--bg);
}

/* Header corners sit above both the sticky body columns and the
   sticky header row. */
.vt-table thead th.vt-col-preview,
.vt-table thead th.vt-col-actions {
  z-index: 4;
  background: var(--bg-muted);
}

/* Follow the row state so the pinned cells don't look detached. */
.vt-table tbody tr:hover .vt-col-preview,
.vt-table tbody tr:hover .vt-col-actions {
  background: var(--bg-muted);
}

.vt-row.is-expanded > td.vt-col-preview,
.vt-row.is-expanded > td.vt-col-actions {
  background: var(--bg-muted);
}

/* Edge shadows make the pinned columns read as floating above the
   scrolling middle. */
.vt-table th.vt-col-preview,
.vt-table td.vt-col-preview {
  box-shadow: 6px 0 6px -6px rgba(15, 23, 42, 0.12);
}

.vt-table th.vt-col-actions,
.vt-table td.vt-col-actions {
  box-shadow: -6px 0 6px -6px rgba(15, 23, 42, 0.12);
}

.vt-dl {
  /* RULE — a state-swapping action button reserves the width of its LONGEST
     state so it never resizes as it flips (Download → Downloading… →
     ✓ Downloaded → Retry). One fixed min-width + centered content = zero
     layout shift, whatever the current label. Applied to the button AND both
     pills in the list variant so all four states share one footprint. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  min-width: 122px;
  padding: 5px 13px;
  font-size: 12.5px;
  font-weight: 700;
  line-height: 1.2;
  white-space: nowrap;
}

/* Pill variants for Downloaded / Downloading — same Primary-signal
   metrics as Status, just rendered as <a>/<span>. */
.vt-pill {
  display: inline-flex;
  align-items: center;
  padding: 5px 13px;
  border-radius: 999px;
  border: 1px solid transparent;   /* match .btn's 1px border so height aligns with Download */
  font-size: 12.5px;
  font-weight: 700;
  line-height: 1.2;
  white-space: nowrap;
  text-decoration: none;
}

.vt-pill-done {
  background: rgba(22, 163, 74, 0.95);
  color: #fff;
  cursor: pointer;
  transition: background-color 0.1s ease;
}

.vt-pill-done:hover {
  background: rgba(22, 163, 74, 1);
}

.vt-pill-active {
  background: rgba(37, 99, 235, 0.92);
  color: #fff;
}

.vt-firstseen {
  font-size: 11.5px;
  color: var(--text-muted);
  white-space: nowrap;
}

.vt-growth {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

.vt-growth.is-positive {
  color: #15803d;
}

.vt-growth.is-negative {
  color: #b91c1c;
}

.vt-growth.is-neutral {
  color: var(--text-muted);
  font-weight: 500;
}

/* Match column — Soft-tag scale (same metrics as Age / Scrapers). */

.vt-match {
  display: inline-flex;
  align-items: center;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 500;
  white-space: nowrap;
}

.vt-match.is-strong  { background: rgba(22,163,74,0.14);  color: #166534; }
.vt-match.is-medium  { background: rgba(234,179,8,0.18);  color: #854d0e; }
.vt-match.is-weak    { background: rgba(148,163,184,0.18); color: #475569; }
.vt-match.is-empty   { color: var(--text-soft); background: transparent; }

/* Score column */

.vt-score {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  font-size: 13px;
  color: var(--text);
  display: inline-block;
  min-width: 28px;
}

.vt-score.is-empty {
  color: var(--text-soft);
  font-weight: 400;
}

/* Status pill rendered as a button for evidence-expand. */

.vt-status-button {
  /* border + padding + font inherit from .vt-status. Do NOT use `font: inherit`
     here — the shorthand resets font-size / font-weight / line-height to body
     (14px) and blows away the 12.5px / 700 / 1.2 set on .vt-status, which
     makes the pill render visibly taller than the Download button. Only override
     the bits a default <button> brings in (font-family + background reset). */
  font-family: inherit;
  background: none;
  cursor: pointer;
}

.vt-row.is-expanded > td {
  background: var(--bg-muted);
}

.vt-row.is-expanded .vt-status-button {
  box-shadow: 0 0 0 2px rgba(15, 23, 42, 0.18);
}

/* Evidence drawer row */

.vt-evidence-row td {
  padding: 0;
  border-bottom: 1px solid var(--border);
  background: var(--bg-muted);
}

.vt-evidence-list {
  list-style: none;
  margin: 0;
  padding: 12px 18px;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 8px 24px;
}

.vt-evidence-list li {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 12.5px;
  color: var(--text);
}

.vt-evidence-key {
  font-weight: 600;
  color: var(--text-muted);
  min-width: 72px;
  text-transform: uppercase;
  font-size: 11px;
  letter-spacing: 0.04em;
}

.vt-evidence-text {
  flex: 1;
}

.vt-evidence-weight {
  font-size: 11px;
  color: var(--text-muted);
  background: var(--bg);
  border: 1px solid var(--border);
  padding: 1px 7px;
  border-radius: 999px;
}

.vt-evidence-empty {
  padding: 12px 18px;
  font-size: 12.5px;
  color: var(--text-soft);
}

.stat-input:focus {
  outline: none;
}

body.input-keyboard .stat-input:focus {
  border-color: var(--accent);
  background-color: var(--bg);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

/* --- Link Analysis (Phase 10) — new-topic "From links" panel --- */
.token-textarea {
  width: 100%;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: 13px;
  line-height: 1.45;
  background: var(--bg);
  color: var(--text);
  resize: vertical;
  box-sizing: border-box;
  min-height: 240px;
}
.token-textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.links-tabs {
  display: flex;
  gap: 6px;
  margin: 14px 0 10px;
  border-bottom: 1px solid var(--border);
}
.links-tab {
  appearance: none;
  border: none;
  background: none;
  padding: 7px 12px;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-muted);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.links-tab.is-active {
  color: var(--accent);
  border-bottom-color: var(--accent);
}
.links-pane[hidden] { display: none; }

.la-qrow {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 6px;
}
/* The links panel is a `.field`, so the global `.field input
   { width:100%; padding; border }` cascades onto BOTH the checkbox and
   the query input — blowing the checkbox into a full-width box and
   hiding the text. Reset both explicitly (these 2-class selectors
   out-specify `.field input`). */
.la-qrow .la-qpick {
  flex: 0 0 auto;
  width: auto;
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
  box-shadow: none;
}
.la-qrow .la-qtext {
  flex: 1 1 auto;
  width: auto;
  min-width: 0;
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: 13px;
  background: var(--bg);
  color: var(--text);
}
.la-qrow .la-qtext:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

/* Settings — growth thresholds editor */
.thr-group { margin-bottom: 16px; }
.thr-group:last-child { margin-bottom: 0; }
.thr-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}

/* ===== Watchlist (Phase 11) ===== */
.wl-avatar {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  border-radius: 50%;
  overflow: hidden;
  background: var(--bg-hover);
  color: var(--text-muted);
  font-weight: 700;
}
.wl-avatar img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.wl-avatar-fallback { line-height: 1; }
.wl-avatar-sm { width: 26px; height: 26px; font-size: 11px; }
.wl-avatar-md { width: 40px; height: 40px; font-size: 15px; }
.wl-avatar-lg { width: 64px; height: 64px; font-size: 24px; }

.wl-card-id {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 2px 0 6px;
}

.wl-head {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  margin-bottom: 14px;
}
.wl-head-body { min-width: 0; }
.wl-head-sub {
  font-size: 13px;
  color: var(--text-muted);
  margin-top: 2px;
}
.wl-head-sub a { color: var(--accent); text-decoration: none; }
.wl-head-about {
  font-size: 12px;
  color: var(--text-soft);
  margin-top: 2px;
}
.wl-bio {
  font-size: 13px;
  color: var(--text-soft);
  margin-top: 6px;
  max-width: 640px;
  white-space: pre-line;   /* keep the bio's own line breaks, like IG */
  line-height: 1.45;
}
.wl-head-counts {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  margin-top: 6px;
  font-size: 13px;
  color: var(--text-muted);
}
.wl-head-counts b {
  color: var(--text);
  font-weight: 600;
}
.wl-verified {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 17px;
  height: 17px;
  margin-left: 7px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  font-size: 11px;
  line-height: 1;
  vertical-align: middle;
}
.wl-bio-links {
  display: flex;
  flex-wrap: wrap;
  gap: 5px 14px;
  margin-top: 6px;
  font-size: 13px;
}
.wl-bio-links a {
  color: var(--accent);
  text-decoration: none;
  font-weight: 500;
}
.wl-bio-links a::before {
  content: "🔗";
  margin-right: 4px;
  font-size: 11px;
  opacity: 0.75;
}
.wl-bio-links a:hover { text-decoration: underline; }

/* "Automatically track" control — IG-only switches (add form + creator page). */
.wl-track {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 20px;
}
/* In the add form it's a field-rhythm block; in the creator header it sits
   inline in the actions row with extra room before Refresh now / Delete. */
#wl-form .wl-track { margin: 14px 0; }
.topic-head-actions .wl-track { margin-right: 16px; }
.wl-track-label { font-size: 13px; font-weight: 600; color: var(--text); }
.wl-track-item {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font-size: 13.5px;
  color: var(--text);
  user-select: none;
}
.wl-track-name { font-weight: 500; }
.wl-switch-input { position: absolute; opacity: 0; width: 0; height: 0; }
.wl-switch {
  position: relative;
  display: inline-block;
  width: 36px;
  height: 20px;
  border-radius: 999px;
  background: var(--border-strong);
  transition: background-color 0.15s ease;
  flex: 0 0 auto;
}
.wl-switch::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 2px rgba(17, 24, 39, 0.25);
  transition: transform 0.15s ease;
}
.wl-switch-input:checked + .wl-switch { background: var(--accent); }
.wl-switch-input:checked + .wl-switch::after { transform: translateX(16px); }
.wl-switch-input:focus-visible + .wl-switch { box-shadow: 0 0 0 3px var(--accent-soft); }

/* Archived-stories grid (creator detail → Stories tab). */
.wl-stories-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 12px;
}
.wl-story-card { text-decoration: none; color: var(--text); display: block; }
.wl-story-thumb {
  position: relative;
  display: block;
  aspect-ratio: 9 / 16;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--bg-hover);
  border: 1px solid var(--border);
}
.wl-story-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.wl-story-card:hover .wl-story-thumb { border-color: var(--border-strong); }
.wl-story-badge {
  position: absolute;
  left: 6px;
  top: 6px;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  border-radius: 4px;
  padding: 0 6px;
  font-size: 11px;
  line-height: 18px;
}
.wl-story-when {
  display: block;
  font-size: 11.5px;
  color: var(--text-muted);
  margin-top: 4px;
}

/* Watchlist detail reuses the Topics head/stats classes
   (.topic-head / .topic-head-actions / .topic-stats / .stat /
   .stat-input) so it looks identical — no watchlist-specific
   controls/stats styles needed. */

/* The watchlist videos table reuses the Topics .vt-* table styles
   verbatim (.vt-scroll / .vt-table / colgroup / .vt-sortable /
   .vt-status / .vt-evidence-row / .vt-fresh …) so it is visually
   identical — no watchlist-specific table CSS. */
.wl-banner {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  margin: 16px 0 4px;
  padding: 12px 14px;
  border: 1px solid #fcd9a8;
  background: #fff8ef;
  border-radius: var(--radius);
  font-size: 13px;
  color: #92400e;
  line-height: 1.5;
}
.wl-banner-icon { flex: 0 0 auto; font-size: 15px; line-height: 1.4; }
.wl-banner-title { font-weight: 600; color: var(--warning); }

/* ===== Hook Cutter ===== */
.dl-tabs {
  display: flex;
  gap: 4px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--border);
}
.dl-tab {
  appearance: none;
  background: none;
  border: none;
  padding: 8px 14px;
  font: inherit;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text-muted);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.dl-tab:hover { color: var(--text); }
.dl-tab.is-active {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

.hook-rename-input {
  width: 100%;
  font: inherit;
  font-weight: 600;
  padding: 2px 6px;
  border: 1px solid var(--border);
  border-radius: 4px;
}

.cutter-modal {
  max-width: 760px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.cutter-body { display: flex; flex-direction: column; gap: 14px; }
.cutter-video {
  width: 100%;
  max-height: 48vh;
  background: #0f172a;
  border-radius: var(--radius-sm);
}
.cutter-timeline {
  position: relative;
  height: 34px;
  display: flex;
  align-items: center;
}
.cutter-track {
  position: absolute;
  left: 0; right: 0;
  height: 6px;
  border-radius: 3px;
  background: var(--bg-hover);
}
.cutter-range {
  position: absolute;
  height: 6px;
  border-radius: 3px;
  background: var(--accent-soft);
  border: 1px solid var(--accent);
  box-sizing: border-box;
  pointer-events: none;
}
/* Two transparent range inputs stacked over the track act as the
   start/end handles; only their thumbs are visible. */
.cutter-handle {
  position: absolute;
  left: 0; right: 0;
  width: 100%;
  margin: 0;
  background: none;
  -webkit-appearance: none;
  appearance: none;
  pointer-events: none;
}
.cutter-handle::-webkit-slider-thumb {
  -webkit-appearance: none;
  pointer-events: auto;
  width: 14px;
  height: 22px;
  border-radius: 4px;
  background: var(--accent);
  border: 2px solid #fff;
  box-shadow: 0 1px 3px rgba(15, 23, 42, 0.35);
  cursor: ew-resize;
}
.cutter-handle::-moz-range-thumb {
  pointer-events: auto;
  width: 14px;
  height: 22px;
  border-radius: 4px;
  background: var(--accent);
  border: 2px solid #fff;
  cursor: ew-resize;
}
.cutter-handle::-webkit-slider-runnable-track { background: none; }
.cutter-handle::-moz-range-track { background: none; }
.cutter-fields {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
}
.cutter-fields .field-label,
.cutter-body > .field-label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 12px;
}
.cutter-fields input,
.cutter-fields select,
.cutter-body > .field-label input {
  font: inherit;
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--text);
}
.cutter-dur { font-weight: 600; padding: 6px 0; }

/* ---- Admin usage dashboard (#/admin) ---- */
.admin-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin: 0 0 14px;
}
.admin-periods { display: inline-flex; gap: 4px; }
.admin-period {
  font: inherit;
  font-size: 13px;
  padding: 5px 12px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text-muted);
  border-radius: 999px;
  cursor: pointer;
  transition: background-color 0.1s ease, color 0.1s ease, border-color 0.1s ease;
}
.admin-period:hover { color: var(--text); border-color: var(--border-strong); }
.admin-period.is-active {
  background: var(--accent-soft);
  color: var(--accent);
  border-color: var(--accent);
}
.admin-card { padding: 4px 4px 8px; overflow-x: auto; }
.admin-table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.admin-table th,
.admin-table td {
  padding: 10px 12px;
  text-align: left;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
/* The usage table fills the panel (a normal Settings card caps at 720px). */
#admin-usage-card .admin-card { width: 100%; max-width: 100%; padding: 0; }
/* Period toggle + Refresh, attached to the top of the table. */
.admin-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--border);
}
/* Status = one switch (green when active). Click flips disable / enable. */
.admin-status {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  font-size: 12.5px;
  color: var(--text-muted);
  cursor: pointer;
}
.admin-status[disabled] { cursor: default; opacity: 0.7; }
/* Fixed-width label so "active" ↔ "disabled" don't resize the button (and so
   the auto-layout table doesn't reflow / jump sideways on toggle). */
.admin-status-text { display: inline-block; min-width: 52px; }
.admin-status-track {
  flex: none;
  width: 34px;
  height: 19px;
  border-radius: 999px;
  background: var(--bg-hover);
  border: 1px solid var(--border);
  position: relative;
  transition: background-color 0.12s ease, border-color 0.12s ease;
}
.admin-status-knob {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
  transition: transform 0.14s ease;
}
.admin-status.is-on .admin-status-track { background: var(--success); border-color: var(--success); }
.admin-status.is-on .admin-status-knob { transform: translateX(15px); }
.admin-status.is-on .admin-status-text { color: var(--text); }
.admin-status:not([disabled]):hover .admin-status-text { color: var(--text); }
/* Limit modal — roomy fields. */
.admin-limit-modal { min-width: 380px; }
.admin-limit-field { display: flex; align-items: center; gap: 14px; margin: 16px 0; }
.admin-limit-flabel { width: 64px; font-size: 13px; color: var(--text-muted); }
.admin-limit-amt-wrap { display: flex; align-items: center; gap: 6px; flex: 1; }
.admin-limit-amt-wrap > span { color: var(--text-muted); }
.admin-limit-amt-wrap input {
  flex: 1; font: inherit; padding: 9px 11px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--bg); color: var(--text);
}
.admin-seg { display: inline-flex; border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; }
.admin-seg button { font: inherit; font-size: 13px; padding: 8px 18px; border: 0; background: var(--bg); color: var(--text-muted); cursor: pointer; }
.admin-seg button + button { border-left: 1px solid var(--border); }
.admin-seg button.is-active { background: var(--accent-soft); color: var(--accent); }
.admin-table thead th {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-soft);
  border-bottom-color: var(--border-strong);
}
.admin-table .num { text-align: right; }
.admin-table tbody tr:hover { background: var(--bg-muted); }
.admin-user { font-weight: 600; color: var(--text); }
.admin-ws { font-size: 11.5px; color: var(--text-soft); margin-top: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 230px; }
.admin-tag {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--accent);
  background: var(--accent-soft);
  padding: 1px 5px;
  border-radius: 4px;
  vertical-align: middle;
}
.admin-spend { font-weight: 600; font-variant-numeric: tabular-nums; }
.admin-bd { font-size: 11px; color: var(--text-soft); font-variant-numeric: tabular-nums; white-space: nowrap; }
.admin-fail { color: var(--danger); font-size: 11.5px; background: none; border: 0; padding: 0; font-family: inherit; cursor: pointer; text-decoration: underline dotted; text-underline-offset: 2px; }
.admin-fail:hover { text-decoration: underline; }
/* Failure-reason popover behind the "N failed" chip (admin usage). */
.admin-fail-pop {
  position: absolute; z-index: 200; width: 360px; max-width: 92vw;
  background: #fff; border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 10px 30px rgba(17, 24, 39, 0.14); padding: 12px 14px;
}
.admin-fail-pop-head { font-size: 12px; color: var(--text-muted); margin-bottom: 8px; }
.admin-fail-row { display: flex; align-items: flex-start; gap: 10px; padding: 9px 0; border-top: 1px solid var(--border); text-align: left; }
.admin-fail-row:first-of-type { border-top: 0; padding-top: 0; }
.admin-fail-scraper { flex: none; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px; color: var(--danger); background: rgba(220, 38, 38, 0.09); padding: 2px 6px; border-radius: 5px; white-space: nowrap; }
.admin-fail-detail { flex: 1; min-width: 0; }
.admin-fail-err { font-size: 12.5px; color: var(--text); word-break: break-word; }
.admin-fail-hint { font-size: 11.5px; color: var(--text-muted); margin-top: 2px; }
.admin-fail-count { flex: none; font-size: 12px; font-weight: 600; color: var(--danger); }
/* Invited (allow-listed, not signed in yet) placeholder row — muted, no data. */
.admin-dash { color: var(--text-muted); }
.admin-badge {
  display: inline-block;
  font-size: 11px;
  font-weight: 500;
  padding: 2px 8px;
  border-radius: 999px;
}
.admin-badge.is-on { color: var(--success); background: rgba(22, 163, 74, 0.1); }
.admin-badge.is-off { color: var(--danger); background: rgba(220, 38, 38, 0.1); }
.admin-badge.is-muted { color: var(--text-soft); background: var(--bg-hover); }
.admin-action { text-align: right; white-space: nowrap; }
.admin-viewas { margin-right: 6px; }
.admin-danger { color: var(--danger); border-color: rgba(220, 38, 38, 0.35); }
.admin-danger:hover { background: rgba(220, 38, 38, 0.08); border-color: var(--danger); }
.admin-totals td { border-top: 2px solid var(--border-strong); border-bottom: none; font-weight: 600; }
.admin-totals .admin-bd { font-weight: 400; }
/* Limit cell — both forms share one fixed width so "Set limit" lines up with
   the value + pencil block above it. */
.admin-limit-cell { white-space: nowrap; font-variant-numeric: tabular-nums; }
.admin-limit-box {
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  width: 116px;
}
.admin-limit-val { color: var(--text); }
.admin-limit-edit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 22px;
  height: 22px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--text-soft);
  cursor: pointer;
}
.admin-limit-edit:hover { border-color: var(--accent); color: var(--accent); }

/* Features cell — the same box as the limit (value + pencil), so the two columns
   read as one thing. The value can be a long list, so it truncates instead of
   widening the row. */
.admin-feat-cell .admin-limit-box { width: 178px; }
.admin-feat-val {
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.admin-feat-all { color: var(--text-soft); }
/* The switches. A checkbox per module + what it actually opens up, so nobody has
   to guess what "groups" means before taking it away from someone. */
.admin-feat-modal { min-width: 440px; }
.admin-feat-list { display: flex; flex-direction: column; gap: 2px; margin: 14px 0 4px; }
.admin-feat-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 9px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.admin-feat-item:hover { background: var(--bg-hover); }
.admin-feat-item input[type="checkbox"] {
  width: 16px;
  height: 16px;
  margin: 1px 0 0;
  flex: none;
  accent-color: var(--accent);
  cursor: pointer;
}
.admin-feat-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.admin-feat-name { font-size: 13.5px; font-weight: 500; color: var(--text); }
.admin-feat-hint { font-size: 12px; color: var(--text-soft); line-height: 1.4; }
.admin-limit-set {
  width: 116px;
  font: inherit;
  font-size: 13px;
  padding: 5px 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--text-muted);
  cursor: pointer;
}
.admin-limit-set:hover { border-color: var(--accent); color: var(--accent); }
/* Delete (trash) — danger icon button. */
.admin-del {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--text-soft);
  cursor: pointer;
  transition: color 0.12s ease, background-color 0.12s ease, border-color 0.12s ease;
}
.admin-del:hover { color: var(--danger); background: rgba(220, 38, 38, 0.08); border-color: rgba(220, 38, 38, 0.35); }
.admin-tag.is-you { color: var(--text-soft); background: var(--bg-hover); }
/* The Access card's email input — match the standard .field input look (#3). */
#admin-card input[type="email"] {
  width: 100%;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: 13.5px;
  font-family: inherit;
  background: var(--bg);
  color: var(--text);
}
#admin-card input[type="email"]:focus { outline: none; }
body.input-keyboard #admin-card input[type="email"]:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
/* Vertical gap between the usage table and the Access card (#7). */
#admin-card { margin-top: 22px; }

/* ---- Settings in-page tabs ---- */
/* While Settings boots, hide the cards so they don't paint stacked and then
   reflow into tabs (#7). applyTabs() drops .settings-booting once tabbed. */
.settings-booting .card { display: none; }
.settings-tabs {
  display: flex;
  gap: 2px;
  flex-wrap: wrap;
  border-bottom: 1px solid var(--border);
  margin: 0 0 18px;
}
.settings-tab {
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  padding: 8px 14px;
  border: 0;
  background: none;
  color: var(--text-muted);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color 0.12s ease, border-color 0.12s ease;
}
.settings-tab:hover { color: var(--text); }
.settings-tab.is-active { color: var(--accent); border-bottom-color: var(--accent); }
/* The admin usage dashboard is embedded as the Admin tab — drop its own page
   header (the Settings header already covers it) and tighten the top. */
#admin-usage-card .page-header { margin: 0 0 12px; }
#admin-usage-card .page-title { font-size: 1.15rem; }

/* ---- Visual-style unification additions (2026-06-29) ---- */

/* .btn-ghost — transparent/quiet variant consistent with .btn.
   Used for secondary icon-adjacent actions where a full border is too heavy. */
.btn-ghost {
  background: transparent;
  border-color: transparent;
  color: var(--text-muted);
}
.btn-ghost:hover:not(:disabled) {
  background: var(--bg-hover);
  border-color: var(--border);
  color: var(--text);
}

/* Toast notification — moved out of inline JS style.cssText.
   Only opacity / transform are toggled in JS (the animation state);
   everything else lives here. */
.toast-stack {
  position: fixed;
  bottom: 22px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 9999;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: center;
  pointer-events: none;
}

.toast {
  background: rgba(24, 24, 27, 0.96);
  color: #fff;
  padding: 10px 16px;
  border-radius: var(--radius);
  font-size: 0.9rem;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.28);
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 0.2s ease, transform 0.2s ease;
  max-width: min(90vw, 420px);
  text-align: center;
}

.toast.is-warn {
  background: rgba(180, 83, 9, 0.96);
}
.toast.is-accent {
  background: rgba(37, 99, 235, 0.96);   /* calm accent-blue — the Download "saved to queue" nudges aren't warnings */
}

/* Actionable toast (e.g. the "no Mac linked → Set up" install nudge): opt back
   into pointer events (the stack disables them) and lay out msg + button + close. */
.toast--action { pointer-events: auto; display: flex; align-items: center; gap: 12px; text-align: left; }
.toast-action {
  flex: none; border: 0; background: rgba(255, 255, 255, 0.16); color: #fff;
  font: inherit; font-weight: 600; padding: 4px 10px; border-radius: 6px; cursor: pointer;
}
.toast-action:hover { background: rgba(255, 255, 255, 0.26); }
.toast-close {
  flex: none; border: 0; background: transparent; color: rgba(255, 255, 255, 0.7);
  font-size: 1.1rem; line-height: 1; padding: 0 2px; cursor: pointer;
}
.toast-close:hover { color: #fff; }

/* Online/offline status dots in the Connections cards' status lines. */
.device-status-on { color: var(--success); }
.device-status-off { opacity: 0.4; }

/* Settings — "Access" card email input + Allow button row.
   Replaces the ad-hoc display:flex+gap+margin inline block. */
.settings-access-row {
  display: flex;
  gap: 8px;
  margin: 12px 0 0;
}
.settings-access-row input { flex: 1; }

/* Settings — code display box (pair/TG bind codes).
   Replaces _CODE_BOX and _CODE string constants. */
.code-box {
  margin-top: 12px;
  padding: 14px 16px;
  border: 1px dashed var(--border-strong);   /* grey dashed, like other borders */
  border-radius: var(--radius);
}
.code-box-hint { margin: 6px 0 0; }
.code-box-exp { margin: 3px 0 0; opacity: .75; font-size: 11.5px; }   /* "Expires…" on its own line */
.code-text {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: 2px;
}
/* Code + copy affordance, centred as one unit. */
.code-row { display: flex; align-items: center; justify-content: center; gap: 10px; }
.code-copy {
  flex: none; width: 28px; height: 28px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); border-radius: 8px;
  background: #fff; color: var(--text-muted); cursor: pointer;
  transition: color .12s, border-color .12s, background .12s;
}
.code-copy:hover { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
.code-copy .ic-check { display: none; }
.code-copy.is-copied { color: var(--success); border-color: var(--success); background: rgba(22,163,74,.08); }
.code-copy.is-copied .ic-copy { display: none; }
.code-copy.is-copied .ic-check { display: inline; }

/* Settings — "?" help popover (numbered how-to steps). Mirrors the Mac app's
   Settings help affordance so the web + Mac Connections read the same. */
.card-title-row { display: flex; align-items: center; margin-bottom: 4px; }
.card-title-row .card-title { margin: 0; }
.s-help { position: relative; display: inline-flex; flex: none; }
.s-help-q {
  width: 18px; height: 18px; margin-left: 7px; padding: 0;
  border: 1px solid var(--border-strong); border-radius: 50%;
  background: #fff; color: var(--text-muted);
  font-size: 11px; line-height: 1; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .12s, border-color .12s, color .12s;
}
.s-help-q:hover,
.s-help-q[aria-expanded="true"] { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.s-help-pop {
  position: absolute; top: calc(100% + 10px); left: 0; z-index: 60;
  width: 300px; padding: 15px 17px; text-align: left;
  background: #fff; border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 10px 30px rgba(17, 24, 39, .12);
}
.s-help-pop::before {
  content: ""; position: absolute; top: -6px; left: 14px; width: 11px; height: 11px;
  background: #fff; border-left: 1px solid var(--border); border-top: 1px solid var(--border);
  transform: rotate(45deg);
}
/* Connect cards keep the "?" at the far right, so its popover opens leftward and
   the tail sits under the button's centre (not off past the card edge). */
.conn-card .s-help-pop { left: auto; right: -15px; }   /* shift the body right so the tail clears the top-right rounded corner */
.conn-card .s-help-pop::before { left: auto; right: 18px; }   /* tail stays under the "?" but past the 12px corner radius */
.s-help-head { display: block; font-size: 12.5px; font-weight: 600; color: var(--text); margin-bottom: 16px; }   /* block so margin-bottom actually applies (it's a <span>, inline margins are ignored) — clear gap from step 1 */
.s-help-steps { margin: 0; padding: 0; list-style: none; counter-reset: hs; }
.s-help-steps li {
  counter-increment: hs; position: relative;
  padding: 0 0 12px 27px; font-size: 12.5px; line-height: 1.55; color: var(--text);
}
.s-help-steps li:last-child { padding-bottom: 0; }
.s-help-steps li::before {
  content: counter(hs); position: absolute; left: 0; top: -1px;
  width: 18px; height: 18px; border-radius: 50%;
  background: var(--accent-soft); color: var(--accent);
  font-size: 11px; font-weight: 600;
  display: inline-flex; align-items: center; justify-content: center;
}
.s-help-steps b { font-weight: 600; }
.s-help-steps code { background: var(--bg-hover); padding: 1px 5px; border-radius: 5px; font-size: 11.5px; }
.s-help-link {
  display: inline-block; margin-top: 10px; padding-top: 9px;
  border-top: 1px solid var(--border); color: var(--accent);
  font-size: 12.5px; text-decoration: none;
}
.s-help-link:hover { text-decoration: underline; }
.tg-bot-open { font-weight: 600; color: var(--accent); text-decoration: none; }
.tg-bot-open:hover { text-decoration: underline; }

/* Settings — scrapers-msg top margin (was inline style="margin-top:8px"). */
.scrapers-msg-gap { margin-top: 8px; }

/* Settings — Telegram test / connect validate row top margin. */
.field-validate-mt { margin-top: 12px; }

/* Connections — Your Mac + Telegram side by side (stack on narrow); the
   "Get the Mac app" strip sits above, shown only until a Mac is linked (p3). */
#conn-block { max-width: 880px; margin-top: 16px; }   /* wider than a single card so the two cards have room (headers on one line) */
#conn-block > .conn-getapp { max-width: none; }   /* the get-app strip spans the full section width */
#telegram-card { max-width: 880px; }   /* alerts card matches the Connections width above it */
.conn-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; align-items: stretch; }
.conn-pair > .card { max-width: none; margin: 0; }   /* fill the half; kill the .card+.card sibling margin-top that pushed the 2nd column 16px down */
.btn-block { width: 100%; justify-content: center; }
.card-title-row .s-help { margin-left: auto; }   /* "?" to the far right */
.conn-card { padding: 24px 26px; }
.conn-card .card-title-row { min-height: 30px; margin-bottom: 0; }   /* equal header height → the two status lines start on the same baseline */
.conn-card .card-title { font-size: 19px; }
#tg-bot-handle { font-weight: 500; font-size: 15px; }   /* the @handle reads smaller than "Telegram" */
#tg-bot-handle .tg-bot-open { font-weight: 500; }
.conn-status { font-size: 14px; color: var(--text-muted); margin: 6px 0 0; min-height: 18px; }
.conn-status .device-status-on, .conn-status .device-status-off { font-size: 1.15em; }
.conn-muted { color: var(--text-muted); }
.conn-action { margin-top: 20px; }
.conn-action .btn { padding: 12px 18px; font-size: 15px; }
.conn-card .code-box { margin-top: 14px; padding: 18px 20px; text-align: center; }
.conn-card .code-text { font-size: 1.7rem; }
.conn-card .code-box-hint { margin-top: 8px; }
.tg-bot-open { color: var(--accent); text-decoration: none; }
.tg-bot-open:hover { text-decoration: underline; }
@media (max-width: 760px) { .conn-pair { grid-template-columns: 1fr; } }
/* Get-the-Mac-app strip — icon / text / button on one centred line. The [hidden]
   rule must beat the flex display: an author `display:flex` outranks the UA
   `[hidden]{display:none}`, so hide it explicitly once a Mac is linked. */
.conn-getapp { display: flex; align-items: center; gap: 14px; padding: 16px 18px; margin-bottom: 14px; background: var(--app-nudge-bg); border-color: var(--app-nudge-border); box-shadow: none; }
.conn-getapp[hidden] { display: none; }
.conn-getapp-ic { flex: none; align-self: center; color: var(--text-muted); }
.conn-getapp-txt { flex: 1; min-width: 0; }
.conn-getapp-title { font-weight: 600; font-size: 14px; }
.conn-getapp-txt .card-hint { margin: 3px 0 0; }   /* kill the base .card-hint 18px bottom margin — it left phantom space that pushed the text off-centre vertically */
.conn-getapp .btn { flex: none; align-self: center; }

/* Settings — danger-msg top margin. */
.danger-msg-gap { margin-top: 10px; }

/* Settings — thr-group scope label (replaces style="font-weight:600;margin-bottom:6px;"). */
.thr-scope-label {
  font-weight: 600;
  margin-bottom: 6px;
}

/* Settings — limit-reached inline alert (was style="margin:8px 0 0"). */
.cost-limit-alert { margin: 8px 0 0; }

/* Watchlist colgroup widths — table-layout:fixed; widths defined here
   so no inline style="" on individual <col> elements. */
.wl-col-preview  { width: 168px; }
.wl-col-status   { width: 110px; }
.wl-col-views    { width: 92px; }
.wl-col-growth   { width: 110px; }
.wl-col-likes    { width: 82px; }
.wl-col-comments { width: 96px; }
.wl-col-eng      { width: 110px; }
.wl-col-date     { width: 130px; }
.wl-col-dur      { width: 92px; }
.wl-col-scrapers { width: 90px; }
.wl-col-actions  { width: 150px; }

/* Watchlist add-form card width cap (was inline style="max-width:620px"). */
.wl-add-card { max-width: 620px; }

/* Watchlist anon hint — hidden by default, shown by JS (was style="display:none"). */
#wl-anon-hint { display: none; }

/* Watchlist field-hint gap (inline style="margin:4px 0 10px" / "margin:4px 0 12px"). */
.wl-section-hint { margin: 4px 0 10px; }
.wl-section-hint-lg { margin: 4px 0 12px; }

/* Watchlist field small gap (was style="margin-bottom:6px"). */
.field-mb-sm { margin-bottom: 6px; }

/* Watchlist assoc group label gap (was style="margin:14px 0 6px"). */
.assoc-section-label { margin: 14px 0 6px; }

/* Watchlist note gap (was style="margin-bottom:8px"). */
.wl-note-gap { margin-bottom: 8px; }

/* Watchlist tab section hosts — top margin + conditional display.
   JS writes display:none / "" directly on the element to toggle; these
   classes just supply the persistent top margin so there's no inline margin. */
.wl-tab-host { margin-top: 14px; }

/* Watchlist creator detail message gap. */
.wl-detail-msg { margin-top: 12px; }

/* Watchlist creator detail tab bar top margin. */
.wl-tabs-mt { margin-top: 18px; }

/* Downloads cutter modal — flex spacer between "Play selected" and Save/Cancel. */
.cutter-spacer { flex: 1; }

/* Ads table col widths — one per real column, in table order. Matches
   .vt-col-preview on the thumb so the two rules don't fight. */
.wl-ads-col-thumb  { width: 168px; }
.wl-ads-col-main   { /* flex — auto */ }
.wl-ads-col-date   { width: 96px; }
.wl-ads-col-plats  { width: 172px; }
.wl-ads-col-status { width: 104px; }
.wl-ads-col-act    { width: 132px; }

/* ============================================================
   Styleguide — owner-only reference page (app/views/styleguide.js).
   A numbered catalogue of every reusable component. Layout only; the
   samples themselves reuse the real component classes above.
   ============================================================ */
.sg-wrap { max-width: 1100px; }
.sg-section { margin: 34px 0 6px; }
.sg-section-head { border-top: 1px solid var(--border); padding-top: 18px; margin-bottom: 16px; }
.sg-section-head h2 { font-size: 15px; font-weight: 700; letter-spacing: 0.02em; margin: 0; color: var(--text); }
.sg-section-head p { font-size: 12.5px; color: var(--text-muted); margin: 4px 0 0; max-width: 78ch; }
.sg-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 14px; }
.sg-item { border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg); box-shadow: var(--shadow-sm); overflow: hidden; }
.sg-item-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; padding: 10px 14px; border-bottom: 1px solid var(--border); background: var(--bg-muted); }
.sg-num { font-variant-numeric: tabular-nums; font-size: 11px; font-weight: 700; color: #fff; background: var(--accent); border-radius: 999px; padding: 1px 8px; letter-spacing: 0.02em; flex: none; }
.sg-label { font-size: 12px; font-weight: 600; color: var(--text); font-family: ui-monospace, "SF Mono", Menlo, monospace; }
.sg-note { font-size: 11.5px; color: var(--text-muted); flex-basis: 100%; }
.sg-demo { padding: 18px 16px; display: flex; flex-wrap: wrap; gap: 10px; align-items: center; min-height: 40px; }
.sg-demo code { font-size: 11px; color: var(--text-muted); }

/* foundations helpers */
.sg-swatches { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 14px; width: 100%; }
.sg-swatch { display: flex; align-items: center; gap: 8px; font-size: 11.5px; }
.sg-swatch code { font-size: 11px; color: var(--text); font-family: ui-monospace, Menlo, monospace; }
.sg-swatch span:last-child { margin-left: auto; color: var(--text-soft); font-variant-numeric: tabular-nums; }
.sg-chip { width: 20px; height: 20px; border-radius: 6px; flex: none; }
.sg-type { display: flex; flex-direction: column; gap: 6px; width: 100%; }
.sg-type > div { margin: 0; }

/* ── Shared data-table identity ─────────────────────────────────────────────
   Every data table in the app reads with ONE header + cell language, so Top 100
   and the Admin/Settings tables no longer look like a different product from the
   canonical Topics/Watchlist table. Only column layout and row density differ
   per table — the identity below (header band, cell rule, hover wash, numeric
   columns, active accent) is SHARED. Catalogued in the styleguide (H · Tables).
   Placed last so it wins over each table's own header rule at equal specificity;
   deliberately omits padding/position/z-index so .vt-table's sticky header and
   per-table density are untouched.

   Tables covered: .vt-table (Topics + Watchlist) · .top-table (Top 100) ·
   .admin-table (Admin usage) · .cost-table (Settings billing). */
.vt-table thead th,
.top-table th,
.admin-table thead th,
.cost-table th {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
  background: var(--bg-muted);
  border-bottom: 1px solid var(--border);
}

/* Body cells: one hairline rule + a single muted hover wash on every table. */
.top-table td,
.admin-table td,
.cost-table td,
.vt-table tbody td { border-bottom: 1px solid var(--border); }
.top-table tbody tr:hover td,
.admin-table tbody tr:hover,
.cost-table tbody tr:hover,
.vt-table tbody tr:hover { background: var(--bg-muted); }

/* Numeric columns: right-aligned, tabular figures — everywhere. */
.top-table .num,
.admin-table .num,
.cost-table .num,
.vt-table .vt-num { text-align: right; font-variant-numeric: tabular-nums; }

/* The one interactive header affordance (sortable columns) accents in brand
   blue when active — the single "blue" state a table ever shows. */
.vt-sortable.is-active,
.vt-arrow.is-active { color: var(--accent); }

/* --- table skeleton -------------------------------------------------
   Placeholder for the video table while its first fetch is in flight.
   The detail shell paints instantly, so without this the table area was
   blank white for the whole round trip (measured: 1.0-1.9s per topic
   open) — which reads as broken rather than loading.

   Sized to match the real table row for row, so swapping in the real
   content moves nothing (docs/ui-conventions.md). Colours come from the
   existing semantic tokens; no new palette. */
.vt-skeleton {
  --skel: color-mix(in srgb, var(--text-soft) 16%, transparent);
}

.vt-skel-line {
  display: inline-block;
  height: 10px;
  border-radius: 5px;
  background: var(--skel);
  animation: vt-skel-pulse 1.4s ease-in-out infinite;
}

.vt-skel-tabs {
  display: flex;
  gap: 18px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 12px;
}

.vt-skel-toolbar {
  margin-bottom: 12px;
}

.vt-skel-head {
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
}

.vt-skel-row {
  display: flex;
  align-items: center;
  gap: 14px;
  /* Matches a real row: 90px thumb + 12px padding top/bottom. */
  padding: 12px;
  border-bottom: 1px solid var(--border);
}

.vt-skel-thumb {
  flex: 0 0 auto;
  width: 62px;
  height: 90px;
  border-radius: 8px;
  background: var(--skel);
  animation: vt-skel-pulse 1.4s ease-in-out infinite;
}

.vt-skel-grow {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}

@keyframes vt-skel-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.45; }
}

@media (prefers-reduced-motion: reduce) {
  .vt-skel-line,
  .vt-skel-thumb { animation: none; }
}

/* Card-grid variant of the skeleton — the Topics and Watchlist indexes render
   the same `.topic-card`, so they wait behind the same placeholder. Inherits
   .topic-card's box (padding, border, radius) so the swap is invisible. */
.vt-skel-card {
  --skel: color-mix(in srgb, var(--text-soft) 16%, transparent);
  /* Matches a real card's natural height: head + name + query + meta. */
  min-height: 118px;
  justify-content: flex-start;
  cursor: default;
}

.vt-skel-card:hover {
  border-color: var(--border);
  box-shadow: none;
}

.vt-skel-card-head {
  margin-bottom: 4px;
}

.vt-skel-card-foot {
  margin-top: auto;
}

/* Avatar / round placeholders and the two profile-shaped skeletons. Sized off
   the real components: .wl-avatar-md is 40px, .wl-avatar-lg 64px. */
.vt-skel-circle {
  flex: 0 0 auto;
  display: inline-block;
  border-radius: 50%;
  background: var(--skel, color-mix(in srgb, var(--text-soft) 16%, transparent));
  animation: vt-skel-pulse 1.4s ease-in-out infinite;
}

.vt-skel-creator {
  /* .wl-head's own gap/alignment come from its class; only spacing below. */
  margin-bottom: 18px;
}

.vt-skel-counts {
  display: flex;
  gap: 14px;
  align-items: center;
  margin-top: 2px;
}

.vt-skel-grouphead {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 12px;
}

.vt-skel-spacer {
  flex: 1 1 auto;
}

.vt-skel-groupstats {
  margin-bottom: 18px;
}

@media (prefers-reduced-motion: reduce) {
  .vt-skel-circle { animation: none; }
}
