/* ============================================================================
   Claude Control Center — light theme
   Calm, minimal, console-style: warm off-white ground, one restrained accent,
   generous spacing, clear hierarchy between sidebar / conversation / composer /
   secondary detail. (A general UX reference only — no external branding assets.)
   ========================================================================== */
/* ---------- Vazirmatn: the Persian face ----------------------------------------------------
   SIL Open Font License 1.1 - the licence travels with the files, in fonts/OFL-Vazirmatn.txt.
   Self-hosted, not pulled from a font CDN: Control Center runs on a local machine with no promise
   of an internet connection, and a Persian summary that quietly drops to Tahoma on an offline
   morning is the very thing this is here to fix. Two subsets of one variable file - Arabic for the
   summaries themselves, Latin for the words, paths and numbers mixed into them - so a sentence does
   not change face halfway along. font-display: swap because the file is a local read: the text is
   never held back waiting for it.
   ------------------------------------------------------------------------------------------- */
@font-face {
  font-family: "Vazirmatn";
  font-style: normal; font-weight: 100 900; font-display: swap;
  src: url("fonts/vazirmatn-arabic.woff2") format("woff2");
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC, U+102E0-102FB, U+10E60-10E7E, U+10EC2-10EC4, U+10EFC-10EFF, U+1EE00-1EE03, U+1EE05-1EE1F, U+1EE21-1EE22, U+1EE24, U+1EE27, U+1EE29-1EE32, U+1EE34-1EE37, U+1EE39, U+1EE3B, U+1EE42, U+1EE47, U+1EE49, U+1EE4B, U+1EE4D-1EE4F, U+1EE51-1EE52, U+1EE54, U+1EE57, U+1EE59, U+1EE5B, U+1EE5D, U+1EE5F, U+1EE61-1EE62, U+1EE64, U+1EE67-1EE6A, U+1EE6C-1EE72, U+1EE74-1EE77, U+1EE79-1EE7C, U+1EE7E, U+1EE80-1EE89, U+1EE8B-1EE9B, U+1EEA1-1EEA3, U+1EEA5-1EEA9, U+1EEAB-1EEBB, U+1EEF0-1EEF1;
}
@font-face {
  font-family: "Vazirmatn";
  font-style: normal; font-weight: 100 900; font-display: swap;
  src: url("fonts/vazirmatn-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  color-scheme: light;

  /* How much room the floating sign-in pill (.auth-bar) takes out of the bottom of the viewport:
     its own `bottom` offset plus its height, plus a little breathing room. The pill is fixed and
     bottom-right, and the composer is sticky to the same corner, so without a shared number the two
     overlap and the pill - being later in the stacking order - swallows the clicks. It covered the
     Send button outright at every width tested (375, 768 and 1440). */
  --auth-bar-clear: 64px;

  /* surfaces */
  --bg: #f7f6f3;          /* app ground — warm off-white            */
  --card: #ffffff;        /* raised surfaces: sidebar, bubbles       */
  --card2: #f2efea;       /* insets: filters, sub-cards, chips       */
  --code-bg: #f6f4f0;     /* code / logs / diff                      */

  /* text */
  --fg: #23201c;          /* primary text — near-black warm          */
  --muted: #6b6459;       /* secondary text / labels                 */

  /* lines & accent */
  --line: #e4dfd6;        /* hairline borders                        */
  --accent: #1d4ed8;      /* calm azure blue — links + primary (AA)  */
  --accent-hover: #1b45be;/* darker azure for hover / active         */
  --accent-weak: #e7eefc; /* accent wash for selected / user bubble  */
  --accent-border: #cbdcfb;/* hairline that pairs with --accent-weak */
  --accent-fg: #ffffff;
  /* The halo drawn just outside a user bubble's edge. A 1px hairline alone is easy to miss once the
     ground behind it is the same card white as a Claude reply, so the hue is given a little width
     without being given a fill: the ring reads at a glance and the text still sits on plain white. */
  --accent-ring: rgba(29, 78, 216, .16);

  /* The QC review session. A second conversation deserves a second identity, so it gets its own hue
     from the same restrained family the rest of the theme uses — a violet that reads clearly against
     the warm ground, next to the azure of a user request and the green of a Claude reply, without
     introducing a new visual language. The primary session's colours are untouched. */
  --qc: #6d28d9;          /* QC labels + badges (AA on --bg and on --qc-weak) */
  --qc-weak: #f2ecfd;     /* QC bubble wash, the counterpart of --accent-weak  */
  --qc-border: #ddd0f7;   /* hairline that pairs with --qc-weak                */
  --qc-ring: rgba(109, 40, 217, .16); /* the QC counterpart of --accent-ring    */

  /* status (AA on light) */
  --add: #1a7f37; --del: #b3261e; --warn: #8a6100; --ok: #1a7f37; --bad: #b3261e;

  --radius: 10px;
  font-synthesis: none;

  /* The interface face, named once so the Persian blocks below can opt back into it for their
     Latin labels. */
  --ui-font: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  /* Persian. Vazirmatn is bundled above, so it is always the one that answers; the rest of the
     stack is what happens if the file ever fails to load. Windows ships no Persian-designed face,
     so that fallback lands on Tahoma - deliberately ahead of Segoe UI, whose Arabic is drawn for
     Arabic and whose ye and kaf read as the wrong letters to a Persian reader. Never a monospace
     fallback: Latin monospace faces carry no Persian glyphs at all, so the text ends up assembled
     from whatever the system substitutes, letter by letter. */
  --fa-font: "Vazirmatn", "Vazir", "Sahel", "Shabnam", "IRANSansX", "IRANSans", "Iranian Sans",
             "Noto Naskh Arabic", "Noto Sans Arabic", Tahoma, "Segoe UI", sans-serif;
}

* { box-sizing: border-box; }
body {
  margin: 0; background: var(--bg); color: var(--fg);
  font: 14px/1.6 var(--ui-font);
  -webkit-font-smoothing: antialiased;
}
h2 { font-size: 15px; margin: 0 0 10px; }
h3 { font-size: 12px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin: 16px 0 6px; }
h4 { font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); margin: 10px 0 4px; }
code { background: var(--card2); padding: 0 4px; border-radius: 4px; font-size: .92em; }
a { color: var(--accent); }
.hidden { display: none !important; }
.fineprint { color: var(--muted); font-size: 12px; }
.empty, .empty-state { color: var(--muted); font-style: italic; }
.empty-state { padding: 40px 30px; text-align: center; }
.pad { padding: 12px; }

button {
  font: inherit; cursor: pointer; border-radius: 8px; border: 1px solid var(--line);
  background: var(--card); color: var(--fg); padding: 7px 13px; font-weight: 600;
  transition: background .12s ease, border-color .12s ease;
}
button:hover { background: var(--card2); }
button.primary { background: var(--accent); color: var(--accent-fg); border-color: var(--accent); font-weight: 700; }
button.primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
button.ghost { background: transparent; }
button.ghost:hover { background: var(--card2); }
button.sm { padding: 5px 10px; font-size: 12px; }
button.xs { padding: 3px 8px; font-size: 11px; }
button:disabled { opacity: .45; cursor: not-allowed; }
button:focus-visible, a:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible,
summary:focus-visible, .sess:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px;
}

input[type=text], input[type=search], input[type=number],
input[type=datetime-local], input[type=date], input[type=time], textarea, select {
  width: 100%; background: var(--card); color: var(--fg); border: 1px solid var(--line);
  border-radius: 8px; padding: 8px 10px; font: inherit;
}
input:focus, select:focus, textarea:focus { border-color: var(--accent); }
textarea { resize: vertical; font-family: ui-monospace, "Cascadia Code", Consolas, monospace; }
label.mini, label.radio { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; }
label.mini input, label.radio input { width: auto; accent-color: var(--accent); }

/* ================= layout ================= */
#app { display: grid; grid-template-columns: 328px 1fr; height: 100vh; }
/* Narrow viewports: the sidebar becomes an off-canvas drawer instead of disappearing, so session
   navigation stays reachable on a phone. Closed, `visibility: hidden` also takes its rows out of
   the tab order, so keyboard focus never lands on an off-screen list. */
.nav-toggle, .nav-scrim { display: none; }
@media (max-width: 860px) {
  #app { grid-template-columns: 1fr; }
  #main { padding-top: 46px; }
  .nav-toggle {
    display: inline-flex; align-items: center; gap: 6px;
    position: fixed; top: 10px; left: 10px; z-index: 30;
    background: var(--card); border: 1px solid var(--line);
  }
  #sidebar {
    position: fixed; top: 0; bottom: 0; left: 0; z-index: 40;
    width: min(88vw, 328px);
    transform: translateX(-100%); visibility: hidden;
    /* visibility flips as a step, never interpolated: hiding is deferred until the slide-out has
       finished, and showing is immediate. So the drawer is never left invisible when the slide is
       skipped or frozen (reduced motion, a throttled tab, an engine that will not animate a box
       that is currently hidden). */
    transition: transform .18s ease, visibility 0s linear .18s;
  }
  #app.nav-open #sidebar {
    transform: none; visibility: visible;
    transition: transform .18s ease, visibility 0s;
  }
  #app.nav-open .nav-scrim {
    display: block; position: fixed; inset: 0; z-index: 35;
    background: var(--fg); opacity: .28;
  }
}
@media (max-width: 860px) and (prefers-reduced-motion: reduce) {
  #sidebar { transition: none; }
}

/* ---- sidebar ---- */
#sidebar { border-right: 1px solid var(--line); display: flex; flex-direction: column; min-height: 0; background: var(--card); }
.side-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 16px; border-bottom: 1px solid var(--line); }
.side-head strong { font-size: 14px; letter-spacing: -.01em; }
.side-filters { padding: 12px 16px; display: flex; flex-direction: column; gap: 8px; border-bottom: 1px solid var(--line); }
.side-filters .row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.side-filters select, .side-filters input { padding: 6px 8px; font-size: 12px; }
/* session navigation: a "Recent" slice + a project-grouped, progressively loaded tree */
#sessionNav { flex: 1; overflow-y: auto; min-height: 0; padding: 4px 0 14px; }
#sessionNav:focus-visible { outline: none; }
.nav-section { border-bottom: 1px solid var(--line); padding: 6px 0 10px; }
.nav-section:last-of-type { border-bottom: 0; }
.nav-h { font-size: 11px; text-transform: uppercase; letter-spacing: .07em; color: var(--muted); margin: 8px 0 4px; padding: 0 16px; }
.nav-status { padding: 12px 16px; font-size: 12px; color: var(--muted); margin: 0; }
.nav-status.subtle { text-align: center; font-style: italic; opacity: .75; }
.nav-status.nav-error { color: var(--bad); display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

.recent-list, .proj-sessions { list-style: none; margin: 0; padding: 0; }

.sess { display: flex; flex-direction: column; gap: 3px; width: 100%; text-align: left; border: 0; border-radius: 0; background: transparent; padding: 9px 16px; font-weight: 400; }
.sess:hover { background: var(--card2); }
.sess.active { background: var(--accent-weak); box-shadow: inset 3px 0 0 var(--accent); }
.sess-l1 { display: flex; align-items: center; gap: 6px; }
/* nowrap: these are compact fixed-size chips plus one elastic name. Wrapping made the row's height
   jump between 18px and 36px depending on label lengths, which is what made the list look ragged.
   Long project names now truncate instead (see .sess-proj). */
.sess-l2 { display: flex; align-items: center; gap: 6px; flex-wrap: nowrap; }
.sess-title { font-weight: 650; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
.dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); flex: none; }
/* The one elastic item in the meta row: shrink-only (never grow, or it would swallow the free
   space that .sess-time's auto margin needs), and truncate rather than push the row to two lines. */
.sess-proj { font-size: 11px; color: var(--muted);
             flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sess-preview { font-size: 12px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Live one-line status on an in-flight sidebar row. Same muted weight as the preview it replaces —
   the state badge beside it already carries the colour, so this stays quiet. */
.sess-live {
  font-size: 12px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  display: flex; align-items: center; gap: 6px;
}
.sess-live::before {
  content: ""; flex: none; width: 5px; height: 5px; border-radius: 50%;
  background: var(--accent); opacity: .75;
}
/* Pushed to the right edge so timestamps form a column down the list. Without this it simply
   follows the badges, whose widths vary with their labels ("failed" 38px vs "completed" 62px), so
   every row started its timestamp at a different x. */
.sess-time { font-size: 11px; margin-left: auto; padding-left: 8px; flex: none; white-space: nowrap; }
/* The badges keep their intrinsic size; only the name gives way. */
.sess-l2 .badge2 { flex: none; }
.sess.group { padding-left: 30px; }
.sess.group .sess-title { font-weight: 550; font-size: 12.5px; }

.proj-group + .proj-group { margin-top: 1px; }
/* The project row is a toggle plus an icon-only add button, side by side. */
.proj-h { margin: 0; padding: 0; font-size: inherit; text-transform: none; letter-spacing: 0; color: inherit;
          display: flex; align-items: center; }
.proj-h:hover { background: var(--card2); }
.proj-h:hover .proj-toggle { background: transparent; }
.proj-toggle { display: flex; align-items: center; gap: 7px; flex: 1; min-width: 0; text-align: left; border: 0; border-radius: 0; background: transparent; padding: 7px 16px; font-size: 12px; font-weight: 700; color: var(--fg); }
.proj-toggle:hover { background: var(--card2); }
.proj-caret { width: 0; height: 0; border-left: 5px solid currentColor; border-top: 4px solid transparent; border-bottom: 4px solid transparent; flex: none; opacity: .55; transition: transform .12s ease; }
.proj-toggle[aria-expanded="true"] .proj-caret { transform: rotate(90deg); }
.proj-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.proj-count { font-size: 10px; font-weight: 700; color: var(--muted); background: var(--card2); border: 1px solid var(--line); border-radius: 9px; padding: 0 6px; min-width: 18px; text-align: center; }

.nav-more { margin: 10px 16px 2px; width: calc(100% - 32px); }
/* Two buttons and the pool counter never fit one 328px sidebar row: space-between kept the
   three apart by squeezing the buttons until their labels broke over two lines. The buttons
   now keep their own width side by side and the counter takes the line beneath them. */
.side-foot { padding: 12px 16px; border-top: 1px solid var(--line); display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.side-foot > button { flex: none; white-space: nowrap; }
.side-foot #poolMini { flex-basis: 100%; }
.side-foot #poolMini:empty { display: none; }

/* ---- main ---- */
#main { overflow-y: auto; min-height: 0; }
/* Every view inside #main shares one column, so switching between them cannot shift the
   content's width, padding or centre line. */
#conversation, #dashboard, #sessionsPanel { max-width: 860px; margin: 0 auto; padding: 28px 32px 96px; }
.pane-head { display: flex; align-items: center; justify-content: space-between; }

/* ---- session lifecycle panel ---- */
/* Named -form, not .sess-new: that name is also a sidebar BADGE modifier (see .sess-new further
   down), and this block's `margin` was landing on the badge — pushing it 5px below the status badge
   beside it and inflating the sidebar's meta row from 18px to 36px (14 + 18 + 4). */
.sess-new-form { display: grid; grid-template-columns: 2fr 1fr auto; gap: 10px; align-items: end; margin: 14px 0 4px; }
.sess-new-form label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; font-weight: 600; color: var(--muted); }
.sess-cards { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; }
.sess-card { border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); padding: 12px 14px; }
.sess-card.is-active { border-color: var(--accent-border); background: var(--accent-weak); }
.sess-card.is-terminal { opacity: .72; }
.sess-card-h { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sess-card-t { font-weight: 650; font-size: 13px; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sess-card-meta { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 5px; }
.sess-card-key { font-family: ui-monospace, Consolas, monospace; font-size: 11px; color: var(--muted); }
.sess-card-acts { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 9px; }
.sess-note { font-size: 12px; color: var(--warn); margin-top: 6px; }
.life-open { background: var(--accent-weak); color: var(--accent); }
.life-closed { background: var(--card2); color: var(--muted); }
.life-abandoned { background: #f7dedb; color: var(--del); }
@media (max-width: 620px) { .sess-new { grid-template-columns: 1fr; } }


#convHead { display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap; padding-bottom: 16px; border-bottom: 1px solid var(--line); margin-bottom: 20px; }
.ch-title { flex: 1; min-width: 200px; display: flex; align-items: center; gap: 8px; }
.title-input { font-size: 19px; font-weight: 700; border: 1px solid transparent; background: transparent; padding: 4px 6px; letter-spacing: -.01em; }
.title-input:hover, .title-input:focus { border-color: var(--line); background: var(--card); }
.ch-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.archived-tag { font-size: 11px; color: var(--warn); border: 1px solid var(--warn); border-radius: 4px; padding: 0 5px; }

/* live-update connection indicator — a quiet status pill, no animation / spinner */
.conn { display: inline-flex; align-items: center; gap: 6px; font-size: 10px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .05em; color: var(--muted);
  border: 1px solid var(--line); border-radius: 5px; padding: 1px 7px; background: var(--card2); }
.conn::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--muted); flex: none; }
.conn.idle { display: none; }
.conn.live { color: var(--ok); } .conn.live::before { background: var(--ok); }
.conn.connecting { color: var(--muted); }
.conn.reconnecting, .conn.polling { color: var(--warn); }
.conn.reconnecting::before, .conn.polling::before { background: var(--warn); }
.conn.offline { color: var(--bad); } .conn.offline::before { background: var(--bad); }

/* ================= conversation timeline ================= */
.timeline { display: flex; flex-direction: column; gap: 22px; margin-bottom: 22px; }
.run-divider { display: flex; align-items: center; gap: 8px; padding: 10px 0 2px; border-top: 1px dashed var(--line); margin-top: 8px; flex-wrap: wrap; }
.run-divider .rd-cancel { margin-left: auto; }
.turn { display: flex; flex-direction: column; gap: 6px; }
.turn-meta { display: flex; align-items: center; gap: 8px; font-size: 11px; }
.src-label { font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
.turn-user .src-label { color: var(--accent); }
.turn-claude .src-label { color: var(--ok); }
.bubble { background: var(--card); border: 1px solid var(--line); border-radius: 12px; padding: 14px 16px; box-shadow: 0 1px 2px rgba(35, 32, 28, .04); }
/* A user turn is marked by its edge, not by a filled ground: the wash tinted long pasted prompts
   and their code blocks along with them. Same card surface as a Claude reply, azure edge — the
   hairline carries the hue and the ring outside it gives that hue enough width to be seen. */
.turn-user .bubble {
  background: var(--card); border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-ring), 0 1px 2px rgba(35, 32, 28, .04);
}
.turn-user .prompt { white-space: pre-wrap; font-family: ui-monospace, Consolas, monospace; font-size: 13px; }
.turn-system .bubble.sys { background: var(--card2); font-size: 12px; color: var(--muted); box-shadow: none; }
.bubble p { margin: 0 0 8px; } .bubble p:last-child { margin: 0; }
.bubble pre { background: var(--code-bg); border: 1px solid var(--line); border-radius: 8px; padding: 10px; overflow: auto; }
.bubble code { background: var(--code-bg); }
.bubble h3, .bubble h4, .bubble h5, .bubble h6 { color: var(--fg); text-transform: none; letter-spacing: 0; margin: 12px 0 6px; }
.bubble ul { margin: 6px 0; padding-left: 20px; }
.ts { font-size: 10px; font-weight: 700; border-radius: 4px; padding: 1px 6px; text-transform: uppercase; letter-spacing: .04em; }
.ts-run { background: #eae2f3; color: #5b3ea6; }
.ts-final { background: #dff3e4; color: var(--ok); }
.ts-error { background: #f7dedb; color: var(--bad); }
.ts-stop { background: #f2e6e4; color: var(--bad); }
.ts-wait { background: #f6eccf; color: var(--warn); }
.recon { font-size: 10px; color: var(--warn); border: 1px solid var(--warn); border-radius: 4px; padding: 0 4px; }

/* ================= composer ================= */
.composer { background: var(--card); border: 1px solid var(--line); border-radius: 14px; padding: 16px; display: flex; flex-direction: column; gap: 12px; position: sticky; bottom: 14px; box-shadow: 0 4px 16px rgba(35, 32, 28, .07); }
/* Clear of the floating .auth-bar pill; see --auth-bar-clear. */
.composer { bottom: var(--auth-bar-clear); }
.comp-row { display: flex; gap: 12px; flex-wrap: wrap; }
.comp-row > label { flex: 1; min-width: 200px; font-size: 12px; font-weight: 600; color: var(--muted); display: flex; flex-direction: column; gap: 4px; }
.seg { flex-direction: row !important; align-items: center; gap: 14px !important; flex-wrap: wrap; }
.seg-label { font-size: 12px; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; }
.comp-send { align-items: center; }
#conflictBox.conflict { background: #f7ecd6; border: 1px solid var(--warn); border-radius: 8px; padding: 10px; font-size: 13px; }
.conf-actions { display: flex; align-items: center; gap: 10px; margin-top: 8px; flex-wrap: wrap; }

/* ================= technical activity / details (secondary) ================= */
.tech { margin-top: 14px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); }
.tech > summary { padding: 12px 16px; cursor: pointer; font-weight: 600; font-size: 12px; color: var(--muted); list-style-position: inside; }
.tech > summary:hover { color: var(--fg); }
.tech[open] > summary { border-bottom: 1px solid var(--line); color: var(--fg); }
.tech > *:not(summary) { margin: 12px 16px; }
.card.sub { background: var(--card2); border: 1px solid var(--line); border-radius: 8px; padding: 14px; }
.tabs { display: flex; gap: 6px; flex-wrap: wrap; }
.tabs button { padding: 6px 12px; }
.tabs button.active { background: var(--accent); color: var(--accent-fg); border-color: var(--accent); }
.filterRow { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; }
.filterRow label { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); font-weight: 600; }
.filterRow select { width: auto; padding: 4px 8px; }
/* --- technical activity feed: compact, semantically grouped entries --- */
.tech-feed { background: var(--code-bg); border: 1px solid var(--line); border-radius: 8px; padding: 6px; max-height: 360px; overflow: auto; display: flex; flex-direction: column; gap: 4px; }
.tech-feed:empty::before { content: "No activity in this view yet."; color: var(--muted); font-style: italic; font-size: 12px; padding: 8px; }

.te { display: grid; grid-template-columns: 62px 1fr; gap: 8px; padding: 6px 8px; border-radius: 6px; background: var(--card); border: 1px solid var(--line); }
.te.sev-warning { border-color: #e6d6ad; }
.te.sev-error { border-color: #e6c4c0; }
.te-rail { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; padding-top: 2px; }
.te-time { font-family: ui-monospace, Consolas, monospace; font-size: 10px; color: var(--muted); letter-spacing: -.02em; }
.te-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--line); flex: none; }
.te.sev-warning .te-dot { background: var(--warn); }
.te.sev-error .te-dot { background: var(--bad); }
.te-body { min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.te-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.te-kind { flex: none; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); background: var(--card2); border: 1px solid var(--line); border-radius: 4px; padding: 0 5px; }
.te-kind.k-error { color: var(--bad); background: #f7dedb; border-color: #e6c4c0; }
.te-kind.k-warning { color: var(--warn); background: #f6eccf; border-color: #e6d6ad; }
.te-kind.k-command, .te-kind.k-tool { color: #5b3ea6; background: #eae2f3; border-color: #d8c9ec; }
.te-kind.k-result { color: var(--ok); background: #dff3e4; border-color: #bfe4c9; }
.te-kind.k-session, .te-kind.k-context { color: var(--accent); background: var(--accent-weak); border-color: var(--accent-border); }
.te-title { font-size: 12.5px; color: var(--fg); word-break: break-word; }
.te-title.mono { font-family: ui-monospace, Consolas, monospace; font-size: 12px; }
.te-badge { flex: none; font-size: 10px; font-weight: 700; border-radius: 4px; padding: 0 5px; background: var(--card2); color: var(--muted); border: 1px solid var(--line); }
.te-badge.b-ok { background: #dff3e4; color: var(--ok); border-color: #bfe4c9; }
.te-badge.b-bad { background: #f7dedb; color: var(--bad); border-color: #e6c4c0; }
.te-badge.b-warn { background: #f6eccf; color: var(--warn); border-color: #e6d6ad; }
.te-sub { font-size: 11px; color: var(--muted); }
.te-text { font-size: 12px; color: var(--fg); white-space: pre-wrap; word-break: break-word; }
.te-text.pre { font-family: ui-monospace, Consolas, monospace; font-size: 11.5px; background: var(--code-bg); border: 1px solid var(--line); border-radius: 6px; padding: 6px 8px; max-height: 180px; overflow: auto; }
.te-data { font-size: 11.5px; }
.te-data > summary { cursor: pointer; color: var(--accent); font-size: 11px; font-weight: 600; }
.tech-json, .tech-pre { margin: 4px 0 0; background: var(--code-bg); border: 1px solid var(--line); border-radius: 6px; padding: 6px 8px; font-family: ui-monospace, Consolas, monospace; font-size: 11px; line-height: 1.5; white-space: pre-wrap; word-break: break-word; max-height: 240px; overflow: auto; }
.tech-json .j-key { color: #7a4fb0; }
.tech-json .j-str { color: var(--add); }
.tech-json .j-num { color: var(--accent); }
.tech-json .j-lit { color: var(--warn); }
.te-raw { margin-top: 2px; }
.te-raw > summary { cursor: pointer; color: var(--muted); font-size: 10px; text-transform: uppercase; letter-spacing: .06em; }
.te-raw > summary:hover { color: var(--fg); }

/* raw-console fallback (the "Raw console" checkbox) */
.tech-feed .tl { font-family: ui-monospace, Consolas, monospace; font-size: 12px; white-space: pre-wrap; word-break: break-word; padding: 1px 4px; }
.tech-feed .tl-time { color: var(--muted); }
.tech-feed .tl-cat { color: #7a4fb0; margin: 0 4px; }
.tech-feed .tl.sev-warning { color: var(--warn); }
.tech-feed .tl.sev-error { color: var(--del); }

/* ================= dashboard task cards ================= */
.task-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; }
.task-card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); padding: 14px; display: flex; flex-direction: column; gap: 6px; box-shadow: 0 1px 2px rgba(35, 32, 28, .04); }
.task-card.a { box-shadow: inset 3px 0 0 var(--ok); }
.task-card.q { box-shadow: inset 3px 0 0 var(--warn); opacity: .92; }
.tc-title { font-weight: 700; font-size: 13px; }
.tc-meta { display: flex; gap: 5px; flex-wrap: wrap; align-items: center; }
.tc-proj { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tc-q { color: var(--warn); }
.tc-foot { display: flex; align-items: center; justify-content: space-between; margin-top: 4px; }
.tc-btns { display: flex; gap: 6px; }
.sched-prompt {
  margin: 0; background: var(--code-bg); border: 1px solid var(--line); border-radius: 6px;
  padding: 8px; font-size: 12.5px; white-space: pre-wrap; overflow-wrap: anywhere;
  max-height: 220px; overflow: auto;
}

/* ================= misc reused ================= */
.chip { display: inline-block; background: var(--card2); border: 1px solid var(--line); border-radius: 5px; padding: 1px 7px; font-size: 11px; }
.badge2 { display: inline-block; border-radius: 5px; padding: 2px 8px; font-size: 12px; font-weight: 700; }
.badge2.xs { font-size: 10px; padding: 1px 6px; }
.t-running { background: #dff3e4; color: var(--ok); }
.t-waiting { background: #f6eccf; color: var(--warn); }
.t-idle { background: #ece9e3; color: var(--muted); }
.t-queued { background: #eae2f3; color: #5b3ea6; }
.t-completed { background: #dff3e4; color: var(--ok); }
.t-failed, .t-stopped { background: #f7dedb; color: var(--bad); }
.t-unknown { background: #ece9e3; color: var(--fg); }
.sess-new { background: #eae2f3; color: #5b3ea6; }
.sess-continued { background: #dff3e4; color: var(--ok); }
.sess-unavailable { background: #ece9e3; color: var(--fg); }
.ctx-verified { background: #dff3e4; color: var(--ok); }
.ctx-forwarded { background: #eae2f3; color: #5b3ea6; }
.ctx-none { background: #ece9e3; color: var(--muted); }
.ctx-unknown { background: #f6eccf; color: var(--warn); }
.ctx-summary { margin: 8px 0; }
.ctx-facts { list-style: none; margin: 6px 0 0; padding: 0; }
.ctx-facts li { display: flex; justify-content: space-between; gap: 12px; padding: 5px 0; border-bottom: 1px solid var(--line); font-size: 13px; }
.ctx-facts li:last-child { border-bottom: 0; }
.ctx-facts .yes { color: var(--ok); font-weight: 700; } .ctx-facts .no { color: var(--muted); font-weight: 700; } .ctx-facts .unk { color: var(--warn); font-weight: 700; }
.meter { background: var(--code-bg); border: 1px solid var(--line); border-radius: 6px; height: 16px; overflow: hidden; }
.meter-fill { height: 100%; background: linear-gradient(90deg, var(--accent), var(--warn)); }
.meter-legend { display: flex; gap: 6px 16px; flex-wrap: wrap; margin-top: 8px; font-size: 12px; }
.est-tag { background: #f6eccf; color: var(--warn); border-radius: 4px; padding: 0 6px; font-weight: 700; font-size: 11px; margin-right: 6px; }
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
/* Grid items default to min-width:auto, so one unbreakable monospace path inside a
   column pushes that track past the card and clips its neighbour. */
.grid2 > * { min-width: 0; }
@media (max-width: 620px) { .grid2 { grid-template-columns: 1fr; } }
.statusList, .tests, .review { background: var(--code-bg); border: 1px solid var(--line); border-radius: 8px; padding: 10px; font-size: 12px; }
.statusList .f, .tests .cmd { font-family: ui-monospace, Consolas, monospace; }
/* A git-status line keeps its fixed 4-character status column; a path too long for the
   box wraps underneath with a hanging indent rather than overflowing the card. */
.statusList { overflow-x: auto; }
.statusList .f { white-space: pre-wrap; word-break: break-all; padding-left: 4ch; text-indent: -4ch; }
.badge { display: inline-block; min-width: 18px; text-align: center; border-radius: 4px; padding: 0 5px; margin-right: 6px; font-size: 11px; font-weight: 700; }
.b-added { background: #dff3e4; color: var(--add); }
.b-modified { background: #eae2f3; color: #5b3ea6; }
.b-deleted { background: #f7dedb; color: var(--del); }
.b-renamed { background: #f6eccf; color: var(--warn); }
.b-untracked { background: #ece9e3; color: var(--muted); }
.pre-flag { color: var(--warn); font-size: 11px; }
.warn { border: 1px solid var(--warn); border-radius: 8px; padding: 10px; color: var(--warn); margin-bottom: 10px; }
.difftabs { display: flex; gap: 6px; margin: 8px 0; }
.difftabs button { padding: 4px 10px; font-size: 12px; }
.difftabs button.active { background: var(--accent); color: var(--accent-fg); border-color: var(--accent); }
.diff { background: var(--code-bg); border: 1px solid var(--line); border-radius: 8px; padding: 10px; max-height: 420px; overflow: auto; font-family: ui-monospace, Consolas, monospace; font-size: 12px; white-space: pre; margin: 0; }
.diff .add { color: var(--add); } .diff .del { color: var(--del); } .diff .hunk { color: var(--accent); } .diff .meta { color: var(--muted); }
.tests .cmd { font-weight: 700; margin-top: 8px; } .tests .cmd.err { color: var(--del); }
.tests pre, .review pre { background: var(--code-bg); border: 1px solid var(--line); border-radius: 6px; padding: 8px; white-space: pre-wrap; max-height: 220px; overflow: auto; }
.rc-link { background: var(--card2); border: 1px solid var(--accent); border-radius: 8px; padding: 10px; margin-top: 8px; }
.rc-link a { word-break: break-all; font-weight: 700; }

/* ================= rendered Markdown (Claude-authored text) =================
   Scope: `.md` — the Claude conversation bubble (.bubble.md) and the
   user-facing Claude-text entries in Technical activity (.te-md.md). A calm,
   document-like treatment that matches the light theme: comfortable vertical
   rhythm, readable tables with horizontal overflow, restrained code blocks,
   and a clear heading hierarchy. Every tag here is one the allow-list renderer
   in markdown.js can emit — nothing else is styled because nothing else is
   produced. */
.md { line-height: 1.62; overflow-wrap: anywhere; }
.md > :first-child { margin-top: 0; }
.md > :last-child { margin-bottom: 0; }

.md p { margin: 0 0 10px; }
.md h1, .md h2, .md h3, .md h4, .md h5, .md h6 {
  color: var(--fg); font-weight: 700; line-height: 1.3;
  text-transform: none; letter-spacing: -.01em; margin: 18px 0 8px;
}
.md h1 { font-size: 1.32em; padding-bottom: .24em; border-bottom: 1px solid var(--line); }
.md h2 { font-size: 1.18em; padding-bottom: .2em; border-bottom: 1px solid var(--line); }
.md h3 { font-size: 1.06em; }
.md h4 { font-size: .96em; }
.md h5, .md h6 { font-size: .9em; color: var(--muted); }

.md ul, .md ol { margin: 8px 0; padding-left: 24px; }
.md li { margin: 3px 0; }
.md li > ul, .md li > ol { margin: 3px 0; }
.md li > p { margin: 0 0 6px; }

.md a { color: var(--accent); text-decoration: none; border-bottom: 1px solid var(--accent-border); }
.md a:hover { color: var(--accent-hover); border-bottom-color: var(--accent-hover); }

.md blockquote {
  margin: 10px 0; padding: 2px 14px; color: var(--muted);
  border-left: 3px solid var(--accent-border); background: var(--card2);
  border-radius: 0 6px 6px 0;
}
.md blockquote > :first-child { margin-top: 0; }
.md blockquote > :last-child { margin-bottom: 0; }

.md hr { border: 0; border-top: 1px solid var(--line); margin: 18px 0; }

/* inline code */
.md code {
  background: var(--card2); border: 1px solid var(--line); border-radius: 4px;
  padding: .06em .35em; font-size: .88em;
  font-family: ui-monospace, "Cascadia Code", Consolas, monospace;
}
/* fenced code block — restrained: one surface, quiet border, scrolls on overflow */
.md pre {
  margin: 10px 0; background: var(--code-bg); border: 1px solid var(--line);
  border-radius: 8px; padding: 12px 14px; overflow-x: auto;
  font-size: 12.5px; line-height: 1.5;
}
.md pre code {
  background: none; border: 0; padding: 0; font-size: inherit; white-space: pre;
}

/* tables — readable, with responsive horizontal overflow via .md-table-wrap */
.md-table-wrap { margin: 12px 0; max-width: 100%; overflow-x: auto; border: 1px solid var(--line); border-radius: 8px; }
.md-table-wrap > table { margin: 0; border: 0; }
.md table { border-collapse: collapse; width: 100%; font-size: .95em; }
.md th, .md td { padding: 7px 12px; border: 1px solid var(--line); text-align: left; vertical-align: top; }
.md thead th { background: var(--card2); font-weight: 700; }
.md tbody tr:nth-child(even) { background: color-mix(in srgb, var(--card2) 45%, transparent); }

/* Technical-activity variant: same rules, tightened for the denser feed */
.te-md.md { font-size: 12px; line-height: 1.55; color: var(--fg); }
.te-md.md pre { font-size: 11.5px; padding: 8px 10px; max-height: 260px; overflow: auto; }
.te-md.md h1, .te-md.md h2 { border-bottom: 0; }
.te-md-wrap { font-size: 11.5px; }
.te-md-wrap > summary { cursor: pointer; color: var(--accent); font-size: 11px; font-weight: 600; }
.te-md-wrap > .te-md { margin-top: 6px; }

/* ============================================================================
   Run outcome — summary cards + the changed-files disclosure list
   Same light palette as the rest of the app: warm ground, hairline borders, one
   accent. The panel sits between the conversation and the composer, so it reads
   as a companion to Claude's Markdown answer rather than a separate screen.
   ========================================================================== */

/* Text for assistive tech only — kind names, "added line", per-file line counts. */
.sr-only {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* Now a repeating child of .timeline, one per settled run, right after that run's own reply — see
   outcomeEl in app.js. .timeline's own flex `gap` spaces it from the turn before and the turn after,
   so this carries no vertical margin of its own (it used to, back when it was a single fixed panel
   below the timeline rather than a flex item inside it). */
.outcome {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--card); padding: 16px 18px 6px;
  box-shadow: 0 1px 2px rgba(35, 32, 28, .04);
}
.outcome-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.outcome-head h2 { margin: 0; font-size: 14px; letter-spacing: -.01em; }
.outcome-meta { overflow-wrap: anywhere; }

/* ---- the four outcome cards ---- */
.outcome-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 10px; margin: 14px 0 0; }
.ocard {
  display: flex; flex-direction: column; gap: 2px;
  background: var(--card2); border: 1px solid var(--line); border-left-width: 3px;
  border-radius: 8px; padding: 10px 12px; min-width: 0;
}
.ocard-label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
.ocard-value { font-size: 18px; font-weight: 700; letter-spacing: -.01em; overflow-wrap: anywhere; }
.ocard-sub { font-size: 11px; color: var(--muted); overflow-wrap: anywhere; }
.ocard.tone-ok      { border-left-color: var(--ok); }
.ocard.tone-ok    .ocard-value { color: var(--ok); }
.ocard.tone-bad     { border-left-color: var(--bad); }
.ocard.tone-bad   .ocard-value { color: var(--bad); }
.ocard.tone-warn    { border-left-color: var(--warn); }
.ocard.tone-warn  .ocard-value { color: var(--warn); }
.ocard.tone-info    { border-left-color: var(--accent); }
.ocard.tone-info  .ocard-value { color: var(--accent); }
.ocard.tone-busy    { border-left-color: #7a5bbf; }
.ocard.tone-busy  .ocard-value { color: #5b3ea6; }
.ocard.tone-neutral { border-left-color: var(--line); }

/* ---- changed files ---- */
.changed-panel { margin-top: 18px; }
.changed-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.changed-head h3 { margin: 0; }
.changed-list { margin-top: 8px; border: 1px solid var(--line); border-radius: 8px; overflow: hidden; background: var(--card); }
.cf-empty { margin: 0; padding: 14px; color: var(--muted); font-style: italic; font-size: 13px; }

/* headline counts above the file rows */
.cf-summary { display: flex; align-items: center; gap: 6px 10px; flex-wrap: wrap; padding: 10px 12px; background: var(--card2); border-bottom: 1px solid var(--line); }
.cf-count { font-size: 11px; font-weight: 700; border-radius: 5px; padding: 1px 8px; }
.cf-totals { margin-left: auto; display: inline-flex; gap: 8px; font-family: ui-monospace, Consolas, monospace; font-size: 12px; font-weight: 700; }
.cf-add { color: var(--add); } .cf-del { color: var(--del); }
.cf-basis, .cf-note { margin: 0; padding: 8px 12px; font-size: 12px; color: var(--muted); }
.changed-list > .cf-basis, .changed-list > .cf-note { border-bottom: 1px solid var(--line); }
.cf-note.cf-warn { color: var(--warn); background: #fdf7e8; }
.cf-basis code { font-size: 11px; }

/* Two sections in one list: what moved since the previous prompt, then everything Git currently
   reports dirty. Only the grouping is new — the file rows below are untouched, and a file changed
   by this run legitimately appears in both. */
.cf-section { border-bottom: 1px solid var(--line); }
.cf-section:last-child { border-bottom: 0; }
.cf-section-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  padding: 10px 12px 6px;
}
.cf-section-title {
  margin: 0; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  color: var(--fg);
}
.cf-section-count { font-size: 11px; color: var(--muted); white-space: nowrap; }
.cf-section-note { margin: 0; padding: 0 12px 8px; font-size: 12px; color: var(--muted); }
.cf-section > .cf-summary { border-top: 1px solid var(--line); }

/* one file = one native disclosure, so keyboard + screen-reader behaviour is the browser's */
.cf { border-bottom: 1px solid var(--line); }
.cf:last-child { border-bottom: 0; }
.cf-sum {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 9px 12px; cursor: pointer; list-style: none; background: var(--card);
}
.cf-sum::-webkit-details-marker { display: none; }
.cf-sum:hover { background: var(--card2); }
.cf[open] > .cf-sum { background: var(--card2); border-bottom: 1px solid var(--line); }
.cf-sum:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.cf-chev { flex: none; width: 0; height: 0; border-left: 5px solid var(--muted); border-top: 4px solid transparent; border-bottom: 4px solid transparent; transition: transform .12s ease; }
.cf[open] > .cf-sum .cf-chev { transform: rotate(90deg); }
@media (prefers-reduced-motion: reduce) { .cf-chev { transition: none; } }

.cf-kind { flex: none; min-width: 18px; text-align: center; border-radius: 4px; padding: 0 5px; font-size: 11px; font-weight: 700; }
.k-added, .cf-count.k-added         { background: #dff3e4; color: var(--add); }
.k-modified, .cf-count.k-modified   { background: #eae2f3; color: #5b3ea6; }
.k-deleted, .cf-count.k-deleted     { background: #f7dedb; color: var(--del); }
.k-renamed, .cf-count.k-renamed     { background: #f6eccf; color: var(--warn); }
.k-untracked, .cf-count.k-untracked { background: #e2edfb; color: var(--accent); }
.k-unknown, .cf-count.k-unknown     { background: #ece9e3; color: var(--muted); }

.cf-path { flex: 1 1 220px; min-width: 0; font-family: ui-monospace, Consolas, monospace; font-size: 12.5px; overflow-wrap: anywhere; }
.cf-dir { color: var(--muted); }
.cf-name { font-weight: 700; }
.cf-chip { flex: none; font-size: 10px; font-weight: 700; border-radius: 4px; padding: 1px 6px; background: var(--card2); border: 1px solid var(--line); color: var(--muted); }
.cf-chip.c-pre { color: var(--warn); border-color: var(--warn); background: transparent; }
.cf-chip.c-test { margin-left: 8px; color: var(--accent); border-color: var(--accent-border); background: var(--accent-weak); }
.cf-stat { flex: none; display: inline-flex; align-items: center; gap: 6px; font-family: ui-monospace, Consolas, monospace; font-size: 12px; font-weight: 700; }
.cf-nostat { color: var(--muted); font-weight: 400; font-size: 11px; }
.cf-bar { display: inline-flex; gap: 1px; }
.cf-bar .sb { width: 7px; height: 7px; border-radius: 1px; }
.cf-bar .sb-a { background: var(--add); }
.cf-bar .sb-d { background: var(--del); }

.cf-body { padding: 10px 12px 14px; background: var(--code-bg); }
.cf-body > .cf-note { padding: 0 0 8px; }

/* honest "there is no diff" states — never an empty diff pretending to be one */
.cf-nodiff { border: 1px dashed var(--line); border-radius: 8px; padding: 12px; background: var(--card); }
.cf-nodiff-h { margin: 0 0 4px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
.cf-nodiff.s-unavailable { border-color: var(--warn); }
.cf-nodiff.s-unavailable .cf-nodiff-h { color: var(--warn); }
.cf-nodiff > .cf-note { padding: 0; }

/* ---- the per-file diff viewer ---- */
/* A focusable labelled region: a wide diff can be scrolled from the keyboard alone. */
.cf-diff { overflow-x: auto; background: var(--card); border: 1px solid var(--line); border-radius: 8px; max-height: 460px; overflow-y: auto; }
.cf-diff:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.dt { border-collapse: collapse; width: max-content; min-width: 100%; font-family: ui-monospace, "Cascadia Code", Consolas, monospace; font-size: 12px; line-height: 1.55; }
.dt td { padding: 0; }
.dn { width: 1%; min-width: 34px; padding: 0 8px; text-align: right; color: var(--muted); background: var(--card2); border-right: 1px solid var(--line); user-select: none; vertical-align: top; }
.dc { padding: 0 12px; white-space: pre; }
.d-sign { display: inline-block; width: 1ch; color: var(--muted); }
.d-add { background: #e9f7ee; } .d-add .d-sign, .d-add .d-text { color: #12662b; }
.d-del { background: #fdecea; } .d-del .d-sign, .d-del .d-text { color: #93190f; }
.d-hunk { background: #eef3fd; } .d-hunk .dc { color: var(--accent); font-weight: 700; }
.d-meta .dc { color: var(--muted); }

/* ---- raw console output: secondary, collapsed, escaped ---- */
.console-drop { margin-top: 16px; }
.console-drop > summary { font-size: 12px; }
.tests .cmd-p { color: var(--accent); font-weight: 700; }
.cmd-out { margin: 4px 0 10px; }

@media (max-width: 620px) {
  .outcome { padding: 14px 12px 6px; }
  .cf-stat { width: 100%; }
}

/* Current-activity line under the streaming assistant bubble. Replaces the old generic
   "Claude is working…" placeholder with what Claude is actually doing. */
.live-act {
  margin: 6px 0 0; padding-left: 2px;
  font-size: 12px; color: var(--muted); font-style: italic;
  display: flex; align-items: center; gap: 7px;
}
.live-act::before {
  content: ""; flex: none; width: 6px; height: 6px; border-radius: 50%;
  background: var(--accent); opacity: .8;
}
@media (max-width: 640px) { .live-act { font-size: 11.5px; } }

/* ---------- compact composer ---------- */
/* The bottom block used to carry project + mode + session + approval permanently. It is now a
   textarea and one bar, so the conversation keeps the height. */
.composer { padding: 10px 12px; gap: 8px; }
.composer textarea {
  width: 100%; resize: vertical; min-height: 44px; max-height: 40vh;
  padding: 9px 11px; border-radius: 9px; border: 1px solid var(--line);
  background: var(--card); color: var(--fg); font: inherit;
}
/* Wraps at any width, not just phone width: with the sidebar taking 328px the bar runs out of
   room around a 900px window, and a bar that cannot wrap pushes Send off the edge instead. */
.comp-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 6px; }
.comp-bar #sendHint { flex: 1; min-width: 0; text-align: right; }

/* One row, one button height. These buttons are flex items, and a button will shrink below its own
   label in a flex row - which is how Commands and Mode ended up two lines tall while Send stayed
   one, leaving the bar visibly ragged. So nothing here wraps or shrinks, every control is the same
   height whatever its font size, and the only label allowed to give way is the run-settings
   summary: it is the one piece of arbitrary-length text, so it ellipsises instead of the row
   breaking. */
.comp-bar > button,
.comp-bar .cmd-wrap > button,
.comp-bar .voice-actions > button {
  flex: none; height: 32px; line-height: 1.2; white-space: nowrap;
}
.comp-bar .cmd-wrap { flex: none; }
#runSettingsBtn {
  flex: 0 1 auto; min-width: 0; max-width: 55%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Icon-only toolbar: Commands / Mode / Settings / Schedule keep their glyph and drop the label, so
   the row stays compact as more controls land in it. font-size: 0 removes the label from the
   SCREEN, not from the accessibility tree the way display:none would — a screen reader still reads
   exactly the name it always did (the button's own text, or its aria-label/title where JS keeps one
   current, e.g. runSettingsBtn and modeBtn). The icon is its own span, given its size back
   explicitly; title puts the label back for a mouse, on hover. */
#cmdBtn, #modeBtn, #runSettingsBtn, #scheduleBtn, #micBtn {
  font-size: 0; padding-left: 8px; padding-right: 8px;
}
#cmdBtn > span[aria-hidden],
#modeBtn > span[aria-hidden],
#runSettingsBtn > span[aria-hidden],
#scheduleBtn > span[aria-hidden] { font-size: 14px; }
/* .mic-ico already carries its own font-size (see .mic .mic-ico above), so it needs no override
   here the way the other three icons do. */

/* ---------- dialogs (run settings, projects) ---------- */
.sheet {
  border: 1px solid var(--line); border-radius: 12px; padding: 0;
  background: var(--card); color: var(--fg);
  width: min(560px, calc(100vw - 32px)); max-height: min(80vh, 720px);
}
.sheet::backdrop { background: rgb(0 0 0 / .35); }
/* 10px more room on every side than the dialog first shipped with - the content was reaching
   the border of the sheet. */
.sheet-form { display: flex; flex-direction: column; gap: 7px; padding: 26px 28px 28px; overflow: auto; }
.sheet-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 4px; }
.sheet-head h2 { margin: 0; font-size: 15px; }
.sheet-form label { font-size: 12px; font-weight: 650; margin-top: 6px; }
.sheet-form select, .sheet-form input[type="text"], .sheet-form input[type="number"] {
  width: 100%; padding: 7px 9px; border-radius: 8px; border: 1px solid var(--line);
  background: var(--bg); color: var(--fg); font: inherit;
}
.row-inline { display: flex; gap: 8px; align-items: center; }
.row-inline > select, .row-inline > input { flex: 1; min-width: 0; }
.form-error { color: var(--bad, #b3261e); font-size: 12px; margin: 4px 0 0; }

/* ---------- project registry ---------- */
.project-list { list-style: none; margin: 4px 0 10px; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.project-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 7px 9px; border: 1px solid var(--line); border-radius: 8px; background: var(--bg);
}
.project-main { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; min-width: 0; }
.project-name { font-weight: 650; font-size: 13px; }
/* The one place a folder path is shown, because here it is the thing being configured. */
.project-path { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
.project-actions { display: flex; gap: 6px; flex: none; }
.project-form { border-top: 1px solid var(--line); padding-top: 10px; display: flex; flex-direction: column; gap: 6px; }
.project-form h3 { margin: 0; font-size: 13px; }
.chip.warn { border-color: var(--bad, #b3261e); color: var(--bad, #b3261e); }

/* ---------- copy button on every message ---------- */
.copy-btn {
  margin-left: auto; display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 8px; font-size: 11px; font-weight: 600; border-radius: 7px;
  border: 1px solid transparent; background: transparent; color: var(--muted);
  opacity: 0; transition: opacity .12s ease, background .12s ease, color .12s ease;
}
/* Revealed on hover, and always present for keyboard users. */
.turn:hover .copy-btn, .copy-btn:focus-visible, .copy-btn.copied { opacity: 1; }
.copy-btn:hover { background: var(--card); color: var(--fg); border-color: var(--line); }
.copy-btn.copied { color: var(--accent); border-color: var(--line); }
@media (hover: none) { .copy-btn { opacity: 1; } }

/* ---------- voice: dictation in, spoken answers out ---------- */
/* The Listen button borrows the copy button's shape so the message header keeps one visual rhythm;
   only its state colours differ, because "this is speaking right now" has to be visible at a glance
   even when the pointer has moved away. */
.speak-btn { margin-left: 0; }
.turn:hover .speak-btn, .speak-btn:focus-visible, .speak-btn.speaking { opacity: 1; }
.speak-btn.speaking { color: var(--accent); border-color: var(--accent-border); background: var(--accent-weak); }
@media (hover: none) { .speak-btn { opacity: 1; } }

/* The playback controls on an answer. They stay fully visible whenever there is anything to say
   about the audio — playing, paused, blocked, failed — because a control that fades out with the
   pointer is no use to someone who needs to press Play after their browser refused to autostart. */
.voice-controls { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.voice-controls .speak-btn { opacity: 1; }
/* Every button in this row is rendered once and shown or hidden by the `hidden` attribute (see
   voiceControlsHtml). But .speak-btn inherits .copy-btn's `display: inline-flex`, and an AUTHOR rule
   outranks the user-agent stylesheet's [hidden]{display:none} - so `hidden` had no effect here and
   all six controls (Listen, Pause, Resume, Replay, Stop, Retry) were on screen at once, whatever the
   state machine said. Same trap, and the same cure, as .auth-gate[hidden] below. The attribute is
   kept rather than swapped for the .hidden class because it also removes the button from the
   accessibility tree, which a class does not. */
.voice-controls [hidden] { display: none !important; }
.voice-controls.speaking .speak-btn { color: var(--accent); border-color: var(--accent-border); background: var(--accent-weak); }
.voice-state { color: var(--muted); font-size: 12px; }
.voice-state:empty { display: none; }
.voice-state.bad { color: var(--bad); }

/* Record sits immediately left of Send, because that is what it leads to. It is permanent furniture
   in the composer: present whenever this browser can hold a microphone, and merely DISABLED while
   PersianWhisper cannot be reached. It is not hidden on an unreachable service, because hiding it
   moved Send, changed the shape of the composer from one page load to the next, and left nothing on
   screen to say that dictation exists at all. `button:disabled` above already dims it and blocks the
   pointer; the reason is on the title and the accessible name. */
.mic { display: inline-flex; align-items: center; gap: 6px; flex: none; }
.mic .mic-ico { font-size: 13px; line-height: 1; }
/* Unavailable, not removed. Dimmed like any disabled control, but still in the tab order and still
   announced (aria-disabled, not the `disabled` property), so a keyboard user can reach it and be
   told why rather than finding a gap where a button was. The focus ring must survive the dimming. */
.mic.unavailable { opacity: .55; }
.mic.unavailable:focus-visible { opacity: 1; }

/* While recording, Record and Send are replaced in place by Send + Cancel, so the primary button
   stays in the same corner and nothing under the pointer shifts. The red dot is the one state a
   user must not miss: the microphone is open. */
.voice-actions { display: inline-flex; align-items: center; gap: 8px; flex: none; }
.voice-actions.recording::before {
  content: ""; width: 9px; height: 9px; border-radius: 50%; background: var(--bad); flex: none;
  animation: mic-pulse 1.2s ease-in-out infinite;
}
@keyframes mic-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .3; } }
/* Respect a user who has asked the system for less motion: the dot alone still carries it. */
@media (prefers-reduced-motion: reduce) { .voice-actions.recording::before { animation: none; } }

/* Reuses .cmd-line for type, but these lines are plain sentences with no bolded label, so the tone
   has to colour the text itself rather than a <b> inside it. */
/* Persian text, wherever it appears. Every such block carries both dir="rtl" and lang="fa", and
   the rule is written on both attributes on purpose: it has to outrank `.turn-user .prompt`, which
   is monospace and was claiming dictated Persian requests. Consolas has no Persian glyphs, so that
   text was being assembled from whatever the system substituted, letter by letter. */
[dir="rtl"][lang="fa"] { font-family: var(--fa-font); }

/* The Persian summary that accompanies each English answer. Set apart by an inset ground and a
   start-edge rule rather than by colour, so it reads as a companion to the answer above it and not
   as a warning. dir="rtl" on the element makes the border land on the right, where it belongs. */
.fa-summary {
  margin: 8px 0 0; padding: 9px 12px;
  background: var(--card2); border-inline-start: 3px solid var(--accent-border);
  border-radius: 9px; font-size: 14.5px; line-height: 2;
  white-space: pre-wrap; overflow-wrap: anywhere;
  font-family: var(--fa-font);
}
.fa-label {
  display: block; margin-bottom: 3px; text-align: start;
  font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); font-family: var(--ui-font);
}
/* Marks a summary Control Center wrote itself (Claude's reply carried none for this run's outcome)
   rather than one Claude authored - a second, separate label so the distinction survives even
   without hovering for the tooltip's full explanation. */
.fa-fallback {
  display: inline; margin-inline-start: 6px;
  color: var(--warn); font-weight: 600;
}

/* Copy / Read-summary, repositioned under the Persian summary (turnEl in app.js) instead of the top
   turn-meta row, so pressing Read summary never requires scrolling past the whole answer above it. */
.turn-actions { display: flex; align-items: center; gap: 8px; margin: 4px 0 0; }

/* Disclosure of what Control Center appended to a prompt. Quiet by default — it is identical on
   every turn — but always one click from the request it was sent with. */
.standing { margin-top: 6px; }
.standing > summary { font-size: 11px; color: var(--muted); cursor: pointer; list-style: none; }
.standing > summary::-webkit-details-marker { display: none; }
.standing > summary::before { content: "▸ "; }
.standing[open] > summary::before { content: "▾ "; }
.standing > summary:hover { color: var(--fg); }
.standing-text {
  margin: 6px 0 0; padding: 8px 10px; background: var(--code-bg); border-radius: 8px;
  font-size: 11.5px; line-height: 1.6; white-space: pre-wrap; overflow-x: auto; color: var(--muted);
}

.vp-chip { color: var(--muted); background: var(--card2); }
/* What was heard, shown as the user's own line on a stage-1 turn. */
.fa-heard { font-size: 14px; line-height: 2; font-family: var(--fa-font); }

/* ---------- dictated turn: the confirm step ---------- */
/* Sits between the conversation and the composer, where the run-outcome panel sits, so a decision
   the user has to make lands in the same place they already look. Deliberately quiet — an accent
   edge, not an alarm colour: nothing has gone wrong, something is merely waiting. */
.voice-preview {
  background: var(--card); border: 1px solid var(--accent-border); border-left-width: 3px;
  border-radius: 12px; padding: 12px 14px; margin-bottom: 10px;
  display: flex; flex-direction: column; gap: 9px;
  box-shadow: 0 2px 10px rgba(35, 32, 28, .05);
}
.vp-head { display: flex; align-items: center; gap: 10px; }
.vp-head h2 { margin: 0; font-size: 14px; }
.vp-badge {
  margin-left: auto; font-size: 10.5px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--ok);
  border: 1px solid var(--line); border-radius: 999px; padding: 2px 8px; background: var(--bg);
}
.vp-heard { margin: 0; font-size: 13.5px; color: var(--muted); line-height: 2; }
.vp-heard:empty { display: none; }

.vp-block { display: flex; flex-direction: column; gap: 4px; }
.vp-label-row { display: flex; align-items: center; gap: 8px; }
.vp-label {
  font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted);
}
.vp-fa {
  margin: 0; padding: 8px 11px; background: var(--card2); border-radius: 9px;
  font-size: 14.5px; line-height: 2; white-space: pre-wrap; overflow-wrap: anywhere;
  font-family: var(--fa-font);
}
/* Read-only until Edit is pressed, and it looks it — an editable-looking box that ignores typing
   is worse than one that plainly says it is locked. */
.vp-prompt {
  width: 100%; resize: vertical; min-height: 62px; max-height: 34vh;
  padding: 8px 10px; border-radius: 9px; border: 1px solid var(--line);
  background: var(--code-bg); color: var(--fg); font: inherit; font-size: 13px; line-height: 1.6;
}
.vp-prompt:read-only { color: var(--muted); cursor: default; }
.vp-prompt:not(:read-only) { background: var(--card); color: var(--fg); border-color: var(--accent-border); }

.vp-problem { margin: 0; font-size: 12.5px; line-height: 1.6; color: var(--warn); }
.vp-actions { display: flex; align-items: center; gap: 8px; }
.vp-actions #vpConfirmBtn { margin-left: auto; }
#vpSpeakBtn { display: inline-flex; align-items: center; gap: 5px; margin-left: auto; }
#vpSpeakBtn.speaking { color: var(--accent); border-color: var(--accent-border); background: var(--accent-weak); }

@media (max-width: 640px) {
  .vp-actions { flex-wrap: wrap; }
  .vp-actions #vpConfirmBtn { margin-left: 0; flex-basis: 100%; }
}

/* Checkbox + label on one line, aligned on the box rather than the text block. */
.check-row { display: flex; align-items: flex-start; gap: 8px; font-weight: 400; font-size: 13px; }
.check-row input { margin: 3px 0 0; flex: none; }

.voice-status { display: flex; flex-direction: column; gap: 4px; }
.voice-status:empty { display: none; }
.voice-status .cmd-line { color: var(--muted); }
.voice-status .cmd-line.tone-ok   { color: var(--ok); }
.voice-status .cmd-line.tone-bad  { color: var(--bad); }
.voice-status .cmd-line.tone-warn { color: var(--warn); }
.voice-status .cmd-line.tone-info { color: var(--muted); }

@media (max-width: 640px) {
  .comp-bar { flex-wrap: wrap; }
  .comp-bar #sendHint { order: 3; flex-basis: 100%; text-align: left; }
  #runSettingsBtn { max-width: 100%; }
  .project-row { flex-direction: column; align-items: flex-start; }
}

/* ---------- icon-only "new session in this project" ---------- */
/* Icon only, never a text label. Quiet until the row is hovered, but always present for keyboard
   users and on touch, where there is no hover to reveal it. */
.proj-add {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; margin-right: 10px; padding: 0;
  border: 1px solid transparent; border-radius: 6px;
  background: transparent; color: var(--muted);
  font-size: 14px; font-weight: 700; line-height: 1;
  opacity: 0; transition: opacity .12s ease, background .12s ease, color .12s ease, border-color .12s ease;
}
.proj-h:hover .proj-add,
.proj-add:focus-visible,
.proj-add.is-busy, .proj-add.is-ok, .proj-add.is-fail { opacity: 1; }
.proj-add:hover { background: var(--card); color: var(--fg); border-color: var(--line); }
.proj-add[disabled] { cursor: default; }
.proj-add.is-busy { color: var(--muted); }
.proj-add.is-busy .proj-add-ico { animation: projAddPulse 1s ease-in-out infinite; }
.proj-add.is-ok { color: var(--accent); border-color: var(--line); }
.proj-add.is-fail { color: var(--bad, #b3261e); border-color: var(--line); }
@keyframes projAddPulse { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .proj-add.is-busy .proj-add-ico { animation: none; opacity: .6; } }
/* No hover on touch, so the control is permanently visible there. */
@media (hover: none) { .proj-add { opacity: 1; } }

.nav-toast { font-size: 11.5px; }

/* ============================================================================
   Per-section sidebar controls + the composer's command menu
   ========================================================================== */

/* One "show more / show less" per section. Quiet by default — it is navigation
   chrome, not an action — and only rendered when its own section overflows. */
.sect-more {
  display: block; width: calc(100% - 8px); margin: 2px 4px 6px;
  text-align: left; color: var(--muted); font-size: 11px; font-weight: 600;
  border-color: transparent; padding: 4px 8px;
}
.sect-more:hover { color: var(--accent); background: var(--card2); }
.sect-more[hidden] { display: none; }

/* ---- command menu ---- */
.cmd-wrap { position: relative; display: inline-flex; }
.cmd-menu {
  position: absolute; bottom: calc(100% + 6px); left: 0; z-index: 20;
  min-width: 290px; max-width: min(360px, calc(100vw - 32px));
  background: var(--card); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 8px 24px rgba(35, 32, 28, .14);
  padding: 4px; display: flex; flex-direction: column; gap: 2px;
}
.cmd-menu[hidden] { display: none; }

.cmd-item {
  display: flex; flex-direction: column; gap: 2px; align-items: flex-start;
  width: 100%; text-align: left; background: transparent; border: 0;
  border-radius: 8px; padding: 8px 10px; font-weight: 400;
}
.cmd-item:hover { background: var(--card2); }
.cmd-item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.cmd-name { font-weight: 700; font-size: 13px; }
.cmd-desc { font-size: 11px; color: var(--muted); line-height: 1.45; }
/* An unavailable command stays focusable and keeps explaining itself — the reason is the
   point, so it must be reachable from the keyboard rather than removed from the tab order. */
.cmd-item.is-disabled { cursor: not-allowed; }
.cmd-item.is-disabled .cmd-name, .cmd-item.is-disabled .cmd-desc { opacity: .5; }
.cmd-item.is-disabled:hover { background: transparent; }
/* The mode popover's current choice (menuitemradio), marked without a second visual language. */
.cmd-item[aria-checked="true"] { background: var(--card2); }
.cmd-item[aria-checked="true"] .cmd-name::after { content: " \2713"; color: var(--accent); }
.cmd-why { font-size: 11px; color: var(--warn); line-height: 1.45; }
.cmd-why[hidden] { display: none; }

/* ---- confirm row + status line ---- */
.cmd-confirm {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  background: var(--card2); border: 1px solid var(--line); border-radius: 8px;
  padding: 8px 10px; font-size: 12px;
}
.cmd-status { display: flex; flex-direction: column; gap: 4px; }
.cmd-status:empty { display: none; }
.cmd-line { margin: 0; font-size: 12px; line-height: 1.5; }
.cmd-line b { font-weight: 700; }
.cmd-line.tone-ok b { color: var(--ok); }
.cmd-line.tone-bad b { color: var(--bad); }
.cmd-line.tone-warn b { color: var(--warn); }
.cmd-line.tone-busy b { color: var(--accent); }
.cmd-detail, .cmd-note { color: var(--muted); }
.cmd-note { display: block; font-style: italic; }
.cmd-link { display: block; word-break: break-all; font-weight: 700; }

/* On a narrow screen the popover becomes a bottom sheet, so it never overflows the viewport
   or hides behind the composer. */
@media (max-width: 620px) {
  .cmd-menu {
    position: fixed; left: 8px; right: 8px; bottom: 8px; top: auto;
    min-width: 0; max-width: none;
  }
}

/* ============================================================================
   Hosted gateway only: login gate + local-core connection status.
   Uses the app's own design tokens (--bg/--card/--fg/--muted/--line/--accent)
   so the added screens are visually part of the product, not a bolted-on form.
   ========================================================================== */

/* Must beat the .auth-gate display rule below: author CSS outranks the UA
   stylesheet's [hidden]{display:none}, so without this the gate stays on screen
   after a successful sign-in. */
.auth-gate[hidden],
.auth-bar[hidden] { display: none !important; }

.auth-gate {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: var(--bg);
}

.auth-card {
  width: 100%;
  max-width: 360px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 28px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--card);
  color: var(--fg);
  box-shadow: 0 12px 32px rgba(35, 32, 28, .10);
}

.auth-card h1 { margin: 0; font-size: 1.15rem; color: var(--fg); }
.auth-card .auth-sub { margin: 0 0 10px; color: var(--muted); font-size: .85rem; }
.auth-card label { font-size: .78rem; color: var(--muted); margin-top: 8px; }

.auth-card input {
  padding: 9px 10px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: var(--card);
  color: var(--fg);
  font: inherit;
}
.auth-card input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.auth-card button { margin-top: 16px; }

.auth-error {
  margin: 10px 0 0;
  padding: 8px 10px;
  border-radius: 7px;
  background: #fdecea;
  border: 1px solid #f3c2bd;
  color: var(--bad);
  font-size: .82rem;
}

.auth-note { margin: 14px 0 0; font-size: .72rem; color: var(--muted); line-height: 1.45; }

.auth-bar {
  position: fixed;
  right: 12px;
  bottom: 10px;
  z-index: 900;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--card);
  font-size: .74rem;
  color: var(--muted);
  box-shadow: 0 4px 14px rgba(35, 32, 28, .10);
}

.auth-who-wrap strong { color: var(--fg); font-weight: 600; }

/* The dot carries the state and the text repeats it, so this is never
   colour-only information. */
.core-status { display: inline-flex; align-items: center; gap: 6px; }
.core-status::before {
  content: "";
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--muted);
  flex: none;
}
.core-status[data-state="online"]::before    { background: var(--ok); }
.core-status[data-state="offline"]::before   { background: var(--bad); }
.core-status[data-state="connecting"]::before,
.core-status[data-state="degraded"]::before  { background: var(--warn); }

@media (max-width: 640px) {
  .auth-bar { left: 12px; right: 12px; justify-content: space-between; }
}

/* ---- jump-to-latest button ----
   `bottom` is set in JS (positionScrollBottomBtn), not here: the composer it must clear grows and
   shrinks with the textarea and with which composer controls are open, so a fixed offset would drift
   out of place. `right`/`display` are the only static part. */
.scroll-bottom-btn {
  position: fixed;
  right: 28px;
  z-index: 25;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--fg);
  font-size: 16px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 4px 14px rgba(35, 32, 28, .18);
}
.scroll-bottom-btn:hover { background: var(--card2); }

/* ---- live-feed indicator (auth bar) ----
   Deliberately not animated: a spinner next to a connection state reads as "working on it" and this
   is a status, not a task. Colour plus a static dot plus the word itself, so it is legible without
   relying on colour alone. */
.live-status { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 700;
  letter-spacing: .02em; text-transform: uppercase; color: var(--muted); white-space: nowrap; }
.live-status::before { content: ""; width: 7px; height: 7px; border-radius: 50%;
  background: currentColor; flex: none; }
.live-status[data-state="live"] { color: var(--ok); }
.live-status[data-state="connecting"] { color: var(--muted); }
.live-status[data-state="reconnecting"], .live-status[data-state="polling"] { color: var(--warn); }
.live-status[data-state="offline"] { color: var(--bad); }
@media (max-width: 720px) {
  /* On a narrow bar the dot alone carries it; the word would push the sign-out control off-screen. */
  .live-status { font-size: 0; gap: 0; }
  .live-status::before { width: 9px; height: 9px; }
}

/* The audio row already wraps at every width; on a narrow bubble the state line is given a full row
   of its own so it sits under the buttons it describes rather than being squeezed beside them. */
@media (max-width: 720px) {
  .voice-controls { row-gap: 4px; }
  .voice-controls .voice-state { flex-basis: 100%; }
}


/* ================= iterative quality control =================
   Two rules only, and both are additive: a QC message is the SAME turn component as any other, in a
   different colour, with a written lane tag beside it so the distinction never rests on colour
   alone. Nothing here changes how a primary-session message looks. */
.turn-qc .src-label { color: var(--qc); }
.turn-qc.turn-user .bubble,
.turn-qc.turn-claude .bubble {
  background: var(--card); border-color: var(--qc);
  box-shadow: 0 0 0 3px var(--qc-ring), 0 1px 2px rgba(35, 32, 28, .04);
}
.lane-tag {
  font-size: 10px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  border-radius: 4px; padding: 1px 6px; background: var(--qc-weak); color: var(--qc);
  border: 1px solid var(--qc-border);
}

/* The status strip above the conversation. Same card language as everything else in the view. */
.qc-bar {
  border: 1px solid var(--qc-border); border-left: 3px solid var(--qc);
  background: var(--qc-weak); border-radius: var(--radius);
  padding: 9px 12px; margin: 0 0 18px;
  display: flex; flex-direction: column; gap: 4px;
}
.qc-bar-main { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.qc-bar-head {
  margin: 0; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  color: var(--qc);
}
.qc-badge { background: var(--card); color: var(--qc); border: 1px solid var(--qc-border); }
/* A stop is never dressed as a success, and a success is never dressed as a warning. */
.qc-badge.qc-ok { background: #dff3e4; color: var(--ok); border-color: #bfe3ca; }
.qc-badge.qc-attention { background: #f6eccf; color: var(--warn); border-color: #e6d3a3; }
.qc-badge.qc-bad { background: #f7dedb; color: var(--bad); border-color: #eec7c2; }
.qc-chip { background: var(--card); border-color: var(--qc-border); color: var(--qc); }
.qc-bar .qc-reason { margin: 0; color: var(--muted); }
.qc-bar #qcCancelBtn { margin-left: auto; }

/* QC settings, opened from the Mode popover's "Quality Control" item into their own dialog (.sheet).
   The options wrap into as many columns as fit and scroll inside themselves, so a tall panel can
   never push the dialog's Done button off the screen. */
.qc-options {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 6px 14px; margin-top: 8px; border-top: 1px solid var(--line); padding-top: 8px;
  max-height: 34vh; overflow-y: auto;
}
.qc-opt { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.qc-opt > label { font-size: 12px; font-weight: 650; }
.qc-opt-wide { grid-column: 1 / -1; gap: 6px; }
.qc-options select, .qc-options input[type="number"] {
  width: 100%; padding: 6px 8px; border-radius: 8px; border: 1px solid var(--line);
  background: var(--bg); color: var(--fg); font: inherit;
}
@media (max-width: 640px) { .qc-options { grid-template-columns: 1fr; max-height: 44vh; } }

/* ---------- Plan mode ----------------------------------------------------------------------- */
/* The refine-then-execute panel above the composer. Same card language as the dictated-turn
   preview it sits beside, in the accent colour rather than QC's purple: a plan is the user's own
   conversation, not a second session reviewing them. */
.plan-panel {
  background: var(--card); border: 1px solid var(--accent-border); border-left-width: 3px;
  border-left-color: var(--accent); border-radius: var(--radius);
  padding: 10px 12px; margin: 0 0 10px;
  display: flex; flex-direction: column; gap: 8px;
}
.plan-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.plan-head h2 { margin: 0; font-size: 14px; }
.plan-collapse-toggle { padding: 2px 6px; line-height: 1; }
.plan-badge { background: var(--accent-weak); color: var(--accent); border: 1px solid var(--accent-border); }
.plan-badge.plan-busy { background: #f6eccf; color: var(--warn); border-color: #e6d3a3; }
.plan-badge.plan-done { background: #dff3e4; color: var(--ok); border-color: #bfe3ca; }
.plan-badge.plan-bad { background: #f7dedb; color: var(--bad); border-color: #eec7c2; }
.plan-chip { background: var(--card2); border-color: var(--accent-border); color: var(--muted); }
.plan-badge-note {
  margin-left: auto; font-size: 11px; color: var(--muted);
  border: 1px dashed var(--line); border-radius: 999px; padding: 1px 8px;
}
.plan-settings-line { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0; }
/* One visual language for "whose setting is this", used in the panel and in both dialogs so the
   Plan / Execution / QC distinction is the same distinction everywhere it is shown. */
.plan-settings-label {
  font-size: 10px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  border-radius: 4px; padding: 1px 6px;
  background: var(--accent-weak); color: var(--accent); border: 1px solid var(--accent-border);
}
.plan-settings-label.is-exec { background: #dff3e4; color: var(--ok); border-color: #bfe3ca; }
.plan-settings-label.is-qc { background: var(--qc-weak); color: var(--qc); border-color: var(--qc-border); }
.plan-hint { margin: 0; line-height: 1.6; }
.plan-block { display: flex; flex-direction: column; gap: 4px; }
.plan-label {
  font-size: 10px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--muted);
}
.plan-prompt { background: var(--card2); color: var(--fg); font-size: 12.5px; line-height: 1.6; }
.plan-prompt:read-only { color: var(--muted); cursor: default; }
.plan-reason { margin: 0; line-height: 1.6; }
.plan-problem { margin: 0; font-size: 12.5px; line-height: 1.6; color: var(--bad); }
.plan-actions { display: flex; align-items: center; gap: 8px; }
.plan-actions #planRunBtn { margin-left: auto; }

/* Execution dialog: the two configurations side by side, so "these may differ" is shown and not
   merely asserted. */
.plan-compare {
  display: flex; flex-direction: column; gap: 4px; margin: 4px 0 0;
  border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px; background: var(--card2);
}
.plan-compare-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.plan-qc-block { border-top: 1px solid var(--line); padding-top: 8px; }
.plan-independence { margin: 8px 0 0; line-height: 1.6; }
.plan-exec-actions { margin-top: 10px; }
.plan-exec-actions #planExecConfirmBtn { margin-left: auto; }
/* A planning turn in the conversation: the user's own message, marked as read-only planning. */
.plan-chip.xs, .chip.plan-turn-chip { color: var(--accent); background: var(--accent-weak); border-color: var(--accent-border); }

/* ============================================================================================
   Scheduling, and the pause a run takes when Claude's usage credits run out.

   Both use the existing warning language rather than an error one, and deliberately so: neither
   is a failure. A scheduled prompt has not run yet, and a paused one has not been thrown away.
   ============================================================================================ */

/* The strip above the conversation when this session's newest run is paused for credits. Same card
   language as the QC strip it sits beside. */
.credit-bar {
  border: 1px solid #e6d3a3; border-left: 3px solid var(--warn);
  background: #fdf6e5; border-radius: var(--radius);
  padding: 9px 12px; margin: 0 0 18px;
  display: flex; flex-direction: column; gap: 4px;
}
.credit-bar-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.credit-bar-head {
  margin: 0; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  color: var(--warn);
}
.credit-bar .credit-reason, .credit-bar .credit-evidence { margin: 0; color: var(--muted); }
.credit-bar .credit-evidence { font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace); font-size: 11px; }
.credit-bar #creditCancelBtn { margin-left: auto; }

/* A schedule card in the dashboard. The amber rule says "not running yet" the same way the queue
   cards do; a finished one drops it, so the list reads at a glance. */
.task-card.sched { box-shadow: inset 3px 0 0 var(--warn); }
.tc-when { color: var(--fg); }

/* The strip above the composer listing requests scheduled to continue THIS conversation. Same
   amber language as .credit-bar — both say "not running yet", not "something is wrong". */
.queued-bar {
  border: 1px solid #e6d3a3; border-left: 3px solid var(--warn);
  background: #fdf6e5; border-radius: var(--radius);
  padding: 9px 12px; margin: 0 0 10px;
  display: flex; flex-direction: column; gap: 8px;
}
.queued-bar h2 {
  margin: 0; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  color: var(--warn);
}
#queuedList { display: flex; flex-direction: column; gap: 8px; }
.queued-item {
  border: 1px solid var(--warn); border-radius: var(--radius);
  padding: 8px 10px;
}
.queued-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.queued-btns { display: flex; gap: 6px; margin-left: auto; }
.sched-done { margin-top: 10px; }
.sched-done > summary { cursor: pointer; color: var(--muted); font-size: 12px; }

/* The Schedule dialog's two mutually-exclusive ways of saying when. */
.sched-when { border: 1px solid var(--line); border-radius: 8px; padding: 10px 12px; margin: 10px 0 6px; }
.sched-when > legend { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); padding: 0 4px; }
.sched-row { margin: 4px 0 10px 22px; gap: 8px; }
/* A date-and-time field has a fixed amount to show, so it is sized to it rather than
   stretched across the dialog the way the delay number + unit pair is. */
#schedAt { flex: none; width: min(240px, 100%); }
.sched-row:last-child { margin-bottom: 0; }
.sched-resolved { color: var(--fg); font-weight: 600; }
