/* Bizzpuls app — chat-first identity pass (owner request 2026-08-19). */

:root {
  --ink: #1c2330;
  --muted: #67707e;
  --line: #e6e8ee;
  --accent: #d64550;
  --accent-soft: #fdf1f2;
  --danger: #d64550;      /* error/danger tone — stays red even when the accent changes */
  --danger-soft: #fdf1f2;
  --focus: #5b7290;   /* neutral focus ring — red is reserved for real errors */
  --bg: #f6f7f9;
  --card: #ffffff;
  --shadow: 0 1px 2px rgba(28, 35, 48, 0.05), 0 4px 16px rgba(28, 35, 48, 0.05);
  --radius: 14px;
  --ok-fg: #1e7f4f;   --ok-bg: #effaf4;   --ok-line: #bfe3d0;
  --warn-fg: #9a6700; --warn-bg: #fdf7e7; --warn-line: #ecd9a9;
  --chan-fg: #3a5a86; --chan-bg: #eef4fb; --chan-line: #c9d8ec;
  --ok-dot: #2c9a63;
  --tile-edge: #e8b6ba;
}

/* Dark mode: system preference by default (pure CSS — no flash), an
   explicit choice in Settings sets data-theme and wins in both
   directions. Keep the two token blocks identical. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ink: #e8ebf0;
    --muted: #98a1ae;
    --line: #2b313b;
    --accent: #e0525d;
    --accent-soft: #33222a;
    --danger: #e0525d;
    --danger-soft: #33222a;
    --focus: #8299b8;
    --bg: #12151b;
    --card: #1b202a;
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.35), 0 4px 16px rgba(0, 0, 0, 0.3);
    --ok-fg: #6fd0a0;   --ok-bg: #17281f;   --ok-line: #29513c;
    --warn-fg: #e3b45a; --warn-bg: #2b2415; --warn-line: #564720;
    --chan-fg: #8fb2e0; --chan-bg: #1b2634; --chan-line: #2e4258;
    --ok-dot: #3fbf7f;
    --tile-edge: #6e3d43;
  }
}
:root[data-theme="dark"] {
  --ink: #e8ebf0;
  --muted: #98a1ae;
  --line: #2b313b;
  --accent: #e0525d;
  --accent-soft: #33222a;
  --danger: #e0525d;
  --danger-soft: #33222a;
  --focus: #8299b8;
  --bg: #12151b;
  --card: #1b202a;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.35), 0 4px 16px rgba(0, 0, 0, 0.3);
  --ok-fg: #6fd0a0;   --ok-bg: #17281f;   --ok-line: #29513c;
  --warn-fg: #e3b45a; --warn-bg: #2b2415; --warn-line: #564720;
  --chan-fg: #8fb2e0; --chan-bg: #1b2634; --chan-line: #2e4258;
  --ok-dot: #3fbf7f;
  --tile-edge: #6e3d43;
}

/* Accent presets (owner request, 2026-08-25): the user picks the app's accent
   colour instead of the default red. The choice is a USER setting stored on
   app_user (migration 022) and applied as data-accent on <html>; the browser
   also caches it in localStorage for a flash-free first paint. The default
   ("red", no data-accent) keeps the hand-tuned red tokens above untouched.

   LIGHT + DARK, like every other token here: --accent is used BOTH as a
   white-text fill (buttons, .msg.user, .badge.urgent) AND as tinted text on
   the dark --accent-soft surface (active nav, wordmark), so a single hue can't
   serve both themes — that is why the built-in red lightens in the dark blocks
   (#d64550 → #e0525d). Each preset therefore defines one base hue
   (--accent-base); --accent is that hue in light mode and a mildly lightened
   version in dark mode (mix 14% white), keeping it dark enough for white text
   yet light enough to read on the dark accent-soft. The soft tint and tile
   edge derive from the resolved --accent against the theme's own surfaces
   (--card/--line) via color-mix, so they follow both the accent and the theme.
   --danger stays red regardless (error/danger semantics). Keep this set in step
   with api/src/functions/prefs.js (ACCENTS) and the Settings buttons in
   index.html. */
:root[data-accent] {
  --accent: var(--accent-base);
  --accent-soft: color-mix(in srgb, var(--accent) 10%, var(--card));
  --tile-edge:  color-mix(in srgb, var(--accent) 38%, var(--line));
}
/* Dark mode lightens the accent — both the system-preference default (guarded
   like the token blocks above) and an explicit Dark choice. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"])[data-accent] { --accent: color-mix(in srgb, var(--accent-base), #fff 14%); }
}
:root[data-theme="dark"][data-accent] { --accent: color-mix(in srgb, var(--accent-base), #fff 14%); }
:root[data-accent="ocean"]  { --accent-base: #2f6fd6; }
:root[data-accent="forest"] { --accent-base: #178a4c; }
:root[data-accent="violet"] { --accent-base: #7c5cd6; }
:root[data-accent="amber"]  { --accent-base: #a76a1e; }
:root[data-accent="teal"]   { --accent-base: #0d8585; }

* { box-sizing: border-box; }

/* Scrollbars — slim, rounded and theme-aware (owner request: nicer than the
   chunky OS default, especially in dark mode). The thumb derives from the
   muted token so it reads in both themes; the 2px transparent border +
   padding-box clip makes it float with a little breathing room, and it
   darkens on hover. Firefox uses the standard two-value syntax. */
* { scrollbar-width: thin; scrollbar-color: color-mix(in srgb, var(--muted) 34%, transparent) transparent; }
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--muted) 28%, transparent);
  border-radius: 999px;
  border: 2px solid transparent;
  background-clip: padding-box;
  min-height: 2rem; /* never a tiny stub on a long list */
}
:hover::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--muted) 42%, transparent); background-clip: padding-box; }
::-webkit-scrollbar-thumb:hover { background: color-mix(in srgb, var(--muted) 62%, transparent); background-clip: padding-box; }
::-webkit-scrollbar-corner { background: transparent; }

html, body { height: 100%; }
body {
  margin: 0;
  font-family: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  color: var(--ink);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}

.hidden { display: none !important; }

.wordmark {
  font-size: 2rem;
  font-weight: 800;
  letter-spacing: -0.03em;
  margin: 0 0 0.25rem;
}
.wordmark span { color: var(--accent); }
.wordmark.small { font-size: 1.2rem; }

/* ---------- Sign-in ---------- */
.login {
  min-height: 100vh;
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: 1.5rem;
  background:
    radial-gradient(50rem 30rem at 85% -10%, var(--accent-soft), transparent 60%),
    var(--bg);
}
.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 2.25rem;
  width: 100%;
  max-width: 25rem;
}
.tagline { color: var(--muted); margin: 0 0 1.5rem; }
/* Self-serve signup extras on the sign-in card (open mode only): the Turnstile
   widget (65px is its managed-widget height, reserved so the card does not jump
   when it renders) and the terms consent row. */
.signup-extra { display: grid; gap: 0.6rem; }
.ts-widget { min-height: 65px; }
.terms-row { display: flex; gap: 0.5rem; align-items: flex-start; font-size: 0.85rem; color: var(--ink); }
.terms-row input { margin: 0.2rem 0 0; padding: 0; }
.terms-row a { color: var(--accent); }
form { display: grid; gap: 0.6rem; }
label { font-size: 0.85rem; color: var(--muted); }
input {
  font: inherit;
  font-size: 1rem;
  padding: 0.65rem 0.8rem;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--card);
  color: var(--ink); /* UA default is black — invisible on the dark theme */
}
input::placeholder { color: var(--muted); }
input:focus { outline: 2px solid var(--focus); border-color: transparent; }
textarea.reply-box {
  width: 100%;
  font: inherit;
  padding: 0.65rem 0.8rem;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--card);
  color: var(--ink);
  resize: vertical;
}
textarea.reply-box::placeholder { color: var(--muted); }
textarea.reply-box:focus { outline: 2px solid var(--focus); border-color: transparent; }
/* AI-first: the reply box is locked until the Assistant drafts (owner). Greyed,
   dashed, not-allowed — a clear "not yet" that points at the Draft reply button. */
textarea.reply-box.locked, textarea.reply-box:disabled {
  background: var(--bg); color: var(--muted); cursor: not-allowed;
  border-style: dashed; resize: none;
}
.reply-lock-hint { margin: 0.4rem 0 0; }
/* Tasks view header — title + the "Draft all replies" action on one row. */
.tasks-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.tasks-head h2 { margin: 0; }
/* D14: a disabled task action stays visible (its title says WHY the
   channel can't do it) but clearly inert. */
.change-actions button:disabled { opacity: 0.45; cursor: not-allowed; }
/* Integrated composer (owner: like Claude's box) — a toolbar INSIDE the input
   box: attach + model/effort chips on the left, tasks pill + Send on the right.
   Each chip opens its own popover above it. */
.composer-bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.5rem; margin-top: 0.35rem;
}
.bar-left, .bar-right { display: flex; align-items: center; gap: 0.4rem; }
/* Send must stay visible however narrow the composer's COLUMN gets — the docked
   task can take ~2/3 of the width, so the chat pane (and this composer) is narrow
   on a WIDE screen. Drive it by the composer's own width (container query), not the
   viewport: the left group shrinks and the model name truncates; Send never does. */
.chat-dock { container-type: inline-size; }
.bar-left { flex: 1 1 auto; min-width: 0; }
.bar-right { flex: 0 0 auto; }
.composer-bar .chip-wrap, .composer-bar .chip { min-width: 0; }
.chip #chip-model { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#chat-send { flex: 0 0 auto; }
@container (max-width: 28rem) { .composer-bar .chip .chip-eff { display: none; } }
.chip-wrap { position: relative; display: inline-flex; min-width: 0; }
/* Keep the authoritative <select> in the DOM (state + persistence) but out of
   sight — the popover list is its face. */
.visually-hidden {
  position: absolute !important; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0);
  white-space: nowrap; border: 0;
}
/* Model / effort chips (like Claude's "Opus 4.8" / "⚡ Auto"). */
.chip {
  display: inline-flex; align-items: center; gap: 0.3rem;
  font: inherit; font-size: 0.8rem; color: var(--ink);
  background: var(--card); border: 1px solid var(--line); border-radius: 999px;
  padding: 0.32rem 0.7rem; cursor: pointer; white-space: nowrap;
}
.chip:hover { border-color: var(--accent); }
.chip:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
.chip .chip-car { color: var(--muted); font-size: 0.6rem; margin-left: 0.05rem; }
.chip[aria-expanded="true"] .chip-car { transform: rotate(180deg); }
.chip .chip-sep, .chip .chip-eff { color: var(--muted); }
/* Picker popover — floats above the model chip (anchored to .chip-wrap). */
.model-pop {
  position: absolute; left: 0; bottom: calc(100% + 8px); z-index: 40;
  width: min(320px, 78vw); max-height: 56vh; overflow-y: auto;
  background: var(--card); border: 1px solid var(--line); border-radius: 12px;
  padding: 0.5rem; box-shadow: 0 12px 34px rgba(0,0,0,0.28);
}
.model-pop-list { display: flex; flex-direction: column; }
.mp-grp {
  font-size: 0.64rem; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--muted); opacity: 0.85; margin: 0.5rem 0.4rem 0.25rem;
}
.mp-grp:first-child { margin-top: 0.15rem; }
.mp-row {
  display: flex; align-items: center; gap: 0.6rem; width: 100%;
  font: inherit; font-size: 0.86rem; color: var(--ink); text-align: left;
  background: none; border: 0; border-radius: 8px; padding: 0.42rem 0.45rem; cursor: pointer;
}
.mp-row:hover { background: var(--bg); }
.mp-row.on { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.mp-row:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.mp-name { flex: 1; }
.mp-idx {
  display: inline-flex; align-items: center; gap: 0.3rem;
  color: var(--muted); font-size: 0.76rem; font-variant-numeric: tabular-nums;
}
.mp-idx .q-ic { color: #f5b301; font-size: 0.8rem; line-height: 1; }
.mp-idx .c-ic {
  display: inline-block; width: 9px; height: 9px; margin-left: 0.1rem;
  background: #2fa36b; transform: rotate(45deg); border-radius: 2px;
}
/* Effort control inside the popover (owner: effort stays in the model menu). */
.model-pop-effort {
  display: flex; align-items: center; gap: 0.5rem;
  border-top: 1px solid var(--line); margin-top: 0.45rem; padding: 0.55rem 0.45rem 0.1rem;
}
.model-pop-effort .mp-label { color: var(--muted); font-size: 0.78rem; }
.model-pop-effort #effort-select { flex: 1; }
.model-pop-tip {
  margin: 0.45rem 0.1rem 0.1rem; color: var(--muted); font-size: 0.74rem; line-height: 1.45;
  border-top: 1px solid var(--line); padding-top: 0.5rem;
}
#model-select, #effort-select {
  font: inherit;
  font-size: 0.78rem;
  padding: 0.2rem 0.4rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--card);
  color: var(--muted);
}
#model-select:focus, #effort-select:focus { outline: 2px solid var(--focus); border-color: transparent; }
/* Shown in place of #effort-select for models with no effort control — a plain,
   non-interactive note (not a dead dropdown), so nothing looks broken. */
.effort-fixed {
  font-size: 0.72rem;
  color: var(--muted);
  opacity: 0.75;
  font-style: italic;
  white-space: nowrap;
}
/* Quality + cost for the chosen model + effort (owner design pick): compact
   "icon + number" pairs, no bars — a gold ★ with the quality score, then a
   green diamond with the cost score (both 1-5, set in app.js). Deliberately
   lightweight so it sits quietly next to the two picker dropdowns. */
.rating { display: inline-flex; align-items: center; gap: 0.55rem; }
.rating .pair { display: inline-flex; align-items: center; gap: 0.25rem; }
.rating .q-ic { color: #f5b301; font-size: 0.85rem; line-height: 1; }
/* Flat solid diamond (a rotated square) — the cost mark; tints stay crisp. */
.rating .c-ic {
  display: inline-block; width: 10px; height: 10px;
  background: #2fa36b; transform: rotate(45deg); border-radius: 2px;
}
.rating .rating-num, .rating .cost-num { color: var(--muted); font-size: 0.75rem; font-variant-numeric: tabular-nums; }
/* Price-tier badge for the chosen model (premium/standard/value) — a small pill
   naming the billing band; empty (no pill) for a model with no tier. */
.tier-badge:empty { display: none; }
.tier-badge {
  display: inline-block; margin-left: 0.4rem;
  font-size: 0.66rem; font-weight: 600; letter-spacing: 0.02em;
  padding: 0.08rem 0.44rem; border-radius: 99px;
  border: 1px solid var(--line); color: var(--muted); background: transparent;
  vertical-align: middle; text-transform: uppercase;
}
.tier-badge.tier-premium { color: #b5852b; border-color: #e6c489; background: rgba(245,179,1,0.10); }
.tier-badge.tier-standard { color: var(--muted); }
.tier-badge.tier-value { color: #2fa36b; border-color: #a6dcc2; background: rgba(47,163,107,0.10); }
#code { letter-spacing: 0.4em; text-align: center; font-size: 1.3rem; }
button {
  font: inherit;
  font-size: 0.95rem;
  font-weight: 500;
  padding: 0.62rem 1rem;
  border: 0;
  border-radius: 10px;
  background: var(--accent);
  color: #fff;
  cursor: pointer;
  transition: filter 0.15s ease, background 0.15s ease;
}
button:hover { filter: brightness(0.94); }
/* Secondary action: outlined + ink text so it unmistakably reads as a button
   (the old borderless ghost looked like plain text — owner: "didn't realize
   Disconnect is a button"). Tiny inline icon-ghosts keep the borderless look. */
button.ghost { background: transparent; color: var(--ink); font-weight: 500; border: 1px solid var(--line); }
button.ghost:hover { background: var(--bg); filter: none; color: var(--ink); border-color: var(--accent); }
button.ghost.tiny { border: 0; color: var(--muted); font-weight: 400; }
button.ghost.tiny:hover { border: 0; }
/* Destructive action: outlined in the danger tone — obviously a button AND
   obviously "careful" (red), while staying quieter than the solid-accent
   primary. Uses --danger (not --accent) so it stays red whatever accent the
   user picks — red is reserved for danger. Always paired with a confirm in JS. */
button.danger { background: transparent; color: var(--danger); border: 1px solid var(--danger); font-weight: 600; }
button.danger:hover { background: var(--danger-soft); filter: none; color: var(--danger); }
button.small { font-size: 0.85rem; padding: 0.45rem 0.7rem; }
.note { min-height: 1.2em; color: var(--muted); font-size: 0.9rem; }
.note.error { color: var(--danger); }
.fine { color: var(--muted); font-size: 0.8rem; margin-top: 1.25rem; }
/* Build stamp at the foot of Settings (owner request) — quiet, for support. */
.app-version { font-size: 0.72rem; opacity: 0.7; font-variant-numeric: tabular-nums; }
.app-version:empty { display: none; }
/* Dev database stamp stands out so a preview is never mistaken for prod. */
#app-db.db-dev { opacity: 1; color: var(--warn-fg); font-weight: 600; }

/* App-vs-DB schema-version drift warning — an amber banner INSIDE the Settings
   "Version" group (owner request: moved off the main page), shown only when the
   DB's applied migration version differs from what the app build expects.
   Compact: warning icon in its own chip · message. */
.schema-badge {
  display: flex; align-items: flex-start; gap: 0.4rem;
  margin-top: 0.6rem; padding: 0.45rem 0.5rem; border-radius: 8px;
  font-size: 0.72rem; line-height: 1.3;
  color: var(--warn-fg); background: var(--warn-bg); border: 1px solid var(--warn-line);
}
.schema-badge__icon {
  flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; border-radius: 5px;
  color: var(--warn-fg); background: color-mix(in srgb, var(--warn-line) 45%, transparent);
}
.schema-badge__msg { flex: 1; min-width: 0; padding-top: 1px; }
/* Benign "dev DB is ahead" note — informational, not an amber warning. */
.schema-badge.neutral { color: var(--muted); background: transparent; border-color: var(--line); }
.schema-badge.neutral .schema-badge__icon { color: var(--muted); background: color-mix(in srgb, var(--line) 45%, transparent); }

/* ---------- Shell: sidebar + main ---------- */
/* 100dvh after each 100vh: iOS/iPadOS Safari sizes 100vh to the LARGEST
   viewport (browser chrome hidden), pushing the chat dock off-screen; dvh
   tracks the visible viewport. The vh line stays as the older-browser
   fallback (owner report, iPad). */
.shell { display: flex; min-height: 100vh; min-height: 100dvh; }
#side {
  width: 15.5rem;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  padding: 1.25rem 1rem;
  background: var(--card);
  border-right: 1px solid var(--line);
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100dvh;
}
#side nav { display: flex; flex-direction: column; gap: 0.15rem; }
#side nav button {
  display: flex;
  align-items: center;
  text-align: left;
  background: transparent;
  color: var(--muted);
  padding: 0.55rem 0.75rem;
  border-radius: 10px;
  font-weight: 500;
}
#side nav button:hover { background: var(--bg); color: var(--ink); filter: none; }
#side nav button.active { background: var(--accent-soft); color: var(--accent); font-weight: 600; }

/* Sidebar recent chats — fills the space between the nav and the account
   block and scrolls on its own, so a long history never pushes the credits away. */
.side-panel {
  border-top: 1px solid var(--line); padding-top: 1rem;
  display: flex; flex-direction: column; min-height: 0; flex: 1 1 auto;
}
.side-panel h5 {
  margin: 0.9rem 0 0.35rem; font-size: 0.72rem; font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted);
}
/* The panel's create action (+ New chat, + Connect a channel): a full-width button on top. */
#side-new-chat, #side-connect { width: 100%; }
.side-chat-list { list-style: none; margin: 0; padding: 0; overflow-y: auto; min-height: 0; flex: 0 1 auto; }
/* Sidebar lists scroll (wheel, trackpad, touch) with no visible scrollbar —
   owner: no inner scrollbars. A bottom fade (.more-below, set by app.js
   markSideOverflow) shows there is more below. */
#side .side-chat-list, #side .side-chan-groups { overflow-y: auto; min-height: 0; scrollbar-width: none; }
#side .side-chat-list::-webkit-scrollbar, #side .side-chan-groups::-webkit-scrollbar { display: none; }
#side .more-below {
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 2.5rem), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 2.5rem), transparent);
}
.side-chat {
  display: block; width: 100%; text-align: left; background: transparent; color: var(--ink);
  padding: 0.42rem 0.6rem; border-radius: 8px; font-size: 0.86rem; font-weight: 400;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.side-chat:hover { background: var(--bg); filter: none; }
.side-chat-row { position: relative; display: flex; align-items: center; border-radius: 8px; }
.side-chat-row:hover { background: var(--bg); }
.side-chat-row .side-chat { flex: 1 1 auto; min-width: 0; }
.side-chat-row .side-chat:hover { background: transparent; }
.side-chat-row.active { background: var(--accent-soft); }
.side-chat-row.active .side-chat { color: var(--accent); font-weight: 500; }

/* "⋯" on a chat row (sidebar + history drawer) — shown on hover / focus, and
   always on touch screens where there is no hover. */
.chat-more {
  flex: 0 0 auto; background: transparent; color: var(--muted); border: 0;
  padding: 0.15rem 0.45rem; margin-right: 0.15rem; border-radius: 6px;
  font-size: 1rem; line-height: 1; letter-spacing: 0.05em; opacity: 0; box-shadow: none;
}
.side-chat-row:hover .chat-more, .hist-row:hover .chat-more,
.chat-more:focus-visible, .chat-more[aria-expanded="true"] { opacity: 1; }
.chat-more:hover { color: var(--ink); background: var(--line); filter: none; }
@media (hover: none) { .chat-more { opacity: 1; } }
.hist-row { display: flex; align-items: center; gap: 0.25rem; }
.hist-row .hist-item { flex: 1 1 auto; min-width: 0; }
.chat-menu {
  position: fixed; z-index: 60; min-width: 9rem; padding: 0.3rem;
  background: var(--card); border: 1px solid var(--line); border-radius: 10px; box-shadow: var(--shadow);
  display: flex; flex-direction: column;
}
.chat-menu button {
  text-align: left; background: transparent; color: var(--ink); border: 0; box-shadow: none;
  padding: 0.45rem 0.65rem; border-radius: 7px; font-size: 0.88rem; font-weight: 500;
}
.chat-menu button:hover, .chat-menu button:focus-visible { background: var(--bg); filter: none; }
.chat-menu button.danger { color: var(--danger, #c0392b); }
.side-panel > h5:first-child { margin-top: 0; }
.side-task .side-ref { color: var(--muted); font-size: 0.78rem; font-variant-numeric: tabular-nums; margin-right: 0.15rem; }
.side-task.urgent .side-ref { color: var(--danger); font-weight: 600; }
.side-chan { display: flex; align-items: center; gap: 0.45rem; }
.side-chan .side-chan-host { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1 1 auto; }
.side-chan em { color: var(--muted); font-style: normal; font-size: 0.75rem; flex: 0 0 auto; }
/* Settings as a gear beside help + theme; active while Settings is open. */
.side-gear { display: inline-flex; align-items: center; justify-content: center; }
.side-gear svg { width: 16px; height: 16px; }
.side-gear.active { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
/* The plan line under the credit total is the way into Plan & credits. */
.credit-gauge .cg-plan-link.active { text-decoration: underline; }
.bill-topup-bonus { color: var(--ok-fg); font-size: 0.82rem; font-weight: 600; }
.credit-gauge .cg-plan-link {
  display: block; background: transparent; border: 0; box-shadow: none; padding: 0; margin: 0.1rem 0 0.15rem;
  color: var(--accent); font-size: 0.8rem; font-weight: 500; cursor: pointer;
}
.credit-gauge .cg-plan-link[hidden] { display: none; }
.credit-gauge .cg-plan-link:hover { text-decoration: underline; filter: none; background: transparent; }
.side-chats-empty { color: var(--muted); font-size: 0.82rem; padding: 0.3rem 0.6rem; }
.side-all {
  align-self: flex-start; background: transparent; color: var(--accent);
  padding: 0.4rem 0.6rem; font-size: 0.84rem; font-weight: 500;
}
.side-all:hover { text-decoration: underline; filter: none; }

.dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.dot.ok { background: var(--ok-dot); }
.dot.err { background: var(--danger); }
.ghost.tiny { padding: 0.1rem 0.35rem; font-size: 0.8rem; line-height: 1; border-radius: 6px; }
.biz-empty { color: var(--muted); font-size: 0.78rem; font-style: italic; margin: 0.15rem 0.4rem; opacity: 0.75; list-style: none; }

.session {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  color: var(--muted);
  font-size: 0.8rem;
  overflow-wrap: anywhere;
  border-top: 1px solid var(--line);
  padding-top: 0.9rem;
}
.session button { align-self: flex-start; }

#views { flex: 1; min-width: 0; display: flex; flex-direction: column; height: 100vh; height: 100dvh; overflow-y: auto; }
.view { max-width: 62rem; width: 100%; margin: 0 auto; padding: 2rem 1.5rem; }
.view h2 { margin-top: 0; letter-spacing: -0.01em; }
.empty {
  color: var(--muted);
  background: var(--card);
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  padding: 2.25rem 1.5rem;
  text-align: center;
}

/* ---------- Chat: the center of the world ---------- */
#view-chat {
  flex: 1;
  display: flex;
  flex-direction: column;
  max-width: 48rem;
  padding-bottom: 1rem;
}
/* The chat view fills #views as a flex column so the log scrolls INSIDE it
   and the dock stays pinned to the bottom (owner: dock must not be pushed
   off-screen as the conversation grows). */
#view-chat { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; padding: 1.25rem 1.5rem 0.9rem; }
.chat-wrap { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.chat-log {
  flex: 1;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: 0.5rem 0.25rem 1rem;
}
/* Task Threads (owner): the bound-task dock sits BESIDE the chat on desktop and
   ABOVE it (collapsible height) on mobile. The chat view widens when docked. */
.chat-split { flex: 1 1 auto; min-height: 0; display: flex; gap: 1.25rem; --task-pct: 62%; }
#view-chat.has-dock { max-width: 78rem; }
.chat-task-dock { min-width: 0; overflow-y: auto; }
.chat-task-dock.hidden { display: none; }
/* No NESTED scrollbars in the docked task (owner: "scroll bars are crap"). The
   incoming message + any preview expand in full and the dock scrolls as ONE
   region — the reply box already auto-grows via JS. (The Tasks-list cards keep
   their compact 14rem cap.) */
.chat-task-dock .pane-body { max-height: none; overflow: visible; }
.chat-task-dock .dock-head { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; color: var(--muted); font-size: 0.8rem; font-weight: 600; padding: 0.15rem 0 0.55rem; }
.chat-task-dock .dock-head-actions { display: inline-flex; align-items: center; gap: 0.25rem; }
.chat-task-dock .dock-close, .chat-task-dock .dock-wide-btn { line-height: 1; padding: 0.1rem 0.5rem; }
.chat-task-dock .dock-wide-btn { font-size: 0.95rem; }
.chat-task-dock .conn-card { margin: 0; }
/* The draggable vertical divider between the docked task and the chat (owner:
   free ratio via a grab bar; the 1/3–2/3 button stays as a quick snap). Hidden
   until the dock shows and only on desktop, where the two sit side by side. */
.chat-divider { display: none; }
/* Desktop split. Coming from a task the task leads (~2/3); the ◨/◧ button snaps
   between ~2/3-task and ~1/3-task, and the divider sets any ratio in between
   (--task-pct, persisted). On mobile the dock is a full-width strip above. */
@media (min-width: 761px) {
  .chat-split { gap: 0; }
  .chat-split > .chat-task-dock { flex: 0 0 var(--task-pct); min-width: 14rem; padding-right: 1rem; }
  .chat-split > .chat-wrap { flex: 1 1 0; min-width: 18rem; padding-left: 1rem; }
  #view-chat.has-dock .chat-divider {
    display: flex; align-items: stretch; justify-content: center;
    flex: 0 0 auto; width: 11px; align-self: stretch;
    cursor: col-resize; touch-action: none; user-select: none;
  }
  #view-chat.has-dock .chat-divider::before {
    content: ""; width: 1px; background: var(--line);
    transition: background 0.15s ease, width 0.15s ease;
  }
  #view-chat.has-dock .chat-divider:hover::before,
  #view-chat.has-dock .chat-divider.dragging::before,
  #view-chat.has-dock .chat-divider:focus-visible::before { background: var(--accent); width: 2px; }
  body.col-resizing { cursor: col-resize; user-select: none; }
}
@media (max-width: 760px) { .chat-split > .chat-wrap { flex: 1 1 auto; min-width: 0; } }
@media (max-width: 760px) {
  .chat-split { flex-direction: column; gap: 0.75rem; }
  .chat-task-dock { flex: 0 0 auto; max-height: 45vh; border-bottom: 1px solid var(--line); padding-bottom: 0.5rem; }
  #view-chat.has-dock { max-width: 48rem; }
}
.msg {
  max-width: 88%;
  padding: 0.7rem 1rem;
  border-radius: 16px;
  white-space: pre-wrap;
  overflow-wrap: anywhere; /* long URLs/tokens wrap instead of overflowing the bubble */
  line-height: 1.55;
  font-size: 0.97rem;
}
.msg.user { align-self: flex-end; background: var(--accent); color: #fff; border-bottom-right-radius: 6px; }
/* Attached image(s) the owner sent — rounded thumbnails above any typed text. */
.msg.user .msg-imgs { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-bottom: 0.4rem; }
.msg.user .msg-imgs:last-child { margin-bottom: 0; }
.msg.user .msg-thumb { position: relative; line-height: 0; display: inline-block; }
.msg.user .msg-img { display: block; max-width: 100%; max-height: 220px; border-radius: 10px; }
/* Several photos in one message sit side by side, each a bit smaller, numbered. */
.msg.user .msg-imgs.multi .msg-img { max-height: 130px; max-width: 100%; object-fit: cover; }
.msg.user .msg-idx {
  position: absolute; top: 4px; left: 4px; min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: 9px; background: rgba(0,0,0,0.72); color: #fff;
  font-size: 0.72rem; font-weight: 700; line-height: 17px; text-align: center;
}
/* Assistant replies read as plain flowing text — no bubble (owner request:
   match Claude's chat, where only the person's turns sit in a bubble). The
   answer spans the column; user messages stay in the accent bubble above. */
.msg.assistant {
  align-self: stretch;
  max-width: 100%;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  padding: 0.15rem 0.35rem 0.35rem;
  position: relative; /* anchor for the copy button */
}
.msg.pending { color: var(--muted); }
/* Links inside chat messages (e.g. a new site's address). Without a rule they
   fall back to the raw UA blue, which is over-saturated on the dark theme (owner
   report). Use the readable channel-blue token — it is tuned for both themes. */
.msg a { color: var(--chan-fg); text-decoration: underline; overflow-wrap: anywhere; }
.msg.user a { color: #fff; } /* on the accent bubble, white reads better; keeps the underline */
/* Copy button on each assistant answer — appears on hover / focus. */
.copy-btn {
  position: absolute;
  top: 0.35rem;
  right: 0.35rem;
  padding: 0.15rem 0.5rem;
  font-size: 0.72rem;
  line-height: 1.4;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--bg);
  color: var(--muted);
  opacity: 0;
  transition: opacity 0.12s ease;
}
.msg.assistant:hover .copy-btn,
.copy-btn:focus { opacity: 1; }
.copy-btn:hover { color: var(--ink); }
/* Per-turn token meter — a small muted line under the answer. */
.msg.tokens {
  align-self: flex-start;
  background: none;
  border: 0;
  box-shadow: none;
  padding: 0 0.35rem;
  margin-top: -0.2rem;
  color: var(--muted);
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}
/* The green ◆ cost mark, echoing the model picker so "credits/cost" reads as
   one visual language across the app (owner request). Sized for the meter text. */
.msg.tokens .c-ic {
  display: inline-block; flex: none;
  width: 8px; height: 8px;
  background: #2fa36b; transform: rotate(45deg); border-radius: 1.5px;
}
@media (hover: none) { .copy-btn { opacity: 1; } } /* touch: always visible */
/* Persistent activity lines — what the assistant did before answering */
.msg.activity {
  align-self: flex-start;
  max-width: 88%;
  background: none;
  border: 0;
  box-shadow: none;
  padding: 0 0.35rem;
  color: var(--muted);
  font-style: italic;
  font-size: 0.85rem;
}
.chat-head { display: flex; justify-content: flex-end; padding: 0 0 0.25rem; }
/* Claude-Code-style working indicator: animated dot · action · elapsed. */
.msg.status {
  align-self: flex-start;
  display: flex;
  align-items: center;
  gap: 0.45rem;
  background: none;
  border: 0;
  box-shadow: none;
  padding: 0 0.35rem;
  color: var(--muted);
  font-size: 0.85rem;
}
.msg.status .spin {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--accent, #6b7cff);
  animation: statuspulse 1.2s ease-in-out infinite;
  flex: 0 0 auto;
}
.msg.status .act { color: var(--ink); }
.msg.status .elapsed { opacity: 0.7; font-variant-numeric: tabular-nums; }
.msg.status .elapsed::before { content: "· "; }
@keyframes statuspulse { 0%, 100% { opacity: 0.35; transform: scale(0.85); } 50% { opacity: 1; transform: scale(1); } }
/* The answer bubble is empty until text streams — the status line is the cue. */
.msg.assistant.pending:empty { display: none; }

.chat-dock { flex: 0 0 auto; padding-top: 0.4rem; }
/* Drag-and-drop an image onto the chat: a clear dashed cue while a file hovers. */
.chat-dock.drag-over {
  outline: 2px dashed var(--focus);
  outline-offset: 3px;
  border-radius: 8px;
  background: color-mix(in srgb, var(--focus) 8%, transparent);
}
.attach-chip .attach-loading { color: var(--muted); font-style: italic; }
.tasksnote {
  margin: 0 0 0.5rem;
  font-size: 0.85rem;
  color: var(--muted);
}
.tasksnote::before { content: "●"; color: var(--accent); margin-right: 0.4rem; font-size: 0.6rem; vertical-align: 2px; }
/* "#" task-mention typeahead in the chat dock. */
.mention-pop {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  margin: 0 0 0.5rem;
  padding: 0.3rem;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--card);
  box-shadow: var(--shadow);
  max-height: 13rem;
  overflow-y: auto;
}
.mention-item {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  text-align: left;
  background: none;
  border: 0;
  padding: 0.4rem 0.55rem;
  border-radius: 7px;
  color: var(--ink);
  font: inherit;
  cursor: pointer;
  width: 100%;
}
.mention-item:hover { background: var(--bg); filter: none; }
.mention-item .mention-id { color: var(--accent); font-weight: 700; flex: 0 0 auto; }
.mention-item .mention-sum { color: var(--muted); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Image attach: button in the input row + a thumbnail chip above it. */
/* "Add" button (owner request: a plus, not a paperclip) — a crisp circular +.
   Opens the picker; paste and drag-drop attach images too (see app.js). */
.chat-input .attach-btn {
  background: transparent;
  border: 1px solid var(--line);
  color: var(--muted);
  font-size: 1.3rem;
  font-weight: 400;
  line-height: 1;
  width: 1.95rem;
  height: 1.95rem;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  border-radius: 999px;
}
.chat-input .attach-btn:hover { border-color: var(--accent); color: var(--ink); background: var(--bg); filter: none; }
/* Voice input placeholder — same circular shape as the "+" button, but muted
   and inert (disabled): the feature is coming, the button just marks the spot. */
.chat-input .mic-btn {
  background: transparent;
  border: 1px solid var(--line);
  color: var(--muted);
  width: 1.95rem;
  height: 1.95rem;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  border-radius: 999px;
  opacity: 0.5;
  cursor: not-allowed;
}
.chat-input .mic-btn svg { width: 1rem; height: 1rem; display: block; }
.attach-chip {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin: 0 0 0.5rem;
  padding: 0.45rem 0.6rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--bg);
  font-size: 0.85rem;
  color: var(--muted);
}
/* Each pending photo is a small thumbnail with a number badge + remove button.
   The thumbnail itself is a button: tapping it inserts "photo N" into the box.
   Badge (top-left) and remove × (top-right) sit INSET on the image corners so
   each control unmistakably belongs to its own photo — floating them outside
   the thumbnail pushed the × into the gap where it read as belonging to the
   next photo, or as decoration. */
.attach-thumb { position: relative; line-height: 0; }
.attach-insert { padding: 0; border: 0; background: none; cursor: pointer; display: block; border-radius: 6px; }
.attach-insert:hover img, .attach-insert:focus-visible img { outline: 2px solid var(--accent); outline-offset: 1px; }
.attach-thumb img { width: 2.75rem; height: 2.75rem; object-fit: cover; border-radius: 6px; display: block; }
/* Badge (top-left) and remove-× (top-right) are a MATCHED PAIR: same 19px
   circle, same white ring + shadow, same inset — mirror-image controls in
   opposite corners so the pair reads as one consistent affordance. */
.attach-thumb .attach-idx {
  position: absolute; top: 3px; left: 3px; width: 19px; height: 19px; padding: 0;
  border: 1.5px solid #fff; border-radius: 50%; background: var(--accent); color: #fff;
  font-size: 0.64rem; font-weight: 700; line-height: 16px; text-align: center;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45); /* lift off the photo underneath */
  pointer-events: none; /* taps fall through to the insert button below */
}
.attach-thumb .attach-x {
  position: absolute; top: 3px; right: 3px; width: 19px; height: 19px; padding: 0;
  border: 1.5px solid #fff; border-radius: 50%; background: rgba(20, 25, 35, 0.85); color: #fff;
  font-size: 0.9rem; font-weight: 400; line-height: 16px; text-align: center; cursor: pointer;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45); /* white ring + shadow read it as a button on any photo */
}
.attach-thumb .attach-x:hover, .attach-thumb .attach-x:focus-visible { background: var(--danger); outline: none; }
/* Stock-photo picker card (find free images to propose). */
.stock-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)); gap: 0.4rem; margin: 0.5rem 0 0.3rem; }
.stock-pick { padding: 0; border: 2px solid transparent; border-radius: 8px; background: none; cursor: pointer; overflow: hidden; line-height: 0; }
.stock-pick img { width: 100%; height: 72px; object-fit: cover; display: block; }
.stock-pick:hover { border-color: var(--accent); }
.stock-pick.chosen { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent); }
.stock-credit { font-size: 0.78rem; color: var(--muted); margin: 0.2rem 0 0; }
/* "Open website" button on a Bizzpuls-hosted connection card — a clear way to
   visit the live site (owner request). */
.conn-open { margin: 0 0 0.6rem; }
.btn-open { display: inline-flex; align-items: center; gap: 0.3rem; padding: 0.45rem 0.9rem; border-radius: 10px;
  background: var(--accent); color: #fff; font-weight: 600; font-size: 0.9rem; text-decoration: none; }
.btn-open:hover { filter: brightness(1.06); }
/* Visual design picker (site_style widget): template cards, colour swatches, font
   samples, and a live preview panel — so the owner SEES the look, not words. */
.style-card .sp-group { margin: 0.6rem 0 0; }
.style-card .sp-label { font-size: 0.78rem; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.03em; margin-bottom: 0.35rem; }
.style-card .sp-row { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.style-card button { font: inherit; cursor: pointer; background: var(--card); border: 2px solid var(--line); border-radius: 10px; color: var(--ink); }
.style-card button:hover { border-color: var(--accent); }
.style-card button.chosen { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent); }
.style-card .sp-tpl { display: inline-flex; align-items: center; gap: 0.4rem; padding: 0.4rem 0.7rem; }
.style-card .sp-tpl-name { font-weight: 600; }
.style-card .sp-rec { font-size: 0.68rem; color: var(--accent); border: 1px solid var(--accent); border-radius: 999px; padding: 0 0.4rem; }
.style-card .sp-col { display: inline-flex; align-items: center; gap: 0.45rem; padding: 0.35rem 0.6rem 0.35rem 0.4rem; }
.style-card .sp-sw { display: inline-flex; border-radius: 6px; overflow: hidden; border: 1px solid var(--line); }
.style-card .sp-sw-a, .style-card .sp-sw-s { width: 16px; height: 22px; display: block; }
.style-card .sp-col-name { font-size: 0.85rem; }
.style-card .sp-font { display: inline-flex; flex-direction: column; align-items: center; gap: 0.1rem; padding: 0.35rem 0.7rem; min-width: 62px; }
.style-card .sp-font-aa { font-size: 1.35rem; line-height: 1.1; }
.style-card .sp-font-name { font-size: 0.72rem; color: var(--muted); }
.style-card .sp-preview { margin: 0.7rem 0 0.2rem; padding: 1rem 1.1rem; border: 1px solid var(--line); border-radius: 12px; background: #fff; color: #111; }
.style-card .sp-pv-head { font-size: 1.3rem; font-weight: 700; line-height: 1.15; }
.style-card .sp-pv-body { font-size: 0.95rem; margin: 0.25rem 0 0.7rem; opacity: 0.85; }
.style-card .sp-pv-btn { display: inline-block; padding: 0.4rem 0.9rem; border-radius: 8px; font-size: 0.85rem; font-weight: 600; }
.style-card .sp-use { margin-top: 0.6rem; }
.attach-chip .attach-err { color: var(--danger); }
/* Which connection a task belongs to (header badge, always visible). */
.badge.conn { background: transparent; color: var(--muted); border-color: var(--line); font-weight: 500; }
/* One integrated box: textarea on top, toolbar row inside below it. */
.chat-input {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 20px;
  box-shadow: var(--shadow);
  padding: 0.65rem 0.7rem 0.55rem;
}
.chat-input textarea {
  width: 100%;
  border: 0;
  padding: 0.3rem 0.2rem;
  background: transparent;
  color: var(--ink);
  font: inherit;
  line-height: 1.4;
  resize: none;      /* the JS auto-grows it up to ~6 lines */
  max-height: 160px;
  overflow-y: auto;
}
.chat-input textarea::placeholder { color: var(--muted); }
.chat-input textarea:focus { outline: none; }
.chat-input:focus-within { outline: 2px solid var(--focus); }
#chat-send { border-radius: 999px; padding: 0.5rem 1.15rem; }

/* Onboarding — chat's empty state teaches the connect concept */
/* Chat is always shown; when nothing is connected a slim nudge sits above it. */
.chat-onboard { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; padding: 0.7rem 0.9rem; margin-bottom: 0.75rem; border: 1px solid var(--line); border-radius: var(--radius); background: var(--accent-soft); }
.chat-onboard.hidden { display: none; }
.chat-onboard p { margin: 0; flex: 1; min-width: 12rem; color: var(--ink); font-size: 0.9rem; line-height: 1.5; }
.chat-onboard em { font-style: normal; font-weight: 600; }
.onboard { margin: auto 0; text-align: center; padding: 2rem 0 3rem; }
.onboard h2 { font-size: 1.7rem; margin-bottom: 0.4rem; }
.onboard .lead { color: var(--muted); max-width: 30rem; margin: 0 auto 2rem; line-height: 1.6; }
.tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 0.9rem;
  text-align: left;
}
.tile {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  padding: 1.1rem 1.2rem;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--card);
  font: inherit;
}
.tile strong { font-size: 0.95rem; }
.tile span { font-size: 0.82rem; color: var(--muted); }
.tile.live { cursor: pointer; box-shadow: var(--shadow); border-color: var(--tile-edge); color: var(--ink); }
.tile.live span { color: var(--accent); font-weight: 600; }
.tile.live:hover { filter: none; border-color: var(--accent); }
.tile.soon { opacity: 0.75; }

/* ---------- Cards (connections, tasks) ---------- */
.connect-card, .conn-card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 1.25rem 1.5rem;
  margin-bottom: 1rem;
}
.connect-card h3 { margin: 0 0 0.4rem; }
.hint { color: var(--muted); font-size: 0.9rem; margin: 0.35rem 0; }
.row { display: flex; gap: 0.6rem; flex-wrap: wrap; }
.row input { flex: 1; min-width: 12rem; }
/* "What Bizzpuls remembers" (D25): a see/correct list of LEARNED facts — one item
   per learned fact with Edit/Forget, its value, and a provenance line. Facts arrive
   by chatting, not by filling in blanks; an item flips to an inline edit control. */
.bp-fields { display: flex; flex-direction: column; gap: 0.9rem; margin-top: 0.6rem; }
.bp-empty { margin: 0.6rem 0; }
.bp-item { padding: 0.6rem 0.75rem; background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius); }
.bp-head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.6rem; }
.bp-label { font-weight: 600; font-size: 0.95rem; }
.bp-actions { display: flex; gap: 0.75rem; flex: 0 0 auto; }
.bp-actions .link { background: none; border: 0; padding: 0; color: var(--accent); cursor: pointer; font: inherit; }
.bp-actions .link:hover { text-decoration: underline; }
.bp-value { margin-top: 0.25rem; white-space: pre-wrap; overflow-wrap: anywhere; }
.bp-prov { color: var(--muted); margin: 0.35rem 0 0; }
.bp-row { display: flex; gap: 0.5rem; align-items: flex-start; flex-wrap: wrap; margin-top: 0.4rem; }
.bp-input { flex: 1 1 16rem; min-width: 0; font: inherit; }
textarea.bp-input { resize: vertical; min-height: 2.6rem; }
.bp-row .primary, .bp-row .ghost { flex: 0 0 auto; align-self: flex-start; }
/* House rules (D25): the same see/remove list — learned by chatting, each with an ×. */
.hr-list { display: flex; flex-direction: column; gap: 0.5rem; margin: 0.6rem 0; }
.hr-row { display: flex; align-items: flex-start; gap: 0.5rem; padding: 0.55rem 0.75rem;
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius); }
.hr-text { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.hr-del { flex: 0 0 auto; padding: 0 0.5rem; line-height: 1.4; font-size: 1.2rem; }
.hr-empty { margin: 0.6rem 0; }
/* Email connect: revealed IMAP/SMTP details, stacked label+input fields. */
.email-details { margin-top: 0.8rem; }
/* Mail-server fields collapse into an optional "Advanced" disclosure — known
   providers never need it; only the guessed custom-domain case opens it. */
.email-advanced { margin-top: 0.9rem; }
.email-advanced > summary { cursor: pointer; color: var(--muted); font-size: 0.85rem; }
.email-advanced[open] > summary { margin-bottom: 0.6rem; }
.chat-widget .cw-adv { margin-top: 0.5rem; }
.chat-widget .cw-adv > summary { cursor: pointer; color: var(--muted); font-size: 0.8rem; }
.chat-widget .cw-adv[open] > summary { margin-bottom: 0.45rem; }
/* Ingest-policy picker (owner chooses what mail to handle at connect + on the card). */
#email-policy, .chat-widget .cw-policy, .conn-policy, .conn-triage { font: inherit; font-size: 1rem; padding: 0.6rem 0.7rem; border: 1px solid var(--line); border-radius: 10px; background: var(--card); color: var(--ink); width: 100%; }
.conn-pull { display: flex; flex-direction: column; gap: 0.2rem; margin-top: 0.7rem; padding-top: 0.7rem; border-top: 1px solid var(--line); }
.conn-pull > span { color: var(--muted); font-size: 0.82rem; }
.conn-pull .conn-policy, .conn-pull .conn-triage { max-width: 24rem; }
/* Read-only group chip on a connection card (assigned in the sidebar). */
.badge.group-chip { margin-left: 0.5rem; background: var(--accent-soft); color: var(--accent); border-color: transparent; font-weight: 600; }
.email-details > .field { display: flex; flex-direction: column; gap: 0.2rem; margin: 0.6rem 0; }
.email-details > .field > span { color: var(--muted); font-size: 0.82rem; }
.email-details .field { display: flex; flex-direction: column; gap: 0.2rem; flex: 1; min-width: 12rem; }
.email-details .field.small { flex: 0 0 6rem; min-width: 5rem; }
.email-details .field span { color: var(--muted); font-size: 0.82rem; }
.email-details .field input { width: 100%; }
/* Email signal card: the sanitised message body + attachment chips. */
.email-msg .email-subj { margin: 0.2rem 0 0.5rem; }
.email-body { max-height: 32rem; overflow: auto; }
.email-body img { max-width: 100%; height: auto; }
.email-atts { display: flex; gap: 0.4rem; flex-wrap: wrap; margin-top: 0.6rem; }
.email-atts .att { font-weight: 400; }
/* D28: the inline preview must never push past the card — wide email tables/images
   scroll INSIDE this box, long unbroken tokens wrap. The faithful full view is the
   "View original" action (a roomy isolated page). */
.email-msg { max-width: 100%; min-width: 0; }
.email-body { max-width: 100%; max-height: 32rem; overflow: auto; overflow-wrap: anywhere; word-break: break-word; }
.email-body img, .email-body table, .email-body pre { max-width: 100%; height: auto; }
/* Card shows a plain-text summary (§3h) — wraps, never overflows, never blank when
   there is text. The faithful full message is the "View original" link. */
.email-summary { max-width: 100%; max-height: 20rem; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word; }
.email-summary.muted { color: var(--muted); font-style: italic; }
.email-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.5rem; }
/* In-chat action cards — secure connect widgets in the chat log. */
/* white-space:normal overrides the .msg pre-wrap so the newlines + indentation
   in the widget's multi-line template literals collapse (owner: kill the big
   gaps between the title, hint and button) — the .cw-* margins alone set the
   rhythm. */
.chat-widget { align-self: stretch; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); padding: 0.9rem 1rem; margin: 0.4rem 0; white-space: normal; }
.chat-widget .cw-title { font-weight: 600; margin: 0 0 0.35rem; }
.chat-widget .cw-host { font-weight: 600; margin: 0.1rem 0; overflow-wrap: anywhere; }
.chat-widget .cw-hint { color: var(--muted); font-size: 0.85rem; margin: 0.2rem 0 0.55rem; }
.chat-widget .cw-row { display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: flex-end; margin-top: 0.65rem; }
.chat-widget .cw-row > input { flex: 1; min-width: 8rem; }
.chat-widget .cw-field { display: flex; flex-direction: column; gap: 0.15rem; flex: 1; min-width: 10rem; }
.chat-widget .cw-field.cw-port { flex: 0 0 5rem; min-width: 4rem; }
.chat-widget .cw-field span { color: var(--muted); font-size: 0.78rem; }
.chat-widget .cw-field input { width: 100%; }
.chat-widget .cw-note { font-size: 0.85rem; margin: 0.4rem 0 0; }
.chat-widget .cw-note.error { color: var(--danger); }
.chat-widget .cw-done { font-weight: 600; }
.chat-widget .cw-go.danger { background: var(--danger); color: #fff; border-color: var(--danger); }

/* --- Secure-form affordances (owner request) ------------------------------ */
/* A lock badge marking a form as the safe place to connect / enter a secret.
   Used on the WordPress + e-mail connect cards and the in-chat credential
   widgets. Quiet, reassuring — not an alarm. */
/* The badge is pinned to the card's TOP-RIGHT corner (owner request) so the
   title keeps the full top line. The card is the positioning context. */
.connect-card.secure, .chat-widget.secure { position: relative; }
.secure-badge {
  display: inline-flex; align-items: center; gap: 0.3rem;
  font-size: 0.72rem; font-weight: 600; line-height: 1;
  color: var(--accent); background: var(--accent-soft);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  padding: 0.28rem 0.5rem; border-radius: 999px;
  position: absolute; margin: 0; z-index: 3;
}
/* Chat widgets have interior padding (0.9rem 1rem); the connect forms sit in
   the modal with padding:0, so their badge hugs the very corner beside Back. */
.chat-widget.secure .secure-badge { top: 0.9rem; right: 1rem; }
.connect-card.secure .secure-badge { top: 0; right: 0; }
.secure-badge svg { width: 0.85rem; height: 0.85rem; flex: 0 0 auto; }
/* Reserve room on the title line so it never runs under the pinned badge. */
.chat-widget.secure .cw-title { padding-right: 7.5rem; }
/* The (i) control + its tooltip. The tip opens down-and-left (toward the card
   interior) so it never spills off the right edge; shown on hover and on
   tap/keyboard focus (mobile-friendly, no JS). */
.secure-info { position: relative; display: inline-flex; }
.secure-info-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1rem; height: 1rem; padding: 0; margin: 0;
  background: none; border: none; color: inherit; cursor: pointer;
  border-radius: 999px; opacity: 0.75; box-shadow: none; filter: none;
}
.secure-info-btn:hover { opacity: 1; background: none; filter: none; }
.secure-info-btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
.secure-tip {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 6;
  width: max-content; max-width: 17rem;
  background: var(--card); color: var(--ink);
  border: 1px solid var(--line); border-radius: 8px; box-shadow: var(--shadow);
  padding: 0.5rem 0.6rem; font-size: 0.76rem; font-weight: 500; line-height: 1.35;
  white-space: normal; text-align: left;
  opacity: 0; visibility: hidden; transform: translateY(-2px);
  transition: opacity 0.12s ease, transform 0.12s ease; pointer-events: none;
}
.secure-info:hover .secure-tip, .secure-info:focus-within .secure-tip {
  opacity: 1; visibility: visible; transform: translateY(0);
}
/* A secure card carries a subtle accent edge so it reads as distinct at a
   glance from a plain card. */
.connect-card.secure, .chat-widget.secure {
  border-color: color-mix(in srgb, var(--accent) 35%, var(--line));
}
/* Tighter vertical rhythm inside the secure cards (owner request: less space
   between paragraphs and components). */
.connect-card.secure > h3 { margin-bottom: 0.35rem; }
.connect-card.secure .hint { margin: 0.28rem 0; }
.connect-card.secure .row { margin-top: 0.65rem; }

/* Password input with a show/hide (eye) toggle. The input reserves room on the
   right; the button sits inside the field, vertically centred. Kept flex:1 so it
   slots into the existing .row / .cw-row layouts beside the connect button. */
.pw-field { position: relative; display: flex; flex: 1; min-width: 8rem; }
.pw-field > input { width: 100%; padding-right: 2.6rem; }
.pw-eye {
  position: absolute; top: 50%; right: 0.3rem; transform: translateY(-50%);
  display: inline-flex; align-items: center; justify-content: center;
  width: 2rem; height: 2rem; padding: 0; margin: 0;
  background: none; border: none; border-radius: 8px;
  color: var(--muted); cursor: pointer; filter: none; box-shadow: none;
}
.pw-eye:hover { background: var(--bg); color: var(--ink); filter: none; }
.pw-eye:focus-visible { outline: 2px solid var(--focus); }
.pw-eye svg { width: 1.15rem; height: 1.15rem; }

/* Open-tasks drawer — an overlay panel over the chat (drawer-first). */
.drawer-backdrop { position: fixed; inset: 0; z-index: 60; display: flex; justify-content: flex-end; background: rgba(28, 35, 48, 0.35); }
.drawer-backdrop.hidden { display: none; }
.drawer-panel { width: min(30rem, 100%); height: 100%; background: var(--card); border-left: 1px solid var(--line); box-shadow: var(--shadow); display: flex; flex-direction: column; }
.drawer-head { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; padding: 0.85rem 1rem; border-bottom: 1px solid var(--line); }
.drawer-head h3 { margin: 0; }
.drawer-body { flex: 1; overflow-y: auto; padding: 0.9rem 1rem; }
.drawer-body .empty { color: var(--muted); }
body.drawer-locked { overflow: hidden; }
@media (max-width: 640px) { .drawer-panel { width: 100%; border-left: none; } }

/* Chat history list (the history drawer — architecture §4). */
.hist-empty { color: var(--muted); padding: 0.5rem 0.2rem; }
.hist-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.35rem; }
.hist-item { width: 100%; display: flex; align-items: baseline; justify-content: space-between; gap: 0.75rem;
  padding: 0.6rem 0.7rem; background: transparent; border: 1px solid var(--line); border-radius: 0.5rem;
  color: inherit; text-align: left; cursor: pointer; font: inherit; }
.hist-item:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); border-color: var(--accent); }
.hist-item:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
.hist-title { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hist-when { color: var(--muted); font-size: 0.82em; flex: none; }
/* Task badge on a history row (like a PR badge on a session). Muted when all
   its tasks are decided; accent-tinted when any are still open. */
.hist-tasks { flex: none; font-size: 0.74em; font-weight: 600; padding: 0.05rem 0.4rem; border-radius: 999px;
  background: color-mix(in srgb, var(--muted) 16%, transparent); color: var(--muted); white-space: nowrap; }
.hist-tasks.open { background: color-mix(in srgb, var(--accent) 16%, transparent); color: var(--accent); }

/* In-chat task strip: the tasks this conversation created, above the log. */
/* Minimal #-badges (Claude-Code-style): the label, then compact task-number
   pills that wrap inline. `display: contents` lets the pills wrap alongside
   the label as one flowing row. */
.chat-strip { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem 0.4rem;
  padding: 0.4rem 0.2rem; margin: 0.25rem 0 0.35rem; }
.chat-strip[hidden] { display: none; }
/* Credit indicator (monetization): ONE total number + an info dot, with the
   account block at the sidebar bottom (owner decision: option C). The dot opens
   a small popover with the monthly-vs-top-up breakdown, reset date and spend
   order. The total number warms when the balance runs near-empty, reddens when
   almost gone — a soft, peripheral signal, never a wall. */
.credit-gauge { display: block; position: relative; margin-bottom: 0.2rem; }
.credit-gauge[hidden] { display: none; }
.credit-gauge .cg-face { display: flex; align-items: baseline; gap: 0.3rem; }
.credit-gauge .cg-num { font-size: 0.92rem; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }
.credit-gauge .cg-unit { font-size: 0.8rem; color: var(--muted); font-weight: 500; }
.credit-gauge.warn .cg-num { color: var(--warn-fg); }
.credit-gauge.crit .cg-num { color: var(--danger); }
/* Info dot sits right after "credits" (one unit), not shoved to the sidebar edge.
   An inline SVG "i" (dot + stem, currentColor) centres cleanly where an italic
   text glyph did not. */
.credit-gauge .cg-info { align-self: center; width: 16px; height: 16px; flex: none; padding: 0;
  border-radius: 999px; border: 1px solid var(--line); background: transparent; color: var(--muted);
  cursor: pointer; display: grid; place-items: center; }
.credit-gauge .cg-info svg { width: 9px; height: 9px; display: block; fill: currentColor; }
.credit-gauge .cg-info:hover { border-color: var(--accent); color: var(--accent); }
.credit-gauge .cg-info:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
/* Popover — opens UPWARD by default (the gauge sits at the sidebar bottom). */
.credit-gauge .cg-pop { position: absolute; left: 0; right: 0; bottom: calc(100% + 6px); z-index: 20;
  background: var(--card); border: 1px solid var(--line); border-radius: 10px; padding: 0.6rem 0.7rem;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.28); }
.credit-gauge .cg-pop[hidden] { display: none; }
.credit-gauge .cg-pop-h { font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.09em;
  color: var(--muted); font-weight: 700; margin-bottom: 0.45rem; }
.credit-gauge .cg-pop-r { display: flex; align-items: baseline; justify-content: space-between; gap: 0.6rem; margin: 0.2rem 0; }
.credit-gauge .cg-pop-r[hidden] { display: none; }
.credit-gauge .cg-pop-k { font-size: 0.8rem; color: var(--ink); }
.credit-gauge .cg-pop-s { display: block; font-size: 0.68rem; color: var(--muted); }
.credit-gauge .cg-pop-v { font-size: 0.8rem; font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
.credit-gauge .cg-bar { height: 3px; border-radius: 999px; background: var(--line); overflow: hidden; margin: 0.25rem 0 0.4rem; }
.credit-gauge .cg-fill { display: block; height: 100%; width: 0; border-radius: 999px;
  background: var(--accent); transition: width 0.4s ease; }
.credit-gauge.warn .cg-fill { background: var(--warn-fg); }
.credit-gauge.crit .cg-fill { background: var(--danger); }
.credit-gauge .cg-pop-div { border-top: 1px solid var(--line); margin: 0.5rem 0; }
.credit-gauge .cg-pop-div[hidden] { display: none; }
.credit-gauge .cg-pop-total .cg-pop-k, .credit-gauge .cg-pop-total .cg-pop-v { font-weight: 700; }
.credit-gauge .cg-pop-total[hidden] { display: none; }
.credit-gauge .cg-pop-note { font-size: 0.7rem; color: var(--muted); line-height: 1.4; margin: 0.45rem 0 0; }
.credit-gauge .cg-pop-note[hidden] { display: none; }
/* Settings → Plan & credits (monetization). Hidden in the pilot (the card carries
   [hidden]); shown only when the plan catalog's `billingUi` toggle is on. */
.bill-head { font-weight: 600; margin: 0 0 0.15rem; }
.bill-bal { margin: 0 0 0.6rem; }
.bill-plans { display: flex; flex-direction: column; gap: 0.2rem; margin: 0.3rem 0 0.6rem; }
.bill-row { display: flex; align-items: center; justify-content: space-between; gap: 0.6rem; }
.bill-topup-label { margin: 0.2rem 0; }
.bill-topups { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.bill-btn { font-size: 0.82rem; padding: 0.25rem 0.7rem; border-radius: 999px;
  background: var(--accent-soft); color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 28%, transparent); cursor: pointer; }
.bill-btn:hover { background: color-mix(in srgb, var(--accent) 16%, var(--card)); }
.bill-btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
.bill-issue { margin: 0 0 0.6rem; padding: 0.45rem 0.7rem; border-radius: 8px; font-size: 0.85rem;
  color: var(--warn-fg); background: var(--warn-bg); border: 1px solid var(--warn-line); }
.bill-row + .bill-plans { margin-top: 0.5rem; }
/* The Plan & credits page: summary, plan cards (current + "most common"
   highlighted), monthly/yearly toggle, top-ups. */
#view-plan .bill-summary { background: var(--card); border: 1px solid var(--line); border-radius: 12px;
  padding: 0.9rem 1rem; margin: 0.6rem 0 1rem; }
#view-plan .bill-summary p { margin: 0.1rem 0; }
.bill-sec { font-size: 1rem; margin: 1.1rem 0 0.5rem; }
.bill-sec-row { display: flex; align-items: center; justify-content: space-between; gap: 0.8rem; flex-wrap: wrap; }
.bill-sec-row .bill-sec { margin-bottom: 0.5rem; }
#view-plan .bill-plans { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 0.7rem; }
.bill-plan { position: relative; display: flex; flex-direction: column; gap: 0.15rem; padding: 0.9rem 1rem;
  background: var(--card); border: 1px solid var(--line); border-radius: 12px; }
.bill-plan h3 { margin: 0 0 0.2rem; font-size: 1.05rem; }
.bill-plan p { margin: 0; }
.bill-plan.is-current { border-color: color-mix(in srgb, var(--accent) 55%, var(--line)); }
.bill-plan.is-popular { border-color: var(--accent); }
.bill-price strong { font-size: 1.35rem; }
.bill-plan-action { margin-top: auto; padding-top: 0.7rem; }
.bill-feats { list-style: none; margin: 0.5rem 0 0; padding: 0; font-size: 0.82rem; color: var(--muted); }
.bill-feats li { padding-left: 1.1rem; position: relative; margin: 0.2rem 0; line-height: 1.35; }
.bill-feats li::before { content: "✓"; position: absolute; left: 0; color: var(--accent); }
.bill-see-all { margin-top: 0.6rem; }
.bill-see-all a, .bill-see-all a:visited { color: var(--accent); font-weight: 600; text-decoration: none; }
.bill-see-all a:hover { text-decoration: underline; }
.bill-flash { margin: 0.6rem 0 0; padding: 0.6rem 0.85rem; border-radius: 10px; font-size: 0.9rem;
  border: 1px solid var(--line); background: var(--card); }
.bill-flash.ok { color: var(--ok-fg); background: var(--ok-bg); border-color: transparent; }
.bill-flash.wait { color: var(--muted); }
.bill-flash[hidden] { display: none; }
.bill-badge { position: absolute; top: -0.6rem; right: 0.8rem; font-size: 0.7rem; padding: 0.1rem 0.5rem;
  border-radius: 999px; background: var(--accent); color: #fff; }
.bill-tag { font-size: 0.8rem; color: var(--accent); }
.bill-tag-muted { color: var(--muted); }
.bill-interval { display: inline-flex; border: 1px solid var(--line); border-radius: 999px; overflow: hidden; }
.bill-int { font-size: 0.8rem; padding: 0.25rem 0.75rem; background: transparent; color: var(--muted); border: 0; cursor: pointer; }
.bill-int.active { background: var(--accent-soft); color: var(--accent); }
.bill-int:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
/* Proposed bulk-command chips (owner: engage these from the Assistant). Sit above
   the composer; each sends its command on click. */
.chat-suggest { display: flex; flex-wrap: wrap; gap: 0.4rem; padding: 0.15rem 0.2rem 0.45rem; }
.chat-suggest.hidden { display: none; }
.suggest-chip { font-size: 0.85rem; padding: 0.35rem 0.7rem; border-radius: 999px; background: var(--accent-soft); color: var(--accent); border: 1px solid color-mix(in srgb, var(--accent) 28%, transparent); filter: none; }
.suggest-chip:hover { background: color-mix(in srgb, var(--accent) 16%, var(--card)); filter: none; }
.strip-label { font-size: 0.76em; color: var(--muted); font-weight: 600; }
.strip-list { display: contents; }
.strip-chip { flex: none; display: inline-flex; align-items: center;
  padding: 0.1rem 0.5rem; border-radius: 999px; border: 1px solid var(--line);
  background: var(--card, transparent); color: var(--muted); font: inherit; font-size: 0.78em;
  font-weight: 600; cursor: pointer; }
.strip-chip:hover { border-color: var(--accent); color: var(--accent); }
.strip-chip:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
.strip-chip.open { border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); color: var(--accent); }
.strip-chip.done { opacity: 0.7; }
.strip-chip.err { border-color: var(--warn, #c46200); color: var(--warn, #c46200); }
/* The task THIS chat is about (a Discuss thread) — filled accent so it reads as
   the subject of the conversation, not just one it touched. */
.strip-chip.bound { background: var(--accent-soft); border-color: color-mix(in srgb, var(--accent) 40%, transparent); color: var(--accent); opacity: 1; }
/* Brief highlight when the drawer lands on a task you clicked in the chat strip. */
@keyframes task-flash {
  from { box-shadow: 0 0 0 2px var(--accent), 0 0 0 7px color-mix(in srgb, var(--accent) 22%, transparent); }
  to   { box-shadow: 0 0 0 2px transparent, 0 0 0 7px transparent; }
}
.task-flash { animation: task-flash 1.4s ease-out; }
/* Same brief highlight when the Connections view lands on a channel card opened
   from the sidebar rail (openConnectionCard). Reuses the task-flash keyframe. */
.conn-flash { animation: task-flash 1.4s ease-out; border-radius: var(--radius); }
@media (prefers-reduced-motion: reduce) { .task-flash, .conn-flash { animation: none; } }

/* "Did N things" trace — a collapsed, plain-language record of a turn's steps,
   shown under the answer. Muted so it's a record, not a distraction. */
.turn-steps { margin: 0.05rem 0 0.6rem; }
.turn-steps-toggle { display: inline-flex; align-items: center; gap: 0.35rem; font: inherit; font-size: 0.8em;
  color: var(--muted); background: transparent; border: none; padding: 0.15rem 0; cursor: pointer; }
.turn-steps-toggle:hover { color: var(--accent); }
.turn-steps-toggle:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: 4px; }
.turn-steps-toggle .ts-check { color: var(--accent); font-weight: 700; }
.turn-steps-toggle .ts-chev { transition: transform 0.15s ease; }
.turn-steps.open .turn-steps-toggle .ts-chev { transform: rotate(180deg); }
.turn-steps-list { list-style: none; margin: 0.25rem 0 0; padding: 0 0 0 1.15rem;
  display: flex; flex-direction: column; gap: 0.12rem; }
.turn-steps-list.hidden { display: none; }
.turn-steps-list li { font-size: 0.82em; color: var(--muted); position: relative; }
.turn-steps-list li::before { content: "·"; position: absolute; left: -0.8rem; color: var(--accent); }

/* "from chat #n" back-link badge on a task card — a clickable badge. */
.badge.fromchat { cursor: pointer; border: 1px solid var(--line); background: transparent; color: var(--muted);
  font: inherit; font-size: 0.74em; padding: 0.05rem 0.4rem; }
.badge.fromchat:hover { border-color: var(--accent); color: var(--accent); }
.badge.fromchat:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
.conn-head { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.conn-head strong { flex: 1; overflow-wrap: anywhere; }

/* ---------- Connections view: header row + empty state + add modal ---------- */
.conn-head-row { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; flex-wrap: wrap; margin-bottom: 0.9rem; }
.conn-head-row h2 { margin: 0; }
button.primary { background: var(--accent); color: #fff; border: 1px solid var(--accent); border-radius: 999px; padding: 0.5rem 1rem; font: inherit; font-weight: 600; cursor: pointer; }
button.primary:hover { filter: brightness(0.96); }
.conn-empty { text-align: center; padding: 2.5rem 1.5rem; border: 1px dashed var(--line); border-radius: var(--radius); background: var(--card); }
.conn-empty p { color: var(--muted); margin: 0 auto 1rem; max-width: 30rem; }
/* Add-a-connection modal: centered sheet over a dimmed backdrop. */
.modal-backdrop { position: fixed; inset: 0; z-index: 70; display: flex; align-items: center; justify-content: center; padding: 1rem; background: rgba(28, 35, 48, 0.4); }
.modal-backdrop.hidden { display: none; }
.modal-sheet { width: min(38rem, 100%); max-height: calc(100vh - 2rem); overflow-y: auto; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); }
.modal-head { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; padding: 0.9rem 1.2rem; border-bottom: 1px solid var(--line); }
.modal-head h3 { margin: 0; }
/* The secure badge in a modal head: in the flow (not pinned), pushed right so
   it sits just left of the close button. */
.modal-head h3 { flex: 1 1 auto; }
.modal-secure .secure-badge { position: static; white-space: nowrap; }
/* "How it works" — the long connect explanation, collapsed by default. */
.how-it-works { margin: 0.2rem 0 0.8rem; }
.how-it-works > summary { cursor: pointer; color: var(--accent); font-size: 0.88rem; font-weight: 500; width: max-content; }
.how-it-works[open] > summary { margin-bottom: 0.4rem; }
#conn-modal-body { padding: 1.1rem 1.2rem 1.3rem; }
/* The connect forms inside the modal drop their standalone card chrome. */
#conn-modal-body .connect-card { border: none; box-shadow: none; background: none; padding: 0; margin: 0; }
.conn-back { margin-bottom: 0.6rem; }
#conn-picker .hint { margin-top: 0; }
.chan-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.75rem; margin-top: 0.6rem; }
.chan-tile { display: flex; flex-direction: column; gap: 0.3rem; padding: 1rem 1.1rem; text-align: left; border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); font: inherit; color: var(--ink); cursor: pointer; }
.chan-tile strong { font-size: 0.95rem; }
.chan-tile span { font-size: 0.82rem; color: var(--muted); }
.chan-tile[data-chan]:hover, .chan-tile.ready:hover { border-color: var(--accent); box-shadow: var(--shadow); }
.chan-tile[data-chan] span, .chan-tile.ready span { color: var(--accent); font-weight: 600; }
.chan-tile.soon { opacity: 0.7; cursor: default; }
.chan-tile.soon span { color: var(--muted); font-weight: 600; text-transform: uppercase; font-size: 0.72rem; letter-spacing: 0.04em; }
body.modal-open { overflow: hidden; }
@media (max-width: 640px) {
  .modal-backdrop { padding: 0; align-items: stretch; }
  .modal-sheet { width: 100%; max-height: 100vh; border-radius: 0; }
  .chan-grid { grid-template-columns: 1fr; }
}

/* Connection card (Connections view) — a tidier layout than the old inline
   row: identity on the left (channel badge + clickable host + "connected as"),
   status pill + actions on the right, a stat-chip row, then access/builder
   labels. Card types + fields are catalogued in ../../connection-cards.md. */
.conn-item .conn-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.75rem; flex-wrap: wrap; }
.conn-id { display: flex; align-items: center; gap: 0.65rem; min-width: 0; }
.conn-badge { flex: 0 0 auto; }
.conn-idtext { display: flex; flex-direction: column; min-width: 0; }
.conn-url { font-weight: 700; font-size: 1.02rem; color: var(--ink); text-decoration: none; overflow-wrap: anywhere; }
.conn-url:hover { color: var(--accent); text-decoration: underline; }
.conn-sub { color: var(--muted); font-size: 0.8rem; }
.conn-actions { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
.status-pill {
  display: inline-flex; align-items: center; gap: 0.4rem;
  font-size: 0.78rem; font-weight: 600;
  padding: 0.24rem 0.65rem; border-radius: 999px;
  border: 1px solid var(--line); color: var(--muted); white-space: nowrap;
}
.status-pill.ok { color: var(--ok-fg); border-color: var(--ok-line); background: var(--ok-bg); }
.status-pill.warn { color: var(--warn-fg); border-color: var(--warn-line); background: var(--warn-bg); }
.conn-actions .conn-refresh { border: 1px solid var(--line); }
/* Destructive zone at the bottom of the details body: a divider sets it apart,
   a muted sentence spells out the consequence, the danger button sits right. */
.conn-danger {
  margin-top: 0.9rem; padding-top: 0.9rem; border-top: 1px solid var(--line);
  display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; flex-wrap: wrap;
}
.conn-danger .hint { margin: 0; flex: 1; min-width: 12rem; }
.conn-stats { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.9rem; }
.conn-stats .stat {
  display: inline-flex; align-items: baseline; gap: 0.35rem;
  padding: 0.32rem 0.7rem; border-radius: 10px;
  background: var(--bg); border: 1px solid var(--line);
  font-size: 0.82rem; color: var(--muted);
}
.conn-stats .stat b { font-size: 1rem; color: var(--ink); font-variant-numeric: tabular-nums; }
.conn-meta { display: flex; gap: 0.4rem; flex-wrap: wrap; margin-top: 0.75rem; }
.conn-meta:empty { display: none; }
/* Collapsible task cards (owner request: compact + tidy). The header is two
   tiers — a meta row (id, type, connection, status, the toggle) and the
   title on its own full-width line so it never gets squeezed into a column. */
.card-toggle {
  cursor: pointer;
  user-select: none;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.45rem;
  border-radius: 8px;
  padding: 0.2rem 0.3rem;
  margin: -0.2rem -0.3rem;
  transition: background 0.12s ease;
}
.card-toggle:hover { background: var(--bg); }
.card-toggle:focus-visible { outline: 2px solid var(--focus, var(--accent)); outline-offset: 2px; }
.card-meta { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.card-toggle .card-summary { flex: 0 0 auto; font-weight: 700; font-size: 1rem; line-height: 1.35; overflow-wrap: anywhere; }
/* The "Details / Hide" pill — the clear affordance, pushed to the right. */
.card-more {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--accent);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.2rem 0.6rem;
  white-space: nowrap;
}
.card-toggle:hover .card-more { border-color: var(--accent); }
/* Pointing at a quick action highlights only that button, not the whole
   header (the header click opens the card; the buttons do their own thing). */
.card-toggle:has(.card-quick:hover) { background: transparent; }
.card-toggle:has(.card-quick:hover) .card-more { border-color: var(--line); }
/* Connection "Details" is a neutral, non-urgent affordance — reserve the brand
   red for actions + danger. (Task cards keep the accent toggle as the "your next
   step" cue.) */
.conn-item .card-more { color: var(--muted); }
.conn-item .conn-top.conn-toggle:hover .card-more { border-color: var(--accent); color: var(--ink); }
.card-more .chev { transition: transform 0.15s ease; font-size: 0.9rem; }
.conn-card.open .card-more .chev { transform: rotate(180deg); }
.conn-card:not(.open) .card-more .more-close { display: none; }
.conn-card.open .card-more .more-open { display: none; }
.conn-card .card-body.hidden { display: none; }
/* Connection cards collapse like task cards: the .conn-top row is the toggle
   (its Details pill + status stay inline), the busy rows live in .card-body. */
.conn-item .conn-top.conn-toggle {
  cursor: pointer; border-radius: 8px; padding: 0.2rem 0.3rem; margin: -0.2rem -0.3rem;
  transition: background 0.12s ease;
}
.conn-item .conn-top.conn-toggle:hover { background: var(--bg); }
.conn-item .conn-top.conn-toggle:focus-visible { outline: 2px solid var(--focus, var(--accent)); outline-offset: 2px; }
.conn-actions .card-more { margin-left: 0; }
.conn-item .conn-top.conn-toggle:hover .card-more { border-color: var(--accent); }
/* First stat row in the body shouldn't double-space against the header. */
.conn-card .card-body > .conn-stats:first-child { margin-top: 0.9rem; }
.conn-danger { margin-top: 0.9rem; }
.change-card.open > .card-body { margin-top: 0.75rem; }
.badge {
  font-size: 0.75rem;
  padding: 0.18rem 0.6rem;
  border-radius: 999px;
  background: var(--bg);
  color: var(--muted);
  border: 1px solid var(--line);
  white-space: nowrap;
}
.badge.ok { color: var(--ok-fg); border-color: var(--ok-line); background: var(--ok-bg); }
.badge.warn { color: var(--warn-fg); border-color: var(--warn-line); background: var(--warn-bg); }
.badge.channel { color: var(--chan-fg); border-color: var(--chan-line); background: var(--chan-bg); font-weight: 600; }
.badge.urgent { background: var(--accent); color: #fff; border-color: var(--accent); }
/* A capability that exists but isn't supported yet (e.g. Instagram DMs): greyed,
   dashed edge, help cursor + focusable so the info tooltip reaches keyboard users. */
.badge.muted { color: var(--muted); background: transparent; border-style: dashed; cursor: help; }
.badge.muted:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
.badge .badge-i { margin-left: 0.32rem; opacity: 0.75; font-style: normal; }

.change-card { border-left: 3px solid var(--warn-line); }
.change-card.urgent { border-left: 3px solid var(--accent); }
.change-actions { margin-top: 0.75rem; flex-wrap: wrap; }
/* D19: draft-on-WordPress link on change cards. */
.draft-row {
  align-items: center;
  gap: 0.5rem 0.75rem;
  flex-wrap: wrap;
  margin-top: 0.6rem;
  padding-top: 0.6rem;
  border-top: 1px dashed var(--line);
}
.draft-row .task-draft { border: 1px solid var(--line); }
.draft-hint { margin: 0; flex: 1 1 14rem; }
/* The wp-admin link inherits the UA anchor blue, which is nearly invisible on
   the dark card (owner report). Use the theme's readable link colour. */
.draft-hint a { color: var(--chan-fg); text-decoration: underline; overflow-wrap: anywhere; }
.story-wrap { margin: 0.2rem 0 0.5rem; }
.story-btn { border: 1px solid var(--line); }
.story-panel {
  margin-top: 0.4rem;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 0.5rem 0.75rem;
  font-size: 0.85rem;
}
.story-row { display: flex; gap: 0.7rem; padding: 0.15rem 0; }
.story-when { color: var(--muted); white-space: nowrap; }
.ba { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; }
@media (max-width: 700px) { .ba { grid-template-columns: 1fr; } }
.pane h4 { margin: 0.4rem 0 0.3rem; font-size: 0.85rem; color: var(--muted); }
.pane h4 a { pointer-events: auto; color: var(--chan-fg); font-weight: 400; }
.pane-body {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 0.6rem 0.75rem;
  font-size: 0.88rem;
  line-height: 1.5;
  max-height: 14rem;
  overflow-y: auto;
  white-space: pre-wrap;
  overflow-wrap: anywhere; /* long URLs/tokens wrap instead of forcing width */
}
.pane-body.steps { white-space: pre-wrap; }

/* Rendered page preview — article typography, everything inert */
.pane-body.rendered {
  white-space: normal;
  background: #ffffff; /* previews show the website's content — sites are light */
  color: #1c2330;
  border-color: #e6e8ee;
  padding: 1rem 1.2rem;
  max-height: 24rem;
  font-size: 0.95rem;
  line-height: 1.65;
}
.rendered h1 { font-size: 1.35rem; margin: 0 0 0.6rem; }
.rendered h2 { font-size: 1.15rem; margin: 1rem 0 0.4rem; }
.rendered h3, .rendered h4 { font-size: 1rem; margin: 0.8rem 0 0.3rem; }
.rendered p { margin: 0 0 0.7rem; }
.rendered ul, .rendered ol { margin: 0 0 0.7rem; padding-left: 1.3rem; }
.rendered img { max-width: 100%; height: auto; border-radius: 8px; }
.rendered a { color: #3a5a86; text-decoration: underline; pointer-events: none; } /* literal — pane is always light */
.rendered blockquote { border-left: 3px solid #e6e8ee; margin: 0 0 0.7rem; padding: 0.2rem 0 0.2rem 0.8rem; color: #67707e; }
.rendered table { border-collapse: collapse; margin: 0 0 0.7rem; max-width: 100%; }
.rendered td, .rendered th { border: 1px solid #e6e8ee; padding: 0.3rem 0.5rem; }
.rendered .feat { margin: 0 0 0.9rem; }
.rendered .feat img { max-height: 180px; border-radius: 8px; display: block; }
.rendered .feat figcaption { color: #67707e; font-size: 0.8rem; margin-top: 0.3rem; }

#nav .count {
  display: inline-block;
  min-width: 1.2rem;
  margin-left: auto;
  pointer-events: none;   /* taps pass through to the Tasks button */
  padding: 0 0.3rem;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font-size: 0.72rem;
  line-height: 1.2rem;
  text-align: center;
}

/* ---------- Small screens: sidebar becomes a top bar ---------- */
@media (max-width: 860px) {
  /* A fixed-height app frame: the top bar keeps its natural height and #views
     takes the rest and scrolls INSIDE, so the chat dock stays pinned and the
     page never double-scrolls — no magic-number height calc to drift. */
  .shell { flex-direction: column; height: 100vh; height: 100dvh; overflow: hidden; }
  #side {
    width: 100%;
    height: auto;
    flex: 0 0 auto;
    flex-direction: row;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.45rem 0.6rem;
    padding: 0.6rem 1rem;
    border-right: 0;
    border-bottom: 1px solid var(--line);
  }
  .side-top { flex: 0 0 auto; gap: 0.5rem; }
  /* Nav takes its own full-width row below the brand + account controls, and
     stays on ONE line — it scrolls sideways instead of wrapping to a 2nd row
     (which looked broken and shrank the tap targets). */
  #side nav {
    flex-direction: row; flex-wrap: nowrap; flex: 1 1 100%; order: 3; gap: 0.2rem;
    overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  #side nav::-webkit-scrollbar { display: none; }
  #side nav button { flex: 0 0 auto; white-space: nowrap; padding: 0.45rem 0.6rem; }
  /* Keep business switching and Sign out reachable on mobile (owner: works on
     mobile too). The recent-chats panel collapses on phones — the chat tab
     strip's History button opens the same list. */
  .side-panel { display: none; }
  .session {
    display: flex;
    flex-direction: row;
    align-items: center;
    border-top: 0;
    padding-top: 0;
    margin-top: 0;
  }
  .session #who { display: none; }   /* the email is space the phone can't spare */
  .session button { align-self: center; }
  /* The gauge rides the top bar on mobile (the sidebar becomes it): the face
     ("129.2 credits ⓘ") is already one compact line; the popover opens DOWNWARD
     here (the bar is at the top of the screen) and is width-capped so it doesn't
     span the whole strip. */
  .session .credit-gauge { margin: 0; }
  .session .credit-gauge .cg-pop { top: calc(100% + 6px); bottom: auto; left: 0; right: auto; min-width: 15rem; }
  /* The shell is a full-height column; #views fills the space under the bar
     and scrolls INSIDE, so the chat dock stays put and the page never
     double-scrolls (replaces the fragile fixed-height calc). */
  #views { height: auto; flex: 1 1 auto; min-height: 0; }
}

/* Comfortable side padding on phones — reclaim the wasted margins. */
@media (max-width: 560px) {
  .view { padding: 1.25rem 1rem; }
  #view-chat { padding: 1rem 1rem 0.8rem; }
  .connect-card, .conn-card { padding: 1rem 1.1rem; }
  .card { padding: 1.75rem 1.4rem; }
  .login { padding: 1rem; }
  /* Composer toolbar must stay on one line with Send always visible. The left
     group yields space (and its chip truncates); the effort suffix on the chip
     and the composer tasks pill (redundant with the nav "Tasks 2" badge) hide;
     Send never shrinks. Fixes Send being clipped off the right edge on phones. */
  .composer-bar { gap: 0.4rem; }
  .bar-left { min-width: 0; flex: 1 1 auto; }
  .composer-bar .chip { min-width: 0; }
  .chip #chip-model { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .chip .chip-eff { display: none; }
  #chat-send { flex: 0 0 auto; }
}

/* Task numbers — shared vocabulary between the owner and the assistant */
/* "Chat handle" chip — the #token you type in chat to reference a card
   (task → #id, connection → #host). Click copies it; matches what the "#"
   mention typeahead inserts. */
.chat-handle {
  display: inline-flex; align-items: center; vertical-align: middle;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.72rem; font-weight: 600; line-height: 1;
  color: var(--muted); background: color-mix(in srgb, var(--muted) 12%, transparent);
  border: 1px solid var(--line); border-radius: 6px;
  padding: 0.16rem 0.34rem; cursor: pointer; white-space: nowrap;
  transition: color 0.12s ease, background 0.12s ease, border-color 0.12s ease;
}
.chat-handle:hover { color: var(--ink); border-color: var(--accent); }
.chat-handle:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.chat-handle.copied { color: #fff; background: var(--accent); border-color: var(--accent); }
/* On connection cards the #id chip leads the header row, before the channel badge. */
.conn-id > .chat-handle { flex-shrink: 0; }

.side-top { display: flex; align-items: center; justify-content: space-between; }
.side-top-actions { display: flex; align-items: center; gap: 0.25rem; }
/* Help, theme and settings: three identical square buttons. */
#theme-toggle, #help-seed, #side-settings, #side-contact {
  width: 30px; height: 30px; padding: 0; display: inline-flex; align-items: center; justify-content: center;
  font-size: 1rem; line-height: 1;
}
#help-seed { font-weight: 700; }

/* Chat tabs */
.chat-tabs { display: flex; align-items: center; gap: 0.35rem; flex-wrap: wrap; padding: 0 0 0.5rem; }
.chat-tabs .tab {
  display: inline-flex;
  align-items: center;
  gap: 0.15rem;
  padding: 0.28rem 0.3rem 0.28rem 0.8rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--card);
  color: var(--muted);
  font-size: 0.85rem;
  cursor: pointer;
  user-select: none;
  max-width: 14rem;
}
.chat-tabs .tab-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-tabs .tab.active { color: var(--ink); border-color: var(--focus); font-weight: 600; }
.chat-tabs .tab-x { background: none; border: 0; color: var(--muted); font-size: 0.95rem; padding: 0 0.3rem; line-height: 1; }
.chat-tabs .tab-x:hover { color: var(--accent); background: none; filter: none; }

/* Settings → Language + Appearance (the two option rows share styling). */
.theme-row { flex-wrap: wrap; }
.theme-opt, .lang-opt { border: 1px solid var(--line); }
.theme-opt.active, .lang-opt.active { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); font-weight: 600; }

/* Accent picker (owner request): each option carries a colour swatch and the
   chosen one gets a neutral highlight (not the accent tint — the swatch already
   names the colour, and the active colour is the one being applied everywhere). */
.accent-opt { display: inline-flex; align-items: center; gap: 8px; border: 1px solid var(--line); }
.accent-opt .sw { width: 14px; height: 14px; border-radius: 50%; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12); flex: none; }
.accent-opt[data-accent="red"]    .sw { background: #d64550; }
.accent-opt[data-accent="ocean"]  .sw { background: #2f6fd6; }
.accent-opt[data-accent="forest"] .sw { background: #178a4c; }
.accent-opt[data-accent="violet"] .sw { background: #7c5cd6; }
.accent-opt[data-accent="amber"]  .sw { background: #a76a1e; }
.accent-opt[data-accent="teal"]   .sw { background: #0d8585; }
.accent-opt.active { border-color: var(--ink); color: var(--ink); background: var(--bg); font-weight: 600; }

/* ── "What's possible" — the dummy-proof capability page ──────────────────────
   Owner decision 2026-08-30: two plain sections (providers, then jobs), binary
   available/not-yet, each item answering what / here-or-not / what-for / how.
   Built on the app's own tokens so both themes and the accent picker apply. */
.poss-head { margin-bottom: 4px; }
.poss-intro { color: var(--muted); max-width: 66ch; line-height: 1.55; margin: 6px 0 0; }

.poss-block-head { display: flex; align-items: baseline; gap: 10px; margin: 28px 0 14px; }
.poss-block-head h3 { margin: 0; font-size: 18px; letter-spacing: -0.01em; }
.poss-block-head span { font-size: 12.5px; color: var(--muted); }

/* Section 1 — the provider grid ("does it work with my stuff?") */
.prov-group { margin: 0 0 20px; }
.prov-gh { font-size: 13px; font-weight: 600; color: var(--muted); margin: 0 0 9px; }
.prov-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(158px, 1fr)); gap: 10px; }
.prov-tile {
  position: relative; text-align: left; width: 100%; font: inherit;
  background: var(--card); border: 1px solid var(--line); border-radius: 13px;
  box-shadow: var(--shadow); padding: 13px; min-height: 108px;
  display: flex; flex-direction: column; gap: 10px;
}
button.prov-tile { cursor: pointer; transition: border-color .15s, transform .1s; }
button.prov-tile:hover { border-color: var(--accent); }
button.prov-tile:active { transform: translateY(1px); }
button.prov-tile:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
.prov-tile.off { background: color-mix(in srgb, var(--card), var(--bg) 55%); border-style: dashed; }
.prov-top { display: flex; align-items: center; gap: 9px; }
.prov-logo {
  width: 32px; height: 32px; border-radius: 8px; flex: none; display: grid; place-items: center;
  color: #fff; font-weight: 700; font-size: 15px; line-height: 1;
}
.prov-tile.off .prov-logo { filter: grayscale(1); opacity: .5; }
.prov-name { font-weight: 600; font-size: 14px; line-height: 1.15; color: var(--ink); }
.prov-why { margin: 0; font-size: 12px; color: var(--muted); line-height: 1.4; }
.prov-state { margin-top: auto; display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; }
.prov-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.st-conn { color: var(--ok-fg); } .st-conn .prov-dot { background: var(--ok-dot); }
.st-works { color: var(--accent); } .st-works .prov-dot { background: var(--accent); }
.st-wait { color: var(--warn-fg); } .st-wait .prov-dot { background: var(--warn-fg); }
.prov-tile.connected { border-color: color-mix(in srgb, var(--ok-dot) 45%, var(--line)); }
.prov-badge {
  position: absolute; top: 11px; right: 11px; font-size: 10px; font-weight: 700;
  color: var(--ok-fg); background: var(--ok-bg); border-radius: 999px; padding: 2px 8px;
}

/* the "want this" button — providers and abilities share it */
.poss-want {
  align-self: flex-start; display: inline-flex; align-items: center; gap: 6px;
  font-family: inherit; font-size: 12px; font-weight: 600; color: var(--muted);
  background: var(--card); border: 1px solid var(--line); border-radius: 999px;
  padding: 4px 11px; cursor: pointer; transition: border-color .15s, color .15s, background .15s;
}
.poss-want:hover { border-color: var(--accent); color: var(--accent); }
.poss-want:hover .pw-heart { color: var(--accent); }
.poss-want:disabled { opacity: .55; cursor: default; }
.poss-want.voted { background: var(--accent); color: #fff; border-color: var(--accent); }
.poss-want.voted .pw-heart { color: #fff; }
.pw-heart { font-size: 12px; line-height: 1; color: var(--accent); }
.pw-count { font-variant-numeric: tabular-nums; font-size: 11px; opacity: .85; }
.pw-count.hidden { display: none; }

/* Section 2 — ability cards ("what will it do for me?") */
.abil-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 12px; }
.abil-card {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 16px 17px; display: flex; flex-direction: column; gap: 12px;
}
.abil-card.off { background: color-mix(in srgb, var(--card), var(--bg) 55%); }
.abil-head2 { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.abil-name2 { font-weight: 700; font-size: 16px; line-height: 1.18; color: var(--ink); }
.abil-avail { flex: none; font-size: 11px; font-weight: 700; border-radius: 999px; padding: 3px 10px; white-space: nowrap; }
.abil-avail.yes { color: var(--ok-fg); background: var(--ok-bg); }
.abil-avail.no { color: var(--warn-fg); background: var(--warn-bg); }
.abil-qa { display: flex; flex-direction: column; gap: 9px; }
.abil-q { display: grid; grid-template-columns: 42px 1fr; gap: 10px; align-items: baseline; }
.abil-q .ql { font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); font-weight: 600; }
.abil-q .qv { font-size: 13.5px; color: var(--ink); line-height: 1.45; }
.abil-q .qv.dim { color: var(--muted); }
.abil-say {
  display: inline-flex; background: var(--accent-soft); color: var(--accent);
  border-radius: 9px; padding: 5px 10px; font-size: 13px; line-height: 1.3;
}
.abil-say::before { content: "\201C"; }
.abil-say::after { content: "\201D"; }
.abil-say.ghost { background: transparent; color: var(--muted); padding: 0; }
.abil-foot2 { margin-top: 2px; }
.poss-more { margin: 18px 0 0; font-size: 13px; color: var(--muted); line-height: 1.5; }

@media (max-width: 560px) {
  .abil-grid { grid-template-columns: 1fr; }
  .abil-q { grid-template-columns: 1fr; gap: 2px; }
}

/* ── Per-page search / filter toolbar (Tasks · Channels · What's possible) ─────
   Owner: those pages had "no grouping or filtering, nor search". A search box +
   optional filter chips, on the app tokens. `.list-toolbar.hidden` wins over the
   base flex so show()/.hidden toggling still hides it regardless of rule order. */
.list-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 12px; margin: 0 0 16px; }
.list-toolbar.hidden { display: none; }
.list-search {
  flex: 1 1 220px; min-width: 0; max-width: 420px;
  font: inherit; font-size: 0.9rem; color: var(--ink);
  background: var(--card); border: 1px solid var(--line); border-radius: 999px; padding: 7px 14px;
}
.list-search.solo { max-width: 100%; }
.list-search::placeholder { color: var(--muted); }
.list-search:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; border-color: var(--accent); }
.list-filters { display: flex; flex-wrap: wrap; gap: 6px; }
.list-fchip {
  font: inherit; font-size: 13px; font-weight: 500; color: var(--muted);
  background: var(--card); border: 1px solid var(--line); border-radius: 999px;
  padding: 5px 13px; cursor: pointer; transition: border-color .15s, color .15s, background .15s;
}
.list-fchip:hover { color: var(--ink); border-color: var(--muted); }
.list-fchip:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
.list-fchip.active { background: var(--ink); color: var(--bg); border-color: var(--ink); }

/* Tasks grouped by channel */
.task-group { margin: 0 0 22px; }
.task-group-head {
  display: flex; align-items: baseline; gap: 8px; margin: 0 0 10px;
  font-size: 13px; font-weight: 600; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.04em;
}
.task-group-count { font-variant-numeric: tabular-nums; font-weight: 500; opacity: 0.8; }

/* Analytics consent banner (static/analytics.js) — owner request 2026-08-28.
   Styled here (not inline) because the CSP style-src has no 'unsafe-inline'.
   Buttons reuse the app's button.primary / button.ghost.small base. */
.consent-banner {
  position: fixed;
  left: 1rem;
  right: 1rem;
  bottom: 1rem;
  z-index: 80;
  margin: 0 auto;
  max-width: 40rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1rem;
  padding: 0.9rem 1.1rem;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}
.consent-text {
  flex: 1 1 16rem;
  margin: 0;
  font-size: 0.9rem;
  line-height: 1.4;
  color: var(--ink);
}
.consent-actions {
  display: flex;
  gap: 0.5rem;
  flex: 0 0 auto;
}

/* "Ask me later" close (✕) on the consent banner — owner request 2026-08-28.
   Reserve a clear strip on the right so the icon never overlaps the buttons. */
.consent-banner { padding-right: 2.4rem; }
.consent-close {
  position: absolute;
  top: 0.4rem;
  right: 0.5rem;
  width: 1.6rem;
  height: 1.6rem;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--muted);
  font-size: 0.95rem;
  line-height: 1;
  border-radius: 6px;
  cursor: pointer;
}
.consent-close:hover { color: var(--ink); background: var(--line); }

/* ── Inline task cards in the conversation (T1–T3, chat-task-cards-proposal.md) ── */
.turn-tasks { display: flex; flex-direction: column; gap: 0.5rem; margin: 0.1rem 0 0.75rem; max-width: 40rem; }
.itask { background: var(--card); border: 1px solid var(--line); border-radius: 12px; padding: 0.7rem 0.85rem;
  display: flex; flex-direction: column; gap: 0.45rem; font-size: 0.9rem; }
.itask.loading { color: var(--muted); flex-direction: row; gap: 0.4rem; }
.itask-head { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; font-size: 0.78rem; color: var(--muted); }
.itask-ref { font-weight: 700; color: var(--ink); }
.itask-label { font-weight: 600; }
.itask-host { margin-left: auto; }
.itask-status { border: 1px solid var(--line); border-radius: 99px; padding: 0.05rem 0.5rem; font-size: 0.72rem; }
.itask-status.pending { color: var(--accent); border-color: var(--accent); }
.itask-status.applied, .itask-status.done { color: var(--ok-fg, #1e7f4f); border-color: var(--ok-line, #bfe3d0); }
.itask-status.failed { color: var(--accent); }
.itask-body { display: flex; flex-direction: column; gap: 0.3rem; }
.itask-line { padding: 0.3rem 0.55rem; border-radius: 8px; word-break: break-word; }
.itask-line.old { background: var(--warn-bg, #fdf7e7); color: var(--warn-fg, #9a6700); text-decoration: line-through; }
.itask-line.new { background: var(--ok-bg, #effaf4); color: var(--ok-fg, #1e7f4f); }
.itask-line.muted { color: var(--muted); padding-left: 0; padding-right: 0; }
.itask-actions { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.itask-approve, .itask-approve-all, .itask-yes { background: var(--accent); color: #fff; border: 0; border-radius: 8px;
  padding: 0.4rem 0.85rem; font: inherit; font-size: 0.85rem; font-weight: 600; cursor: pointer; }
.itask-approve:hover, .itask-approve-all:hover, .itask-yes:hover { filter: brightness(1.07); }
.itask-approve:disabled, .itask-yes:disabled { opacity: 0.6; cursor: default; }
.itask-confirm { display: inline-flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.itask-ask { color: var(--muted); font-size: 0.85rem; }
.itask-done { font-weight: 600; color: var(--ok-fg, #1e7f4f); }
.itask-done.err { color: var(--accent); }
.itask-done a { margin-left: 0.2rem; }
.itask.collapsed .itask-body, .itask.collapsed .itask-approve, .itask.collapsed .itask-details { display: none; }
.itask.collapsed { opacity: 0.75; }
.itask-group { display: flex; justify-content: flex-end; align-items: center; gap: 8px; flex-wrap: wrap; }
/* P6: a caution when a turn's drafts came from outside content — flags "Approve all". */
.itask-caution { flex: 1 1 auto; font-size: 12px; color: var(--muted, #6b7280); line-height: 1.35; }
/* B4/#3: the inline reject-reason picker on a change task card. */
.reject-picker { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
.reject-picker .rp-q { font-size: 13px; color: var(--muted, #6b7280); }
.reject-picker select, .reject-picker input { padding: 6px 8px; border: 1px solid var(--border, #d1d5db); border-radius: 8px; font: inherit; }

/* ── The login opener: what is waiting + the offer (tasks first-class) ── */
/* .msg is pre-wrap (typed answers keep their line breaks); the opener is built
   markup, so its source newlines must NOT become blank lines (owner: too much
   space between components). */
.msg.opener { white-space: normal; }
.msg.opener p { margin: 0 0 0.3rem; }
.msg.opener .opener-extra { color: var(--muted); }
.opener-chips { display: flex; flex-wrap: wrap; gap: 0.45rem; margin-top: 0.55rem; }
.opener-chip { font: inherit; font-size: 0.86rem; font-weight: 600; cursor: pointer; border-radius: 99px;
  padding: 0.42rem 0.9rem; border: 1px solid var(--line); background: var(--card); color: var(--ink); }
.opener-chip:hover { border-color: var(--accent); color: var(--accent); }
.opener-chip.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.opener-chip.primary:hover { filter: brightness(1.07); color: #fff; }
.opener-chip.ghost { background: transparent; color: var(--muted); }
.opener-chip:focus-visible { outline: 2px solid var(--focus, var(--accent)); outline-offset: 2px; }

/* What's possible: which plan includes each ability, and a "locked" state for a
   live ability above the tenant's plan (shown only while billing is on). */
.abil-plan-row { margin: 0.15rem 0 0.35rem; }
.abil-plan { display: inline-block; font-size: 0.72rem; padding: 0.08rem 0.5rem; border-radius: 999px;
  border: 1px solid var(--line); color: var(--muted); }
.abil-plan-starter { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 35%, var(--line)); }
.abil-plan-pro { color: var(--accent); border-color: var(--accent); }
.abil-plan-business { color: var(--ink); border-color: var(--ink); }
.abil-avail.lock { color: var(--warn-fg); background: var(--warn-bg); border: 1px solid var(--warn-line); }
.abil-card.locked { opacity: 0.85; }
.abil-card.locked .abil-name2::before { content: "🔒 "; }

/* Top-up cards (same card look as the plans) and the downgrade note. */
#view-plan .bill-topups { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 0.6rem; max-width: 52rem; }
/* A top-up is one compact row: amount (+bonus) and price on the left, Buy on the right. */
.bill-topup-card {
  display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
  background: var(--card); border: 1px solid var(--line); border-radius: 12px; padding: 0.7rem 0.9rem;
}
.bill-topup-card p { margin: 0.15rem 0 0; }
.bill-topup-card h3 { font-size: 1rem; }
.bill-topup-intro { margin: 0 0 0.6rem; }
.bill-down-note { margin-top: 0.35rem; }

/* Task card quick actions (collapsed header) and the leave animation. */
.card-quick { display: inline-flex; gap: 0.15rem; margin-left: auto; }
.card-quick + .card-more { margin-left: 0.35rem; }
.card-quick button.ghost.tiny { font-size: 0.78rem; padding: 0.25rem 0.5rem; border-radius: 999px; color: var(--muted); }
.card-quick button.ghost.tiny:hover { color: var(--ink); background: var(--bg); }
.card-quick .task-remove:hover { color: var(--danger); }
.conn-card.task-leaving { opacity: 0; transform: translateX(12px); transition: opacity 0.2s ease, transform 0.2s ease; }
/* Bottom toast */
.app-toast {
  position: fixed; left: 50%; bottom: 1.5rem; transform: translate(-50%, 1rem); z-index: 80;
  background: var(--ink); color: var(--bg); padding: 0.6rem 1rem; border-radius: 10px;
  font-size: 0.88rem; box-shadow: var(--shadow); opacity: 0; pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s ease; max-width: calc(100vw - 2rem);
}
.app-toast.show { opacity: 1; transform: translate(-50%, 0); }
.app-toast.error { background: var(--danger); color: #fff; }
@media (prefers-reduced-motion: reduce) { .conn-card.task-leaving, .app-toast { transition: none; } }

/* Plan summary: plan + balance on the left, Manage billing on the right. */
#view-plan .bill-summary { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
#view-plan .bill-summary .bill-bal { font-weight: 400; color: var(--muted); margin: 0; }
#view-plan .bill-summary .bill-head { margin: 0; }

/* Chat groups in the Ask Puls panel — the same groups channels use. */
.chat-grp { list-style: none; border: 1px solid transparent; border-radius: 9px; margin: 0 0 0.35rem; padding: 0.1rem; }
.chat-grp.drag-over { border-color: var(--accent); background: var(--accent-soft); }
.chat-grp-head { display: flex; align-items: center; gap: 0.4rem; padding: 0.25rem 0.45rem 0.1rem; }
.chat-grp-head span { flex: 1; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); overflow-wrap: anywhere; }
.chat-grp:last-child .chat-grp-head span { opacity: 0.7; }
.chat-grp-body { list-style: none; margin: 0; padding: 0; }
.chat-grp-head .chat-more { margin-right: 0; }
.chat-grp-head:hover .chat-more { opacity: 1; }
/* Panel heading with its "+ New group" action — the same in all three panels. */
.side-head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem; margin: 0.9rem 0 0.35rem; }
.side-panel > .side-head:first-child { margin-top: 0; }
.side-head h5 { margin: 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.side-head-act {
  background: transparent; border: 0; box-shadow: none; color: var(--accent);
  padding: 0.1rem 0.3rem; font-size: 0.78rem; font-weight: 500; white-space: nowrap;
}
.side-head-act:hover { text-decoration: underline; filter: none; background: transparent; }
.side-chat-row.dragging { opacity: 0.5; }

/* Contact us (the sidebar envelope → #contact-modal; functions/feedback.js). */
.contact-sheet { padding: 0.6rem 1.2rem 1.1rem; }
.contact-sheet .field { display: flex; flex-direction: column; gap: 0.2rem; margin: 0.6rem 0; }
.contact-sheet select,
.contact-sheet textarea { width: 100%; font: inherit; padding: 0.55rem 0.7rem; border: 1px solid var(--line); border-radius: 10px; background: var(--card); color: var(--ink); box-sizing: border-box; }
.contact-sheet textarea { resize: vertical; min-height: 8rem; }
.contact-sheet .hint.error { color: var(--danger); }
.contact-sheet .hint.ok { color: var(--ink); font-weight: 600; }
