/* TrueScale Social Dashboard - two themes via [data-theme]:
   dark = neutral-pro (ink surfaces);  light = brand (white + deep teal).
   Layout/components read CSS vars, so only the palette swaps. */
:root, :root[data-theme="dark"] {
  --bg:#0a1020; --panel:#161e33; --panel2:#10182a; --raised:#222c48;
  --ink:#ccd3df; --muted:#9aa3b5; --faint:#6b7488; --head:#d2d9e5;
  --accent:#f59e0b; --accent-ink:#1a1206;
  --warn:#d4706e; --warn-ink:#fca5a5; --ok:#4fa676; --info:#56b3f0; --planned:#a78bfa;
  --line:rgba(232,236,245,.16); --line2:rgba(232,236,245,.10);
  --shadow:0 1px 3px rgba(0,0,0,.35);
  --row-alt:rgba(255,255,255,.035);
  --scope-bg:rgba(245,158,11,.18);   /* selected/scoped row - accent-tinted, clearly > row-alt */
  --pv-media-bg:#0a1020;
}
:root[data-theme="light"] {
  --bg:#eef2f1; --panel:#ffffff; --panel2:#f2f6f5; --raised:#eaf0ee;
  --ink:#14302d; --muted:#54706c; --faint:#8aa09c; --head:#0d4a4a;
  --accent:#0e7c7b; --accent-ink:#ffffff;
  --warn:#dc2626; --warn-ink:#b91c1c; --ok:#0e9f6e; --info:#0e7490; --planned:#6d54d6;
  --line:#d3ddda; --line2:#e6edeb;
  --shadow:0 1px 3px rgba(13,74,74,.08);
  --row-alt:#f3f8f7;
  --scope-bg:rgba(14,124,123,.15);   /* selected/scoped row - accent-tinted, clearly > row-alt */
  --pv-media-bg:#eef3f1;
}
:root {
  --sans:Inter,"Segoe UI Variable","Segoe UI",system-ui,-apple-system,Roboto,sans-serif;
  --mono:"Cascadia Code","Cascadia Mono",Consolas,ui-monospace,SFMono-Regular,monospace;
  /* Soft brand tint for SECTION HEADERS only (keep content surfaces neutral).
     Theme-aware via the accent; falls back to --panel2 where color-mix is absent. */
  --head-tint:color-mix(in srgb, var(--accent) 14%, var(--panel));
  /* Stronger, clearly-visible divider for the tinted GROUP headers
     (queue batch folders + activity date rows). Falls back to --line. */
  --group-divider:color-mix(in srgb, var(--accent) 50%, var(--line));
  /* Uniform white gap between a tab's panel top and its first content element,
     applied on the panel so it stays fixed (doesn't scroll away). */
  --pane-gap:10px;
}
* { box-sizing:border-box; margin:0; padding:0; }
/* The `hidden` attribute must win over component display rules (e.g. .hpop /
   .upload-card use display:flex), otherwise they'd show on load. */
[hidden] { display:none !important; }
html { background:var(--bg); }
body { background:var(--bg); color:var(--ink); height:100vh; overflow:hidden;
  display:flex; flex-direction:column; font-family:var(--sans);
  -webkit-font-smoothing:antialiased; font-size:14px; line-height:1.5;
  padding:14px 24px 16px; max-width:1480px; margin:0 auto; }
a { color:var(--info); text-decoration:none; }
a:hover { text-decoration:underline; }
code { font-family:var(--mono); font-size:.9em; }

/* top bar */
.topbar { display:flex; justify-content:space-between; align-items:center; gap:18px;
  flex-wrap:wrap; margin-bottom:12px; padding-bottom:10px; border-bottom:1px solid var(--line); }
h1 { font-weight:700; letter-spacing:-.01em; font-size:18px; display:flex;
  align-items:center; gap:9px; }
h1 .dot { width:9px; height:9px; border-radius:50%; background:var(--accent); }
/* Brand logo in the top bar (replaces the dot + client-name text). Adjust the
   height to taste; width follows the image's aspect ratio. */
.brand-logo { height:40px; width:auto; display:block; max-height:48px; }
.client { color:var(--muted); font-weight:500; }
/* Header: the client logo leads; "TrueScale Social" is the app name, shown
   bold + prominent (not the old small muted tag) so it reads as the product. */
.product-name { color:var(--head); font-weight:700; font-size:15px;
  letter-spacing:.01em; padding-left:12px; margin-left:2px;
  border-left:1px solid var(--line); }
.strip { display:flex; gap:16px; align-items:center; font-family:var(--mono); font-size:11px;
  color:var(--muted); }
.hdot { display:inline-block; width:7px; height:7px; border-radius:50%; margin-right:6px;
  background:var(--faint); vertical-align:middle; }
.hdot.on { background:var(--ok); box-shadow:0 0 0 2px rgba(79,166,118,.15); }
/* Configured but a feed is muted for testing -> amber (not live green). Must
   follow .hdot.on (same specificity) so it wins when both classes are set. */
.hdot.testing { background:#f59e0b; box-shadow:0 0 0 2px rgba(245,158,11,.18); }
.hitem { cursor:default; }

/* Process Engine warning chip -- shown ONLY when the worker is down AND jobs are
   waiting (engine.warn). A clickable warning pill that gently pulses to draw the
   eye; clicking it respawns the worker. Default display is inline-flex, so the
   [hidden] override below is required to actually hide it. */
.engine-warn { display:inline-flex; align-items:center; gap:6px;
  font-family:var(--mono); font-size:11px; letter-spacing:.02em;
  color:var(--warn); background:rgba(212,112,110,.12);
  border:1px solid rgba(212,112,110,.4); border-radius:999px; padding:4px 11px;
  cursor:pointer; animation:engine-pulse 1.8s ease-in-out infinite; }
.engine-warn:hover { background:rgba(212,112,110,.2); border-color:var(--warn); }
.engine-warn[hidden] { display:none; }
.ew-ico { font-size:12px; line-height:1; }
.ew-jobs { opacity:.85; }
.ew-cta { opacity:.7; font-style:italic; }
@keyframes engine-pulse { 0%, 100% { box-shadow:0 0 0 0 rgba(212,112,110,0); }
  50% { box-shadow:0 0 0 3px rgba(212,112,110,.12); } }
.reviewer-box { color:var(--muted); font-size:13px; }
.reviewer-box input { background:var(--panel); color:var(--ink); border:1px solid var(--line);
  border-radius:7px; padding:7px 10px; font-family:var(--mono); font-size:12px; width:150px;
  margin-left:6px; }
.reviewer-box input:focus { outline:none; border-color:var(--accent); }

/* Settings overflow menu (UX-8): compact gear button + right-aligned popover
   (reuses .hpop-wrap / .hpop). Houses the theme toggle and future settings. */
.settings-btn { font-size:15px; line-height:1; padding:6px 10px; }
.settings-pop { right:0; left:auto; min-width:160px; }
.menu-item { background:none; border:none; text-align:left; cursor:pointer;
  color:var(--ink); font-family:var(--sans); font-size:13px; padding:7px 9px;
  border-radius:6px; width:100%; }
.menu-item:hover { background:var(--raised); }
.menu-item.menu-on { color:var(--accent); font-weight:600; }   /* active "View as" */

/* Persistent testing-mode banner (k11): a feed is disabled_for_testing, so the
   pipeline runs but publish API calls are skipped - keep that always visible so
   nobody thinks a "publish" reached the live account. */
.testing-banner { display:flex; align-items:center; gap:9px; margin:0 0 10px;
  padding:6px 14px; border-radius:8px; font-size:12.5px; line-height:1.4;
  background:rgba(245,158,11,.10); border:1px solid rgba(245,158,11,.40);
  color:var(--head); }
.testing-banner .tb-icon { color:#f59e0b; font-size:15px; flex:none; }
.testing-banner strong { color:#f59e0b; }

/* regions */
section.region { background:var(--panel); border:1px solid var(--line); border-radius:12px;
  margin-bottom:0; box-shadow:var(--shadow); padding-top:var(--pane-gap); }
/* Approval Queue: inset the whole pane (filter bar + list/detail split) from the
   panel border so it floats like the Recent Activity content. The top gap already
   comes from --pane-gap; this adds the matching 8px left/right gap (same as the
   Recent Activity .inner side padding) plus a bottom gap so the card floats fully. */
.region[data-rpane="queue"] { padding-left:8px; padding-right:8px;
  padding-bottom:var(--pane-gap); }
.region > h2 { font-family:var(--mono); font-size:11.5px; letter-spacing:.12em;
  text-transform:uppercase; color:var(--head); font-weight:600; padding:13px 18px;
  display:flex; align-items:center; gap:8px; justify-content:flex-start;
  border-bottom:1px solid var(--line2); }
.region .inner { padding:6px 8px 10px; }
.region > h2 .hctrl { margin-left:auto; display:flex; gap:8px; align-items:center;
  text-transform:none; letter-spacing:0; font-size:12px; color:var(--muted); }
.region > h2 .hctrl input[type=number] { width:44px; padding:3px 7px; background:var(--panel2);
  color:var(--ink); border:1px solid var(--line); border-radius:6px; font-family:var(--sans);
  font-size:12px; font-weight:400; text-align:right; }
.hgroup { display:flex; gap:8px; align-items:center; }
.hsep { width:1px; align-self:stretch; min-height:24px; background:var(--line); margin:0 4px; }
.caret-dn { font-size:10px; color:var(--muted); }
.hpop-wrap { position:relative; }
.hpop { position:absolute; right:0; top:calc(100% + 8px); z-index:30; width:158px;
  background:var(--panel); border:1px solid var(--line); border-radius:10px;
  box-shadow:0 10px 30px rgba(0,0,0,.28); padding:8px; display:flex;
  flex-direction:column; gap:5px; }
.hpop-title { font-size:10px; color:var(--muted); text-transform:uppercase;
  letter-spacing:.12em; margin-bottom:1px; }
.hpop label { display:grid; grid-template-columns:1fr auto; gap:10px; align-items:center;
  font-family:var(--sans); font-weight:400; font-size:12.5px; color:var(--ink); }
.hpop input[type=number] { width:44px; padding:3px 7px; font-family:var(--sans);
  font-size:12px; font-weight:400; line-height:1.2; background:var(--panel2);
  color:var(--ink); border:1px solid var(--line); border-radius:6px; text-align:right; }
.hpop input[type=number]:focus { outline:none; border-color:var(--accent); }
.hpop .btn.primary { width:100%; }
.rcaret { background:none; border:none; color:var(--ink); cursor:pointer;
  font-size:20px; font-weight:700; line-height:1;
  padding:0 4px 0 0; margin-right:2px;
  transition:transform .15s; }
.rcaret:hover { color:var(--accent); }
.region.collapsed .rcaret { transform:rotate(-90deg); }
.region.collapsed .inner, .region.collapsed .tabs, .region.collapsed .qsplit,
.region.collapsed .qgrid, .region.collapsed .upload-card { display:none; }

/* --- App-shell + region tabs: lock the page to ONE scroll context. The window
   never scrolls; the SELECTED tab's region fills the full height and scrolls
   internally, so the queue gets the whole area. No double scrollbar. ---------- */
body > .topbar, body > .testing-banner, body > .region-tabs { flex:0 0 auto; }
body > .region { flex:1 1 0; min-height:0; display:flex; flex-direction:column;
  overflow:hidden; }
/* Show only the active tab's pane (full height); hide the others. */
body > .region[data-rpane] { display:none; }
body[data-rtab="queue"]    > .region[data-rpane="queue"],
body[data-rtab="activity"] > .region[data-rpane="activity"],
body[data-rtab="stats"]    > .region[data-rpane="stats"],
body[data-rtab="inbox"]    > .region[data-rpane="inbox"] { display:flex; }
/* Inside the shown region: header + tabs fixed; the body fills + scrolls. */
.region > h2, .region > .tabs { flex:0 0 auto; }
.region > .upload-card { flex:0 1 auto; min-height:0; overflow:auto; }
.region > .inner { flex:1 1 auto; min-height:0; overflow:auto; padding-top:0; }
.region > .inbox-split { flex:1 1 auto; min-height:0; padding-top:0; }
.region > .qsplit { flex:1 1 auto; min-height:0; grid-template-rows:minmax(0, 1fr); }
.region > .qgrid:not([hidden]) { flex:1 1 auto; min-height:0; overflow:auto; }
.rcaret { display:none; }   /* tabs replace per-region collapse */

/* Region tab bar */
/* Folder-style tabs that sit ON the active pane: the active tab shares the
   pane's background and overlaps its top border (margin -1px + z-index) so the
   two read as one tab+pane unit. Inactive tabs are recessed (page background). */
.region-tabs { display:flex; flex-wrap:wrap; align-items:center; gap:4px; margin-bottom:0;
  padding-left:6px; position:relative; z-index:1; }
.rtab { font-family:var(--sans); font-size:13px; font-weight:600; padding:9px 18px;
  border:1px solid var(--line); border-bottom:0; border-radius:10px 10px 0 0;
  margin-bottom:-1px; background:transparent; color:var(--muted); cursor:pointer;
  display:flex; align-items:center; gap:7px; transition:.12s; }
.rtab:hover:not(.active) { color:var(--ink); background:var(--panel2); }
.rtab.active { background:var(--panel); color:var(--accent); border-color:var(--accent); }   /* uniform accent outline */
.rtab-count { font-family:var(--mono); font-size:10px; font-weight:700; padding:1px 7px;
  border-radius:999px; background:var(--panel2); color:var(--accent); }
.rtab.active .rtab-count { background:var(--accent); color:var(--accent-ink); }
/* Region action buttons live on the tab-bar row; only the active tab's show. */
.rtab-actions { margin-left:auto; display:flex; align-items:center; gap:8px; }
.rtab-act { display:none; align-items:center; gap:8px; font-size:12px; color:var(--muted);
  text-transform:none; letter-spacing:0; }
body[data-rtab="queue"]    .rtab-act[data-rpane="queue"],
body[data-rtab="activity"] .rtab-act[data-rpane="activity"],
body[data-rtab="inbox"]    .rtab-act[data-rpane="inbox"] { display:flex; }

/* Brief highlight on the just-kicked-off Activity row(s) after "Process Selected",
   so the eye lands on the new job's progress. */
@keyframes rowFlash { 0%, 100% { background-color:transparent; }
  30% { background-color:rgba(86,179,240,.22); } }
.activity-table .job.row-flash > td { animation:rowFlash 1.1s ease-in-out 2; }
/* Persistent highlight on a row jumped-to from the Inbox (doesn't fade) -
   bracketed by an accent bar on the left AND right. */
#activity tr.jump-target > td { background:rgba(14,124,123,.13); }
#activity tr.jump-target > td:first-child { box-shadow:inset 3px 0 0 var(--accent); }
#activity tr.jump-target > td:last-child { box-shadow:inset -3px 0 0 var(--accent); }
/* Completion pulse: flash a finished job's produced-draft chips. */
.job-produced.chips-flash { animation:rowFlash 1.1s ease-in-out 2; border-radius:6px; }

/* Count badge on a region header (e.g. "Inbox (3)") - readable even collapsed. */
.region-count { font-family:var(--mono); font-size:10px; font-weight:700;
  color:var(--accent); background:var(--panel2); border:1px solid var(--line);
  border-radius:999px; padding:1px 7px; margin-left:6px; vertical-align:middle; }

/* buttons */
.btn { font-family:var(--sans); font-size:12px; font-weight:500; padding:8px 13px;
  border-radius:7px; border:1px solid var(--line); background:var(--raised); color:var(--ink);
  cursor:pointer; white-space:nowrap; }
/* Hover: FILL with the brand accent (like the Retry pill) - a clear color
   change, not just a border shift. :not(:disabled) keeps faded buttons inert;
   the colored variants get their own fills (higher specificity wins). */
.btn:not(:disabled):hover { background:var(--accent); color:var(--accent-ink);
  border-color:var(--accent); }
.btn:disabled { opacity:.45; cursor:not-allowed; }
.btn.primary { background:var(--accent); color:var(--accent-ink); border-color:var(--accent);
  font-weight:600; }
.btn.primary:not(:disabled):hover { filter:brightness(1.07); }
.btn.danger { color:var(--warn); border-color:rgba(212,112,110,.35); background:transparent; }
.btn.danger:not(:disabled):hover { background:var(--warn); color:#fff; border-color:var(--warn); }
.btn.verify { color:var(--accent); border-color:rgba(245,158,11,.35); background:transparent; }
.btn.verify:not(:disabled):hover { background:var(--accent); color:var(--accent-ink);
  border-color:var(--accent); }

/* pills */
.pill { font-family:var(--mono); font-size:10px; padding:3px 9px; border-radius:6px;
  letter-spacing:.04em; text-transform:uppercase; font-weight:600; }
.pill.draft { background:rgba(138,147,166,.15); color:var(--muted); }
.pill.approved { background:rgba(79,166,118,.16); color:var(--ok); }
.pill.published { background:rgba(245,158,11,.16); color:var(--accent); }
.pill.rejected { background:rgba(212,112,110,.16); color:var(--warn); }
.pill.src { background:rgba(56,189,248,.12); color:var(--info); }

/* upload card */
.plus { font-weight:700; margin-right:2px; }
.upload-card { background:var(--panel); border:1px solid var(--line);
  border-radius:12px; overflow:hidden; box-shadow:var(--shadow); }
.uc-head { display:flex; align-items:center; gap:8px;
  padding:0 14px; min-height:48px; border-bottom:1px solid var(--line);
  background:var(--panel2); background:var(--head-tint); }
.uc-collapse { background:none; border:none; color:var(--muted); cursor:pointer;
  padding:0 2px; line-height:1; }
.uc-collapse:hover { color:var(--ink); }
.uc-caret { display:inline-block; font-size:12px; transition:transform .15s; }
.inbox-split.upload-collapsed .uc-caret { transform:rotate(-90deg); }
.uc-title { font-weight:600; font-size:13.5px; color:var(--ink); }
.uc-close { background:none; border:none; color:var(--muted); font-size:20px;
  line-height:1; cursor:pointer; padding:0 4px; }
.uc-close:hover { color:var(--ink); }
.uc-body { display:grid; grid-template-columns:300px 1fr; gap:18px; padding:16px;
  align-items:stretch; }
.uc-left { display:flex; flex-direction:column; gap:14px; }
.uc-fields { display:flex; flex-direction:column; gap:12px; }
.fld { font-size:12px; color:var(--muted); display:flex; flex-direction:column;
  align-items:flex-start; gap:5px; }
.uc-fields select, .uc-fields input[type=date], #up-slug { width:100%;
  background:var(--panel); color:var(--ink); border:1px solid var(--line);
  border-radius:8px; padding:8px 10px; font-family:var(--sans); font-size:13px; }
.dropzone { border:1.5px dashed var(--line); border-radius:12px; padding:24px 18px;
  text-align:center; color:var(--muted); display:flex; flex-direction:column;
  align-items:center; justify-content:center; gap:9px; transition:.12s; min-height:210px; }
@media (max-width:760px) { .uc-body { grid-template-columns:1fr; } }
.dropzone.drag { border-color:var(--accent); background:rgba(14,124,123,.07); color:var(--ink); }
.dz-icon { color:var(--accent); opacity:.9; }
.dz-main { font-size:14px; color:var(--ink); font-weight:500; }
.dz-browse-btn { cursor:pointer; }
.dz-hint { font-size:12px; color:var(--muted); }
.up-files { display:flex; flex-wrap:wrap; gap:8px; }
.filechip { display:inline-flex; align-items:center; gap:8px; background:var(--panel);
  border:1px solid var(--line); border-radius:999px; padding:5px 6px 5px 12px;
  font-size:12.5px; color:var(--ink); }
.filechip small { color:var(--muted); font-family:var(--mono); font-size:11px; }
.chip-x { background:var(--raised); border:none; color:var(--muted); cursor:pointer;
  width:18px; height:18px; border-radius:50%; line-height:1; font-size:13px; }
.chip-x:hover { color:var(--warn); }
/* In-card submit footer (the "Add to inbox" button lives inside the card now). */
.uc-cardfoot { display:flex; align-items:center; gap:12px; margin-top:6px; }
.uc-progress { flex:1; height:6px; background:var(--raised); border-radius:999px; overflow:hidden; }
.uc-bar { height:100%; width:0; background:var(--accent); transition:width .12s; }
.uc-submit { margin-left:auto; }

/* The single upload card fills the upload column and blends into the panel
   (no inner border/shadow - the panel is the box). */
#upload-cards { padding:14px 16px 16px; }
.ucard { border:none; border-radius:0; padding:0; background:transparent;
  display:flex; flex-direction:column; gap:9px; }
.ucard-row { display:flex; gap:12px; align-items:flex-end; flex-wrap:wrap; }
.ucard .fld { flex-direction:column; align-items:flex-start; gap:4px; font-size:11px; }
/* Anonymize checkbox sits inline (checkbox + label on one row), bottom-aligned. */
.ucard .fld-anon { flex-direction:row; align-items:center; gap:6px; cursor:pointer;
  padding-bottom:6px; }
.ucard .fld-anon .uc-anon { margin:0; cursor:pointer; }
.ucard .uc-kind, .ucard .uc-date { background:var(--panel2); color:var(--ink);
  border:1px solid var(--line); border-radius:7px; padding:6px 8px;
  font-family:var(--sans); font-size:12.5px; }
.ucard .fld-label { width:100%; margin:10px 0 0; font-size:11px; }
.ucard .fld-hint { color:var(--faint); font-weight:400; }
.ucard .uc-label { width:100%; background:var(--panel2); color:var(--ink);
  border:1px solid var(--line); border-radius:7px; padding:6px 9px; margin-top:3px;
  font-family:var(--sans); font-size:12.5px; }
.ucard-dz { min-height:auto; padding:12px; flex-direction:column; gap:4px; }
.ucard-dz .dz-main { font-size:12.5px; color:var(--ink); }
.ucard-dz .dz-hint { font-size:11px; }
.uc-prev { font-family:var(--mono); font-size:11.5px; color:var(--muted); word-break:break-all; }
.uc-prev b { color:var(--ink); font-weight:600; }
.uc-status { font-size:12px; color:var(--muted); }
.uc-status.err-text { color:var(--warn); }
.uc-files { display:flex; flex-wrap:wrap; gap:6px; }
.ucard .filechip { font-size:12px; padding:4px 6px 4px 9px; }
.filechip .chip-kind { font-family:var(--mono); font-size:9px; text-transform:uppercase;
  letter-spacing:.04em; background:var(--raised); color:var(--muted); padding:2px 5px;
  border-radius:4px; margin-right:2px; }
/* ---- two-pane inbox: upload (left, collapsible) + queue (right) ---------- */
.inbox-split { display:flex; gap:16px; align-items:flex-start; height:100%;
  min-height:0; padding:14px; box-sizing:border-box; }
.inbox-split .upload-col { flex:0 0 400px; max-width:400px; }
.inbox-split .queue-col { flex:1 1 auto; min-width:0; min-height:0;
  display:flex; flex-direction:column; align-self:stretch; }
.queue-col .inner { flex:1 1 auto; overflow:auto; min-height:0; }
/* Collapsed: the upload becomes a slim full-width bar; the queue takes over. */
.inbox-split.upload-collapsed { flex-direction:column; }
.inbox-split.upload-collapsed .upload-col { flex:0 0 auto; width:100%; max-width:none; }
.inbox-split.upload-collapsed #upload-cards { display:none; }
.inbox-split.upload-collapsed .upload-card { box-shadow:none; }
/* Queue header: title + Run Research / Process Selected. */
.iq-head { display:flex; align-items:center; justify-content:space-between;
  gap:10px; margin-bottom:10px; padding:0 14px; min-height:48px;
  border:1px solid var(--line); border-radius:9px;
  background:var(--panel2); background:var(--head-tint); }
.iq-title { font-weight:600; font-size:13.5px; color:var(--head); }
.iq-actions { display:flex; align-items:center; gap:8px; }
/* "or Run Research" - the second input method, grouped with Upload. */
.upload-alt { display:flex; align-items:center; gap:10px; }
/* Expanded: centered + stacked below the upload card (so the popover, which
   opens down/right-aligned, stays fully on-screen). */
.upload-col > .upload-alt { flex-direction:column; gap:6px; margin-top:14px; }
/* Collapsed: JS moves it into the header; sit inline, pushed to the right
   (and drop the "or" - it only reads as a separator under the card). */
.uc-head .upload-alt { margin-left:auto; }
.uc-head .upload-alt-or { display:none; }
.upload-alt-or { font-family:var(--mono); font-size:10px; color:var(--faint);
  text-transform:uppercase; letter-spacing:.08em; }
/* Run Research: outlined-accent (border + text = accent, the hover fill color);
   fills with the accent on hover, like the app's other outline buttons. */
.btn[data-act="research-toggle"] { border-color:var(--accent); color:var(--accent);
  background:transparent; }
/* Stack on narrow screens. */
@media (max-width:900px) {
  .inbox-split { flex-direction:column; }
  .inbox-split .upload-col { flex:0 0 auto; width:100%; max-width:none; }
}

/* inbox + activity */
table.inbox { width:100%; border-collapse:collapse; font-size:13px; }
table.inbox td, table.inbox th { padding:8px 10px; text-align:left; border-bottom:1px solid var(--line2); }
table.inbox th { font-family:var(--mono); font-size:10px; color:var(--muted); text-transform:uppercase;
  letter-spacing:.08em; }
.ready { color:var(--ok); } .notready { color:var(--warn); }
/* A folder with an active Process job: locked row (no checkbox/delete) + spinner,
   and the status links to the live job in Recent Activity. */
.processing-st { color:var(--info); white-space:nowrap; }
.processing-st .spin { vertical-align:-1px; margin-right:6px; }
.retry-hint { color:var(--warn); font-size:11px; margin-left:6px; white-space:nowrap; }
/* Zebra striping so the queue reads as rows, not a flat block (header is row 1). */
table.inbox tr:nth-child(even) td { background:var(--panel2); }
table.inbox tr:hover td { background:var(--raised); }
/* Rows with a run are clickable (jump to Recent Activity). */
table.inbox tr[data-act="jump-activity"] { cursor:pointer; }

/* ---- Stats tab ---------------------------------------------------------- */
.stats-toolbar { display:flex; align-items:center; justify-content:space-between;
  gap:12px; flex-wrap:wrap; margin-bottom:12px; }
.stats-filters, .stats-actions { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.stats-filters select, .stats-filters input[type=date] { background:var(--panel2);
  color:var(--ink); border:1px solid var(--line); border-radius:7px; padding:6px 8px;
  font-family:var(--sans); font-size:12.5px; }
.stats-cols-pop .col-opt { display:block; padding:3px 4px; font-size:12.5px; cursor:pointer; }
.stats-cards { display:grid; grid-template-columns:1fr 1fr; gap:14px; margin-bottom:14px; }
@media (max-width:860px) { .stats-cards { grid-template-columns:1fr; } }
.stat-card { border:1px solid var(--line); border-radius:10px; background:var(--panel);
  padding:12px 14px; overflow:hidden; }
.stat-card-title { font-weight:600; font-size:12.5px; color:var(--head);
  margin:-12px -14px 12px; padding:8px 14px; border-bottom:1px solid var(--line);
  background:var(--panel2); background:var(--head-tint);
  display:flex; align-items:center; justify-content:space-between; }
/* funnel (horizontal bars) */
.funnel { display:flex; flex-direction:column; gap:7px; }
.funnel-row { display:flex; align-items:center; gap:10px; font-size:12px; }
.funnel-label { width:74px; color:var(--muted); flex:none; }
.funnel-track { flex:1; height:16px; background:var(--raised); border-radius:5px; overflow:hidden; }
.funnel-bar { display:block; height:100%; background:var(--accent); border-radius:5px; min-width:2px; }
.funnel-val { width:38px; text-align:right; font-family:var(--mono); color:var(--ink); flex:none; }
.funnel-foot { margin-top:8px; font-size:11px; color:var(--muted); }
/* published-per-week (stacked vertical bars) */
.ot-chart { display:flex; align-items:flex-end; gap:6px; height:120px; }
.ot-col { flex:1; display:flex; flex-direction:column; align-items:center; gap:4px;
  height:100%; justify-content:flex-end; min-width:12px; }
.ot-stack { width:100%; max-width:26px; display:flex; flex-direction:column;
  justify-content:flex-end; flex:1; }
.ot-fb, .ot-ig { width:100%; }
.ot-fb { background:var(--accent); border-radius:2px 2px 0 0; }
.ot-ig { background:var(--info); }
.ot-wk { font-size:9px; color:var(--muted); font-family:var(--mono); }
.ot-legend { font-size:10px; color:var(--muted); font-weight:400; display:inline-flex;
  align-items:center; gap:4px; }
.ot-key { width:9px; height:9px; border-radius:2px; display:inline-block; }
.ot-key-fb { background:var(--accent); } .ot-key-ig { background:var(--info); }
/* two-level table */
.stats-table-wrap { overflow:auto; }
table.stats-table { width:100%; border-collapse:collapse; font-size:12px; white-space:nowrap; }
.stats-table th, .stats-table td { padding:6px 9px; text-align:left;
  border-bottom:1px solid var(--line2); }
.stats-table th { font-family:var(--mono); font-size:10px; color:var(--muted);
  text-transform:uppercase; letter-spacing:.05em; position:sticky; top:0; background:var(--panel); }
.stats-table .st-exp { width:22px; }
.st-batch { cursor:pointer; font-weight:600; color:var(--head); }
.st-batch:hover td { background:var(--raised); }
.st-caret { display:inline-block; color:var(--muted); font-size:11px; }
.st-row td { color:var(--muted); font-weight:400; }
.st-row td[data-col="draft_id"] { font-family:var(--mono); }
.stats-table .col-hidden { display:none; }
.inbox-actions { text-align:right; width:40px; }
.icon-btn { background:none; border:1px solid var(--line); color:var(--muted);
  cursor:pointer; width:24px; height:24px; border-radius:6px; line-height:1; font-size:14px; }
.icon-btn:hover { color:var(--warn); border-color:rgba(212,112,110,.4); }

/* Reprocess section: processed folders eligible for re-running the pipeline. */
.reproc-section { margin-top:10px; padding:10px 12px 6px; border-top:1px dashed var(--line2); }
.reproc-title { font-family:var(--mono); font-size:10px; color:var(--muted);
  text-transform:uppercase; letter-spacing:.08em; margin-bottom:6px; }
.reproc-title code { font-size:10px; }
table.inbox.reproc { opacity:.92; }
table.inbox.reproc .inbox-actions { width:auto; }
table.inbox.reproc .btn { padding:4px 9px; font-size:11px; font-family:var(--mono); }
/* Activity is a real HTML table -- columns naturally align across every row
   without any grid/flex math. Group headers + produced-drafts strips are
   colspan rows woven into the same tbody, so the table stays a single
   coherent grid. */
.activity-table { width:100%; border-collapse:collapse;
  font-size:13px; table-layout:fixed; }
/* Column widths via <colgroup>. table-layout:fixed locks these in so the
   browser doesn't reflow based on content -- guarantees vertical alignment. */
.activity-table col.col-kind   { width:auto;  }    /* flex; takes remaining */
.activity-table col.col-st     { width:78px;  }    /* fits FAILED */
.activity-table col.col-stages { width:600px; }    /* fits 4 stages on one line incl AI marks */
.activity-table col.col-time   { width:150px; }    /* compact time + muted duration */
.activity-table col.col-log    { width:118px; }    /* Retry pill + Log link, one line */

.activity-table td { padding:10px 8px; vertical-align:top;
  border-bottom:1px solid var(--line2); }
.activity-table tr:last-child td { border-bottom:0; }
/* The main kind line is the first row inside the kind cell. Its line-height
   matches the table-row line-height of other cells (status pill, stages, time,
   log) so they all visually start at the same baseline. */
.activity-table td .kind-line { line-height:1.5; padding:2px 0; }

/* Date group header row -- clickable, expands/collapses its jobs. The header
   is a single row spanning all columns, sitting inside the same table. */
.job-group-row { cursor:pointer; user-select:none; }
.job-group-row:hover .job-group-header { color:var(--ink); }
.job-group-row:hover td { background:color-mix(in srgb, var(--accent) 22%, var(--panel)); }
.job-group-row td { padding:12px 12px 4px;
  border-top:1px solid var(--line); border-top:1px solid var(--group-divider);
  border-bottom:1px solid var(--line); border-bottom:1px solid var(--group-divider);
  background:var(--panel2); background:var(--head-tint); }
.activity-table tr.job-group-row:first-child td { border-top:0; padding-top:6px;
  border-radius:10px 10px 0 0; }
.job-group-header { font-family:var(--mono); font-size:10px; letter-spacing:.12em;
  text-transform:uppercase; color:var(--muted); }
.job-group-header .cnt { color:var(--faint); margin-left:3px; }
.job-group-header .grp-caret { display:inline-block; width:14px; color:var(--ink);
  font-size:13px; font-weight:700; vertical-align:middle;
  transition:transform .15s ease; }
/* When collapsed, the header bottom-border becomes the divider to the next
   group (since the rows below are hidden). */
.job-group-row.collapsed td { padding-bottom:12px;
  border-bottom:1px solid var(--line); border-bottom:1px solid var(--group-divider); }

/* Job row: status-tinted left border bar (3px). Source-order matters --
   the more-specific .job-bar-* rule comes after the base .job rule. */
.job { border-left:3px solid transparent; }
.job td:first-child { padding-left:10px; }
.job.job-bar-done    { border-left-color:rgba(79,166,118,.55); }
.job.job-bar-failed  { border-left-color:rgba(212,112,110,.65); }
.job.job-bar-running { border-left-color:rgba(245,158,11,.65);
                       background:rgba(245,158,11,.05); }
.job.job-bar-queued  { border-left-color:rgba(138,147,166,.45); }
.job.job-bar-skipped { border-left-color:rgba(138,147,166,.45); }
.job.job-bar-partial { border-left-color:rgba(245,158,11,.65); }
/* Zebra striping: every other job (computed in the template via loop.index0)
   gets a faint background tint so the eye can track wide rows. */
.job.job-zebra { background:var(--row-alt); }
.job.job-zebra.job-bar-running { background:rgba(245,158,11,.05); }

/* Cell-specific styling. */
.activity-table td.kind { color:var(--ink); word-wrap:break-word;
  overflow-wrap:anywhere; }
.activity-table td.st-cell { text-align:left; }
.activity-table td.stages-cell .stages { display:flex; gap:5px; flex-wrap:wrap;
  align-items:center; }
.activity-table td.job-time { font-family:var(--mono); font-size:11px;
  color:var(--muted); text-align:right; white-space:nowrap; overflow:hidden; }
.activity-table td.job-time .job-dur { color:var(--faint); }   /* duration is secondary */
.activity-table td.job-log-cell { text-align:right; white-space:nowrap; }

/* Sub-rows (the per-row NOTE and the produced-drafts strip) each live on their
   own full-width <tr class="job-sub ..."> so the content gets the WHOLE table
   width - no wrap, no ellipsis. The parent row drops its bottom border
   (.has-sub) and the sub-rows repeat its left status-bar + zebra so the stack
   reads as one tall row. A note row followed by a produced row drops its own
   bottom border too (.has-after). */
.activity-table tr.job.has-sub > td { border-bottom:0; }
.job-sub > td { padding:0 8px 9px 10px; border-bottom:1px solid var(--line2); }
.job-sub.has-after > td { border-bottom:0; }
.job-sub.job-bar-done    { border-left:3px solid rgba(79,166,118,.55); }
.job-sub.job-bar-failed  { border-left:3px solid rgba(212,112,110,.65); }
.job-sub.job-bar-skipped { border-left:3px solid rgba(138,147,166,.45); }
.job-sub.job-bar-partial { border-left:3px solid rgba(245,158,11,.65); }
.job-sub.job-bar-running { border-left:3px solid rgba(245,158,11,.65);
                           background:rgba(245,158,11,.05); }
.job-sub.job-bar-queued  { border-left:3px solid rgba(138,147,166,.45); }
.job-sub.evt-bar-approve { border-left:3px solid rgba(79,166,118,.55); }
.job-sub.evt-bar-reject  { border-left:3px solid rgba(212,112,110,.6); }
.job-sub.evt-bar-edit    { border-left:3px solid var(--planned); }
.job-sub.job-zebra { background:var(--row-alt); }
.job-sub.job-zebra.job-bar-running { background:rgba(245,158,11,.05); }

/* The note sub-row content: it has the WHOLE table width, so it shows the full
   reason on one line (wrapping only if a very long error exceeds the table
   edge - never truncated). Muted, warn-tinted on a failure; the status pill
   above already carries the state color. */
.job-note { font-size:11.5px; line-height:1.4; padding-left:8px; color:var(--muted); }
.job-note.job-note-failed { color:var(--warn); }

/* Queue scope filter (e): clicking an Activity row (or its produced strip, or a
   Batch chip) scopes the queue to that row's draft(s); a chip in the queue-tabs
   row shows the active scope + a × to clear. Clickable rows get a pointer +
   hover so the affordance is visible. */
.activity-table tr.job[data-act="filter-related"] { cursor:pointer; }
.activity-table tr.job[data-act="filter-related"]:hover .kind-name { color:var(--accent); }
/* The row(s) currently driving the queue scope: tinted + accent left bar so it's
   obvious which row's drafts the queue is filtered to. */
.activity-table tr.job.scope-active { background:var(--scope-bg);
  border-left-color:var(--accent); }
.activity-table tr.job.scope-active .kind-name { color:var(--accent); }
.job-produced[data-act] { cursor:pointer; border-radius:6px; }
.job-produced[data-act]:hover { background:var(--row-alt); }
.produced-filter-hint { font-family:var(--mono); font-size:10px; color:var(--accent);
  margin-left:8px; opacity:0; transition:opacity .1s; }
.job-produced[data-act]:hover .produced-filter-hint { opacity:1; }
.batch-chip[data-act] { cursor:pointer; }
.batch-chip[data-act]:hover { border-color:var(--accent); color:var(--accent); }
/* Sits right after the status tabs (viewtoggle keeps its margin-left:auto far
   right). A state pill ("FILTERED TO <id>") + a separate "SHOW ALL" button, both
   in the uppercase-mono vocabulary of the tabs so the row reads consistently. */
.batch-filter { display:inline-flex; align-items:center; gap:7px; }
.bf-chip { display:inline-flex; align-items:center; gap:6px; padding:3px 9px;
  border:1px solid var(--line); border-radius:12px; background:rgba(20,140,130,.07); }
.bf-label { font-family:var(--mono); font-size:9.5px; text-transform:uppercase;
  letter-spacing:.06em; color:var(--muted); }
.batch-filter-id { font-family:var(--mono); font-size:11px; color:var(--accent); }
.batch-filter-x { font-family:var(--mono); font-size:10px; text-transform:uppercase;
  letter-spacing:.05em; color:#fff; background:var(--accent); border:0;
  border-radius:8px; padding:3px 10px; cursor:pointer; }
.batch-filter-x:hover { filter:brightness(1.08); }   /* subtle, stays green */

/* The strip itself: no wrap, no ellipsis -- the row has the whole table
   width to work with, which fits typical 4-10 draft chips comfortably. */
.job-produced { font-size:11.5px; padding-left:8px; }
.job-produced-label { font-family:var(--mono); font-size:10px; color:var(--muted);
  text-transform:uppercase; letter-spacing:.06em; margin-right:5px; }
.produced-chip { display:inline-block; font-family:var(--mono); font-size:10px;
  padding:1px 6px; border-radius:5px; border:1px solid var(--line);
  background:var(--panel2); color:var(--muted); text-decoration:none;
  margin-right:4px; vertical-align:middle; }
.produced-chip:hover { color:var(--ink); border-color:var(--accent);
  text-decoration:none; }
.produced-chip.ps-draft     { color:var(--muted); }
.produced-chip.ps-approved  { color:var(--ok);   border-color:rgba(79,166,118,.35); }
.produced-chip.ps-published { color:var(--accent); border-color:rgba(245,158,11,.45); }
.produced-chip.ps-rejected  { color:var(--warn); border-color:rgba(212,112,110,.35);
  text-decoration:line-through; }

/* Single-draft target shown as the draft's angle/headline (human label, not the
   UUID). A subtle inline link -- dashed underline signals it jumps to the draft;
   the full id lives in the title tooltip. */
.kind-target-title { color:var(--muted); cursor:pointer; text-decoration:none;
  font-family:var(--sans); font-size:12.5px;
  border-bottom:1px dashed transparent; padding-bottom:1px; }
.kind-target-title:hover { color:var(--accent); border-bottom-color:var(--accent); }
.job .st { width:78px; font-family:var(--mono); font-size:11px; letter-spacing:.04em;
  text-transform:uppercase; font-weight:600; }
.job .kind { color:var(--ink); min-width:0; }
/* First line (kind + target chip): stays single-line, ellipsised if long. */
.job .kind-main { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.job .kind-name   { font-weight:600; color:var(--head); }
/* "×N" badge on a collapsed run of identical Activity rows (UX-4). Times in the
   hover tooltip. */
.dup-badge { font-family:var(--mono); font-size:10px; font-weight:600;
  color:var(--muted); background:var(--panel2); border:1px solid var(--line);
  border-radius:9px; padding:0 6px; margin-left:7px; vertical-align:middle;
  cursor:default; }
.job .kind-target { color:var(--muted); font-family:var(--mono); font-size:12px; }
/* Second line: the "why" behind a SKIPPED/FAILED (or 0-draft) row. Muted,
   smaller, single line - ellipsised with the full text on hover (title), so it
   reads clean + consistent with the line above instead of wrapping in the
   narrow kind column. Failures get a subtle warn tint; everything else stays
   muted so the status pill carries the color. */
.job .kind-note { margin-top:2px; font-size:11px; line-height:1.4;
  color:var(--muted); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.job .kind-note-failed { color:var(--warn); }
/* Clickable draft-target inside an Activity row -- jumps to that draft in the
   queue. Underline on hover so it's recognizable as a link without being noisy. */
.job .kind-target-link { color:var(--info); text-decoration:none;
  border-bottom:1px dashed transparent; padding-bottom:1px; }
.job .kind-target-link:hover { border-bottom-color:var(--info); text-decoration:none; }
.job .job-log { font-size:11px; color:var(--muted); font-family:var(--mono);
  text-transform:uppercase; letter-spacing:.06em;
  background:none; border:none; padding:0; cursor:pointer; }   /* muted secondary link */
.job .job-log:hover { color:var(--ink); text-decoration:underline; }
/* Retry on a failed job row - an accent PILL (distinct from the muted Log link,
   so there's no "is this a link or a button" confusion); fills on hover. */
.job .job-retry { font-family:var(--mono); font-size:10px; letter-spacing:.04em;
  color:var(--accent); background:none; border:1px solid var(--accent);
  border-radius:999px; padding:1px 8px; margin-right:8px; cursor:pointer;
  vertical-align:middle; }
.job .job-retry:hover { background:var(--accent); color:var(--accent-ink); }

.st.queued  { color:var(--muted); }
.st.done    { color:var(--ok); }
.st.failed  { color:var(--warn); }
.st.skipped { color:var(--muted); }
.st.partial { color:#f59e0b; }
.st.running { color:var(--accent); animation:blink 1.1s ease-in-out infinite; }
@keyframes blink { 0%, 100% { opacity:1; } 50% { opacity:.4; } }
.spin { width:11px; height:11px; border:2px solid var(--line); border-top-color:var(--accent);
  border-radius:50%; display:inline-block; animation:spin .7s linear infinite; flex:none; }
@keyframes spin { to { transform:rotate(360deg); } }

/* Stage badges - one pill per stage of the job's kind. State drives color
   only; the icon (✓/●/✗) tells the operator what's done at a glance. Pending
   pills are intentionally bare (no leading dot) so the row reads as a
   left-to-right progress strip rather than a row of identical bullets. */
.stages { display:flex; gap:5px; flex:none; align-items:center; }
.stage { font-family:var(--sans); font-size:11px; padding:3px 9px; border-radius:11px;
  border:1px solid var(--line); color:var(--muted); background:var(--panel2);
  line-height:1.6; white-space:nowrap; display:inline-flex; align-items:center;
  gap:5px; font-weight:500; }
.stage .ico { font-size:10px; line-height:1; }
.stage.done    { color:var(--ok);     border-color:rgba(79,166,118,.35);
                 background:rgba(79,166,118,.10); }
.stage.running { color:var(--accent); border-color:rgba(245,158,11,.45);
                 background:rgba(245,158,11,.12); animation:blink 1.1s ease-in-out infinite; }
.stage.failed  { color:var(--warn);   border-color:rgba(212,112,110,.45);
                 background:rgba(212,112,110,.10); }
.stage.pending { opacity:.65; }
.stage.pending .ico { display:none; }
/* "skipped" = ran but did nothing (e.g. publish with all targets already
   posted / testing-skipped). Muted + dashed so it reads as a deliberate no-op,
   visually distinct from a green "done" pill. */
.stage.skipped { opacity:.7; color:var(--muted); border-style:dashed;
                 font-style:italic; }
.stage.skipped .ico { display:none; }
/* "partial" = posted to some targets, not all. Amber, distinct from green done. */
.stage.partial { color:#f59e0b; border-color:rgba(245,158,11,.45);
                 background:rgba(245,158,11,.10); }
.stage.partial .ico { display:none; }
/* Tiny "AI" badge on stages that hit the LLM, so cost-bearing steps are
   visually distinct from local-only ones at a glance. */
.stage .ai-mark { font-family:var(--mono); font-size:8.5px; letter-spacing:.06em;
  background:var(--planned); color:#fff; padding:1px 4px; border-radius:4px;
  margin-left:2px; font-weight:600; line-height:1.2; }
.stage.pending .ai-mark { opacity:.85; }
/* Per-feed publish badges (FB ✓ / IG ✗ ...) - same pill shape as a stage, one
   per platform, colored by that feed's outcome. Reason lives in the title. */
.feed { font-family:var(--sans); font-size:11px; padding:3px 9px; border-radius:11px;
  border:1px solid var(--line); color:var(--muted); background:var(--panel2);
  white-space:nowrap; display:inline-flex; align-items:center; font-weight:600;
  cursor:default; }
.feed-success { color:var(--ok);   border-color:rgba(79,166,118,.35);  background:rgba(79,166,118,.10); }
.feed-failed  { color:var(--warn); border-color:rgba(212,112,110,.45);  background:rgba(212,112,110,.10); }
.feed-skipped { color:var(--muted); border-style:dashed; }

/* Unified-feed synchronous-action rows (approve / reject / edit) + the action
   filter dropdown. Event rows reuse the .job table layout but carry no stages
   or log; their left bar + status pill are colored by the action. */
.evt-hidden { display:none !important; }            /* type-filtered out */
.job.evt.evt-bar-approve { border-left-color:rgba(79,166,118,.55); }
.job.evt.evt-bar-reject  { border-left-color:rgba(212,112,110,.6); }
.job.evt.evt-bar-edit    { border-left-color:var(--planned); }
.st.evt-approve { color:var(--ok); }
.st.evt-reject  { color:var(--warn); }
.st.evt-edit    { color:var(--muted); }
.evt-by  { color:var(--muted); font-family:var(--mono); font-size:11.5px; }
.evt-tag { font-family:var(--mono); font-size:9.5px; padding:1px 5px; border-radius:4px;
  background:var(--panel); color:var(--muted); margin-left:5px; }
/* action filter (dropdown lives in the Activity header). Scoped to override the
   generic `.hpop label { display:grid }` so each option is a clean one-line row:
   [checkbox] [label, left-aligned] [count]. Compact + regular-weight so it reads
   as a crisp checklist rather than a heavy menu. */
.evt-filter-summary { color:var(--muted); font-size:11px; font-family:var(--mono); }
.evt-filter-pop { width:184px; padding:8px; gap:1px; }     /* tighten .hpop 13px/11px */
.evt-filter-pop .hpop-title { margin-bottom:4px; }
.evt-filter-pop .evt-opt { display:flex; align-items:center; gap:8px; margin:0;
  padding:4px 6px; border-radius:5px; cursor:pointer; white-space:nowrap;
  line-height:1.3; }
.evt-filter-pop .evt-opt:hover { background:var(--row-alt); }
.evt-filter-pop .evt-opt input { flex:none; margin:0; width:13px; height:13px;
  accent-color:var(--accent); }
.evt-filter-pop .evt-opt-l { flex:1; text-align:left; font-family:var(--sans);
  font-weight:400; font-size:12.5px; color:var(--ink); }
.evt-filter-pop .evt-count { flex:none; color:var(--muted); font-family:var(--mono);
  font-size:10.5px; }
.note { color:var(--muted); font-size:12px; }

/* filter tabs */
.tabs { display:flex; gap:6px; padding:6px 14px 10px; flex-wrap:wrap;
  border-bottom:1px solid var(--line); }
.tab { font-family:var(--mono); font-size:11px; text-transform:uppercase; letter-spacing:.05em;
  padding:6px 11px; border-radius:7px; cursor:pointer; border:1px solid transparent;
  background:transparent; color:var(--muted); }
.tab:hover { color:var(--ink); background:var(--raised); }
.tab.active { color:var(--ink); background:var(--raised); border-color:var(--line); }
.tab.active .cnt { color:var(--accent); }
.tab .cnt { opacity:.8; }

/* Approval Queue filter toolbar: tint the chrome bar (it's a header surface,
   not content), and lift the ACTIVE chip + the List/Grid toggle to a solid
   white pill so the selection still reads clearly on the tint. */
#queue-tabs { background:var(--panel2); background:var(--head-tint);
  border:1px solid var(--line); border-radius:10px 10px 0 0; }
#queue-tabs .tab.active { background:var(--panel);
  border-color:var(--group-divider); box-shadow:0 1px 2px rgba(0,0,0,.06); }
#queue-tabs .viewtoggle { background:var(--panel); }

/* "Needs attention" - a cross-cut filter (warned drafts still awaiting action),
   set apart from the mutually-exclusive status tabs; amber only when >0 (the
   .has-warn class is toggled by applyFilter). Neutral/muted at zero. */
.tab.tab-attention { margin-left:6px; }
.tab.tab-attention.has-warn, .tab.tab-attention.has-warn .cnt { color:#b45309; }
:root[data-theme="dark"] .tab.tab-attention.has-warn,
:root[data-theme="dark"] .tab.tab-attention.has-warn .cnt { color:#fbbf24; }

/* Retention tier tabs (Older / Archived) - set apart from the status tabs by a
   thin divider; Archived is staff-only (rendered only when show_dev). */
.tab-sep { width:1px; align-self:stretch; margin:2px 5px; background:var(--line);
  display:inline-block; }
.tab.tab-archived { color:var(--faint); }

/* "kept" marker on an unarchived (pinned) row - it overrides the age policy. */
.pin-flag { font-family:var(--mono); font-size:10px; color:var(--accent);
  background:rgba(14,124,123,.10); border:1px solid rgba(14,124,123,.30);
  border-radius:999px; padding:0 6px; line-height:16px; white-space:nowrap; }

/* Archived (cold-storage) view - replaces the list/grid pane when its tab is on. */
.archived-panel { flex:1 1 auto; min-height:0; overflow:auto; padding:10px 16px 18px;
  border:1px solid var(--line); border-top:0; border-radius:0 0 10px 10px; }
/* Auto-archive scheduler status + controls (staff). */
.arch-auto { display:flex; align-items:center; gap:9px; flex-wrap:wrap;
  padding:9px 11px; margin-bottom:10px; border:1px solid var(--line);
  border-radius:9px; background:var(--panel2); font-size:12.5px; color:var(--ink); }
.arch-auto .auto-txt { margin-right:auto; color:var(--muted); }
.arch-auto .auto-txt b { color:var(--ink); }
.auto-dot { width:8px; height:8px; border-radius:50%; flex:none; }
.auto-dot.on { background:var(--ok, #1f9d55); box-shadow:0 0 0 3px rgba(31,157,85,.15); }
.auto-dot.off { background:var(--muted); }
.arch-bar { display:flex; align-items:center; justify-content:space-between;
  gap:12px; flex-wrap:wrap; margin-bottom:10px; }
.arch-filter { display:flex; align-items:flex-end; gap:10px; flex-wrap:wrap; }
.arch-fld { display:flex; flex-direction:column; gap:3px; font-size:11px;
  color:var(--muted); }
.arch-fld input { background:var(--panel2); color:var(--ink);
  border:1px solid var(--line); border-radius:7px; padding:6px 8px;
  font-family:var(--sans); font-size:12.5px; }
.arch-count { font-family:var(--mono); font-size:11px; color:var(--muted); }
/* Folder-level "Unarchive (N)" on an archived batch header - same slot as the
   queue's "Reject remaining", but neutral/accent (restoring isn't destructive). */
.batch-restore { flex:none; font-family:var(--sans); font-size:11px; font-weight:600;
  padding:2px 10px; border:1px solid var(--accent); border-radius:999px;
  background:transparent; color:var(--accent); cursor:pointer; }
.batch-restore:hover { background:var(--accent); color:#fff; }
/* Archived rows open a read-only preview on click. */
#archived-panel .crow { cursor:pointer; }
/* Read-only banner at the top of an archived draft's preview. */
.readonly-note { font-size:12px; color:var(--muted); background:var(--panel2);
  border:1px solid var(--line); border-radius:8px; padding:7px 10px; margin:0 0 10px; }

/* Detail-pane "Archive" action (staff): a neutral, set-apart control. */
.actions .act-archive { margin-left:8px; color:var(--muted); }
.actions .act-archive:hover { color:var(--ink); border-color:var(--line); }

/* view toggle (List | Grid) */
.viewtoggle { margin-left:auto; display:inline-flex; border:1px solid var(--line);
  border-radius:7px; overflow:hidden; }
.vbtn { font-family:var(--mono); font-size:11px; text-transform:uppercase; letter-spacing:.05em;
  padding:5px 12px; background:transparent; border:none; color:var(--muted); cursor:pointer; }
.vbtn.active { background:var(--raised); color:var(--ink); }

/* grid (Instagram-style gallery) */
.qgrid { display:grid; grid-template-columns:repeat(auto-fill, minmax(168px, 1fr));
  gap:10px; padding:14px; border:1px solid var(--line); border-top:0;
  border-radius:0 0 10px 10px; }
.gtile { position:relative; aspect-ratio:1/1; border-radius:9px; overflow:hidden;
  cursor:pointer; background:var(--panel2); border:1px solid var(--line); }
.gtile:hover { border-color:var(--accent); }
.gtile img, .gtile video { width:100%; height:100%; object-fit:cover; display:block; }
.gtext { padding:14px; font-size:12.5px; line-height:1.45; color:var(--muted);
  height:100%; overflow:hidden; display:flex; align-items:center; }
.gbadge { position:absolute; bottom:7px; left:7px; }
.gsel { position:absolute; top:7px; left:7px; width:17px; height:17px; cursor:pointer;
  accent-color:var(--accent); z-index:2; }
.gtile.selected { outline:2px solid var(--accent); outline-offset:-2px; }
.gplay { position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  color:#fff; font-size:30px; text-shadow:0 1px 6px rgba(0,0,0,.6); pointer-events:none; }
.gwarn { position:absolute; top:7px; right:7px; color:#fff; background:rgba(212,112,110,.85);
  border-radius:5px; padding:1px 6px; font-size:11px; }

/* ── split: list (left) + detail (right) ───────────────────────────────── */
.qsplit { display:grid; grid-template-columns:minmax(360px,480px) 1fr;
  border:1px solid var(--line); border-top:0; border-radius:0 0 10px 10px;
  overflow:hidden; }
.qlist { border-right:1px solid var(--line); min-height:0; overflow:auto; }
/* Detail pane is a flex column: the content scrolls in .detail-scroll, while the
   action bar (.actions) is a fixed footer below it -- so nothing scrolls past
   the actions. (Was overflow:auto with a sticky footer, which let content show
   through the bottom padding.) */
.qdetail { min-height:0; display:flex; flex-direction:column; overflow:hidden; }
.detail-scroll { flex:1 1 auto; min-height:0; overflow:auto; padding:18px 20px; }

/* Sticky "select all" header above the list rows (bulk-select). */
.qlist-selall { position:sticky; top:0; z-index:2; display:flex; align-items:center;
  gap:8px; padding:7px 12px; background:var(--panel); border-bottom:1px solid var(--line2);
  font-size:12px; color:var(--muted); cursor:pointer; user-select:none; }
.qlist-selall input { cursor:pointer; margin:0; }
.menu-item.bulk-danger { color:var(--warn); }   /* reject is destructive */

/* Batch groups in the queue list (collapsible, newest first) */
.batch + .batch { border-top:1px solid var(--line); border-top:1px solid var(--group-divider); }
.batch-head { display:flex; align-items:center; gap:8px; padding:7px 12px;
  background:var(--panel2); background:var(--head-tint); cursor:pointer; font-size:11.5px; line-height:1.3;
  white-space:nowrap; overflow:hidden; }
.batch-head:hover { background:var(--raised); background:color-mix(in srgb, var(--accent) 22%, var(--panel)); }
/* Close the header band with a clear divider when its drafts are showing. */
.batch:not(.collapsed) .batch-head { border-bottom:1px solid var(--group-divider); }
.batch-caret { background:none; border:0; color:var(--muted); cursor:pointer;
  font-size:13px; font-weight:700; padding:0; line-height:1; flex:none; transition:transform .12s; }
.batch-label { font-weight:700; color:var(--head); flex:none; }
.batch-id { font-family:var(--mono); font-size:10px; color:var(--muted); font-weight:400; }
.batch-date { color:var(--muted); flex:none; }
.batch-stats { color:var(--muted); flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; }
.batch-clear { flex:none; font-family:var(--sans); font-size:11px; font-weight:600;
  padding:2px 10px; border:1px solid rgba(212,112,110,.4); border-radius:999px;
  background:transparent; color:var(--warn); cursor:pointer; }
.batch-clear:hover { background:var(--warn); color:#fff; border-color:var(--warn); }
.batch.collapsed .batch-rows { display:none; }
.batch.collapsed .batch-caret { transform:rotate(-90deg); }

/* list rows */
.crow { display:flex; align-items:flex-start; gap:10px; padding:12px 14px; cursor:pointer;
  border-bottom:1px solid var(--line2);
  border-left:3px solid transparent; border-right:3px solid transparent; }
.crow:nth-child(even) { background:var(--row-alt); }   /* zebra striping */
.crow:hover { background:var(--raised); }
/* Selected row: accent-tinted background with accent bars on BOTH left and
   right edges so the row is clearly bookended. No bold text inside -- the
   color shift + bars are enough; bold made the row jiggle on selection. */
.crow.active { background:rgba(245,158,11,.14);
  border-left-color:var(--accent); border-right-color:var(--accent); }
:root[data-theme="light"] .crow.active { background:rgba(14,124,123,.10); }
.crow.active .csnip { color:var(--ink); }    /* full opacity, no bold */
.crow .card-select { margin-top:3px; }
.crow-main { flex:1; min-width:0; }
.crow-top { display:flex; align-items:center; gap:6px; margin-bottom:4px;
  flex-wrap:wrap; }
.crow-top .crow-date { margin-left:auto; font-family:var(--mono); font-size:10px; color:var(--faint); }
/* Master row body: bold ANGLE line + muted CAPTION line beneath the chip
   header. Two pieces of info per row, each on its own single ellipsised
   line -- skim the angles to find what to review, the caption supports. */
.crow-sub { min-width:0; margin-top:5px; }
.crow-sub .cangle { font-size:13.5px; font-weight:600; color:var(--ink);
  line-height:1.4; margin-bottom:2px;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.crow-sub .csnip { font-size:12.5px; color:var(--ink); opacity:.72;
  line-height:1.4; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }

/* Generic chip pattern -- shared by type / id / batch chips across master row,
   detail header, and audit modal header. Each chip carries data-* attributes
   (data-post-type / data-draft-id / data-batch-id) so future filtering can
   wire click-to-filter without restructuring the HTML. */
.chip { font-family:var(--mono); font-size:10px; padding:2px 7px; border-radius:5px;
  border:1px solid var(--line); background:var(--panel2); color:var(--muted);
  letter-spacing:.04em; line-height:1.4; white-space:nowrap; }
/* type-chip: post_type display (Img / Txt / Reel). Subtle accent so it
   doesn't compete with the status pill but is still recognizable. */
.type-chip { color:var(--head); border-color:var(--line); }
/* id-chip: the draft id. Plain mono inside a chip outline -- the same
   visual treatment as before, just inside a bordered box for consistency. */
.id-chip { color:var(--muted); }
/* batch-chip: "Batch <8-char-prefix>". Full id stays in the tooltip and is
   available in the audit modal. */
.batch-chip { color:var(--planned); border-color:rgba(167,139,250,.30);
  background:rgba(167,139,250,.08); }
.warnflag { font-family:var(--mono); font-size:10px; color:var(--warn);
  background:rgba(212,112,110,.12); padding:1px 6px; border-radius:5px; white-space:nowrap; }
/* Backdate badge: a post dated in the PAST is important context before
   publishing, so it's a solid amber attention pill (theme accent) rather than the
   muted note it used to be -- amber/teal "noteworthy", not red "error". */
.backdate-badge { font-family:var(--sans); font-size:11px; font-weight:700;
  letter-spacing:.02em; color:var(--accent-ink); background:var(--accent);
  border-radius:999px; padding:3px 10px; white-space:nowrap;
  display:inline-flex; align-items:center; gap:5px; }

/* detail pane */
.qdetail .empty-detail { color:var(--muted); text-align:center; padding:60px 20px; }
.detail-head { display:flex; align-items:center; gap:8px; margin-bottom:14px; flex-wrap:wrap; }
.detail-head .cid { font-family:var(--mono); font-size:11px; color:var(--muted); }
.detail-grid { display:grid; grid-template-columns:1fr 320px; gap:22px; align-items:start; }
.pillar { font-family:var(--mono); font-size:11px; color:var(--accent); letter-spacing:.1em;
  text-transform:uppercase; margin-bottom:7px; }
.angle { font-size:18px; font-weight:600; line-height:1.35; margin-bottom:8px; letter-spacing:-.01em; }
/* The on-card headline (f): shown beneath the angle so the operator sees the
   big text the graphic will render, distinct from the caption. */
.headline-line { font-size:14px; color:var(--head); line-height:1.4; margin-bottom:12px; }
.headline-line .hl-label { font-family:var(--mono); font-size:9px; text-transform:uppercase;
  letter-spacing:.08em; color:var(--muted); border:1px solid var(--line);
  border-radius:4px; padding:1px 5px; margin-right:8px; vertical-align:middle; }
.caption { background:var(--panel2); padding:13px; border-radius:9px; font-size:13.5px; line-height:1.55;
  white-space:pre-wrap; border:1px solid var(--line2); margin-bottom:14px; }
.section-title { font-family:var(--mono); font-size:10px; letter-spacing:.12em; text-transform:uppercase;
  color:var(--muted); margin:16px 0 7px; }

/* Collapsible detail sections via <details>/<summary>. Native browser collapse,
   no JS. The <summary> reuses .section-title styling so the look is unchanged
   when expanded; collapsed sections just hide their body. */
.dsection { margin:14px 0; }
.dsection > summary { list-style:none; cursor:pointer; display:flex; align-items:center;
  gap:4px; user-select:none; margin:0 0 6px; }
.dsection > summary::-webkit-details-marker { display:none; }       /* Safari */
.dsection > summary::marker { content:""; }                          /* others */
.dsection > summary .caret { display:inline-block; font-size:13px; color:var(--ink);
  font-weight:700; transition:transform .15s ease; transform-origin:center;
  line-height:1; margin-right:2px; }
.dsection[open] > summary .caret { transform:rotate(90deg); }
.dsection > summary:hover .caret { color:var(--accent); }
.dsection > summary .cnt { color:var(--muted); font-weight:400; margin-left:2px; }

/* Warnings as a collapsible section. The body keeps the red-tinted panel for
   urgency; the summary stays themed-neutral so a stack of section titles all
   line up vertically. */
.dsection.warnings > summary { color:var(--warn); }
.dsection.warnings > div, .dsection.warnings > ul { background:rgba(212,112,110,.08);
  border:1px solid rgba(212,112,110,.25); padding:11px 13px;
  border-radius:9px; font-size:13px; color:var(--warn-ink); }
.warn-list { list-style:none; }
.warn-list li { padding:6px 0; border-bottom:1px solid rgba(212,112,110,.18); }
.warn-list li:last-child { border-bottom:0; }
.warn-text { line-height:1.45; }
.warn-time { font-size:11px; margin-top:2px; opacity:.85; }
/* Per-kind side stripe so override / rejection / error are distinguishable. */
.warn-list li.warn-override  { border-left:3px solid var(--accent); padding-left:9px; }
.warn-list li.warn-rejection { border-left:3px solid var(--warn);   padding-left:9px; }
.warn-list li.warn-error     { border-left:3px solid var(--warn);   padding-left:9px; }
.warn-list li.warn-derived   { border-left:3px solid var(--planned);padding-left:9px; }
.sources li, .claims li { font-size:13px; padding:7px 0; border-bottom:1px solid var(--line2); list-style:none; }

/* Publish-state cards: FB + IG side-by-side. Each card carries the platform
   name, status, timestamp, and post id at a glance -- so the operator doesn't
   have to read a list to compare. Tint by status (green = posted, amber =
   pending). At narrow widths they stack instead of squeezing. */
.ps-cards { display:grid; grid-template-columns:repeat(auto-fit, minmax(220px, 1fr));
  gap:10px; }
.ps-card { padding:11px 13px; border-radius:9px; border:1px solid var(--line2);
  background:var(--panel2); font-size:13px; min-width:0; }
.ps-card-head { display:flex; align-items:baseline; justify-content:space-between;
  gap:8px; margin-bottom:5px; }
.ps-platform { font-weight:600; color:var(--head); font-size:13px; }
.ps-status { font-family:var(--mono); font-size:11px; letter-spacing:.03em; }
.ps-card.ps-done    { border-color:rgba(79,166,118,.35); background:rgba(79,166,118,.08); }
.ps-card.ps-done    .ps-status { color:var(--ok); }
.ps-card.ps-pending { border-color:rgba(245,158,11,.35); background:rgba(245,158,11,.08); }
.ps-card.ps-pending .ps-status { color:var(--accent); }
/* Testing-mode feed: neutral (it's not pending work, it just won't post). */
.ps-card.ps-testing { border-color:var(--line2); background:var(--panel2); }
.ps-card.ps-testing .ps-status { color:var(--muted); }
.ps-tflag { font-family:var(--mono); font-size:9px; text-transform:uppercase;
  letter-spacing:.06em; color:var(--muted); border:1px solid var(--line);
  border-radius:4px; padding:1px 4px; margin-left:4px; vertical-align:middle; }
.ps-when { font-family:var(--mono); font-size:11.5px; color:var(--muted); }
.ps-id { font-family:var(--mono); font-size:11px; color:var(--muted); margin-top:3px;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ps-id a { color:var(--info); text-decoration:none; }
.ps-id a:hover { text-decoration:underline; }
.ps-pending { font-size:12px; color:var(--muted); font-style:italic; }
/* Last-failure reason on a still-pending feed (k6). */
.ps-err { font-size:12px; color:var(--warn); margin-bottom:6px; line-height:1.4;
  overflow:hidden; text-overflow:ellipsis; display:-webkit-box;
  -webkit-line-clamp:3; -webkit-box-orient:vertical; }
/* Compact retry button on a publish-state card (k4). */
.btn.small { padding:4px 10px; font-size:11px; }
.verdict { font-family:var(--mono); font-size:10px; padding:3px 8px; border-radius:5px; letter-spacing:.04em;
  text-transform:uppercase; }
.verdict.supported { background:rgba(79,166,118,.16); color:var(--ok); }
.verdict.unsupported { background:rgba(212,112,110,.16); color:var(--warn); }
/* Claim-verification badge on the collapsed Claims header (UX-5): glanceable
   trust signal so the reviewer needn't expand the section. */
.claim-badge { font-family:var(--sans); font-size:11px; font-weight:600;
  letter-spacing:0; padding:1px 8px; border-radius:999px; margin-left:6px;
  text-transform:none; }
.claim-badge.cb-good { background:rgba(79,166,118,.15); color:var(--ok); }
.claim-badge.cb-bad { background:rgba(212,112,110,.15); color:var(--warn); }
.claim-badge.cb-unchecked { background:rgba(245,158,11,.15); color:#b45309; }
:root[data-theme="dark"] .claim-badge.cb-unchecked { color:#fbbf24; }
.verdict.unchecked { background:rgba(138,147,166,.14); color:var(--muted); }
.verdict.partial { background:rgba(245,158,11,.16); color:var(--accent); }
.targets { display:flex; gap:16px; align-items:center; margin:12px 0; font-size:13px;
  flex-wrap:wrap; }
.targets label { cursor:pointer; display:flex; gap:6px; align-items:center; }

/* Small "i" info pill (used today for testing-mode hints next to a checkbox).
   Hover OR keyboard focus shows the native browser tooltip from the title
   attribute -- no JS, accessible, doesn't take up row width.  */
.info-pill { display:inline-flex; align-items:center; justify-content:center;
  width:15px; height:15px; border-radius:50%; border:1px solid var(--info);
  color:var(--info); background:var(--panel2);
  font-family:var(--sans); font-size:10px; font-weight:700; font-style:italic;
  cursor:help; line-height:1; user-select:none; margin-left:2px; }
.info-pill:hover, .info-pill:focus { background:var(--info); color:var(--bg);
  outline:none; }

/* Small "(Testing Mode)" tag next to a platform name when that platform is
   paused via brand.toml. Visible at-a-glance label so the operator doesn't
   need to hover the info pill to know what's going on. */
.testing-tag { font-family:var(--mono); font-size:10.5px; letter-spacing:.04em;
  color:var(--accent); background:rgba(245,158,11,.10);
  border:1px solid rgba(245,158,11,.35); border-radius:5px;
  padding:1px 6px; text-transform:uppercase; font-weight:600; }
/* Action bar (UX-1): a fixed footer of the detail pane (flex sibling below the
   scrolling .detail-scroll), so the reviewer never scrolls to reach
   Approve/Reject and no content scrolls past it. Top border + upward shadow read
   it as a toolbar. */
.actions { flex:0 0 auto; padding:13px 20px;
  border-top:1px solid var(--line); background:var(--panel);
  box-shadow:0 -10px 18px -10px rgba(0,0,0,.28);
  display:flex; gap:8px; flex-wrap:wrap; align-items:center; }
.actions .act-discard { margin-left:auto; }   /* destructive action set apart */
/* Operator-only "Reset publish state" - amber, sits apart from normal actions. */
.actions .act-reset { margin-left:auto; border-color:var(--warn); color:var(--warn); }
.actions .act-discard ~ .act-reset { margin-left:8px; }  /* both present: don't double-push */
.actions .act-reset:hover { background:var(--warn); color:#fff; }
.reason-form { display:none; gap:8px; margin-top:10px; width:100%; }
.reason-form.show { display:flex; }
.reason-form input, .edit-form input, .edit-form select { background:var(--panel2); color:var(--ink);
  border:1px solid var(--line); border-radius:7px; padding:8px 10px; font-size:13px; font-family:var(--sans); }
.reason-form input { flex:1; }
.edit-form { display:flex; gap:8px; align-items:center; margin-top:6px; flex-wrap:wrap; }

/* platform preview (Planable-style "see the real post") */
.preview { background:var(--panel2); border:1px solid var(--line); border-radius:12px;
  overflow:hidden; position:sticky; top:0; }
.pv-head { display:flex; align-items:center; gap:9px; padding:11px 13px; border-bottom:1px solid var(--line2); }
.pv-avatar { width:30px; height:30px; border-radius:50%;
  background:linear-gradient(135deg,var(--accent),#b45309); flex:none; }
.pv-name { font-weight:600; font-size:13px; }
.pv-sub { font-family:var(--mono); font-size:10px; color:var(--muted); }
.pv-media { width:100%; aspect-ratio:1/1; background:var(--pv-media-bg); display:flex; align-items:center;
  justify-content:center; color:var(--faint); font-family:var(--mono); font-size:11px; }
.pv-media img, .pv-media video { width:100%; height:100%; object-fit:cover; }
/* Empty preview (UX-9): a small quiet strip instead of a big empty square that
   dominates the pane. No forced aspect-ratio; muted italic note. */
.pv-media.pv-media-empty { aspect-ratio:auto; min-height:0; padding:10px 12px;
  background:transparent; border-bottom:1px solid var(--line2); }
.pv-nomedia { color:var(--muted); font-family:var(--sans); font-size:12px;
  font-style:italic; }
/* Inline "Generate image" CTA in the empty preview (UI gap #5) -- a link-style
   button that triggers the same render action as the action-bar button. */
.pv-gen { font-family:var(--sans); font-style:normal; font-size:12px; font-weight:600;
  color:var(--accent); background:none; border:none; padding:0; cursor:pointer;
  text-decoration:underline; }
/* Bulk-select hint next to the Approve/Publish-selected buttons (UI gap #6). */
.bulk-hint { font-size:11px; font-style:italic; }
/* Rejected-draft guidance note in the detail pane (UI gap #8). */
.rejected-note { margin:10px 0 4px; padding:9px 12px; border-radius:7px;
  background:rgba(220,38,38,.08); border:1px solid rgba(220,38,38,.25);
  color:var(--ink); font-size:12.5px; line-height:1.45; }
.pv-body { padding:11px 13px; font-size:13px; line-height:1.5; white-space:pre-wrap; }
.pv-tags { color:var(--info); margin-top:6px; font-size:12.5px; }
.pv-toggle { display:flex; gap:0; }
.pv-toggle button { flex:1; font-family:var(--mono); font-size:10px; text-transform:uppercase;
  letter-spacing:.06em; padding:7px; border:0; border-bottom:2px solid transparent;
  background:transparent; color:var(--muted); cursor:pointer; }
.pv-toggle button.on { color:var(--accent-ink); background:var(--accent);
  border-bottom-color:var(--accent); font-weight:700; }

/* detail modal (grid tile -> detail over the gallery) */
.modal { position:fixed; inset:0; z-index:60; background:rgba(0,0,0,.5);
  display:flex; align-items:flex-start; justify-content:center; padding:40px 20px; overflow:auto; }
.modal-card { position:relative; background:var(--panel); border:1px solid var(--line);
  border-radius:14px; box-shadow:0 24px 70px rgba(0,0,0,.5); width:min(840px, 100%); padding:22px; }
.modal-x { position:absolute; top:10px; right:13px; background:none; border:none;
  color:var(--muted); font-size:22px; line-height:1; cursor:pointer; z-index:1; }
.modal-x:hover { color:var(--ink); }

/* In-app confirm dialog (bulk actions etc.) - replaces native confirm/prompt. */
#confirm-modal { align-items:center; }
.confirm-card { width:min(440px, 100%); padding:20px 22px; }
.confirm-title { font-size:15px; font-weight:700; color:var(--head); margin-bottom:8px; }
.confirm-body { font-size:13.5px; color:var(--ink); line-height:1.5; }
.confirm-body .skip-note { display:block; margin-top:7px; font-size:12.5px; color:var(--muted); }
.confirm-reason { margin-top:13px; }
.confirm-reason input { width:100%; padding:8px 11px; border:1px solid var(--line);
  border-radius:7px; background:var(--panel2); color:var(--ink); font-family:var(--sans);
  font-size:13px; }
.confirm-reason input.input-error { border-color:var(--warn); }
.confirm-actions { display:flex; justify-content:flex-end; gap:8px; margin-top:18px; }
#confirm-ok.danger { background:var(--warn); color:#fff; border-color:var(--warn); }
#confirm-ok.danger:not(:disabled):hover { filter:brightness(1.07); }

/* Detail modal (grid view): same flex-column treatment as the side pane so the
   action footer pins to the bottom and content scrolls inside, not past it. The
   close-X floats over the top-right (extra top padding keeps it off the head). */
#detail-modal .modal-card { padding:0; max-height:85vh;
  display:flex; flex-direction:column; overflow:hidden; }
#modal-detail { display:flex; flex-direction:column; min-height:0; flex:1 1 auto;
  overflow:hidden; }
#modal-detail > .detail-scroll { padding:30px 22px 18px; }
#modal-detail > .empty-detail { padding:48px 22px; text-align:center;
  color:var(--muted); }

/* Job log modal: centered card with a sticky header and a scrollable monospace
   body. Reuses .modal / .modal-card; the close-X sits inline in the header. */
.log-card { width:min(760px, 100%); padding:0; overflow:hidden;
  max-height:80vh; display:flex; flex-direction:column; }
.log-head { display:flex; align-items:center; justify-content:space-between;
  gap:10px; padding:12px 16px; border-bottom:1px solid var(--line);
  background:var(--panel2); background:var(--head-tint); }
.log-head .modal-x { position:static; }
.log-actions { display:flex; align-items:center; gap:10px; }
.log-copy { font-family:var(--mono); font-size:11px; letter-spacing:.04em;
  background:none; border:1px solid var(--line); color:var(--info);
  border-radius:6px; padding:3px 10px; cursor:pointer; }
.log-copy:hover { border-color:var(--info); color:var(--ink); }
.log-title { font-family:var(--mono); font-size:12px; letter-spacing:.04em;
  color:var(--head); }
.log-body { margin:0; padding:14px 16px; overflow:auto; flex:1;
  font-family:var(--mono); font-size:12px; line-height:1.5; color:var(--ink);
  white-space:pre-wrap; word-break:break-word; background:var(--panel2); }

/* Dock modes: when #audit-modal has a dock-* class, it becomes a fixed
   side panel or freely-positioned floating card (no backdrop, page
   interactable behind it). Modes: right (default), left, bottom, free
   (drag-anywhere via the header). */
#audit-modal.dock-right,
#audit-modal.dock-left,
#audit-modal.dock-bottom,
#audit-modal.dock-free {
  background:transparent;            /* no backdrop - see-through to page */
  padding:0;
  pointer-events:none;               /* clicks pass through except on the card */
  align-items:stretch; justify-content:stretch;
  display:block;                     /* override flex centering */
}
#audit-modal.dock-right  .modal-card,
#audit-modal.dock-left   .modal-card,
#audit-modal.dock-bottom .modal-card,
#audit-modal.dock-free   .modal-card {
  pointer-events:auto;               /* clicks land on the card itself */
  width:auto; max-width:none;
  position:fixed; margin:0; border-radius:0;
  box-shadow:-12px 0 40px rgba(0,0,0,.4);
  overflow:auto;
}
/* Side docks aren't full viewport height -- they leave breathing room top
   and bottom so the operator can still see the top bar and bottom of the
   page underneath. Border radius on the OPEN edges only so it reads as a
   docked panel, not a wall. */
#audit-modal.dock-right .modal-card {
  top:5vh; right:0; bottom:5vh; width:min(560px, 50vw);
  border-left:1px solid var(--line);
  border-radius:12px 0 0 12px;
}
#audit-modal.dock-left .modal-card {
  top:5vh; left:0; bottom:5vh; width:min(560px, 50vw);
  border-right:1px solid var(--line);
  border-radius:0 12px 12px 0;
  box-shadow:12px 0 40px rgba(0,0,0,.4);
}
#audit-modal.dock-bottom .modal-card {
  left:5vw; right:5vw; bottom:0; height:min(55vh, 680px);
  border-top:1px solid var(--line);
  border-radius:12px 12px 0 0;
  box-shadow:0 -12px 40px rgba(0,0,0,.4);
}
/* Free mode: floating card, position set inline by the drag JS (left + top).
   Default size mirrors the side-dock so it doesn't reflow on first drag. */
#audit-modal.dock-free .modal-card {
  width:min(560px, 80vw);
  max-height:min(80vh, 800px);
  border:1px solid var(--line);
  border-radius:12px;
  box-shadow:0 24px 70px rgba(0,0,0,.5);
  /* top + left set as inline styles by the drag handler */
}
/* Draggable header: cursor + grab feedback. Skips clicks on buttons/links
   inside the header so the dock toggle + close-X still work normally. */
#audit-modal .audit-head { cursor:move; user-select:none; }
#audit-modal .audit-head button,
#audit-modal .audit-head a,
#audit-modal .audit-head .dock-toggle { cursor:auto; }
#audit-modal.dragging .audit-head { cursor:grabbing; }
#audit-modal.dragging .modal-card  { transition:none;
  box-shadow:0 30px 80px rgba(0,0,0,.6); }

/* Dock-toggle control on the audit header. */
.dock-toggle { display:inline-flex; gap:2px; margin-left:auto;
  border:1px solid var(--line); border-radius:7px; padding:2px;
  background:var(--panel2); }
.dock-toggle button { background:none; border:none; cursor:pointer;
  font-family:var(--mono); font-size:10px; letter-spacing:.05em;
  text-transform:uppercase; color:var(--muted); padding:3px 8px; border-radius:5px; }
.dock-toggle button:hover { color:var(--ink); }
.dock-toggle button.active { background:var(--accent); color:var(--accent-ink); }

/* Backfill marker on a publish-attempt entry whose snapshot was reconstructed
   after the fact (historical content unknown -- shows current draft instead). */
.attempt-backfilled { border-style:dashed; }
.backfill-note { font-family:var(--mono); font-size:10.5px; color:var(--planned);
  margin:4px 0 6px; padding:3px 7px; border-radius:5px;
  background:rgba(167,139,250,.10); display:inline-block; }

/* Audit-history affordance on the detail header. */
.audit-link { font-family:var(--mono); font-size:11px; letter-spacing:.04em;
  color:var(--muted); padding:3px 8px; border:1px solid var(--line); border-radius:7px;
  background:var(--panel2); margin-left:8px; }
.audit-link:hover { color:var(--accent); border-color:var(--accent); text-decoration:none; }
.audit-cnt { opacity:.8; }

/* Audit modal interior. The modal-card scrolls; the head stays pinned to its
   top so the dock toggle + status pill are always reachable as you scroll.
   The card has NO padding here (override below) -- padding is moved into
   the .audit-body wrapper, so the sticky header touches the card's actual
   top edge with no gap above for scrolling content to peek through. */
#audit-modal .modal-card { padding:0; }
.audit { display:flex; flex-direction:column; }
.audit-head {
  display:flex; align-items:center; gap:9px; flex-wrap:wrap;
  padding:13px 22px;
  background:var(--panel2); background:var(--head-tint);
  border-bottom:1px solid var(--line);
  position:sticky; top:0; z-index:5;
}
/* Body content gets its own padding (since the card no longer carries it).
   Vertical gap between successive <details> sections matches the prior
   .audit gap:14px so the visual rhythm doesn't change. */
.audit-body { padding:14px 22px 22px; display:flex; flex-direction:column; gap:14px; }
.audit-title { font-family:var(--mono); font-size:12px; letter-spacing:.08em;
  text-transform:uppercase; color:var(--head); margin-left:auto; }
/* Close-X lives INSIDE the sticky .audit-head, so it sticks with the header
   instead of scrolling away with the content. Position relative to the
   sticky element rather than the modal-card. */
#audit-modal .audit-head .modal-x { position:absolute; top:8px; right:10px;
  z-index:10; font-size:22px; line-height:1; padding:2px 7px;
  color:var(--muted); background:var(--panel); border:1px solid transparent;
  border-radius:6px; cursor:pointer; }
#audit-modal .audit-head .modal-x:hover { color:var(--warn);
  border-color:var(--line); }
/* Leave room on the right of the header for the close button so the dock
   toggle doesn't sit underneath it. */
#audit-modal .audit-head { padding-right:46px; }

/* Timeline list -- newest at top, time on the left, body on the right. */
.timeline { list-style:none; }
.tev { display:flex; gap:14px; padding:9px 0; border-bottom:1px solid var(--line2);
  align-items:flex-start; }
.tev:last-child { border-bottom:0; }
.tev-time { width:170px; font-family:var(--mono); font-size:11px; color:var(--muted);
  flex:none; padding-top:2px; }
.tev-body { flex:1; min-width:0; font-size:13px; }
.tev-label { font-weight:500; color:var(--ink); }
.tev-kind { font-weight:600; }
.tev-by { color:var(--muted); font-family:var(--mono); font-size:11.5px; margin-left:4px; }
.tev-tag { font-family:var(--mono); font-size:9.5px; padding:1px 5px; border-radius:4px;
  background:var(--planned); color:#fff; margin-left:6px; letter-spacing:.04em; }
/* Per-kind side stripe so the eye can skim event types. */
.tev-created   { border-left:3px solid var(--muted);  padding-left:10px; }
.tev-approved  { border-left:3px solid var(--ok);     padding-left:10px; }
.tev-rejected  { border-left:3px solid var(--warn);   padding-left:10px; }
.tev-reverted  { border-left:3px solid #f59e0b;       padding-left:10px; }
.tev-published { border-left:3px solid var(--accent); padding-left:10px; }
.tev-edit      { border-left:3px solid var(--planned);padding-left:10px; }
.tev-publish   { border-left:3px solid var(--info);   padding-left:10px; }
.tev-note { font-size:12px; color:var(--muted); margin-top:3px; font-style:italic; }
.tev-diff { font-family:var(--mono); font-size:11.5px; margin-top:4px;
  display:flex; gap:6px; align-items:center; flex-wrap:wrap; }
.tev-field { color:var(--muted); }
.tev-old { color:var(--warn); text-decoration:line-through; opacity:.85;
  max-width:280px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  display:inline-block; vertical-align:bottom; }
.tev-new { color:var(--ok); max-width:280px; overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap; display:inline-block; vertical-align:bottom; }
.tev-arrow { color:var(--muted); }
.tev-outcome { font-family:var(--mono); font-size:12px; margin-top:3px; color:var(--head); }

/* Publish attempts -- richer per-attempt card. */
.attempts { list-style:none; display:flex; flex-direction:column; gap:14px; }
.attempt { border:1px solid var(--line2); border-radius:9px; padding:11px 13px;
  background:var(--panel2); }
.attempt-head { display:flex; gap:10px; align-items:baseline; margin-bottom:7px;
  flex-wrap:wrap; }
.attempt-n { font-family:var(--mono); font-weight:700; color:var(--head); }
.attempt-time { font-family:var(--mono); font-size:11.5px; color:var(--muted); }
.attempt-by { color:var(--muted); font-size:12px; }
.attempt-targets { margin-left:auto; font-family:var(--mono); font-size:10.5px; }
.attempt-outcomes { list-style:none; display:flex; flex-direction:column; gap:3px;
  margin:6px 0; }
.att-out { font-size:13px; }
.att-out.att-success { color:var(--ok); }
.att-out.att-failed  { color:var(--warn); }
.att-out.att-skipped { color:var(--muted); }
.att-err { font-family:var(--mono); font-size:11.5px; margin-top:3px; color:var(--warn);
  padding-left:18px; }
.attempt-diff, .attempt-snap { margin-top:8px; }
.attempt-diff > summary, .attempt-snap > summary { cursor:pointer; user-select:none; }
.diff-list { list-style:none; margin-top:6px; font-family:var(--mono); font-size:11.5px;
  display:flex; flex-direction:column; gap:5px; }
.diff-field { color:var(--head); font-weight:600; }
.diff-old { color:var(--warn); text-decoration:line-through; opacity:.85;
  max-width:380px; overflow:hidden; text-overflow:ellipsis; display:inline-block;
  vertical-align:bottom; }
.diff-new { color:var(--ok); max-width:380px; overflow:hidden;
  text-overflow:ellipsis; display:inline-block; vertical-align:bottom; }
.snap-dl { display:grid; grid-template-columns:max-content 1fr; gap:4px 12px;
  margin-top:6px; font-size:13px; }
.snap-dl dt { font-family:var(--mono); font-size:10.5px; text-transform:uppercase;
  color:var(--muted); letter-spacing:.06em; padding-top:3px; }
.snap-dl dd { white-space:pre-wrap; word-break:break-word; }

/* All-errors section in the audit modal. */
.audit-errors { list-style:none; display:flex; flex-direction:column; gap:9px; }
.audit-errors li { padding:8px 11px; border-radius:7px; background:rgba(212,112,110,.06);
  border:1px solid rgba(212,112,110,.18); font-size:13px; }
.err-stage { font-family:var(--mono); font-size:11px; color:var(--warn); font-weight:600; }
.err-msg { margin-top:3px; font-family:var(--mono); font-size:11.5px;
  word-break:break-word; }

/* toast */
#toasts { position:fixed; bottom:20px; right:20px; display:flex; flex-direction:column; gap:8px; z-index:50; }
.toast { background:var(--raised); border:1px solid var(--line); border-left:3px solid var(--ok);
  border-radius:8px; padding:11px 15px; font-size:13px; min-width:200px; box-shadow:0 8px 24px rgba(0,0,0,.4); }
.toast.err { border-left-color:var(--warn); }
/* Heads-up: action succeeded but did nothing (e.g. a bulk that skipped all). */
.toast.warn { border-left-color:#f59e0b; }
/* Ineligible selection checkbox (published / rejected / fully-testing draft):
   dimmed + not-allowed so it reads as "can't act on this", with a tooltip. */
.card-select:disabled, .gsel:disabled { opacity:.35; cursor:not-allowed; }

@media (max-width:900px) {
  .qsplit { grid-template-columns:1fr; }
  .qlist { max-height:none; border-right:0; border-bottom:1px solid var(--line); }
  .detail-grid { grid-template-columns:1fr; }
}

/* ============================================================================
   UX-18 · Typography pass - sentence-case sans-serif for headers, labels, pills,
   tabs and status badges. MONOSPACE is reserved for true code/IDs (draft & batch
   ids, hashes, post ids, timestamps, error/stage text), which keep their styling
   above and are NOT touched here. These rules deliberately OVERRIDE the
   per-component rules earlier in the file (same selectors, later in the cascade)
   so the whole change lives in one reviewable, revertible place. `capitalize` is
   used where the source text is lowercase (status, tabs, verdicts); `none` where
   the markup is already sentence/Title case (multi-word headers).
   ========================================================================== */

/* Headers - the biggest perception shift. */
.region > h2 { font-family:var(--sans); text-transform:none; letter-spacing:-.005em;
  font-size:14px; font-weight:650; }
.section-title { font-family:var(--sans); text-transform:none; letter-spacing:0;
  font-size:12px; font-weight:600; }
.hpop-title { text-transform:none; letter-spacing:.01em; font-size:11px; }
.job-group-header { font-family:var(--sans); text-transform:none; letter-spacing:.01em;
  font-size:11px; }
table.inbox th { font-family:var(--sans); text-transform:capitalize; letter-spacing:.01em;
  font-size:11px; }
.reproc-title { font-family:var(--sans); text-transform:none; letter-spacing:.01em; }
.audit-title { font-family:var(--sans); text-transform:none; letter-spacing:.02em; }
.snap-dl dt { font-family:var(--sans); text-transform:capitalize; letter-spacing:0;
  font-size:11px; }

/* Labels + small tags. */
.pillar { font-family:var(--sans); text-transform:none; letter-spacing:.02em;
  font-size:11px; font-weight:600; }
.headline-line .hl-label { font-family:var(--sans); text-transform:none;
  letter-spacing:0; font-size:10px; }
.bf-label { font-family:var(--sans); text-transform:capitalize; letter-spacing:.01em;
  font-size:10px; }
.job-produced-label { font-family:var(--sans); text-transform:none; letter-spacing:.01em; }
.filechip .chip-kind { font-family:var(--sans); text-transform:capitalize; letter-spacing:0; }
.ps-tflag { font-family:var(--sans); text-transform:none; letter-spacing:.02em; }
.testing-tag { font-family:var(--sans); text-transform:none; letter-spacing:0; }

/* Pills, status badges, verdicts. */
.pill { font-family:var(--sans); text-transform:capitalize; letter-spacing:0;
  font-size:11px; }
.job .st { font-family:var(--sans); text-transform:none; letter-spacing:.02em; }
.verdict { font-family:var(--sans); text-transform:capitalize; letter-spacing:0; }

/* Tabs, toggles, small text buttons. */
.tab { font-family:var(--sans); text-transform:none; letter-spacing:.01em; }
.vbtn { font-family:var(--sans); text-transform:none; letter-spacing:.01em; }
.pv-toggle button { font-family:var(--sans); text-transform:none; letter-spacing:.02em;
  font-size:11px; }
.dock-toggle button { font-family:var(--sans); text-transform:capitalize; letter-spacing:.01em; }
.batch-filter-x { font-family:var(--sans); text-transform:capitalize; letter-spacing:.01em; }
.job .job-log { font-family:var(--sans); text-transform:capitalize; letter-spacing:.02em; }
/* Activity attribution + tags are labels (a reviewer name, a "system" marker),
   not code -> sans. Timestamps + the AI marker stay mono (code-like). */
.evt-by { font-family:var(--sans); font-size:12px; }
.evt-tag { font-family:var(--sans); text-transform:none; letter-spacing:0; }

/* ----------------------------------------------------------------------------
   Casing: TITLE CASE for the user-facing chrome (operator preference - overrides
   the sentence-case default in the block above). `capitalize` -> "Ready To
   Review", "Approval Queue", "Approve Selected", "Testing Mode". Deliberately
   EXCLUDES content (draft titles `.kind-target-title`, captions, the reviewer
   name `.evt-by`, the data-valued `.pillar`) and all IDs/code (which stay mono).
   ---------------------------------------------------------------------------- */
.region > h2, .section-title, .hpop-title, .job-group-header, .reproc-title,
.audit-title, .headline-line .hl-label, .job-produced-label, .ps-tflag,
.testing-tag, .job .st, .tab, .vbtn, .pv-toggle button, .evt-tag, .feed, .btn,
.menu-item, .testing-banner strong {
  text-transform: capitalize;
}

/* ----------------------------------------------------------------------------
   Login page (P4 auth). Centered card; reuses the theme variables + .btn.
   ---------------------------------------------------------------------------- */
.login-body { display:flex; align-items:center; justify-content:center;
  min-height:100vh; padding:0; max-width:none; }
.login-card { width:320px; max-width:90vw; background:var(--panel);
  border:1px solid var(--line); border-radius:12px; padding:28px 26px;
  display:flex; flex-direction:column; gap:14px; box-shadow:var(--shadow); }
.login-title { display:flex; align-items:center; gap:8px; margin:0;
  font-size:18px; color:var(--ink); }
.login-sub { margin:-8px 0 6px 16px; color:var(--muted); font-size:12px; }
.login-field { display:flex; flex-direction:column; gap:5px; font-size:12px;
  color:var(--muted); }
.login-field input { padding:9px 10px; border:1px solid var(--line);
  border-radius:7px; background:var(--bg); color:var(--ink); font-size:14px;
  font-family:var(--sans); }
.login-field input:focus { outline:2px solid var(--accent); outline-offset:0; }
.login-btn { margin-top:6px; padding:10px; font-size:14px; }
.login-error { color:var(--warn); font-size:12.5px; text-align:center; }
