/* Geist Sans (Timos design-system round 2026-08-20) — self-hosted, SIL OFL
   (fonts/OFL.txt). Static weights only: the design system caps weight at 600
   (headings) and uses 400/500 elsewhere; one true italic for editor/body
   emphasis. Served same-origin from this static dir — no external requests.
   unicode-range (2026-09-15): these Latin subsets carry six stray Greek code
   points and map ω (U+03C9) to the capital-Ω glyph, so every ω rendered as Ω.
   Geist is therefore limited to the Latin/symbol ranges it really covers and
   Greek/Cyrillic fall through to the system fallback font on purpose. */
@font-face {
  font-family: "Geist";
  src: url("fonts/Geist-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-024F, U+02B0-02FF, U+2000-206F, U+20A0-20CF, U+2100-214F, U+2190-21FF, U+2200-22FF;
}
@font-face {
  font-family: "Geist";
  src: url("fonts/Geist-Medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-024F, U+02B0-02FF, U+2000-206F, U+20A0-20CF, U+2100-214F, U+2190-21FF, U+2200-22FF;
}
@font-face {
  font-family: "Geist";
  src: url("fonts/Geist-SemiBold.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-024F, U+02B0-02FF, U+2000-206F, U+20A0-20CF, U+2100-214F, U+2190-21FF, U+2200-22FF;
}
@font-face {
  font-family: "Geist";
  src: url("fonts/Geist-Italic.woff2") format("woff2");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
  unicode-range: U+0000-024F, U+02B0-02FF, U+2000-206F, U+20A0-20CF, U+2100-214F, U+2190-21FF, U+2200-22FF;
}

/* =========================================================================
   Design tokens (Timos design-system round, 2026-08-20 — the stakeholders'
   "Editorial Intelligence" spec). LIGHT is the default theme, defined here
   on :root; Dark and Slate redefine the color tokens on body.palette-dark /
   body.palette-slate (which beats :root for descendants — the precedence
   chain with the admin colors from general.yaml is documented at the
   palette blocks below). The Social Dragon page (composer.css, .sc-app)
   keeps its own --sc-* system by design.
   ========================================================================= */
:root {
  --font-ui: "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  /* Spacing scale (use for new/reworked rules; legacy literals migrate as
     sections are rebuilt). */
  --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
  --space-5: 20px; --space-6: 24px; --space-7: 28px; --space-8: 32px;
  --space-10: 40px;
  /* Control metrics + radii. */
  --control-h: 42px;
  --control-h-sm: 36px;
  --radius-sm: 8px;       /* compact controls, small chips */
  --radius-control: 10px; /* inputs, selects, buttons */
  --radius-panel: 14px;   /* primary panels/cards */
  --radius-lg: 18px;      /* floating panels (messenger, voice) */
  /* Color tokens — LIGHT theme values. */
  --page: #f4f6fa;        /* application background */
  --page-deep: #eaf0f7;   /* deeper page areas */
  --panel: #ffffff;       /* raised content surfaces */
  --border: #d5dde8;
  --text: #172033;
  --text-soft: #354156;
  --text-muted: #5b687b;
  --text-faint: #748094;
  /* Accent — blue is the ONLY general accent (red/green are semantic only).
     --accent (+ the two chat bubble colors) is admin-overridable via
     base.html's inline <style> from general.yaml (Settings → General). */
  --accent: #2864dc;
  --accent-hover: #1f4fb6;
  --accent-soft: #e9f0ff;
  /* Semantic status colors. */
  --success: #18794e; --success-soft: #e7f6ee;
  --warning: #9a5b13; --warning-soft: #fff5df;
  --danger: #c4322b;  --danger-soft: #fff0ee;
  /* Neutral fills + scrollbars. */
  --tint: #eef2f8;        /* chips, code, zebra stripes, table heads */
  --tint-hover: #e4ecf7;  /* clickable-row / menu-item hover */
  --scrollbar: #c3cdda; --scrollbar-hover: #a9b6c8;
  /* Elevation + motion. */
  --shadow-panel: 0 8px 24px rgba(15, 23, 42, .055);
  --shadow-hover: 0 14px 34px rgba(15, 23, 42, .09);
  --motion-fast: 160ms ease;
  /* Side-nav shell — dark navy in BOTH themes (spec: only subtle tonal
     shifts allowed between themes). */
  --nav-bg: #0f192b; --nav-border: #26354d;
  --nav-hover: #192741; --nav-active: #20365e;
  --nav-bar: #6e9cff; --nav-icon: #8db0ff; --nav-text: #9eacc1;
  --nav-foot: #0c1525; --nav-account: #17253d;
  /* Chat bubble colors (admin-overridable, see --accent note above). */
  --chat-user-bg: #2864dc;
  --chat-assistant-bg: #ffffff;
  /* Type scale (2026-08-07 stakeholder rule: 14px minimum, kept). Every
     font-size in this file rides one of these six vars. */
  --fs-sm: 14px;    /* small text / metadata — the floor */
  --fs-md: 15px;    /* dense-panel body */
  --fs-base: 15px;  /* body text (spec: 15px/1.55) */
  --fs-lg: 17px;    /* card headings */
  --fs-xl: 19px;    /* section headings */
  --fs-2xl: 21px;   /* page-level headings */
}
* { box-sizing: border-box; }
/* The hidden attribute must always win, even over rules that set display
   (e.g. .modal-backdrop's flex, .btn's inline-block). */
[hidden] { display: none !important; }
/* One family everywhere: form controls don't inherit fonts by default. */
button, input, select, textarea { font: inherit; }
/* Keyboard-focus ring on everything focusable (spec §4: never remove focus
   outlines without a visible replacement). Text fields swap it for the
   border+ring treatment below. */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
input:focus-visible, textarea:focus-visible, select:focus-visible,
[contenteditable]:focus-visible {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
body {
  margin: 0;
  /* Split from the `font:` shorthand so the size can ride the type scale. */
  font-family: var(--font-ui);
  font-size: var(--fs-base);
  line-height: 1.5;
  color: var(--text);
  background: var(--page);
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
code { background: var(--tint); padding: 1px 5px; border-radius: 4px; font-size: 0.9em; }

/* The one compact header row per working page (spec §6): ~54px, panel
   surface, single bottom border, no radius/shadow. The page name renders
   here (.tagline, from the title block) since the 2026-08-01 rebrand. */
.topbar {
  display: flex; align-items: center; gap: 12px;
  min-height: 54px; padding: 9px 24px;
  background: var(--panel); border-bottom: 1px solid var(--border);
}
.brand { font-weight: 600; font-size: var(--fs-2xl); color: var(--text); }
/* The logo replaces the brand text (2026-07-30); slight rounding because the
   source art has a dark square background. Height-capped so the bar stays
   compact — the image file itself is 256px for crispness on retina screens. */
.brand-logo { display: block; height: 38px; width: auto; border-radius: 8px; }
/* The page title in the header row: compact, one line (spec §6). */
.tagline {
  color: var(--text); font-size: 20px; font-weight: 600;
  letter-spacing: -0.015em; line-height: 1.2;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.topnav { margin-left: auto; display: flex; align-items: center; gap: 16px; font-size: var(--fs-sm); }
.topnav .who { color: var(--text-muted); }
/* Grouped nav with hover/focus dropdowns (Agents, Settings, username). */
.topnav .navitem { position: relative; display: inline-flex; }
.topnav .navlink {
  background: none; border: none; padding: 0; margin: 0;
  font: inherit; color: var(--accent); cursor: pointer;
}
.topnav .navlink:hover { filter: brightness(1.1); }
/* Nav icons (2026-09-10; _icons.html): the top-bar layout stays text-only. */
.topnav .nav-icon { display: none; }
.topnav .navlink.who { color: var(--text-muted); }
.topnav .submenu {
  position: absolute; top: 100%; right: 0; display: none;
  flex-direction: column; min-width: 150px; padding: 6px 0;
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-sm);
  box-shadow: 0 4px 14px rgba(0, 0, 0, .08); z-index: 20;
}
.topnav .navitem:hover .submenu,
.topnav .navitem:focus-within .submenu { display: flex; }
.topnav .submenu a { padding: 6px 14px; color: var(--text); white-space: nowrap; }
.topnav .submenu a:hover { background: var(--tint); text-decoration: none; }
/* Active page in a top-layout dropdown (2026-08-29): the tint surface +
   weight — theme-token based so every palette renders it sensibly. */
.topnav .submenu a.active { background: var(--tint); font-weight: 600; }
/* Labelled group inside a submenu (e.g. Tools -> Chat's two pages). */
.topnav .submenu .submenu-group {
  display: block; padding: 6px 14px 2px; font-size: var(--fs-sm);
  color: var(--text-muted);
  cursor: default; white-space: nowrap;
}
.topnav .submenu a.submenu-indent { padding-left: 26px; }
/* The logout form's button renders exactly like the sibling submenu links
   (logout is a POST — state-changing — so it can't be a plain <a>). */
.topnav .submenu .logout-form { display: flex; }
.topnav .submenu .linklike {
  flex: 1; padding: 6px 14px; border: 0; background: none; color: var(--text);
  font: inherit; text-align: left; white-space: nowrap; cursor: pointer;
}
.topnav .submenu .linklike:hover { background: var(--tint); }
/* Edit prompts: the menu item's live name beside the agent key (2026-09-14). */
.cfg-menu-name { font-weight: 600; color: var(--accent); }
/* Menu-item tooltips (2026-09-14, navtip.js): ONE viewport-fixed bubble
   (escapes the sidebar's overflow clipping) on the dark nav surface, so it
   reads as part of the menu under every theme; the <strong> line is the
   item's label, shown only in the collapsed icon rail. */
.nav-tip {
  position: fixed; z-index: 70; max-width: 280px; padding: 8px 11px;
  background: var(--nav-bg); color: #fff; border: 1px solid var(--nav-border);
  border-radius: var(--radius-sm); box-shadow: 0 8px 24px rgba(0, 0, 0, .28);
  font-size: var(--fs-sm); line-height: 1.45; pointer-events: none;
  opacity: 0; transition: opacity var(--motion-fast);
}
.nav-tip.show { opacity: 1; }
.nav-tip strong { display: block; margin-bottom: 2px; font-weight: 600; }
/* "Send feedback" — always the first nav item, styled apart from the
   plain links so it reads as a call-out rather than navigation. */
.topnav .nav-feedback {
  padding: 3px 10px; border: 1px solid var(--accent); border-radius: 999px;
  font-weight: 600; white-space: nowrap;
}
.topnav .nav-feedback:hover { background: var(--accent); color: #fff; text-decoration: none; }
/* ---- Stats page (Settings → Stats; tabbed dashboard 2026-09-20) --------
   Token-only so every theme (light/dark/slate/custom) renders it natively.
   KPI tile = .card chrome + label/value/sub; .bar = an in-cell CSS meter
   (width in --w) whose track is the pale accent so the mark reads as one
   hue; .stats-window = the per-panel 7/30/90/365 selector (links carry
   the panel's hash). Side-by-side blocks reuse .stats-grid (stacks ≤760). */
.stats-grid { display: flex; gap: 28px; flex-wrap: wrap; align-items: flex-start; }
.stats-grid > div { min-width: 220px; flex: 1 1 280px; }
.stats-grid.modules > .card.module { flex: 1 1 320px; }
.stats-kpis {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: var(--space-3); margin: var(--space-4) 0;
}
.stats-kpis.compact { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); margin: var(--space-2) 0 var(--space-3); }
.card.kpi { gap: 2px; padding: var(--space-3) var(--space-4); }
.kpi-label {
  font-size: var(--fs-sm); font-weight: 600; text-transform: uppercase;
  letter-spacing: .03em; color: var(--text-muted);
}
.kpi-value { font-size: calc(var(--fs-2xl) * 1.5); font-weight: 600; line-height: 1.15; color: var(--text); letter-spacing: -0.01em; }
.stats-kpis.compact .kpi-value { font-size: var(--fs-2xl); }
.kpi-sub { font-size: var(--fs-sm); color: var(--text-soft); }
.bar {
  display: inline-block; vertical-align: middle; width: 120px; height: 8px;
  margin-left: 8px; border-radius: 999px; background: var(--accent-soft); overflow: hidden;
}
.bar::before {
  content: ""; display: block; height: 100%; width: var(--w, 0%);
  min-width: 2px; border-radius: 999px; background: var(--accent);
}
.stats-window { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: var(--space-4) 0 0; }
.stats-window .btn-small {
  display: inline-block; border: 1px solid var(--border); color: var(--text-soft);
  background: var(--panel); text-decoration: none; font-weight: 500;
}
.stats-window .btn-small:hover { color: var(--text); background: var(--tint); text-decoration: none; }
.stats-window .btn-small.active { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); font-weight: 600; }
/* The per-user tables: a two-row header (metric / window + all-time). */
table.stats-users th.sub { font-weight: 500; text-transform: none; letter-spacing: 0; font-size: var(--fs-sm); }
table.stats-users td.strong { font-weight: 600; }
table.stats-users tr.inactive td { color: var(--text-muted); }
table.stats-users tr.inactive .status { margin-left: 4px; }
.card.module { gap: var(--space-2); }
.card.module h3 { margin: 0; }
.card.module table { box-shadow: none; }

/* Feedback inbox (Settings → Feedback inbox) */
.feedback-new td { font-weight: 600; }
td.feedback-message { white-space: pre-wrap; overflow-wrap: anywhere; max-width: 640px; font-weight: 400; }
.empty.error { color: var(--danger); font-style: normal; font-weight: 600; }

/* Flash messages */
.flashes { margin: 0 0 16px; }
.flash { padding: 8px 12px; border-radius: var(--radius-sm); margin: 0 0 8px; font-size: var(--fs-md); }
.flash-success { background: var(--success-soft); color: var(--success); }
.flash-error { background: var(--danger-soft); color: var(--danger); }
.flash-warn { background: var(--warning-soft); color: var(--warning); }

/* Centered page-width wrappers: block centered in .content, content inside
   stays left-aligned. Two families (2026-09-20, stakeholder ask "the Edit
   prompts table has no reason to be limited"):
   - FORMS stay narrow (.page-slim / .page-run / .page-mid-fixed) — a
     560px-wide input is readable, a 1700px one is not;
   - TABLES + EDITORS (.page-narrow / .page-wide / .page-mid — the config
     list, the agent prompt editor, Roles, Users, Categories, Config
     activity, Audit rules, Archive, Content Health, Story Engine,
     Transcriber, Call recordings) share ONE cap, --page-cap: they fill the
     .content column (its 28px padding is the margin) up to a readable
     ceiling on ultra-wide monitors. Retune the ceiling in one place. */
:root { --page-cap: 1400px; }
.page-slim   { max-width: 560px;  margin-inline: auto; }  /* login, settings forms */
.page-run    { max-width: 700px;  margin-inline: auto; }  /* Full Run + Researcher forms */
.page-run .run-form { max-width: none; }                  /* fill the wrapper */
.page-mid-fixed { max-width: 1000px; margin-inline: auto; }  /* the Writer's From-prompt / Paste tabs */
.page-narrow, .page-wide, .page-mid { max-width: var(--page-cap); margin-inline: auto; }

/* Config editor + user admin: the prompt editor's form fills its wrapper
   (the textareas are width:100%; the friendly form's short inputs keep
   their own caps — .field.param-field below). */
.run-form.wide { max-width: none; }
.run-form textarea {
  width: 100%; padding: 12px 14px; border: 1px solid var(--border);
  border-radius: var(--radius-control); font: inherit; font-size: var(--fs-md);
  line-height: 1.55; background: var(--panel); color: var(--text);
}
.run-form textarea.code {
  font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
  font-size: var(--fs-sm); white-space: pre-wrap;
}
.run-form details { margin: 0 0 18px; }
.run-form details summary { cursor: pointer; color: var(--accent); font-weight: 600; margin-bottom: 8px; }
form.inline { display: inline; margin: 0; white-space: nowrap; }
.user-actions { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.wp-author-input { width: 90px; max-width: 100%; }
/* The "shows as: …" line under the WP-author id form: capped width + small
   type so a long name/fallback can never widen the column and squeeze the
   row's other cells. */
.wp-author-name { font-size: var(--fs-sm); line-height: 1.35; margin-top: 4px; max-width: 220px; overflow-wrap: anywhere; }
/* Main column: ~80% of the viewport on desktop (the app is used mainly on
   computers), capped so ultra-wide monitors don't produce unreadable lines.
   Mobile rules live in the media queries at the end of this file. */
.content { width: min(1500px, 86vw); margin: 0 auto; padding: 28px; }
/* Opt-in full-width main column (base.html's content_class block) — used by
   the Social Dragon page when the Copy Dragon chat shares the row. */
.content.content-wide { width: 96vw; max-width: none; }
/* Login page: small centered logo at the bottom (the old footer text's spot). */
.login-logo { display: block; margin: 36px auto 0; width: 84px; height: 84px; }

/* --- Anonymous pages / the restricted-access login screen (spec §17).
   body.logged-out is set by base.html when no user is signed in: the page
   goes full-bleed dark navy with radial blue glows (one deliberate look in
   every theme), and the absent sidebar's padding is dropped. ------------- */
body.logged-out, body.nav-side.logged-out { padding-left: 0; }
body.logged-out {
  min-height: 100vh;
  background:
    radial-gradient(720px 420px at 6% -4%, rgba(47, 99, 216, .30), transparent 62%),
    radial-gradient(760px 460px at 98% 104%, rgba(35, 108, 199, .24), transparent 62%),
    #0d1830;
}
body.logged-out .topbar {
  background: transparent; border-bottom: 1px solid rgba(255, 255, 255, .08);
}
body.logged-out .tagline { color: #e7eefc; }
.content.login-page { padding-top: max(7vh, 40px); }
.login-page .page-slim { max-width: 620px; }
.login-card {
  background: rgba(17, 31, 59, .75);
  border: 1px solid rgba(150, 180, 235, .25);
  border-radius: 24px; padding: 38px 42px;
  backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px);
  box-shadow: 0 28px 90px rgba(4, 12, 27, .34);
  color: #c5d0e3;
}
.login-card h1 { color: #fff; font-size: 34px; letter-spacing: -0.02em; }
.login-card .run-form label { color: #c5d0e3; }
.login-card .run-form input[type=text], .login-card .run-form input[type=password] {
  background: rgba(255, 255, 255, .07);
  border-color: rgba(150, 180, 235, .30); color: #fff;
}
.login-card .run-form input:focus-visible {
  border-color: #82a9ff; box-shadow: 0 0 0 3px rgba(130, 169, 255, .25);
}
.login-card .run-form .actions button { background: #2f63d8; }
.login-card .empty.error { color: #ff9a93; }

h1 { font-size: 22px; font-weight: 600; letter-spacing: -0.02em; margin: 0 0 18px; }
/* Weight cap (spec §3): hierarchy comes from size/spacing, not heavy bold —
   and 600 is the heaviest Geist face shipped, so 700 would synthesize. */
b, strong { font-weight: 600; }
/* Page h1 headings hidden for now (owner ask 2026-08-01): the header shows
   the page name since the rebrand, so the big in-page heading is redundant.
   The markup stays in every template — DELETE THIS RULE to bring them all
   back. `.keep-h1` marks the non-redundant ones that stay visible (home's
   app title, run detail's live-status heading, login/error). */
main.content h1:not(.keep-h1) { display: none; }
h2 { font-size: var(--fs-xl); font-weight: 600; letter-spacing: -0.01em; margin: 28px 0 10px; border-bottom: 1px solid var(--border); padding-bottom: 6px; }
/* Bare h3 (Stats/Menu-settings sections): Geist ships no 700, so the UA's
   bold synthesized — pin 600 like every other heading (2026-09-05 audit). */
h3 { font-size: var(--fs-lg); font-weight: 600; margin: 18px 0 8px; }
.count { color: var(--text-muted); font-weight: 400; font-size: var(--fs-md); }
.back { margin: 0 0 8px; font-size: var(--fs-sm); }
.empty { color: var(--text-muted); font-style: italic; }

table {
  width: 100%; border-collapse: collapse; background: var(--panel);
  border: 1px solid var(--border); border-radius: var(--radius-panel);
  box-shadow: var(--shadow-panel); overflow: hidden;
}
th, td { text-align: left; padding: 8px 12px; border-bottom: 1px solid var(--border); vertical-align: top; }
th { background: var(--tint); font-size: var(--fs-sm); font-weight: 600; text-transform: uppercase; letter-spacing: .03em; color: var(--text-muted); }
tr:last-child td { border-bottom: none; }
.num { text-align: right; font-variant-numeric: tabular-nums; }
.ts { color: var(--text-muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
.topic { max-width: 320px; }

/* Light zebra striping (Writer Ready-to-write, Editor tables). */
table.zebra tbody tr:nth-child(odd) td { background: var(--tint); }
/* Row-gap look: a few px of air between rows (pairs with .zebra). */
table.spaced { border-collapse: separate; border-spacing: 0 8px; }
table.spaced td { border-bottom: none; }
/* Cell alignment utilities: .mid centers a table's cells vertically;
   td/th.center centers one column horizontally. */
table.mid td { vertical-align: middle; }
td.center, th.center { text-align: center; }
/* Clickable rows (runs list): the whole row navigates via rowlink.js. */
tr[data-href] { cursor: pointer; }
tr[data-href]:hover td { background: var(--tint-hover); }
/* Admin CRUD tables (Sites, Users): styled inline inputs/selects, controls
   vertically centered in their row. */
table.crud td { vertical-align: middle; }
table.crud input[type=text], table.crud input[type=password], table.crud input[type=number], table.crud input[type=url], table.crud select {
  padding: 7px 10px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  font: inherit; font-size: var(--fs-sm); background: var(--panel); color: var(--text);
}
/* Row-action buttons sized to match the controls beside them. */
table.crud .btn { padding: 7px 12px; font-size: var(--fs-md); }
/* …unless the row explicitly asked for the compact variant (2026-09-05:
   .btn-small used to be a no-op here, so row actions had two sizes). */
table.crud .btn.btn-small { padding: 4px 10px; font-size: var(--fs-sm); }
/* Inputs that are a cell's sole content (Sites rows) fill their column;
   inputs inside inline forms (Users' reset-pw) keep their natural size. */
table.crud td > input[type=text], table.crud td > input[type=url] { width: 100%; }

/* Transcriber page (/transcriber): compact rows around the native player. */
.transcriber-play audio { width: 230px; max-width: 100%; height: 32px; display: block; }
.transcriber-play .ts { display: inline-block; max-width: 230px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }
td.transcriber-actions { white-space: nowrap; }
td.transcriber-actions .inline { display: inline-block; }
/* Call recordings page (2026-08-29, calls round 2): the transcript/notes
   viewers — a scrollable pre inside a <details>, token colors only. */
td.callrec-text { white-space: normal; max-width: 420px; }
td.callrec-text details { margin: 2px 0; }
td.callrec-text summary { cursor: pointer; color: var(--accent);
                          font-size: var(--fs-sm); }
.callrec-pre {
  white-space: pre-wrap; overflow-wrap: anywhere; overflow-y: auto;
  max-height: 320px; margin: 4px 0; padding: 8px 10px;
  background: var(--tint); border: 1px solid var(--border);
  border-radius: var(--radius-control); font: inherit;
  font-size: var(--fs-sm); color: var(--text);
}

.status {
  display: inline-block; padding: 1px 8px; border-radius: 999px;
  font-size: var(--fs-sm); font-weight: 600; background: var(--tint); color: var(--text);
}
.status-open, .status-running, .status-candidate { background: var(--warning-soft); color: var(--warning); }
.status-complete, .status-approved, .status-publish, .status-draft-created { background: var(--success-soft); color: var(--success); }
.status-error, .status-rejected { background: var(--danger-soft); color: var(--danger); }
/* Sent to the editor, awaiting a verdict (the Writer page's finalize gate). */
.status-finalized { background: var(--accent-soft); color: var(--accent); }
/* Assignment inbox (/assignments): pending blue / overdue red / completed
   green — theme tokens, so dark/slate/custom themes recolor for free.
   (.status-complete — no "d" — is the Transcriber "Transcribed" pill.) */
.status-pending { background: var(--accent-soft); color: var(--accent); }
.status-overdue { background: var(--danger-soft); color: var(--danger); }
.status-completed { background: var(--success-soft); color: var(--success); }
/* Custom-task priority pills (2026-08-29) — normal renders no pill; theme
   tokens so every palette recolors them for free. */
.status.prio-high { background: var(--danger-soft); color: var(--danger); }
.status.prio-low { background: var(--tint); color: var(--text-muted); }
td.assign-note { max-width: 340px; overflow-wrap: anywhere; }
/* Note clamp (2026-09-20): `--note-lines` comes inline from general.yaml
   assignments.note_preview_lines; the full text stays in the DOM (the
   popup clones it). pre-line lives on the text boxes, not the cell, so the
   template's whitespace between the clamp and the button adds no gaps. */
.assign-note-text {
  display: -webkit-box; -webkit-box-orient: vertical;
  -webkit-line-clamp: var(--note-lines, 3); line-clamp: var(--note-lines, 3);
  overflow: hidden; white-space: pre-line;
}
.assign-note-more {
  display: inline; margin: 2px 0 0; padding: 0; border: 0; background: none;
  color: var(--accent); font: inherit; font-size: var(--fs-sm); cursor: pointer;
}
.assign-note-more:hover { text-decoration: underline; }
.assign-note-full { white-space: pre-line; overflow-wrap: anywhere; overflow: auto; max-height: 60vh; }
/* Custom-task attachments (2026-09-14): 📎 download links in the note cell;
   in the Edit modal the same list carries a per-file "remove" tick. */
.assign-files { list-style: none; margin: 4px 0 0; padding: 0; font-size: var(--fs-sm); }
.assign-files li { margin: 2px 0; overflow-wrap: anywhere; }
.assign-files-edit li { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.assign-files-edit label { display: inline-flex; align-items: center; gap: 4px; color: var(--text-muted); white-space: nowrap; }
.assign-files-edit { margin-bottom: 6px; }
td.assign-due-overdue { color: var(--danger); font-weight: 600; }
td.assign-actions { white-space: nowrap; }
td.assign-actions .inline { display: inline-block; }
/* "Managed from <page>" (2026-10-09): the non-custom rows' stand-in for the
   Edit/Delete buttons — a muted in-app link (or plain text) to the page
   that owns the assignment. */
td.assign-actions .assign-managed { font-size: var(--fs-sm); white-space: nowrap; }
/* Inbox trim (2026-09-24, stakeholder ask "gain space, keep the colors,
   minimal"): tighter cells, sortable header buttons with a caret, the 📎
   count chip, 16px icon-only action buttons. Tokens only. */
table.assign-table th, table.assign-table td { padding: 6px 10px; }
table.assign-table td.assign-note { max-width: 420px; }
th.sortable { white-space: nowrap; }
th.sortable button {
  all: unset; cursor: pointer; display: inline-flex; gap: 4px; align-items: center;
  font: inherit; color: inherit; text-transform: inherit; letter-spacing: inherit;
}
th.sortable button::after { content: "\2195"; opacity: .35; font-size: var(--fs-sm); }
th.sortable[aria-sort="ascending"] button::after { content: "\2191"; opacity: 1; color: var(--accent); }
th.sortable[aria-sort="descending"] button::after { content: "\2193"; opacity: 1; color: var(--accent); }
th.sortable button:hover, th.sortable button:focus-visible { color: var(--text); }
.assign-files-chip {
  display: inline-block; margin: 2px 0 0 var(--space-2); padding: 0 8px;
  border: 1px solid var(--border); border-radius: 999px; background: var(--tint);
  color: var(--text-soft); font: inherit; font-size: var(--fs-sm); line-height: 1.5;
  cursor: pointer; white-space: nowrap;
}
.assign-files-chip:hover { background: var(--tint-hover); color: var(--text); }
.btn-icon { width: 16px; height: 16px; display: block; }
td.assign-actions .btn-small { padding: 5px 6px; line-height: 0; vertical-align: middle; }
td.assign-actions .inline + .inline, td.assign-actions .inline + .task-edit-btn,
td.assign-actions .task-edit-btn + .inline { margin-left: 2px; }

.meta { display: flex; flex-wrap: wrap; gap: 6px 28px; margin: 0 0 8px; }
.meta div { display: flex; gap: 8px; }
.meta dt { color: var(--text-muted); margin: 0; min-width: 64px; }
.meta dd { margin: 0; }

.timeline td.detail { color: var(--text-muted); font-size: var(--fs-sm); font-family: monospace; max-width: 280px; word-break: break-word; }
.timeline tr.lvl-error td { background: var(--danger-soft); }
.timeline tr.lvl-warn td { background: var(--warning-soft); }

ol.stories { padding-left: 20px; }
ol.stories li { margin-bottom: 12px; }
.story-title { font-weight: 600; }
.story-meta { color: var(--text-muted); font-size: var(--fs-sm); }
.story-summary { margin: 4px 0 0; }

.draft { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-panel); box-shadow: var(--shadow-panel); padding: 14px 16px; margin-bottom: 14px; }
.draft header { display: flex; align-items: center; gap: 10px; }
.draft-title { font-weight: 600; }
.draft-meta { color: var(--text-muted); font-size: var(--fs-sm); margin: 4px 0; }
.tags { margin: 4px 0; font-size: var(--fs-sm); color: var(--text-muted); }
.chip { display: inline-block; background: var(--tint); border-radius: 4px; padding: 1px 7px; margin: 0 4px 4px 0; color: var(--text); }
/* Editor feedback / note blocks: blue-tinted info surface. */
.notes {
  margin: 8px 0; padding: 8px 12px; background: var(--accent-soft);
  border-left: 3px solid var(--accent); border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  font-size: var(--fs-md); white-space: pre-wrap;
}
details { margin-top: 8px; }
summary { cursor: pointer; color: var(--accent); font-size: var(--fs-md); }
/* Article previews open at a fixed height and scroll inside; the bottom-right
   resize grip lets the reader grow/shrink the box. Covers the read-only
   previews everywhere and the Writer page's editable variant (.body-edit, a
   contenteditable div whose HTML is posted by its Save button). */
.article-body {
  border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 14px 16px;
  margin-top: 10px; background: var(--panel);
  height: 400px; overflow: auto; resize: vertical;
}
.article-body img { max-width: 100%; height: auto; }
.body-edit:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
/* Formatting toolbar above the editable body (writer.js wires the buttons). */
.body-toolbar { display: flex; gap: 6px; margin-top: 10px; }
/* HTML source mode (2026-10-09): the box's textarea twin (same .article-body
   frame/height) in the app's monospace stack; the toggle shows pressed. */
.body-source {
  font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
  font-size: var(--fs-sm); line-height: 1.5; white-space: pre-wrap;
  width: 100%; resize: vertical; color: var(--text);
}
.body-source:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.body-toolbar button[aria-pressed="true"] { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
/* Accordion draft lists (tabs.js acc-main/acc-detail): the title row toggles
   its detail rows; .acc-ready is added by JS so no-JS pages show no caret. */
tr.acc-main.acc-ready { cursor: pointer; }
tr.acc-main.acc-ready > td:first-child::before { content: "▸ "; color: var(--accent); }
tr.acc-main.acc-open > td:first-child::before { content: "▾ "; }
/* The Editor page's expanded review actions (agent check + manual forms). */
.review-actions { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; margin-top: 10px; }
/* Extra in-content images (Writing tab "More images" + read-only splices). */
.extra-images { margin-top: 14px; padding-top: 10px; border-top: 1px solid var(--border); }
.extra-image { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin: 8px 0; }
.extra-image .extra-image-meta { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.extra-image-meta input[type=text] { min-width: 220px; }
.extra-pos { color: var(--text-muted); font-size: var(--fs-sm); }
/* The featured image shown above a preview (display-only — at publish time the
   Publisher embeds the real one at the top of the post content). */
.preview-featured { margin: 10px 0 0; }
.preview-featured img { max-width: 100%; max-height: 240px; height: auto; border-radius: var(--radius-sm); border: 1px solid var(--border); }
.preview-featured figcaption { color: var(--text-muted); font-size: var(--fs-sm); margin-top: 4px; }

/* New-run trigger (Phase 2) */
.page-head { display: flex; align-items: center; justify-content: space-between; }
/* Keep the head-row button right-aligned even when its sibling h1 is hidden
   (the hide-h1 rule above) — harmless when the h1 is visible too. */
.page-head > .btn { margin-left: auto; }
/* Buttons (spec §4): 10px radius, 14px/500 label, 160ms transitions, 1px
   press. Primary = solid accent; hover uses brightness so an admin-custom
   accent keeps a matching hover (a fixed --accent-hover would not). */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  vertical-align: middle; gap: 6px;
  padding: 9px 15px; border-radius: var(--radius-control);
  background: var(--accent); color: #fff;
  font-size: var(--fs-sm); font-weight: 500; line-height: 1.2;
  border: none; cursor: pointer;
  transition: filter var(--motion-fast), box-shadow var(--motion-fast),
              transform var(--motion-fast), background var(--motion-fast);
}
.btn:hover {
  text-decoration: none; filter: brightness(1.07);
  box-shadow: 0 4px 12px rgba(15, 23, 42, .14);
}
.btn:active { transform: translateY(1px); }
.btn:disabled, .run-form button:disabled {
  cursor: default; filter: saturate(.45) brightness(1.05);
}
button.btn, .run-form button { font: inherit; font-weight: 500; }
/* Secondary action (e.g. Editor "Manual approve"): panel surface + border. */
.btn-secondary { background: var(--panel); color: var(--accent); border: 1px solid var(--border); }
/* Compact inline action (e.g. Writer per-story Approve/Reject). */
.btn-small { padding: 4px 10px; font-size: var(--fs-sm); border-radius: var(--radius-sm); }
/* Was a purple filled variant (Writer "Combined write") — blue-only rule:
   now the pale-blue accent-secondary look so it still reads as different
   from the primary beside it. */
.btn-alt { background: var(--accent-soft); color: var(--accent); border: 1px solid var(--accent); }
/* Green "ship it" variant (Writer "Send straight to WordPress" direct send)
   — green stays success-only per the spec. */
.btn.success { background: var(--success); color: #fff; }
/* Destructive (spec §4): pale red surface, red text + tinted border. */
.btn.danger, button.danger { background: var(--danger-soft); color: var(--danger); border: 1px solid var(--danger); }
/* The direct-send shortcut sits on its own line under the finalize button. */
.direct-send-form { margin-top: 8px; }
.inline-form { display: inline; margin: 0; }

/* Editor dashboard: per-run group of drafts awaiting review. */
.run-group { margin: 0 0 22px; }
.run-group h2 { font-size: var(--fs-lg); margin: 0 0 8px; }
.run-group h2 .muted { font-weight: 400; }

/* Writer dashboard: per-story selection. The title lives in its own flex
   column (.story-text) so a long title wraps within it, never under the
   checkbox. Written stories are grayed (already have a draft; not selectable). */
.story-pick { list-style: none; border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 8px 12px; margin: 0 0 10px; }
.story-pick legend { font-weight: 600; padding: 0 4px; }
.story-opt { display: flex; align-items: flex-start; gap: 6px; font-weight: 400; margin: 4px 0; }
.story-opt input[type=checkbox] { margin-top: 3px; flex: none; }
.story-opt .story-text { flex: 1; min-width: 0; }
/* The whole row toggles the checkbox (a <label> around checkbox + title). */
.story-opt .story-row {
  display: flex; align-items: flex-start; gap: 6px; flex: 1; min-width: 0;
  cursor: pointer; padding: 2px 4px; margin: -2px -4px; border-radius: 4px;
}
.story-opt .story-row:hover { background: var(--tint-hover); }
.story-opt.written { color: var(--text-muted); }
.story-opt .status { flex: none; }

/* Model dropdowns on the dashboards (Writer/Editor) — same look as the
   run-form selects, but sized to their content. */
select.model-pick {
  min-height: var(--control-h-sm); padding: 6px 12px;
  border: 1px solid var(--border); border-radius: var(--radius-control);
  font: inherit; font-size: var(--fs-sm); color: var(--text);
  background: var(--panel); max-width: 100%; margin: 0 0 6px;
}

/* Deep-write opt-in (Writer page: Ready-to-write actions + From-prompt form). */
label.deep-write {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--fs-sm); color: var(--text-muted); margin: 0 0 6px; cursor: pointer;
}

.run-form { max-width: 560px; }
.run-form .field { margin: 0 0 18px; }
.run-form label { display: block; font-weight: 600; margin-bottom: 5px; }
.run-form input[type=text], .run-form input[type=password], .run-form input[type=number], .run-form input[type=url], .run-form input[type=date], .run-form select {
  width: 100%; height: var(--control-h); padding: 0 14px; border: 1px solid var(--border);
  border-radius: var(--radius-control); font: inherit; font-size: var(--fs-md);
  background: var(--panel); color: var(--text);
}
.run-form .hint, .hint { color: var(--text-muted); font-size: var(--fs-sm); margin: 5px 0 0; }
.run-form .field.check { margin: 0 0 6px; }
.run-form .field.check label { display: inline-flex; align-items: center; gap: 8px; font-weight: 400; margin: 0; }
.run-form .field.check label .hint { margin: 0; }
.run-form fieldset.models { border: 1px solid var(--border); border-radius: var(--radius-control); padding: 12px 16px; margin: 0 0 18px; }
.run-form legend { font-weight: 600; padding: 0 6px; color: var(--text-muted); font-size: var(--fs-sm); text-transform: uppercase; letter-spacing: .03em; }
.run-form .actions button {
  padding: 9px 18px; border-radius: var(--radius-sm); background: var(--accent); color: #fff;
  font-weight: 600; border: none; cursor: pointer;
}
.run-form .actions button:hover { filter: brightness(1.07); }
/* .actions rows (Save + Delete forms, page-top buttons) relied on inline
   whitespace for their gap; every other action row is flex (2026-09-05). */
.actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.actions-top { margin-bottom: var(--space-3); }
/* .field outside a .run-form (modal + card forms) had no rhythm at all. */
.field { margin: 0 0 var(--space-3); }
.modal .field > label { display: block; font-weight: 600; margin-bottom: 5px; }
.modal .field.check > label { display: inline-flex; align-items: center; gap: 8px; font-weight: 400; }
/* Form controls OUTSIDE .run-form (modals, cards, inline tool forms) fell
   back to the OS widget — white under Dark/Slate, 24px tall beside 36px
   selects. One scoped rule (2026-09-05 audit) gives them the .model-pick
   height + tokens; .run-form/.model-pick/table.crud keep their own rules. */
.modal input[type=text], .modal input[type=password], .modal input[type=number],
.modal input[type=url], .modal input[type=date], .modal input[type=search],
.modal select, .modal textarea,
.card input[type=text], .card input[type=number], .card input[type=url],
.card input[type=date], .card select, .card textarea,
.arch-card input[type=text], .arch-card input[type=number], .arch-card input[type=url],
.arch-card input[type=date], .arch-card select, .arch-card textarea,
.ch-url-audit input[type=url], .extra-image-meta input[type=text],
.extra-image-meta select, .img-actions select, .se-chat-form textarea {
  box-sizing: border-box; min-height: var(--control-h-sm); padding: 6px 12px;
  border: 1px solid var(--border); border-radius: var(--radius-control);
  font: inherit; font-size: var(--fs-md); background: var(--panel); color: var(--text);
}
.modal textarea, .card textarea, .arch-card textarea, .se-chat-form textarea {
  line-height: 1.5; resize: vertical;
}
.modal .field input[type=text], .modal .field input[type=number], .modal .field input[type=url],
.modal .field input[type=date], .modal .field select, .modal .field textarea { width: 100%; }

/* Tabs (Publisher dashboard) */
.tabs { display: flex; gap: 6px; border-bottom: 1px solid var(--border); margin: 18px 0 0; }
/* The hidden attribute must beat the flex display (rss.js hides the whole
   News-feeds bar when every source is empty). */
.tabs[hidden] { display: none; }
.tabs .tab {
  background: none; border: 1px solid var(--border); border-bottom: none;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0; padding: 9px 16px; margin-bottom: -1px;
  font: inherit; font-size: var(--fs-sm); font-weight: 500;
  color: var(--text-muted); cursor: pointer;
  transition: color var(--motion-fast), background var(--motion-fast);
}
.tabs .tab:hover { color: var(--text); }
.tabs .tab.active {
  color: var(--text); font-weight: 600; background: var(--panel);
  box-shadow: inset 0 2px 0 var(--accent);
}
.tab-panel { display: none; padding-top: 16px; }
.tab-panel.active { display: block; }
.muted { color: var(--text-muted); }

/* Language picker (2026-08-15): inline SVG flags in the nav + its submenu. */
.lang-flag { width: 18px; height: 12px; border-radius: 2px; vertical-align: -1px; }
.lang-pick { display: inline-flex; align-items: center; gap: 5px; }
.submenu .lang-current {
  display: flex; align-items: center; gap: 7px; padding: 8px 14px;
  color: var(--text-muted); white-space: nowrap; font-weight: 600;
}
.submenu .linklike .lang-flag { margin-right: 3px; }

/* Settings → Translations (2026-08-15): compact three-column rows. */
.tr-toolbar { margin: 12px 0; }
.tr-toolbar .tr-filter { width: min(420px, 100%); padding: 7px 10px; border: 1px solid var(--border); border-radius: var(--radius-sm); font: inherit; }
.tr-group { font-size: var(--fs-lg); margin: 18px 0 6px; text-transform: capitalize; border-bottom: 1px solid var(--border); padding-bottom: 4px; }
.tr-row { display: grid; grid-template-columns: 220px 1fr 1fr; gap: 10px; align-items: center; padding: 3px 0; }
.tr-row .tr-key { font-size: var(--fs-sm); word-break: break-all; }
.tr-row .tr-en { font-size: var(--fs-sm); color: var(--text-muted); }
.tr-row input { width: 100%; padding: 5px 8px; border: 1px solid var(--border); border-radius: var(--radius-sm); font: inherit; font-size: var(--fs-sm); }
/* The English tab has no reference column — two columns only. */
.tab-panel[data-tab="lang-en"] .tr-row { grid-template-columns: 220px 1fr; }
.tr-actions { position: sticky; bottom: 0; background: var(--page); padding: 10px 0; border-top: 1px solid var(--border); }

.live-banner {
  display: flex; align-items: center; gap: 10px;
  background: var(--warning-soft); color: var(--warning); border: 1px solid var(--warning);
  border-radius: 8px; padding: 10px 14px; margin: 0 0 18px; font-size: var(--fs-md);
}
.spinner {
  width: 14px; height: 14px; border-radius: 50%;
  border: 2px solid var(--border); border-top-color: var(--warning);
  display: inline-block; animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* Home / landing page — the ONE page allowed a spacious, presentation-style
   composition (spec §1/§7): the gradient hero band above the working
   panels/cards. */
.home { text-align: center; padding-top: 4px; }
.home-hero {
  position: relative; overflow: hidden; text-align: left;
  border-radius: 20px; padding: 38px; margin: 0 0 24px;
  background:
    radial-gradient(620px 320px at 88% -10%, rgba(120, 170, 255, .35), transparent 60%),
    linear-gradient(120deg, #102b55, #174a8f 55%, #236cc7);
  box-shadow: 0 20px 50px rgba(18, 57, 111, .18);
  color: #fff;
}
.home-hero-main { display: flex; align-items: center; gap: 26px; flex-wrap: wrap; }
/* Fixed width, free height — the animated GIF may not be square; pinning
   both dimensions would distort it. */
.home-logo { width: 110px; height: auto; border-radius: 14px; flex: none; }
.home-logo-small { width: 56px; border-radius: 12px; }
/* The client's uploaded hero image (Settings → General, 2026-10-09): any
   aspect ratio, height-locked to the 110px post-it cards so the hero row
   never grows; wide banners cap at 240px without stretching. */
.home-logo-custom { width: auto; height: 110px; max-width: 240px; object-fit: contain; }
.home-hero-copy { min-width: 0; }
/* --hero-text / --hero-sub-opacity are set only by base.html's inline
   .home-hero block when theme.hero_text is configured; the var() fallbacks
   ARE the Timos defaults, so an unset knob renders exactly as before. */
.home-title {
  color: var(--hero-text, #fff); font-size: 34px; font-weight: 500; letter-spacing: -0.02em;
  line-height: 1.12; margin: 0;
}
.home-hero-sub {
  color: var(--hero-text, #dbeafe); opacity: var(--hero-sub-opacity, 1);
  font-size: 16px; line-height: 1.6; margin: 6px 0 0;
}
/* Home post-it notes (2026-09-20, stakeholder ask: "use the blue space for
   post-it notes"): a right-hand sticky board INSIDE the hero's flex row.
   Fixed-size cards — height = the 110px logo, so the hero row never grows
   on desktop and the panels below stay put; the text is a 3-line clamp.
   The note background is the palette hex from general.yaml (inline
   --note-bg) and the ink is fixed dark whatever the theme: the notes are
   content on the fixed-color hero, like its white title — not chrome, so
   no theme tokens. Width rides the viewport (clamp) so four notes fit
   beside the title with or without the 248px sidebar. */
.home-hero-copy { flex: 1 1 260px; }
.home-notes {
  display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: flex-start;
  gap: 14px; margin-left: auto; padding: 4px 6px 0 0;
}
.home-note {
  position: relative; width: clamp(140px, 11vw, 172px); height: 110px; box-sizing: border-box;
  padding: 10px 12px 8px; border-radius: 3px;
  background: var(--note-bg, #fff3a3); color: #1f2937;
  box-shadow: 0 10px 22px rgba(0, 0, 0, .28), inset 0 1px 0 rgba(255, 255, 255, .35);
  display: flex; flex-direction: column; text-align: left;
  font-family: inherit; font-size: var(--fs-sm); line-height: 1.35;
  transform: rotate(-1.6deg);
}
.home-note:nth-child(even) { transform: rotate(1.3deg); }
.home-note-text {
  margin: 0; display: -webkit-box; -webkit-box-orient: vertical;
  -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden;
  overflow-wrap: anywhere; white-space: pre-line;
}
.home-note-meta {
  display: flex; align-items: center; gap: 6px; margin-top: auto;
  font-size: 11px; line-height: 1.2; color: #4b5563;
}
.home-note-author { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.home-note-actions { margin-left: auto; display: inline-flex; gap: 2px; flex: none; }
.home-note-actions .inline { display: inline; }
.home-note-edit, .home-note-delete {
  border: 0; background: none; padding: 0 3px; cursor: pointer;
  color: #374151; font-size: 13px; line-height: 1; border-radius: 3px;
}
.home-note-edit:hover, .home-note-delete:hover { background: rgba(0, 0, 0, .1); }
.home-note-delete:hover { color: #b91c1c; }
/* The "+ Add note" ghost card: same footprint, dashed, on the hero's blue. */
.home-notes .home-note-add {
  align-items: center; justify-content: center; gap: 4px; cursor: pointer;
  background: rgba(255, 255, 255, .12); color: var(--hero-text, #fff);
  border: 1.5px dashed rgba(255, 255, 255, .55); box-shadow: none; transform: none;
  /* Follows a configured hero text color (2026-10-09); the rgba lines above
     stay as the fallback for browsers without color-mix(). */
  background: color-mix(in srgb, var(--hero-text, #fff) 12%, transparent);
  border-color: color-mix(in srgb, var(--hero-text, #fff) 55%, transparent);
}
.home-notes .home-note-add:hover:not([disabled]) {
  background: rgba(255, 255, 255, .2);
  background: color-mix(in srgb, var(--hero-text, #fff) 20%, transparent);
}
/* Settings → General: the current hero image preview. */
.hero-preview { height: 56px; width: auto; max-width: 160px; object-fit: contain; border-radius: 10px; vertical-align: middle; }
.hero-preview-row { display: flex; align-items: center; gap: 12px; margin: 4px 0 8px; }
.home-notes .home-note-add[disabled] { opacity: .45; cursor: not-allowed; }
.home-note-add-plus { font-size: 22px; line-height: 1; }
/* The add/edit modal's palette swatches (the radio stays visible for
   keyboard users; the checked swatch gets an accent ring). */
.home-note-color-label { display: block; font-weight: 600; margin-bottom: 2px; }
.home-note-swatches { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 6px; }
.home-note-swatch { display: inline-flex; align-items: center; gap: 4px; cursor: pointer; }
.home-note-swatch-box {
  width: 28px; height: 28px; border-radius: 4px; background: var(--note-bg);
  border: 1px solid rgba(0, 0, 0, .15);
}
.home-note-swatch:has(input:checked) .home-note-swatch-box { outline: 2px solid var(--accent); outline-offset: 2px; }
body.density-tight .home-note { height: 96px; }
@media (max-width: 960px) {
  /* Stacked: the board wraps under the title (the hero may grow here). */
  .home-notes { margin-left: 0; justify-content: flex-start; width: 100%; padding: 0; }
}
/* Home RSS news panel (config/rss_feeds.yaml; items rendered by rss.js).
   The height rides the --rss-panel-height CSS var, set inline from the
   panel_height_px config value; the list scrolls inside it. */
/* Home layout (2026-09-14): the panel row + the drafts column share the
   full width — the former "expanded" view is now the only one (the home
   cards and the compact/expanded/show-hide buttons were retired with
   them; see home.html). */
.home-layout {
  display: flex; gap: 24px; align-items: flex-start; justify-content: center;
  /* Never overflow the page horizontally (spec quality gate). */
  flex-wrap: wrap;
}
/* The home panels' container (one aside per visible `panels:` entry): thin
   panels in ONE horizontal row (owner ask 2026-08-07 — use the width to the
   max before growing vertically). The row takes all the width the drafts
   column leaves free; each panel grows from its configured minimum width
   (--rss-panel-width, panel_width_px), and once the minimums overflow the
   row scrolls horizontally behind a thin styled scrollbar (the .rss-list
   vertical pattern, horizontal). The 200px basis/floor (owner pick
   2026-08-20) lets the strip squeeze behind its own scrollbar on narrow
   desktop windows instead of wrapping. */
.rss-panels {
  display: flex; align-items: flex-start; gap: 16px; align-self: flex-start;
  flex: 1 1 200px; min-width: 200px;
  overflow-x: auto; overflow-y: hidden; padding-bottom: 6px;
  scrollbar-width: thin; scrollbar-color: var(--scrollbar) transparent;
}
.rss-panels::-webkit-scrollbar { height: 8px; }
.rss-panels::-webkit-scrollbar-track { background: transparent; }
.rss-panels::-webkit-scrollbar-thumb { background: var(--scrollbar); border-radius: 4px; }
.rss-panels::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-hover); }
.rss-panel {
  flex: 0 0 300px; width: 300px; height: var(--rss-panel-height, 525px);
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius-panel); box-shadow: var(--shadow-panel);
  padding: 14px 16px; text-align: left; display: flex; flex-direction: column;
}
/* Inside the row each panel grows from its configured width and shrinks no
   further — the container scrolls instead. */
.rss-panels .rss-panel {
  width: auto; flex: 1 0 var(--rss-panel-width, 240px);
  min-width: var(--rss-panel-width, 240px);
}
/* Latest-articles column (home): a direct .home-layout child reusing the
   .rss-panel chrome, fixed at the configured panel width — it never grows
   (the RSS row keeps the flex-grow slack). Height rides --rss-panel-height
   via the base .rss-panel rule. */
.home-drafts-panel {
  width: auto; flex: 0 0 var(--rss-panel-width, 240px);
  min-width: var(--rss-panel-width, 240px);
}
/* Its rows: the author link (opens a messenger DM) + the status chip on the
   byline; colors come from the shared .status / accent tokens. */
.home-drafts-panel .hd-dm { color: var(--accent); text-decoration: none; }
.home-drafts-panel .hd-dm:hover { text-decoration: underline; }
.home-drafts-panel .rss-src .status { margin-left: 6px; }
.rss-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin: 0 0 8px; }
.rss-panel h2 { font-size: var(--fs-base); margin: 0; }
/* Long titles ellipsize instead of pushing the filter out of a thin panel. */
.rss-head h2 { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rss-filter {
  width: 120px; min-width: 60px; flex: 0 1 120px; padding: 4px 8px;
  font: inherit; font-size: var(--fs-sm);
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--panel);
}
/* Vertical scrollbar visible (thin, muted — owner ask 2026-08-01; the earlier
   hide-both-axes rule was meant for the horizontal one only, which
   overflow-x: hidden now suppresses). */
.rss-list {
  margin: 0; padding: 0; list-style: none; overflow-y: auto; overflow-x: hidden;
  flex: 1; scrollbar-width: thin; scrollbar-color: var(--scrollbar) transparent;
}
.rss-list::-webkit-scrollbar { width: 8px; }
.rss-list::-webkit-scrollbar-track { background: transparent; }
.rss-list::-webkit-scrollbar-thumb { background: var(--scrollbar); border-radius: 4px; }
.rss-list::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-hover); }
.rss-list li { padding: 7px 0; border-bottom: 1px solid var(--border); font-size: var(--fs-sm); }
.rss-list li:last-child { border-bottom: none; }
.rss-title { color: var(--text); }
.rss-src { color: var(--text-muted); font-size: var(--fs-sm); display: block; margin: 2px 0; }
.rss-send {
  background: none; border: none; padding: 0; color: var(--accent);
  font-size: var(--fs-sm); cursor: pointer; white-space: nowrap;
}
.rss-send:hover { text-decoration: underline; }
/* One row for the per-item actions (badge + both Send buttons + Assign /
   "Assigned to X"). Non-wrapping since 2026-08-08; with the third action
   (2026-09-10) a 260px panel can't hold four items on one line, so the row
   may wrap onto a second line — each action itself never breaks. */
.rss-actions { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 10px; }
.rss-actions > * { white-space: nowrap; }
/* Assigned marker (2026-09-10): muted text, green once the inbox marks it
   done; never a link — the item is locked. */
.rss-assigned { color: var(--text-muted); font-size: var(--fs-sm); }
.rss-assigned-done { color: var(--success); }
/* "Already sent to drafts" marker (any user; see storage.sent_rss_urls). */
.rss-sent-badge { color: var(--success); font-size: var(--fs-sm); }
/* News feeds page (news_feeds.html): the home panel's internals at full
   width, one per source tab; a wider filter box fits the extra room. */
.rss-panel.rss-panel-wide { width: auto; flex: none; }
.rss-panel-wide .rss-filter { width: 240px; }

/* Settings -> RSS feeds page (config_rss.html): one tab per source. */
.tabs.wrap { flex-wrap: wrap; row-gap: 6px; }
.tabs.wrap .tab { margin-bottom: 0; }
/* The "Panels" tab: one SUB-tab per home panel (config_rss.js). The bar
   reuses the .tabs container look, but the buttons/panels carry their own
   classes so tabs.js's global .tab/.tab-panel group ignores them. The
   sub-tabs are draggable — dropping one reorders + auto-saves. */
.subtabs .subtab {
  background: none; border: 1px solid var(--border); border-bottom: none;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0; padding: 9px 16px; margin-bottom: 0;
  font: inherit; font-size: var(--fs-sm); font-weight: 500;
  color: var(--text-muted); cursor: pointer;
}
.subtabs .subtab.active {
  color: var(--text); font-weight: 600; background: var(--panel);
  box-shadow: inset 0 2px 0 var(--accent);
}
.subtabs .subtab[draggable="true"] { cursor: grab; }
.subtabs .subtab.dragging { opacity: 0.5; cursor: grabbing; }
.subtab-panel { display: none; padding-top: 16px; }
.subtab-panel.active { display: block; }
/* Menu settings (config_menu.html): the two drag-reorder lists. */
.menu-orders { display: flex; gap: 28px; flex-wrap: wrap; align-items: flex-start; }
.menu-order-col { flex: 1 1 220px; }
.menu-order { list-style: none; margin: 8px 0 0; padding: 0; }
.menu-order-item {
  background: var(--panel); border: 1px solid var(--border); border-radius: 8px;
  padding: 8px 12px; margin-bottom: 6px; cursor: grab; user-select: none;
  font-size: var(--fs-md);
}
.menu-order-item.dragging { opacity: 0.5; cursor: grabbing; }
/* Group blocks in the Tools order list: the label row plus a nested,
   indented member list (its own drag context — see config_menu.js). */
.menu-order-group { background: var(--tint); }
.menu-order .menu-order-nested { margin: 6px 0 0 18px; }
/* Menu settings: an item's "In group" select, indented under its name. */
.menu-indent { margin-left: 22px; }
.rss-panel-def { margin-bottom: 18px; }
.rss-panel-feeds {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 4px 14px; border: 1px solid var(--border); border-radius: var(--radius-control);
  padding: 10px 12px; background: var(--panel);
}
.check-inline { font-weight: normal; font-size: var(--fs-sm); }
.check-inline input { margin-right: 6px; }
.rss-dot.bad { color: var(--danger); }
.rss-dot.off { color: var(--text-muted); }
.rss-diag {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-control);
  padding: 10px 14px; font-size: var(--fs-md); margin: 0 0 14px;
}
.rss-diag.ok .rss-diag-mode { color: var(--success); }
.rss-diag.bad .rss-diag-mode { color: var(--danger); }
.rss-diag-mode { font-weight: 600; }
.rss-diag form { margin-left: auto; }
.rss-results td.nowrap { white-space: nowrap; }
.rss-results td.muted { font-size: var(--fs-sm); }
.field-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 0 18px;
}
.rss-scrape-fields {
  border: 1px solid var(--border); border-radius: var(--radius-control);
  padding: 12px 14px 0; margin: 0 0 18px;
}
.rss-scrape-fields legend { font-size: var(--fs-sm); font-weight: 600; padding: 0 6px; }

/* Action-progress modal (see progress.js + CLAUDE.md convention). The
   shared .modal-backdrop/.modal primitives also dress the media picker,
   the RSS write-type chooser, the consent blocker, the home chat chooser,
   and BusyModal (photogpt — built in JS from the same class names). */
.modal-backdrop {
  position: fixed; inset: 0; z-index: 50;
  background: rgba(13, 21, 40, .64);
  backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
  display: flex; align-items: center; justify-content: center; padding: 20px;
}
.modal {
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius-panel);
  padding: 20px 24px; width: 100%; max-width: 560px;
  max-height: calc(100vh - 36px);
  display: flex; flex-direction: column; gap: 12px;
  box-shadow: 0 28px 90px rgba(4, 12, 27, .34);
}
.modal h2 { margin: 0; border: none; padding: 0; font-size: var(--fs-2xl); }
/* Pre-send image metadata dialog (2026-09-24, writer-image-meta.js): the
   Photo Dragon send-dialog recipe (.modal + .field) with a thumbnail, a
   step line and a Suggest row. Tokens only. */
.image-meta-modal { max-width: 600px; overflow-y: auto; }
.image-meta-modal .imgmeta-step { margin: 0; color: var(--text-muted); font-size: var(--fs-sm); }
.image-meta-modal .imgmeta-thumb {
  display: block; max-width: 100%; max-height: 140px; margin: 0 auto;
  border: 1px solid var(--border); border-radius: var(--radius-sm); object-fit: contain;
}
.image-meta-modal .run-form { display: flex; flex-direction: column; gap: 8px; }
.image-meta-modal .imgmeta-suggest { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.image-meta-modal .imgmeta-suggest .hint { margin: 0; font-size: var(--fs-sm); }
.image-meta-modal .modal-actions .btn[hidden] { display: none; }
/* The Writer page's per-image WP-metadata inputs (same Save button as the
   caption): the description textarea takes the full row. */
.caption-form textarea, .extra-image-meta textarea {
  flex: 1 1 100%; min-width: 260px; padding: 6px 10px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); font: inherit; font-size: var(--fs-sm);
  background: var(--panel); color: var(--text); resize: vertical;
}
.image-meta-form { border-top: 1px dashed var(--border); padding-top: 6px; }
.progress-track {
  height: 6px; border-radius: 999px; background: var(--tint);
  overflow: hidden; position: relative;
}
.progress-fill {
  position: absolute; top: 0; bottom: 0; width: 35%;
  border-radius: 999px; background: var(--accent);
  animation: progress-sweep 1.4s ease-in-out infinite;
}
@keyframes progress-sweep {
  0% { left: -35%; }
  100% { left: 100%; }
}
.progress-log {
  font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
  font-size: var(--fs-sm); line-height: 1.6; white-space: pre-wrap; word-break: break-word;
  background: var(--tint); border-radius: var(--radius-sm); padding: 10px 12px;
  overflow-y: auto; max-height: 40vh;
}
.progress-success { color: var(--success); font-weight: 600; }
.progress-error { color: var(--danger); font-weight: 600; }
.modal-actions { display: flex; gap: 10px; justify-content: flex-end; }

/* Writer dashboard: each draft is a 3-row group (main / preview / tools).
   The inner rows drop their top borders so the group reads as one card; the
   tools row keeps the group's bottom border. */
/* .draft-groups uses separate borders so the white gap between groups and the
   1px divider under a group can coexist (collapsed borders would merge them). */
table.draft-groups { border-collapse: separate; border-spacing: 0; }
tr.draft-main td { border-bottom: none; }
/* Editable draft title in the My drafts row (2026-09-10): reads as the
   table's bold title until focused — border only on hover/focus, so the
   list keeps its table look. Tokens only (every theme restyles it). */
.draft-title-edit {
  display: inline-block; vertical-align: middle;
  width: calc(100% - 24px); min-width: 160px; padding: 4px 8px; margin: -4px 0;
  border: 1px solid transparent; border-radius: var(--radius-sm);
  background: transparent; color: var(--text); font: inherit; font-weight: 600;
  transition: border-color var(--motion-fast), background var(--motion-fast);
}
.draft-title-edit:hover { border-color: var(--border); }
.draft-title-edit:focus {
  outline: none; border-color: var(--accent); background: var(--panel);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
tr.draft-preview td { border-bottom: none; padding-top: 0; }
tr.draft-preview details { margin-top: 0; }
tr.draft-tools td { padding-top: 0; border-bottom: none; }
/* Finalize row: the last row of a Writing-tab draft group. */
tr.draft-actions td { text-align: right; padding-top: 0; border-bottom: none; }
/* One flex row for the bottom action buttons (2026-08-08): Delete on the
   left of the send button(s), everything right-aligned as before. */
.draft-actions-row { display: flex; gap: 8px; justify-content: flex-end; align-items: center; flex-wrap: wrap; }
/* Alternate draft groups get a light tint (parity class set in the template);
   a white top border on each following group reads as a gap between groups. */
tbody tr.draft-main:not(:first-child) td { border-top: 10px solid var(--panel); }
tr.group-alt td { background: var(--tint); }
.draft-tools .tools { display: flex; flex-wrap: wrap; gap: 10px 28px; align-items: flex-start; }
/* The draft's single "Save draft changes" button: a full-width line under the
   metadata + Yoast boxes (the button covers every field of the group). */
.tools .save-draft-row { width: 100%; }
/* One light box groups categories + tags (saved by the single Save button
   below), so the revise controls read as a clearly separate action. */
.draft-tools .meta-form {
  display: flex; flex-wrap: wrap; gap: 10px 28px; align-items: flex-start;
  flex: 1; min-width: 0;
  border: 1px solid var(--border); border-radius: var(--radius-control);
  padding: 12px 14px; background: var(--panel);
}
/* SEO (Yoast) fields: a second box in the same style as the metadata box. */
.draft-tools .seo-form {
  display: flex; flex-wrap: wrap; gap: 10px 20px; align-items: flex-end;
  flex: 1; min-width: 280px;
  border: 1px solid var(--border); border-radius: var(--radius-control);
  padding: 12px 14px; background: var(--panel);
}
.draft-tools .seo-form .meta-label { width: 100%; }
.seo-field { display: block; font-size: var(--fs-sm); flex: 1; min-width: 180px; }
.seo-field input[type=text], .seo-field textarea {
  display: block; width: 100%; margin-top: 4px; padding: 6px 10px;
  border: 1px solid var(--border); border-radius: var(--radius-sm); font: inherit; background: var(--panel);
}
/* Meta description: a wrapping multi-line box on its own full-width line, so
   the whole phrase is readable (word-wrap is the textarea default). */
.seo-field-wide { flex: 1 1 100%; }
.seo-field textarea { resize: vertical; min-height: 60px; }
/* Editable image caption under the preview's featured image. */
.caption-form {
  display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: center;
  margin: 6px 0 12px;
}
.caption-form .meta-label { margin: 0; }
.caption-form input[type=text] {
  flex: 1; min-width: 260px; padding: 6px 10px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); font: inherit; font-size: var(--fs-sm); background: var(--panel);
}
/* "From prompt" tab: the free-text prompt + model/style pickers. */
.prompt-form textarea {
  width: 100%; padding: 8px 10px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); font: inherit; background: var(--panel); resize: vertical;
}
.prompt-form .meta-label { display: block; margin: 0 0 4px; }
.prompt-controls { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 8px; }
.prompt-controls select.model-pick { margin: 0; }
/* Publisher Ready-to-send: keep the Yoast column readable, not dominant. */
td.meta-info { max-width: 300px; font-size: var(--fs-sm); }
/* Per-draft revise box + version history (under the metadata row). */
.revise-block {
  margin-top: 24px; padding-top: 10px; border-top: 1px dashed var(--border);
  display: flex; flex-direction: column; flex: 1;   /* absorb the cell's leftover height */
}
/* The revise box absorbs the cell's leftover height (the image cell beside it
   is usually taller): the td height:1px trick makes height:100% resolve on
   .cell-stack, then the flex column chain pushes all growth into
   .revise-controls, whose input stretches while the model picker and Revise
   button keep their own height, top-aligned. */
.draft-tools .tools-cell { height: 1px; }
.tools-cell .cell-stack { display: flex; flex-direction: column; height: 100%; }
.revise-form { display: flex; flex-direction: column; flex: 1; }
.revise-controls { display: flex; flex-wrap: wrap; gap: 8px; align-items: stretch; flex: 1; }
.revise-controls input[type=text] {
  flex: 1; min-width: 240px; height: auto; min-height: 90px;
  padding: 6px 10px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); font: inherit; background: var(--panel);
}
.revise-controls select.model-pick { margin: 0; align-self: flex-start; }
.revise-controls .btn { align-self: flex-start; }
/* Featured image (Writer page): preview-sized thumbnail + provenance +
   pick/upload/clear controls; results grid in the media-library modal. */
.image-cell { min-width: 220px; }
.img-thumb { max-width: 180px; max-height: 110px; border-radius: var(--radius-sm); border: 1px solid var(--border); display: block; }
.feat-image .img-meta { margin: 6px 0 0; font-size: var(--fs-sm); color: var(--text-muted); }
.feat-image .img-attr { margin: 2px 0 0; font-size: var(--fs-sm); color: var(--text-muted); font-style: italic; max-width: 260px; }
/* The featured-image label reads as the cell's heading (extra .draft-tools
   scope so it outranks the generic .draft-tools .meta-label sizing below). */
.draft-tools .image-cell .meta-label, .tools .image-cell .meta-label,
.image-cell .meta-label { font-size: var(--fs-sm); font-weight: 600; color: var(--text); }
.img-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; align-items: center; }
.img-actions .btn { padding: 6px 12px; font-size: var(--fs-sm); }
.img-actions form { margin: 0; }
.img-url-form { display: flex; gap: 6px; width: 100%; }
.img-url-form input[type=url] {
  flex: 1; min-width: 120px; padding: 5px 8px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); font: inherit; font-size: var(--fs-sm); background: var(--panel);
}
.img-upload-form label { margin: 0; }
/* Media picker (2026-10-09, stakeholder ask — the 140px x 90px tiles in a
   720px box were too small to browse): a near-full-width modal, a 70vh
   results area, SQUARE tiles (aspect-ratio 1/1 over WP's medium rendition,
   ~180px columns ≈ 6 per row) with the title only under each — the
   caption/credit rides the hover (the tile's title attribute). */
.media-modal { max-width: 1200px; }
.media-modal input[type=search] {
  width: 100%; padding: 8px 10px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); font: inherit; background: var(--panel);
}
.media-results {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: var(--space-3); overflow-y: auto; max-height: 70vh; padding: 2px;
}
.media-item {
  padding: 0; border: 2px solid var(--border); border-radius: var(--radius-sm); background: var(--panel);
  cursor: pointer; overflow: hidden; display: flex; flex-direction: column;
  text-align: left; font: inherit; color: var(--text);
}
.media-item:hover { border-color: var(--accent); }
.media-item img { width: 100%; aspect-ratio: 1 / 1; height: auto; object-fit: cover; display: block; flex: none; }
.media-label {
  padding: 4px 6px 5px; font-size: var(--fs-sm); line-height: 1.3;
  border-top: 1px solid var(--border); min-width: 0;
}
.media-title {
  display: block; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.media-pager { display: flex; justify-content: space-between; align-items: center; gap: 10px; margin-top: 8px; }
/* Per-page selector (2026-08-16) — sits after the pager buttons; explicit
   surface tokens so it follows the dark/slate palettes like the search box
   (it lives outside any .run-form, which is where selects get styled). */
.media-per-page { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-sm); }
.media-per-page select {
  padding: 4px 6px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  font: inherit; background: var(--panel); color: var(--text);
}

/* Link picker modal (_link_modal.html): custom-URL row + the archive search
   list. Reuses the media-pager classes for its pager. */
.link-modal { max-width: 680px; }
.link-section { display: flex; flex-direction: column; gap: 8px; }
.link-custom-row { display: flex; gap: 8px; }
.link-custom-row input[type=url], .link-modal input[type=search] {
  width: 100%; padding: 8px 10px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); font: inherit; background: var(--panel);
  color: var(--text);
}
.link-custom-row input[type=url] { flex: 1; min-width: 0; }
.link-results {
  display: flex; flex-direction: column; gap: 6px;
  overflow-y: auto; max-height: 40vh; padding: 2px;
}
.link-result {
  display: flex; flex-direction: column; gap: 2px; align-items: flex-start;
  text-align: left; padding: 8px 10px; cursor: pointer;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--panel); color: var(--text); font: inherit;
}
.link-result:hover { border-color: var(--accent); }
.link-result-title { font-weight: 600; }
.link-result-meta { font-size: var(--fs-sm); }
/* Link text row (2026-10-09): shown only when the Writer's link bubble opens
   the modal in edit mode. */
.link-text-row { display: flex; flex-direction: column; gap: 4px; }
.link-text-row input[type=text] {
  width: 100%; padding: 8px 10px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); font: inherit; background: var(--panel); color: var(--text);
}

/* Link bubble (2026-10-09): the floating address / Open / Edit / Unlink panel
   writer.js shows under the body-box link the caret sits in. Fixed so it
   escapes the box's inner scroll; writer.js follows scroll + resize. */
.link-bubble {
  position: fixed; z-index: 60; display: flex; align-items: center; gap: var(--space-2);
  padding: 6px 8px; background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-panel); font-size: var(--fs-sm);
}
.link-bubble[hidden] { display: none; }
.link-bubble-href {
  max-width: 280px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--accent); font-weight: 500;
}

/* "Propose me links" (Writer drafts, under the Yoast box): a third box in
   the metadata/Yoast style on its own full-width line above the Save row. */
.draft-tools .link-propose {
  width: 100%;
  border: 1px solid var(--border); border-radius: var(--radius-control);
  padding: 12px 14px; background: var(--panel);
}
.link-propose .hint-inline { margin-left: 8px; }
.link-propose-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.link-propose-results { margin-top: 10px; display: flex; flex-direction: column; gap: 6px; }
.link-propose-item {
  display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline;
  padding: 6px 8px; border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.link-propose-item a { font-weight: 600; }
.link-propose-meta { font-size: var(--fs-sm); flex: 1; min-width: 200px; }

.versions { margin-top: 8px; }
.version-list { list-style: none; margin: 6px 0 0; padding: 0; font-size: var(--fs-sm); }
.version-list li { display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: center; padding: 4px 0; border-bottom: 1px solid var(--border); }
.version-list li:last-child { border-bottom: none; }
.version-title { font-weight: 600; }
/* Box titles ("Categories", "Tags", "Ask Timos…", "Yoast fields", "Caption",
   the From-prompt label): black and bold for readability (stakeholder ask). */
.draft-tools .meta-label, .caption-form .meta-label, .prompt-form .meta-label {
  display: block; font-weight: 600; font-size: var(--fs-sm); color: var(--text); margin: 0 0 4px;
}
/* From-prompt / Paste-article tabs: field labels a notch bigger for
   readability, and the (element-only) title input styled like the textarea. */
.prompt-form .meta-label { font-size: var(--fs-base); margin: 14px 0 6px; }
.prompt-form .hint-inline { font-weight: 400; font-size: var(--fs-sm); color: var(--text-muted); }
.prompt-form input[type=text] {
  width: 100%; padding: 8px 10px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); font: inherit; background: var(--panel);
}
.draft-tools .hint-inline { font-weight: 400; text-transform: none; letter-spacing: normal; }
.draft-tools .cat-opt { display: inline-flex; align-items: center; gap: 4px; margin: 0 10px 4px 0; font-size: var(--fs-md); white-space: nowrap; }
/* Categories flagged bold: true in categories.yaml (the most-used ones). */
.draft-tools .cat-opt.cat-bold { font-weight: 600; }
.draft-tools .meta-tags { flex: 1; min-width: 240px; }
.draft-tools .meta-tags input[type=text] {
  width: 100%; padding: 6px 10px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); font: inherit; background: var(--panel);
}
.draft-tools .btn { align-self: flex-end; }

/* Editor page: the read-only "Article meta" expandable under each preview
   (categories/tags chips + the Yoast fields), the Manual-reject form, and the
   Writer page's "sent back by the editor" note. */
.meta-details .tools {
  display: flex; flex-wrap: wrap; gap: 10px 28px; align-items: flex-start;
  border: 1px solid var(--border); border-radius: var(--radius-control);
  padding: 12px 14px; margin: 6px 0; background: var(--panel);
}
.meta-details .meta-label {
  display: block; font-weight: 600; font-size: var(--fs-sm); color: var(--text); margin: 0 0 4px;
}
.meta-details .meta-yoast { flex: 1; min-width: 240px; }
.meta-details .yoast-line { margin: 2px 0; font-size: var(--fs-sm); }
.reject-form { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.reject-form input[type=text] {
  flex: 1; min-width: 160px; padding: 4px 8px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); font: inherit; font-size: var(--fs-sm); background: var(--panel);
}
.sent-back-note {
  margin: 6px 0 0; padding: 6px 10px; font-size: var(--fs-sm);
  background: var(--warning-soft); border-left: 3px solid var(--warning); border-radius: 4px;
}
/* The full review text (a details body): keep the model's line breaks. */
.sent-back-note summary { color: inherit; font-weight: 600; font-size: var(--fs-sm); }
.sent-back-note .note-body { white-space: pre-wrap; margin-top: 6px; }

/* -------------------------------------------------------------------------
   Responsive program (spec §18) — content-based steps at ~1180 / 960 / 760
   / 700 / 560. Wide content (tables, board-like rows) scrolls inside its
   own box; the page never scrolls horizontally. The sidebar's own ≤960px
   drawer rules live in the sidebar section below.
   ------------------------------------------------------------------------- */
@media (max-width: 1180px) {
  .content { padding: 22px 20px; }
  .home-hero { padding: 30px; }
}
@media (max-width: 960px) {
  .content { width: auto; padding: 16px; }
  .topbar { flex-wrap: wrap; padding: 9px 16px; }
  .topnav { flex-wrap: wrap; }
  /* The RSS panels stack under the drafts column on small screens; the
     row keeps its horizontal touch scroll (panels hold their minimum
     width). */
  .home-layout { flex-direction: column; align-items: stretch; }
  .rss-panels { order: 2; min-width: 0; }
  /* Stacked order: latest articles, RSS row. flex: none is required —
     in the column layout the row rule's flex-basis would become the HEIGHT. */
  .home-drafts-panel { order: 1; flex: none; }
}
@media (max-width: 760px) {
  table { display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .stats-grid, .menu-orders { flex-direction: column; }
  .stats-kpis { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
  .bar { width: 64px; }
  .tr-row, .tab-panel[data-tab="lang-en"] .tr-row { grid-template-columns: 1fr; gap: 4px; padding: 8px 0; }
  .field-grid { grid-template-columns: 1fr; }
  .page-head { flex-wrap: wrap; gap: 8px; }
}
@media (max-width: 700px) {
  .home-hero { padding: 25px 20px; }
  .home-title { font-size: 30px; }
  .chat-search { padding: 12px 20px; }
  /* Floating panels go near-fullscreen (spec §13/§14). The Messages page's
     panel is static (.msgr-page below) and keeps its own height. */
  .msgr-panel:not(.msgr-page *) {
    width: calc(100vw - 16px); height: calc(100vh - 16px);
    right: 8px; bottom: 8px; border-radius: 16px;
  }
  .voice-panel { width: calc(100vw - 44px); }
}
@media (max-width: 560px) {
  .modal-backdrop { padding: 8px; }
  .modal { max-height: calc(100vh - 16px); border-radius: 12px; padding: 16px 18px; }
  .media-results { grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); } /* 2-3 square tiles per row on a phone */
  /* 42px touch targets for ordinary actions (dense header/table icon
     controls may stay 34–36px per spec when clearly separated). */
  .btn, .tabs .tab { min-height: 42px; }
  .content { padding: 12px; }
}

/* Consent documents: the user Consent page + the blocking required-doc
   card (consent.html; enforcement is server-side — web/auth). */
.consent-doc {
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius-panel); box-shadow: var(--shadow-panel);
  padding: 16px 18px; margin-bottom: 14px;
}
.consent-doc h2 { margin: 0 0 8px; font-size: var(--fs-lg); }
.consent-badge {
  display: inline-block; margin-left: 8px; padding: 1px 8px; border-radius: 10px;
  background: var(--danger-soft); border: 1px solid var(--danger); color: var(--danger);
  font-size: var(--fs-sm); font-weight: 600; vertical-align: 2px;
}
.consent-doc details { margin: 8px 0 10px; }
.consent-text, .consent-scroll {
  border: 1px solid var(--border); border-radius: 8px; background: var(--panel);
  padding: 12px 14px; overflow-y: auto;
}
.consent-text { max-height: 320px; margin-top: 8px; }
/* ~half-screen blocking card: the body area scrolls, the actions pin below. */
.cfg-cost { margin-bottom: var(--space-4); }
.modal-consent { max-width: 720px; width: 92vw; }
.modal-consent .consent-scroll { max-height: 55vh; }
.consent-actions { align-items: center; gap: 12px; }
.consent-admin-doc { margin-bottom: 18px; }
.consent-admin-doc .doc-meta { font-size: var(--fs-sm); font-weight: normal; }

/* Settings: friendly params form, roles matrix, config-activity badge. */
.nav-badge {
  display: inline-block; min-width: 18px; padding: 0 5px; border-radius: 9px;
  background: var(--danger); color: #fff; font-size: var(--fs-sm); font-weight: 600;
  text-align: center; line-height: 18px; vertical-align: 1px;
}
.tier-chip {
  display: inline-block; margin-left: 6px; padding: 0 6px; border-radius: 4px;
  background: var(--warning-soft); border: 1px solid var(--warning); color: var(--warning);
  font-size: var(--fs-sm); font-weight: 600; vertical-align: 1px;
}
.param-field input[type="number"], .param-field input[type="text"] { max-width: 260px; }
.param-field select { max-width: 340px; }
.table-scroll { overflow-x: auto; }
.roles-matrix th { text-align: center; }
.roles-matrix td.cap-cell { text-align: center; }
.roles-matrix td:first-child { min-width: 220px; }
/* Roles page, add/delete section: slightly bigger controls than the compact
   CRUD defaults, and air between the add-role form and the delete table. */
.roles-crud .run-form .actions button { padding: 11px 22px; font-size: var(--fs-base); }
.roles-crud table.crud { margin-top: 22px; }
.roles-crud table.crud .btn { padding: 9px 16px; font-size: var(--fs-base); }

/* --- Chat page (chat.html / chat.js): LLM-style conversation ------------- */
.chat { max-width: 980px; margin: 0 auto; }
.chat-toolbar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
.chat-toolbar-end { justify-content: flex-end; }
.chat-model { font-size: var(--fs-md); color: var(--text-muted); }
.chat-log { display: flex; flex-direction: column; gap: 12px; margin-bottom: 14px; }
.chat-msg {
  padding: 10px 14px; border-radius: 14px;
  font-size: var(--fs-base); line-height: 1.55; word-wrap: break-word;
}
/* User messages: right-aligned accent bubble, plain text. */
.chat-msg.user {
  align-self: flex-end; max-width: 78%; background: var(--chat-user-bg); color: #fff;
  border-bottom-right-radius: 4px; white-space: pre-wrap;
}
/* Assistant replies: full-width white card with rendered (sanitized)
   markdown inside — headings, lists, bold — like an LLM site's answer pane. */
.chat-msg.assistant {
  align-self: stretch; max-width: 100%; background: var(--chat-assistant-bg);
  border: 1px solid var(--border); border-radius: 10px; padding: 14px 18px;
}
.chat-msg.assistant h3, .chat-msg.assistant h4, .chat-msg.assistant h5 {
  margin: 14px 0 6px; border: none; padding: 0;
}
.chat-msg.assistant h3 { font-size: var(--fs-lg); }
.chat-msg.assistant h4 { font-size: var(--fs-base); }
.chat-msg.assistant h5 { font-size: var(--fs-base); color: var(--text-soft); }
.chat-msg.assistant > :first-child { margin-top: 0; }
.chat-msg.assistant > :last-child { margin-bottom: 0; }
.chat-msg.assistant p { margin: 8px 0; }
.chat-msg.assistant ul, .chat-msg.assistant ol { margin: 8px 0; padding-left: 24px; }
.chat-msg.assistant li { margin: 3px 0; }
.chat-msg.assistant code {
  background: var(--tint); border: 1px solid var(--border); border-radius: 4px;
  padding: 0 4px; font-size: var(--fs-sm);
}
.chat-msg.typing-msg { color: var(--text-faint); font-style: italic; white-space: pre-wrap; }
.chat-msg.error { align-self: center; background: var(--danger-soft); border: 1px solid var(--danger); color: var(--danger); font-size: var(--fs-md); }
/* The "Restored your previous chat" note at the top of a restored
   transcript (2026-10-02): a quiet centered line, no bubble. */
.chat-msg.chat-restored { align-self: center; background: transparent; border: none; color: var(--text-muted); font-size: var(--fs-sm); padding: 2px 0; }
.chat-start { text-align: center; padding: 36px 0; }
.chat-search { padding: 14px 46px; font-size: var(--fs-xl); }
/* Input: full width, styled like the app's other form inputs. */
.chat-input-row { display: flex; flex-direction: column; gap: 8px; }
.chat-input-row textarea {
  width: 100%; padding: 10px 12px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); font: inherit; resize: vertical; background: var(--panel);
}
.chat-input-row textarea:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
/* Send + the optional "Give me final version" button share one right-aligned
   row (the latter styled as a secondary action, per the stakeholders). */
.chat-btn-row { display: flex; gap: 10px; align-self: flex-end; }
.chat-btn-row .btn { padding: 9px 28px; }
.chat-write {
  display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
  margin-top: 16px; padding: 12px 14px; background: var(--panel);
  border: 1px solid var(--border); border-radius: var(--radius-control);
  box-shadow: var(--shadow-panel);
}
/* GPT pages (Social/Photo GPT): attached-photo chips above the input, image
   thumbnails inside user bubbles, and generated images in assistant bubbles. */
.chat-chips { display: flex; gap: 8px; flex-wrap: wrap; }
.chat-chip {
  position: relative; display: inline-flex; border: 1px solid var(--border);
  border-radius: var(--radius-sm); overflow: hidden; background: var(--panel);
}
.chat-chip img { width: 64px; height: 64px; object-fit: cover; display: block; }
/* Document chips (2026-09-14, the Story shaper's attachments): a 📎 + the
   file name, same chip chrome as the photo thumbnails. */
.chat-chip-doc { align-items: center; padding: 8px 28px 8px 10px; max-width: 240px; }
.chat-chip-name {
  font-size: var(--fs-sm); color: var(--text); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.chat-chip-x {
  position: absolute; top: 0; right: 0; border: 0; cursor: pointer;
  background: rgba(0, 0, 0, .55); color: #fff; width: 18px; height: 18px;
  line-height: 16px; font-size: var(--fs-sm); padding: 0;
}
.chat-msg .chat-attach-img {
  display: block; max-width: 220px; max-height: 220px; border-radius: var(--radius-sm);
  margin-top: 8px;
}
/* An attached document in a sent bubble (2026-09-14): a 📎 name line. */
.chat-msg .chat-attach-doc { display: block; margin-top: 6px; font-size: var(--fs-sm); opacity: .92; }
.chat-msg .chat-gen-img {
  display: block; max-width: 100%; border-radius: var(--radius-sm); margin-top: 10px;
  border: 1px solid var(--border);
}
.sgpt-export { align-self: flex-end; margin-top: 10px; }
.pgpt-prefills { align-self: flex-start; flex-wrap: wrap; }
.pgpt-prefills .btn { padding: 7px 14px; font-size: var(--fs-md); }
.pgpt-counter { margin-left: auto; align-self: center; }
.pgpt-actions { margin-top: 8px; align-self: flex-start; flex-wrap: wrap; }
.pgpt-actions .btn { padding: 6px 14px; font-size: var(--fs-md); }
/* The per-reply "Take this to drafts" row under an assistant reply
   (2026-10-02): a small secondary-sized button, hidden with the drafts form. */
.chat-msg .chat-msg-actions { margin-top: 10px; display: flex; gap: 6px; flex-wrap: wrap; }
.chat-msg .chat-msg-actions[hidden] { display: none; }

/* Avatars (profile page; the messenger reuses these): round image with an
   initials-circle fallback. */
.avatar { border-radius: 50%; object-fit: cover; background: var(--tint); }
.avatar-lg { width: 72px; height: 72px; }
.avatar-sm { width: 30px; height: 30px; }
.avatar-fallback {
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--accent); font-weight: 600; font-size: var(--fs-xl);
}
.profile-avatar-row { display: flex; align-items: center; gap: 14px; margin: 0 0 18px; }

/* Custom theme builder (Settings → Style, 2026-08-20): per-theme
   <details> editors with a compact token-color grid; .opt-color is the
   generic picker+inherit-checkbox unit (wired by the page's one script). */
.theme-editor {
  border: 1px solid var(--border); border-radius: var(--radius-panel);
  padding: 12px 16px; margin: 0 0 12px; background: var(--panel);
}
.theme-editor > summary { cursor: pointer; }
.theme-editor .run-form { margin-top: 10px; }
.theme-token-group {
  font-size: var(--fs-sm); text-transform: uppercase; letter-spacing: .05em;
  color: var(--text-muted); margin: 14px 0 6px;
}
.theme-token-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 8px 16px;
}
.theme-token label:first-child { display: block; font-size: var(--fs-sm); }
.opt-color-row { display: inline-flex; align-items: center; gap: 8px; }
.opt-color-inherit {
  display: inline-flex; gap: 5px; align-items: center;
  font-weight: 400; font-size: var(--fs-sm); color: var(--text-muted);
}
.theme-delete { margin-top: 8px; }

/* --- Newsroom Chat popup (messenger.js + _messenger.html; docs/chat.md).
   Fixed bottom-right launcher + panel; rides the surface/palette vars so
   the Style palettes restyle it too. ---------------------------------- */
.msgr-launcher {
  position: fixed; right: 24px; bottom: 22px; z-index: 40;
  display: inline-flex; align-items: center; gap: 8px;
  height: 50px;
  background: var(--accent); color: #fff; border: none; cursor: pointer;
  border-radius: 999px; padding: 0 17px;
  font: inherit; font-size: var(--fs-md); font-weight: 500;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .18);
  transition: filter var(--motion-fast), box-shadow var(--motion-fast);
}
.msgr-launcher:hover { filter: brightness(1.07); box-shadow: 0 8px 22px rgba(0, 0, 0, .22); }
.msgr-icon { width: 18px; height: 18px; }
.msgr-badge {
  display: inline-block; min-width: 18px; padding: 0 5px; border-radius: 9px;
  background: var(--danger); color: #fff; font-size: var(--fs-sm); font-weight: 600;
  text-align: center; line-height: 18px;
}
.msgr-panel {
  position: fixed; right: 22px; bottom: 22px; z-index: 40;
  width: min(430px, calc(100vw - 16px)); height: min(700px, 88vh);
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: 0 18px 50px rgba(4, 12, 27, .30);
  display: flex; flex-direction: column; overflow: hidden;
}
.msgr-view { display: flex; flex-direction: column; flex: 1; min-height: 0; }
/* The /messages page (2026-10-12, docs/chat.md): the former popup's body
   as a page. The panel loses its fixed placement and fills the column.
   The pane switch is ONE state class, .msgr-panel.has-conv (messenger.js) —
   never the hidden attribute, which the global [hidden] rule above makes
   win over everything (the first page build hid the inbox that way, tried
   to re-show it from CSS and lost: one pane, no Back). Narrow screens show
   one pane at a time with Back; from 960px the inbox (left, 35%) and the
   conversation (right, 65%) sit side by side like any chat app, Back is
   hidden and an empty right pane shows the placeholder. */
.msgr-page { display: flex; flex-direction: column; min-height: 0; }
.msgr-page .msgr-page-title { margin: 0 0 12px; }
.msgr-page .msgr-panel {
  position: static; width: auto; max-width: none; z-index: auto;
  height: calc(100vh - 150px); min-height: 520px;
  border-radius: var(--radius-lg);
  box-shadow: 0 8px 24px rgba(4, 12, 27, .10);
}
.msgr-page .msgr-panel:not(.has-conv) #msgr-conv { display: none; }
.msgr-page .msgr-panel.has-conv #msgr-inbox { display: none; }
@media (min-width: 960px) {
  .msgr-page .msgr-panel { flex-direction: row; }
  .msgr-page .msgr-panel.has-conv #msgr-inbox,
  .msgr-page .msgr-panel:not(.has-conv) #msgr-conv { display: flex; }
  .msgr-page #msgr-inbox {
    flex: 0 0 35%; min-width: 260px; border-right: 1px solid var(--border);
  }
  .msgr-page #msgr-conv { flex: 1 1 65%; min-width: 0; }
  .msgr-page #msgr-back { display: none; }
}
/* The empty right pane: only the placeholder shows until a row is picked. */
.msgr-conv-empty {
  flex: 1; display: flex; align-items: center; justify-content: center;
  padding: 24px; text-align: center; color: var(--text-muted);
  font-style: italic; background: var(--page);
}
.msgr-page .msgr-panel:not(.has-conv) #msgr-conv > :not(.msgr-conv-empty) { display: none; }
.msgr-page .msgr-panel.has-conv .msgr-conv-empty { display: none; }
.msgr-view[hidden] { display: none; }
/* The open conversation's row in the inbox (aria-current set by the JS). */
.msgr-item.active { background: var(--accent-soft); }
.msgr-item.active:hover { background: var(--accent-soft); }
/* Gradient header (spec §13) — an intentional-gradient exception, white
   text overrides for everything inside it. */
.msgr-head {
  display: flex; align-items: center; gap: 8px;
  min-height: 64px; padding: 14px 16px;
  background: linear-gradient(120deg, #172d5e, #244da6); color: #fff;
  border-bottom: none;
}
.msgr-title { font-weight: 600; }
.msgr-head .msgr-title { color: #fff; }
.msgr-conv-sub { font-size: var(--fs-sm); flex: 1; min-width: 0;
                 overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msgr-head .msgr-conv-sub { color: rgba(255, 255, 255, .75); }
.msgr-head .msgr-iconbtn { color: rgba(255, 255, 255, .85); }
.msgr-head .msgr-iconbtn:hover { color: #fff; }
.msgr-head .msgr-iconbtn:last-child { margin-left: auto; }
.msgr-iconbtn {
  background: none; border: none; cursor: pointer; color: var(--text-muted);
  font: inherit; font-size: var(--fs-lg); padding: 0 4px; line-height: 1;
}
.msgr-search {
  margin: 10px 14px 6px; padding: 7px 10px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); font: inherit; font-size: var(--fs-sm);
  background: var(--panel);
}
.msgr-newgroup { margin: 0 14px 6px; align-self: flex-start; }
.msgr-group-form { padding: 0 14px 8px; display: flex; flex-direction: column; gap: 6px; }
.msgr-group-form input[type=text] {
  padding: 6px 10px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  font: inherit; font-size: var(--fs-sm); background: var(--panel);
}
.msgr-group-members {
  max-height: 140px; overflow-y: auto; display: flex; flex-direction: column;
  gap: 2px; font-size: var(--fs-sm);
}
.msgr-pick { cursor: pointer; text-align: left; }
.msgr-row { display: flex; gap: 8px; }
.msgr-list { flex: 1; overflow-y: auto; padding: 4px 0 10px; }
.msgr-item {
  display: flex; gap: 10px; align-items: center;
  width: 100%; text-align: left; background: none;
  border: none; border-bottom: 1px solid var(--border); cursor: pointer;
  font: inherit; padding: 8px 14px; color: var(--text);
}
.msgr-item:hover { background: var(--tint); }
/* Profile photos in the inbox (2026-08-20): 30px avatar/initials circle
   left of the row's text column. */
.msgr-item-ava { flex: none; }
.msgr-item-ava.avatar-fallback { font-size: var(--fs-md); }
.msgr-item-main { flex: 1; min-width: 0; }
.msgr-item-top { display: flex; justify-content: space-between; gap: 8px; }
.msgr-item-title { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msgr-item-time { color: var(--text-muted); font-size: var(--fs-sm); flex: none; }
.msgr-item-bottom { display: flex; justify-content: space-between; gap: 8px; align-items: center; }
.msgr-item-preview {
  color: var(--text-muted); font-size: var(--fs-sm); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.msgr-empty { color: var(--text-muted); font-style: italic; padding: 10px 14px; }
.msgr-manage { padding: 8px 14px; border-bottom: 1px solid var(--border);
               display: flex; flex-direction: column; gap: 6px; }
.msgr-manage input[type=text] {
  flex: 1; padding: 6px 10px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); font: inherit; font-size: var(--fs-sm);
  background: var(--panel);
}
.msgr-members { display: flex; flex-direction: column; gap: 2px; font-size: var(--fs-sm); }
.msgr-member { display: flex; justify-content: space-between; gap: 8px; }
.msgr-older { align-self: center; margin: 6px 0 0; color: var(--accent);
              background: none; border: none; cursor: pointer; font: inherit;
              font-size: var(--fs-sm); }
/* Message area sits on the pale page tone so the bubbles pop (spec §13). */
.msgr-log { flex: 1; overflow-y: auto; padding: 10px 14px; display: flex;
            flex-direction: column; gap: 8px; background: var(--page); }
.msgr-msg { display: flex; flex-direction: column; align-items: flex-start;
            max-width: 82%; }
.msgr-msg.mine { align-self: flex-end; align-items: flex-end; }
.msgr-sender {
  color: var(--text-muted); font-size: var(--fs-sm); margin: 0 0 1px;
  display: inline-flex; align-items: center; gap: 5px;
}
/* Font-height sender photo (owner ask: "same sized with font"). */
.msgr-sender-ava { width: 15px; height: 15px; flex: none; }
span.msgr-sender-ava { font-size: 9px; }
/* Incoming: panel surface + border, 13px radius with a 4px bottom-left
   corner; own: solid blue (the admin-overridable --chat-user-bg), mirrored. */
.msgr-bubble {
  background: var(--panel); border: 1px solid var(--border);
  border-radius: 13px; padding: 7px 12px;
  white-space: pre-wrap; overflow-wrap: anywhere; font-size: var(--fs-md);
  border-bottom-left-radius: 4px;
}
.msgr-msg.mine .msgr-bubble {
  background: var(--chat-user-bg); color: #fff; border: none;
  border-bottom-left-radius: 13px; border-bottom-right-radius: 4px;
}
/* Detected links inside bubbles (2026-08-25). Own bubbles keep their
   fixed white text (chat-user-bg is themeable), so inherit + underline
   stays readable under every theme; others' bubbles use the accent. */
.msgr-link { color: var(--accent); text-decoration: underline; overflow-wrap: anywhere; }
.msgr-msg.mine .msgr-link { color: inherit; }
.msgr-msg .msgr-time { color: var(--text-muted); font-size: var(--fs-sm); margin-top: 1px; }
.msgr-pending .msgr-bubble { opacity: .7; }
/* Reactions (2026-08-25): pill row under the bubble; "+" unfolds the
   configured palette inline. Token colors only (all themes). */
.msgr-reacts { display: flex; flex-wrap: wrap; gap: 3px; margin-top: 2px; }
.msgr-react {
  display: inline-flex; align-items: center; gap: 3px;
  background: var(--tint); border: 1px solid var(--border);
  border-radius: 999px; padding: 0 7px; min-height: 20px;
  font: inherit; font-size: var(--fs-sm); color: var(--text);
  cursor: pointer; line-height: 1.4;
}
.msgr-react:hover { border-color: var(--accent); }
.msgr-react.mine { background: var(--accent-soft); border-color: var(--accent); }
.msgr-react-count { color: var(--text-muted); font-size: var(--fs-sm); }
.msgr-react-add { color: var(--text-muted); font-weight: 600; }
.msgr-react-pal { display: inline-flex; gap: 3px; }
.msgr-retry { color: var(--accent); background: none; border: none;
              cursor: pointer; font: inherit; font-size: var(--fs-sm); padding: 0; }
.msgr-seen { align-self: flex-end; color: var(--text-muted); font-size: var(--fs-sm); }
.msgr-composer { display: flex; gap: 8px; padding: 11px;
                 border-top: 1px solid var(--border); align-items: flex-end; }
.msgr-composer textarea {
  flex: 1; min-height: 43px; padding: 10px 12px; border: 1px solid var(--border);
  border-radius: var(--radius-control); font: inherit; font-size: var(--fs-md);
  resize: none; background: var(--panel); color: var(--text);
}

/* --- Chat attachments (2026-08-16, docs/chat.md) — everything rides the
   palette vars so the Style presets restyle it like the rest. ---------- */
.msgr-atts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px;
             max-width: 100%; }
.msgr-att-img {
  max-width: 270px; max-height: 200px; border-radius: 10px; display: block;
  border: 1px solid var(--border); background: var(--panel);
}
.msgr-att-chip {
  display: inline-flex; align-items: center; gap: 6px; max-width: 260px;
  padding: 5px 10px; border: 1px solid var(--border); border-radius: 10px;
  background: var(--panel); color: var(--text); text-decoration: none;
  font-size: var(--fs-sm);
}
.msgr-att-chip:hover { border-color: var(--accent); }
.msgr-att-name { overflow: hidden; text-overflow: ellipsis;
                 white-space: nowrap; max-width: 160px; }
.msgr-att-size { color: var(--text-muted); flex-shrink: 0; }
.msgr-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
              padding: 6px 14px 0; }
.msgr-chip { display: inline-flex; align-items: center; gap: 6px;
             padding: 3px 8px; border: 1px solid var(--border);
             border-radius: 10px; background: var(--panel);
             font-size: var(--fs-sm); }
.msgr-chip-uploading { opacity: .7; }
.msgr-chip-failed { border-color: var(--danger); }
.msgr-chip-x { background: none; border: none; cursor: pointer;
               color: var(--text-muted); font: inherit; padding: 0 2px; }
.msgr-chip-x:hover { color: var(--text); }
.msgr-quota { font-size: var(--fs-sm); margin-left: auto; }

/* --- Chat audio calls (2026-08-29, docs/chat.md → Calls) — token colors
   only, so every Style theme (light/dark/slate/custom) restyles them.
   The ONLY non-semantic fixed colors are the danger-red Leave button and
   the ring overlay's scrim, both semantic by intent. ------------------- */
.msgr-item-call { flex: none; font-size: var(--fs-sm); }
.msgr-call-form { display: flex; flex-direction: column; gap: 6px;
                  padding: 8px 14px; border-bottom: 1px solid var(--border);
                  background: var(--tint); }
.msgr-call-form input[type="text"] {
  padding: 8px 10px; border: 1px solid var(--border);
  border-radius: var(--radius-control); font: inherit;
  font-size: var(--fs-md); background: var(--panel); color: var(--text);
}
/* Round 2: notes-mode radios + the consent disclosure line, and the live
   recording indicators (the dot rides the semantic danger red). */
.msgr-call-modes { display: flex; flex-direction: column; gap: 2px;
                   font-size: var(--fs-sm); }
.msgr-call-disclosure { margin: 0; font-size: var(--fs-sm);
                        color: var(--text-muted); }
.msgr-call-recline, .msgr-ring-rec {
  font-size: var(--fs-sm); color: var(--danger); font-weight: 600;
}
.msgr-ring-rec { overflow-wrap: anywhere; }
.msgr-callbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; padding: 8px 14px; font-size: var(--fs-sm); color: var(--text);
  background: var(--accent-soft); border-bottom: 1px solid var(--border);
}
.msgr-callpanel {
  display: flex; flex-direction: column; gap: 8px; padding: 10px 14px;
  background: var(--tint); border-bottom: 1px solid var(--border);
}
.msgr-call-subjline { font-size: var(--fs-sm); color: var(--text-muted);
                      overflow: hidden; text-overflow: ellipsis;
                      white-space: nowrap; }
.msgr-call-avas { display: flex; flex-wrap: wrap; gap: 10px; }
.msgr-call-ava { display: inline-flex; flex-direction: column;
                 align-items: center; gap: 3px; max-width: 64px; }
.msgr-call-face { width: 34px; height: 34px; flex: none;
                  border: 2px solid var(--accent); border-radius: 50%; }
.msgr-call-ava.mic-off .msgr-call-face { border-color: var(--border);
                                         opacity: .55; }
.msgr-call-name { font-size: var(--fs-sm); color: var(--text-muted);
                  max-width: 64px; overflow: hidden;
                  text-overflow: ellipsis; white-space: nowrap; }
.msgr-call-controls { display: flex; align-items: center; gap: 8px; }
.msgr-call-timer { font-variant-numeric: tabular-nums;
                   font-size: var(--fs-sm); color: var(--text-muted);
                   margin-right: auto; }
.msgr-call-leavebtn { background: var(--danger); border-color: var(--danger);
                      color: #fff; }
.msgr-call-leavebtn:hover { filter: brightness(.92); }
/* In-log call bubble: the base bubble + a muted status line. */
.msgr-call-bubble { display: flex; flex-direction: column; gap: 2px; }
.msgr-call-bhead { font-weight: 600; }
.msgr-call-bline { font-size: var(--fs-sm); opacity: .8; }
/* Incoming-ring overlay: fixed, above every page overlay except the voice
   mic (z 1000001) — ringing must win over modals like the mic does. */
.msgr-ring {
  position: fixed; inset: 0; z-index: 1000000;
  display: flex; align-items: flex-start; justify-content: center;
  padding-top: 12vh; background: rgba(10, 16, 32, .45);
}
.msgr-ring[hidden] { display: none; }
.msgr-ring-card {
  background: var(--panel); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-card, 14px);
  box-shadow: 0 18px 48px rgba(0, 0, 0, .28);
  padding: 18px 22px; min-width: min(320px, calc(100vw - 40px));
  display: flex; flex-direction: column; gap: 8px; text-align: center;
  animation: msgr-ring-pulse 1.4s ease-in-out infinite;
}
@keyframes msgr-ring-pulse {
  0%, 100% { box-shadow: 0 18px 48px rgba(0, 0, 0, .28); }
  50% { box-shadow: 0 18px 48px rgba(0, 0, 0, .28),
                    0 0 0 8px var(--accent-soft); }
}
.msgr-ring-who { font-weight: 600; font-size: var(--fs-lg, 1.05rem); }
.msgr-ring-sub { color: var(--text-muted); font-size: var(--fs-sm);
                 overflow-wrap: anywhere; }
.msgr-ring-actions { justify-content: center; }

/* --- Voice dictation control (voice.js + _voice.html; docs/voice.md).
   Bottom-LEFT so it never fights the messenger launcher; the sidebar
   layout shifts it past the fixed nav column. z-index keeps the mic
   usable ABOVE every overlay — .modal-backdrop (50) and the Social
   Composer's page-local #sc-modal/#sc-story-modal (999999) — so users
   can dictate into modal inputs (stakeholder ask 2026-08-27). --------- */
.voice-control { position: fixed; left: 22px; bottom: 22px; z-index: 1000001; }
body.nav-side .voice-control { left: 270px; }
body.nav-side.nav-rail .voice-control { left: 98px; }
/* Launcher: pale-blue pill tone (spec §14) — red ONLY while listening. */
.voice-mic {
  display: inline-flex; align-items: center; justify-content: center;
  width: 46px; height: 46px; border-radius: 50%;
  border: 1px solid var(--accent);
  cursor: pointer; background: var(--accent-soft); color: var(--accent);
  box-shadow: 0 6px 18px rgba(0, 0, 0, .14);
  transition: background var(--motion-fast), color var(--motion-fast);
}
.voice-mic:hover { background: var(--accent); color: #fff; }
.voice-mic svg { width: 22px; height: 22px; }
.voice-buttons { display: flex; align-items: flex-end; gap: 8px; }
/* The command button: smaller, neutral secondary tone. */
.voice-cmd {
  width: 36px; height: 36px;
  background: var(--panel); color: var(--text-muted); border-color: var(--border);
}
.voice-cmd:hover { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); }
.voice-cmd svg { width: 16px; height: 16px; }
.voice-mic.recording {
  background: var(--danger); color: #fff; border-color: var(--danger);
  animation: voice-pulse 1.2s ease-in-out infinite;
}
@keyframes voice-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(196, 50, 43, .45); }
  50% { box-shadow: 0 0 0 10px rgba(196, 50, 43, 0); }
}
.voice-panel {
  position: absolute; left: 0; bottom: 56px; width: min(360px, calc(100vw - 32px));
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: 0 18px 50px rgba(4, 12, 27, .30);
  padding: 0 12px 10px; display: flex; flex-direction: column; gap: 8px;
  font-size: var(--fs-sm); overflow: hidden;
}
/* Same gradient header language as the messenger (spec §14). */
.voice-head {
  display: flex; justify-content: space-between; gap: 8px; align-items: center;
  margin: 0 -12px 2px; padding: 12px 14px;
  background: linear-gradient(120deg, #172d5e, #244da6); color: #fff;
}
.voice-close {
  background: none; border: none; cursor: pointer; color: rgba(255, 255, 255, .85);
  font: inherit; font-size: var(--fs-lg); line-height: 1; padding: 0 2px;
}
.voice-close:hover { color: #fff; }
.voice-title { font-weight: 600; color: #fff; }
.voice-state { color: var(--text-muted); }
.voice-state[data-state="listening"] { color: var(--danger); font-weight: 600; }
.voice-state[data-state="denied"], .voice-state[data-state="failed"] { color: var(--danger); }
.voice-state[data-state="inserted"] { color: var(--success); }
/* …and light variants inside the gradient header. */
.voice-head .voice-state { color: rgba(255, 255, 255, .8); }
.voice-head .voice-state[data-state="listening"],
.voice-head .voice-state[data-state="denied"],
.voice-head .voice-state[data-state="failed"] { color: #ffb4ad; }
.voice-head .voice-state[data-state="inserted"] { color: #9ff0c3; }
.voice-target { color: var(--text-muted); }
.voice-row { display: flex; gap: 6px; flex-wrap: wrap; }
.voice-recover textarea {
  width: 100%; padding: 6px 8px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); font: inherit; font-size: var(--fs-sm);
  background: var(--panel); resize: vertical;
}
.voice-recover-hint { margin: 0 0 4px; color: var(--text-muted); }

/* =========================================================================
   Style presets (Settings → Style, 2026-08-15). Everything below is scoped
   to the body classes _inject_general derives from general.yaml `style:` —
   the Standard look (no classes) is untouched by construction. (The
   card_layout / card_descriptions knobs — cards-compact / no-card-descs —
   retired 2026-09-14 with the home cards.)
   ========================================================================= */

/* Small/hidden logo: handled by the hero markup itself (home.html picks the
   artwork per knob); no extra rules needed since the 2026-08-20 hero. */

/* Tight vertical rhythm: a slimmer hero, the news panels rise up. */
body.density-tight .home { padding-top: 0; }
body.density-tight .home-hero { padding: 22px 28px; margin-bottom: 14px; }
body.density-tight .home-layout { gap: 16px; }

/* =========================================================================
   Style round 2 (2026-08-15): palettes, font scale, content width.
   Palettes redefine the color vars ON BODY, which beats the inline :root
   theme block in base.html for every descendant. PRECEDENCE POLICY: a
   palette supplies every color the admin left at its default; a theme
   color the admin explicitly customized (Settings → General) is re-emitted
   by base.html in a `body[class]` block AFTER these rules — same
   specificity, later in the cascade — so it always wins over the palette.
   ========================================================================= */

/* Dark theme (spec §2 substitution — same hierarchy, darker tokens).
   Status colors get dark-tuned variants: the base --danger/--success double
   as fills (recording mic, ship-it button), so they stay mid-strength
   rather than pastel; the -soft tokens become deep tinted backgrounds. */
body.palette-dark {
  --page: #0d1422;
  --page-deep: #152033;
  --panel: #192539;
  --border: #3b4a61;
  --text: #f6f8fb;
  --text-soft: #e1e7f0;
  --text-muted: #bcc7d7;
  --text-faint: #9baabd;
  --accent: #82a9ff;
  --accent-hover: #a4c0ff;
  --accent-soft: #253757;
  --success: #45c98c; --success-soft: #1a3328;
  --warning: #d9a13f; --warning-soft: #362b12;
  --danger: #e0554d;  --danger-soft: #3b1f1e;
  --tint: #223048; --tint-hover: #2a3a56;
  --scrollbar: #4a5a72; --scrollbar-hover: #5d6f8a;
  --shadow-panel: 0 10px 28px rgba(0, 0, 0, .20);
  --shadow-hover: 0 16px 40px rgba(0, 0, 0, .28);
  --chat-user-bg: #2864dc;
  --chat-assistant-bg: #192539;
}
/* Accent-FILLED controls in dark: the light accent (#82a9ff) fails AA with
   white text, so filled controls flip to a dark label instead (the admin's
   accent override still recolors the fill — the contract holds). Semantic
   fills (success/danger) are mid-strength above, dark label works there too. */
body.palette-dark .btn,
body.palette-dark .run-form .actions button,
body.palette-dark .msgr-launcher,
body.palette-dark .nav-feedback:hover {
  color: #0d1422;
}
body.palette-dark .voice-mic:hover:not(.recording) { color: #0d1422; }
/* …but the outline/soft variants keep their own text colors. */
body.palette-dark .btn-secondary { background: var(--panel); color: var(--accent); }
body.palette-dark .btn-alt { color: var(--accent); }
body.palette-dark .btn.danger, body.palette-dark button.danger { color: var(--danger); }

/* Slate theme: muted cool grays, a steel accent — calmer than Light. */
body.palette-slate {
  --page: #e7ebf1;
  --page-deep: #dde4ec;
  --panel: #f4f6f9;
  --border: #c6cfda;
  --text: #1f2635;
  --text-soft: #3a445a;
  --text-muted: #5c6577;
  --text-faint: #79839a;
  --accent: #3d5a80;
  --accent-hover: #32496a;
  --accent-soft: #dbe4ef;
  --tint: #e2e8f0; --tint-hover: #d8e1ec;
  --scrollbar: #aeb9c9; --scrollbar-hover: #93a1b5;
  --chat-assistant-bg: #f4f6f9;
}

/* Font scale up: the whole six-var type scale one step larger. */
body.fs-large {
  --fs-sm: 15px;
  --fs-md: 16px;
  --fs-base: 16px;
  --fs-lg: 18px;
  --fs-xl: 20px;
  --fs-2xl: 22px;
}

/* Wider main column (body-scoped so the per-page content-wide class — the
   Social Dragon full-width layout — is unaffected). */
body.layout-wide .content { width: min(1800px, 94vw); }

/* -------------------------------------------------------------------------
   Sidebar menu layout — the flagship shell (nav_layout: side →
   body.nav-side; markup in base.html's sidebar branch, items shared via
   _nav.html). Dark navy in BOTH themes (the --nav-* tokens); 248px
   expanded, 76px rail (body.nav-rail — sidenav.js click-toggle, persisted),
   off-canvas drawer on small screens (body.sidenav-open). Submenus render
   as in-flow expandable groups behind a thin vertical rule; groups also
   open on :focus-within with JS off. Only .sidenav-nav scrolls — the
   aside itself stays fixed so the collapse control can sit on its edge.
   ------------------------------------------------------------------------- */
body.nav-side { padding-left: 248px; }
body.nav-side .content { width: auto; max-width: 1500px; }
body.nav-side.layout-wide .content { max-width: 1800px; }
body.nav-side .content.content-wide { width: auto; max-width: none; }
.sidenav {
  position: fixed; top: 0; left: 0; bottom: 0; width: 248px; z-index: 30;
  background: var(--nav-bg); border-right: 1px solid var(--nav-border);
  display: flex; flex-direction: column;
  transition: width 220ms ease;
}
/* Brand area: gradient mark + product name/subtitle. */
.sidenav-head {
  display: flex; align-items: center; gap: 12px;
  min-height: 94px; padding: 20px 18px; position: relative; flex: none;
}
.sidenav-head .brand { display: flex; align-items: center; gap: 12px; min-width: 0; }
.brand-mark {
  display: flex; align-items: center; justify-content: center; flex: none;
  width: 42px; height: 42px; border-radius: 12px;
  background: linear-gradient(135deg, #3979f1, #1f55c7);
  box-shadow: 0 4px 12px rgba(57, 121, 241, .35); overflow: hidden;
}
.sidenav .brand-logo { width: 42px; height: 42px; border-radius: 12px; }
.brand-copy { display: flex; flex-direction: column; min-width: 0; }
.brand-name {
  color: #fff; font-size: 16px; font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.brand-sub {
  color: var(--nav-text); font-size: 12px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Collapse control: 28px circle riding the sidebar's right edge. */
.sidenav-toggle {
  position: absolute; top: 40px; right: -14px; z-index: 31;
  width: 28px; height: 28px; border-radius: 50%; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--nav-active); color: #cdd9f4;
  border: 1px solid var(--nav-border); box-shadow: 0 2px 8px rgba(0, 0, 0, .3);
  cursor: pointer; font-size: 15px; line-height: 1;
  transition: transform 220ms ease;
}
.sidenav-toggle:hover { color: #fff; background: var(--nav-hover); }
.sidenav-mobile {
  background: none; border: none; cursor: pointer; color: var(--text-muted);
  font-size: var(--fs-xl); padding: 2px 6px; line-height: 1;
}
/* Navigation region: the only scrolling part of the sidebar. */
.sidenav-nav {
  flex: 1; overflow-y: auto; overflow-x: hidden;
  display: flex; flex-direction: column; gap: 2px;
  padding: 18px 12px 12px; font-size: var(--fs-sm);
  scrollbar-width: thin; scrollbar-color: var(--nav-border) transparent;
}
/* Rows: 40px (owner pick 2026-09-14 — the menu items are first-level
   since the Tools accordion went, so ~18 rows + the fixed entries must fit
   a laptop viewport; the list still scrolls when they don't). */
.sidenav-nav > a, .sidenav .navlink {
  display: flex; align-items: center; width: 100%;
  min-height: 40px; padding: 8px 14px; border-radius: var(--radius-control);
  color: var(--nav-text); font-weight: 500; text-align: left;
  background: none; border: none; cursor: pointer; font: inherit;
  font-size: var(--fs-sm);
  transition: background var(--motion-fast), color var(--motion-fast);
}
.sidenav-nav > a:hover, .sidenav .navlink:hover {
  background: var(--nav-hover); color: #fff; text-decoration: none;
}
/* Current page on a FIRST-LEVEL item (2026-09-14: the flat menu items). */
.sidenav-nav > a.active {
  background: var(--nav-active); color: #fff; font-weight: 600;
}
.sidenav .navlink.who { color: var(--nav-text); }
/* Icons beside every entry (2026-09-10, stakeholder ask; glyphs from
   _icons.html — the same ones the home cards use). */
.sidenav .nav-icon { width: 18px; height: 18px; flex: none; margin-right: 10px; }
.sidenav .nav-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sidenav .nav-caret { margin-left: auto; padding-left: 6px; }
.sidenav .navlink .nav-badge { margin-left: 6px; }
/* Live unread count on a menu LINK (2026-10-12: the Messages item,
   nav_badge.js fills it) — same pill as the group trigger's badge. */
.sidenav-nav > a .nav-badge, .sidenav .submenu a .nav-badge { margin-left: 6px; }
.sidenav-nav > a, .sidenav .submenu a { position: relative; }
.sidenav-nav > .nav-feedback {
  /* Thin whitish pill border (owner ask 2026-08-20) — the shared link rule
     above sets border:none, so this must set the FULL border shorthand
     (a bare border-color would be a no-op). */
  border: 1px solid rgba(255, 255, 255, .45);
  color: #cdd9f4; justify-content: center;
  min-height: 36px; margin: 0 2px 6px; border-radius: 999px;
}
.sidenav-nav > .nav-feedback:hover { background: var(--nav-active); color: #fff; }
.sidenav .navitem { position: static; display: block; }
/* An open group keeps its trigger highlighted (the active-surface tone). */
.sidenav .navitem.open > .navlink { background: var(--nav-active); color: #fff; }
/* Active page (2026-08-29): when a CHILD link carries the current page,
   the child gets the active surface and the parent trigger softens to the
   hover tone — before this, an open "Tools" read as the active item and
   confused users. Ordered after the .open rule so it wins. */
.sidenav .navitem.open.has-active > .navlink { background: var(--nav-hover); }
/* Submenus: in-flow, indented behind a thin vertical rule (spec §5). */
.sidenav .submenu {
  position: static; display: none; flex-direction: column; min-width: 0;
  margin: 2px 0 6px 22px; padding: 0 0 0 6px;
  border: none; border-left: 2px solid var(--nav-border); border-radius: 0;
  background: none; box-shadow: none;
}
/* :focus-within is the no-JS/keyboard fallback ONLY. It also fires on a
   mouse click (the trigger keeps focus), which used to hold the submenu
   open after click-to-close — sidenav.js now blurs the trigger when a
   click closes a group, so the .open class stays the single source of
   truth for mouse users. */
.sidenav .navitem.open > .submenu,
.sidenav .navitem:focus-within > .submenu { display: flex; }
.sidenav .submenu a, .sidenav .submenu .linklike {
  display: flex; align-items: center; min-height: 36px;
  padding: 6px 12px; border-radius: var(--radius-sm);
  color: var(--nav-text); font-size: var(--fs-sm);
}
.sidenav .submenu a:hover, .sidenav .submenu .linklike:hover {
  background: var(--nav-hover); color: #fff; text-decoration: none;
}
.sidenav .submenu a.active {
  background: var(--nav-active); color: #fff; font-weight: 600;
}
.sidenav .submenu .linklike {
  border: 0; background: none; font: inherit; font-size: var(--fs-sm);
  text-align: left; cursor: pointer; width: 100%;
}
.sidenav .submenu .logout-form { display: flex; }
.sidenav .submenu .logout-form .linklike { flex: 1; }
.sidenav .submenu .submenu-group {
  display: block; padding: 8px 12px 2px; font-size: 12px;
  text-transform: uppercase; letter-spacing: .05em; color: var(--nav-text);
}
.sidenav .submenu a.submenu-indent { padding-left: 24px; }
.sidenav .lang-current {
  display: flex; align-items: center; gap: 7px; padding: 6px 12px;
  color: var(--nav-text); font-weight: 600;
}
/* Footer: account surface + the theme toggle. */
.sidenav-foot {
  flex: none; padding: 12px;
  background: var(--nav-foot); border-top: 1px solid var(--nav-border);
}
.sidenav-account {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border-radius: var(--radius-control);
  background: var(--nav-account);
}
.sidenav-account-link {
  display: flex; align-items: center; gap: 10px; min-width: 0; flex: 1;
  color: inherit;
}
.sidenav-account-link:hover { text-decoration: none; }
.sidenav-account-ava {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border-radius: 50%; flex: none;
  background: linear-gradient(135deg, #3979f1, #1f55c7);
  color: #fff; font-weight: 600; font-size: var(--fs-sm);
}
/* Uploaded profile photo replaces the initials circle (same footprint). */
img.sidenav-account-ava { object-fit: cover; background: var(--nav-account); }
.sidenav-account-copy { display: flex; flex-direction: column; min-width: 0; }
.sidenav-account-name {
  color: #fff; font-size: var(--fs-sm); font-weight: 500;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sidenav-account-role { color: var(--nav-text); font-size: 12px; }
.theme-toggle-form { display: flex; flex: none; }
.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; border-radius: var(--radius-sm);
  background: none; border: 1px solid var(--nav-border);
  color: var(--nav-text); cursor: pointer;
  transition: background var(--motion-fast), color var(--motion-fast);
}
.theme-toggle:hover { background: var(--nav-hover); color: #fff; }
.theme-toggle svg { width: 18px; height: 18px; }
/* Slim top strip: page title only (the brand lives in the sidebar). */
.topbar-side { padding: 9px 20px; }
.topbar-side .sidenav-mobile { display: none; }
/* Collapsed rail (76px). Until 2026-09-10 it held the brand mark only; now
   it is an ICON RAIL (stakeholder ask): every entry's icon stays visible
   with its label visually hidden (navtip.js shows label + tooltip on
   hover). The menu items are first-level rows since 2026-09-14, so every
   group (Settings, Backup URLs, Custom links, Language, Account, and any
   Menu-settings group) opens as a labeled FLYOUT to the right of the rail
   on the same sidenav.js click-toggle. The footer stays hidden (the Account
   icon covers it); the badge becomes a dot. */
body.nav-rail { padding-left: 76px; }
body.nav-rail .sidenav { width: 76px; }
body.nav-rail .sidenav-head { padding: 20px 0 12px; justify-content: center; }
body.nav-rail .sidenav-head .brand { justify-content: center; }
body.nav-rail .brand-copy { display: none; }
body.nav-rail .sidenav-foot { display: none; }
body.nav-rail .sidenav-toggle { transform: rotate(180deg); }
body.nav-rail .sidenav-nav { padding: 14px 8px 12px; }
body.nav-rail .sidenav .nav-label, body.nav-rail .sidenav .nav-caret,
body.nav-rail .sidenav .submenu-group {
  position: absolute; width: 1px; height: 1px; margin: 0; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
body.nav-rail .sidenav-nav > a, body.nav-rail .sidenav .navlink,
body.nav-rail .sidenav .submenu a {
  justify-content: center; padding: 10px 0; position: relative;
}
body.nav-rail .sidenav .nav-icon { margin-right: 0; width: 20px; height: 20px; }
body.nav-rail .sidenav-nav > .nav-feedback { margin: 0 0 6px; min-height: 40px; }
body.nav-rail .sidenav .navlink.lang-pick svg:not(.nav-icon) { margin: 0; }
/* Groups: a flyout beside the rail, labels visible inside it. The nav list
   still scrolls (20+ rows never fit a viewport), so the flyout is
   viewport-FIXED — sidenav.js sets --flyout-top from the trigger's
   position on open and closes flyouts when the list scrolls. */
body.nav-rail .sidenav .navitem > .submenu {
  position: fixed; left: 86px; top: var(--flyout-top, 100px); z-index: 60;
  max-height: calc(100vh - 96px); overflow-y: auto;  /* clears the voice button row */
  min-width: 230px; margin: 0; padding: 6px;
  background: var(--nav-bg); border: 1px solid var(--nav-border);
  border-radius: var(--radius-control); box-shadow: 0 8px 24px rgba(0, 0, 0, .35);
}
body.nav-rail .sidenav .navitem > .submenu .nav-label {
  position: static; width: auto; height: auto; clip: auto; overflow: hidden;
}
body.nav-rail .sidenav .navitem > .submenu a,
body.nav-rail .sidenav .navitem > .submenu .linklike,
body.nav-rail .sidenav .navitem > .submenu .lang-current {
  justify-content: flex-start; padding: 6px 12px; min-height: 36px;
}
/* The unpushed-config badge shrinks to a dot on the Settings icon — and
   the Messages unread count to a dot on its icon (2026-10-12). */
body.nav-rail .sidenav .navlink .nav-badge, body.nav-rail .sidenav-nav > a .nav-badge {
  position: absolute; top: 6px; right: 12px; margin: 0; padding: 0;
  width: 9px; height: 9px; min-width: 0; border-radius: 50%;
  font-size: 0; line-height: 9px;
}
@media (max-width: 960px) {
  /* The sidebar becomes an off-canvas drawer behind the top strip's ☰
     (spec §5: the fixed rail gives way below 960px). */
  body.nav-side, body.nav-side.nav-rail { padding-left: 0; }
  body.nav-side .voice-control, body.nav-side.nav-rail .voice-control { left: 22px; }
  body.nav-side .sidenav {
    width: 248px;
    transform: translateX(-100%); transition: transform .15s ease;
  }
  body.nav-side.sidenav-open .sidenav {
    transform: none; box-shadow: 0 0 24px rgba(0, 0, 0, .35);
  }
  body.nav-side.sidenav-open .sidenav .brand,
  body.nav-side.sidenav-open .sidenav-nav { display: flex; }
  body.nav-side.sidenav-open .sidenav-foot { display: block; }
  .topbar-side .sidenav-mobile { display: inline-block; }
  .sidenav-toggle { display: none; }
}

/* -------------------------------------------------------------------------
   Reduced motion (spec §18/§20): decorative pulses, sweeps, and lifts go
   away for users who ask; state changes still land instantly. Keep this
   block LAST so it wins the cascade.
   ------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  * { transition-duration: .01ms !important; animation-duration: .01ms !important; }
  .voice-mic.recording, .progress-fill, .spinner { animation: none !important; }
  .home-card:hover, .btn:active { transform: none !important; }
}

/* ---- Archive republishing cards (/archive, 2026-08-22) ----------------
   Token-only colors so every palette/theme (light/dark/slate/custom) and
   both nav layouts render it natively. */
.arch-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
  gap: var(--space-4);
  margin: var(--space-4) 0;
}
/* .card (Story Engine idea/feed cards, the config editor's cost box) is the
   same chrome — it used to be an undefined class (2026-09-05 audit). */
.arch-card, .card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-panel);
  padding: var(--space-4);
  box-shadow: var(--shadow-panel);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.arch-card > header { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--space-1) var(--space-2); }
.arch-card > header > a {
  font-weight: 600;
  color: var(--text);
  display: block;
  flex: 1 1 200px;
  margin-bottom: var(--space-1);
}
.arch-card > header > .arch-chips { flex-basis: 100%; }
.arch-chips { display: flex; flex-wrap: wrap; gap: var(--space-1); }
.arch-chip {
  font-size: var(--fs-sm);
  padding: 2px 8px;
  border-radius: var(--radius-sm);
  background: var(--tint);
  color: var(--text-soft);
}
.arch-chip.action { background: var(--accent-soft); color: var(--accent); }
.arch-card.status-approved { border-color: var(--success); }
.arch-card.status-rejected { border-color: var(--danger); opacity: .7; }
.arch-card.status-postponed { border-color: var(--warning); }
.arch-meta { color: var(--text-muted); font-size: var(--fs-sm); }
/* Assigner's note under the assigned line (plain text, links clickable). */
.arch-note { white-space: pre-line; overflow-wrap: anywhere; }
.arch-brief { white-space: pre-wrap; }
.arch-actions {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-top: auto;
  padding-top: var(--space-2);
}
/* A select + button form inside a narrow card must be allowed to wrap
   (form.inline is nowrap for table cells). */
.arch-actions form.inline {
  display: inline-flex; flex-wrap: wrap; gap: var(--space-1); align-items: center;
  white-space: normal;
}
.arch-batches { margin-top: var(--space-6); }
/* Screening/review toolbar + per-card review picks (2026-08-25 split).
   Layout-only rules (spacing tokens, no colors) — theme/preset safe. */
.arch-toolbar {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  align-items: center;
}
.arch-toolbar form.inline {
  display: flex;
  gap: var(--space-2);
  align-items: center;
}
.arch-pick {
  margin: 3px 0 0;
}
.arch-filters {
  display: flex;
  gap: var(--space-3);
  flex-wrap: wrap;
  align-items: center;
  margin: var(--space-3) 0;
  color: var(--text-muted);
  font-size: var(--fs-sm);
}
.arch-filters label {
  display: flex;
  gap: var(--space-1);
  align-items: center;
}
.arch-assigned { margin: var(--space-4) 0; }
/* Collapsible cards (2026-09-24, cardfold.js) — Republisher cards + Story
   Engine ideas. .folded hides everything but the header and the .fold-keep
   lines (the assigned line); the chevron is a CSS-drawn caret on a bare
   icon button (tokens only: theme/preset safe). */
.fold-btn {
  flex: 0 0 auto; width: 28px; height: 28px; margin-left: auto; border: 0;
  background: transparent; border-radius: var(--radius-sm);
  color: var(--text-muted); cursor: pointer; position: relative; padding: 0;
}
.fold-btn::before {
  content: ""; position: absolute; left: 10px; top: 8px; width: 7px; height: 7px;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg); transition: transform var(--motion-fast), top var(--motion-fast);
}
.fold-btn:hover, .fold-btn:focus-visible { background: var(--tint); color: var(--text); }
.folded .fold-btn::before { transform: rotate(-45deg); top: 11px; }
.arch-card.folded > :not(header):not(.fold-keep),
.se-idea.folded > :not(header):not(.fold-keep) { display: none; }
.arch-card.folded > header > a { margin-bottom: 0; }
.fold-head { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.fold-head h2 { margin: 0; flex: 1 1 auto; }
.fold-all { font-size: var(--fs-sm); }
.arch-filters .fold-all-group { margin-left: auto; display: flex; gap: var(--space-1); }
.se-fold-head { justify-content: flex-end; margin: 0 0 var(--space-2); }

/* ---- Content Health (/content-health; docs/content_health.md) ----------
   Token-only rules (theme/preset safe). Severity pills reuse the shared
   .status pill base; the danger/warning/tint tokens carry the colors. */
.ch-toolbar { display: flex; gap: var(--space-2); flex-wrap: wrap; align-items: center; }
.ch-toolbar form.inline { display: flex; gap: var(--space-2); align-items: center; }
/* Audit-a-URL box (2026-08-29) — token-based, preset-safe. */
.ch-url-audit {
  display: flex; gap: var(--space-2); flex-wrap: wrap; align-items: center;
  margin: var(--space-2) 0;
}
.ch-url-audit .ch-url-label { display: flex; gap: var(--space-1); align-items: center; flex: 1 1 340px; }
.ch-url-audit input[type="url"] { flex: 1; min-width: 220px; }
.ch-links-toggle {
  display: flex;
  gap: var(--space-1);
  align-items: center;
  color: var(--text-soft);
  font-size: var(--fs-sm);
}
.ch-summary { margin: var(--space-3) 0; }
.ch-tiles { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.ch-tile {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-panel);
  padding: var(--space-2) var(--space-4);
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 84px;
}
.ch-tile strong { font-size: var(--fs-xl); }
.ch-tile span { color: var(--text-muted); font-size: var(--fs-sm); }
.ch-sev-critical { background: var(--danger-soft); color: var(--danger); }
.ch-sev-high { background: var(--danger-soft); color: var(--danger); }
.ch-sev-medium { background: var(--warning-soft); color: var(--warning); }
.ch-sev-low { background: var(--tint); color: var(--text-soft); }
.ch-tile.ch-sev-critical, .ch-tile.ch-sev-high { background: var(--panel); border-color: var(--danger); color: var(--danger); }
.ch-tile.ch-sev-medium { background: var(--panel); border-color: var(--warning); color: var(--warning); }
.ch-tile.ch-sev-low { background: var(--panel); color: var(--text-soft); }
.ch-st-ignored, .ch-st-exception { background: var(--tint); color: var(--text-muted); }
.ch-st-confirmed { background: var(--accent-soft); color: var(--accent); }
.ch-st-corrected { background: var(--success-soft); color: var(--success); }
.ch-filters {
  display: flex;
  gap: var(--space-3);
  flex-wrap: wrap;
  align-items: center;
  margin: var(--space-3) 0;
  color: var(--text-muted);
  font-size: var(--fs-sm);
}
.ch-filters label { display: flex; gap: var(--space-1); align-items: center; }
.ch-queue .ch-open-counts { display: flex; gap: var(--space-1); flex-wrap: wrap; }
.ch-pager {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  margin: var(--space-3) 0;
}
.ch-family { margin: var(--space-5) 0; }
.ch-family h2 { margin-bottom: var(--space-2); }
.ch-finding { margin-bottom: var(--space-3); }
.ch-finding > header { display: flex; gap: var(--space-2); flex-wrap: wrap; align-items: baseline; justify-content: space-between; }
.ch-finding-title { font-weight: 600; }
.ch-finding-resolved { opacity: .75; }
.ch-article-head { margin-bottom: var(--space-4); }
/* Before/after comparison: two columns, stacking on narrow screens. */
.ch-diff {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-2);
}
.ch-diff-col {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--space-2);
  overflow-wrap: anywhere;
}
.ch-diff-before { background: var(--danger-soft); }
.ch-diff-after { background: var(--success-soft); }
.ch-diff-label {
  display: block;
  font-size: var(--fs-sm);
  color: var(--text-muted);
  margin-bottom: var(--space-1);
}
.ch-evidence { margin: var(--space-2) 0 0 var(--space-4); overflow-wrap: anywhere; }
.ch-resolved { margin-top: var(--space-5); }
/* Rules page (/content-health/rules): per-rule form cards. */
.ch-rule { margin-bottom: var(--space-3); position: relative; }
.ch-rule-inactive { opacity: .75; }
.ch-rule-grid {
  display: flex;
  gap: var(--space-3);
  flex-wrap: wrap;
  align-items: flex-start;
  margin: var(--space-2) 0;
}
.ch-rule-grid label,
.ch-rule-instruction {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  color: var(--text-muted);
  font-size: var(--fs-sm);
}
.ch-rule-grid label.ch-rule-active { flex-direction: row; align-items: center; }
.ch-rule-instruction textarea { width: 100%; }
.ch-rule-cats { min-width: 200px; }
/* Sits in a .btn-small row — match the compact height (2026-09-05). */
.ch-rule-samples { max-width: 220px; min-height: 0; padding: 4px 10px; font-size: var(--fs-sm); }
.ch-rule-delete { position: absolute; top: var(--space-3); right: var(--space-4); }
.ch-test-results {
  margin-top: var(--space-2);
  padding: var(--space-2);
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
  font-size: var(--fs-sm);
}
.ch-test-results ul { margin: var(--space-1) 0 var(--space-2) var(--space-4); }

/* ---- Story Engine (/story-engine; docs/story_engine.md) — token-only,
   reusing the ch-tile summary tiles and arch-chip chips. ---- */
.se-toolbar { display: flex; gap: var(--space-2); flex-wrap: wrap; align-items: center; }
.se-recipes { display: flex; gap: var(--space-2); flex-wrap: wrap; align-items: center; }
.se-recipe-pick {
  display: inline-flex; gap: var(--space-1); align-items: center;
  font-size: var(--fs-sm); color: var(--text-soft); white-space: nowrap;
}
.se-tiles { margin: var(--space-3) 0; }
.se-goal { margin: var(--space-2) 0; }
.se-goal label { display: inline-flex; gap: var(--space-1); align-items: center; }
.se-trends .arch-chip { margin: 0 var(--space-1) var(--space-1) 0; }
.se-idea { margin-bottom: var(--space-3); }
.se-idea-head {
  display: flex; gap: var(--space-2); flex-wrap: wrap;
  align-items: baseline; justify-content: space-between;
}
.se-idea-title { font-weight: 600; }
.se-chips { display: flex; gap: var(--space-1); flex-wrap: wrap; align-items: center; }
.se-band-exceptional, .se-band-strong { background: var(--success-soft); color: var(--success); }
.se-band-moderate { background: var(--accent-soft); color: var(--accent); }
.se-band-experimental { background: var(--tint); color: var(--text-soft); }
.se-dup { background: var(--warning-soft); color: var(--warning); }
.se-st-saved { background: var(--accent-soft); color: var(--accent); }
.se-st-approved { background: var(--success-soft); color: var(--success); }
.se-st-rejected, .se-st-expired, .se-st-merged { background: var(--tint); color: var(--text-muted); }
.se-st-postponed { background: var(--warning-soft); color: var(--warning); }
.se-stale { background: var(--warning-soft); color: var(--warning); }
.se-signals { display: flex; gap: var(--space-1); flex-wrap: wrap; }
.se-articles { margin: var(--space-2) 0; }
.se-articles ul { margin: var(--space-1) 0 0 var(--space-4); }
.se-actions {
  display: flex; gap: var(--space-2); flex-wrap: wrap; align-items: center;
  margin-top: var(--space-2);
}
.se-actions form.inline { display: inline-flex; gap: var(--space-1); align-items: center; }
/* inline-flex column: the summary-button sits on the action row's baseline
   when closed and the reject form drops below it when open. */
.se-reject { display: inline-flex; flex-direction: column; }
.se-reject summary { list-style: none; cursor: pointer; }
.se-reject summary::-webkit-details-marker { display: none; }
.se-reject-form {
  display: flex; gap: var(--space-2); flex-wrap: wrap; align-items: center;
  margin-top: var(--space-2);
}
.se-package {
  border-left: 3px solid var(--accent);
  padding-left: var(--space-3);
  margin-bottom: var(--space-4);
}
.se-package-head {
  display: flex; gap: var(--space-2); align-items: center;
  justify-content: space-between; margin-bottom: var(--space-2);
}
.se-feeds { margin-bottom: var(--space-4); }
/* A feed card holds the wide save form + two inline action forms: keep the
   card's column chrome from stacking the buttons one per line (2026-09-05). */
.se-feed { margin-bottom: var(--space-2); flex-direction: row; flex-wrap: wrap; align-items: center; }
.se-feed > .se-feed-form { flex: 1 1 100%; }
.se-feed-form {
  /* flex-direction: row explicitly — the .card chrome it also carries is a
     flex COLUMN, and column + align-items:flex-end right-aligned every field. */
  display: flex; flex-direction: row; gap: var(--space-2); flex-wrap: wrap; align-items: flex-end;
}
.se-feed-form label { display: flex; flex-direction: column; gap: var(--space-1); font-size: var(--fs-sm); }
.se-feed-instruction { flex: 1 1 260px; }
.se-feed-instruction textarea { width: 100%; }
.se-feed-form label.se-feed-active { flex-direction: row; align-items: center; }
.se-feed-meta { align-self: center; }
.se-feed-add summary { list-style: none; cursor: pointer; margin: var(--space-2) 0; }
.se-feed-add summary::-webkit-details-marker { display: none; }
.se-batches { margin-top: var(--space-5); }
.se-batches ul { margin: var(--space-1) 0 0 var(--space-4); }
.se-chat { max-width: 720px; }
.se-chat-log {
  display: flex; flex-direction: column; gap: var(--space-2);
  max-height: 420px; overflow-y: auto; margin-bottom: var(--space-3);
}
.se-chat-msg {
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  max-width: 85%;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.se-chat-user { align-self: flex-end; background: var(--chat-user-bg); color: #fff; }
.se-chat-assistant {
  align-self: flex-start;
  background: var(--chat-assistant-bg);
  border: 1px solid var(--border);
}
.se-chat-ideas { display: flex; gap: var(--space-1); flex-wrap: wrap; align-items: center; }
.se-chat-form { display: flex; gap: var(--space-2); align-items: flex-end; }
.se-chat-form textarea { flex: 1; }

/* Settings -> Chat storage (2026-09-28, chat_storage.html): three action
   cards side by side, one button each; the figure line is the card's own
   KPI. Tokens only. */
.chatstore-actions { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: var(--space-4); margin-bottom: var(--space-6); }
.chatstore-actions .card h3 { margin: 0; }
.chatstore-actions .card .hint { margin: 0; }
.chatstore-actions .card form { margin-top: auto; }
.chatstore-figure { margin: 0; font-size: var(--fs-lg); font-weight: 600; color: var(--text); }

/* Guide page prose (2026-09-28, guide.html): rendered markdown at a reading
   width; every size/color a token, so themes and font scales apply. */
.prose { max-width: 860px; line-height: 1.6; color: var(--text); }
.prose .prose-title { margin-top: var(--space-2); }
.prose h2 { margin-top: var(--space-6); }
.prose h3 { margin-top: var(--space-5); }
.prose p { margin: 0 0 var(--space-3); }
.prose ol, .prose ul { margin: 0 0 var(--space-4); padding-left: 1.6em; }
.prose li { margin: 0 0 var(--space-2); }
.prose li::marker { color: var(--text-soft); font-weight: 600; }
.prose strong { color: var(--text); }
.prose code { font-size: var(--fs-sm); }
.prose a { color: var(--accent); }

/* Config activity -> revision diff (refactor Phase 2): a unified diff in a
   <pre>; ins/del carry the +/- lines, em the hunk headers. */
.cfg-diff { font-size: 0.85rem; line-height: 1.4; overflow-x: auto; padding: 12px; border: 1px solid var(--border, #d0d4dc); border-radius: 8px; background: var(--panel, #fff); white-space: pre; }
.cfg-diff ins { text-decoration: none; display: block; background: rgba(46, 160, 67, 0.15); }
.cfg-diff del { text-decoration: none; display: block; background: rgba(248, 81, 73, 0.15); }
.cfg-diff em { display: block; font-style: normal; opacity: 0.7; }
