claude-mission-control 1.18.0 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/collector.js CHANGED
@@ -7,7 +7,7 @@ const { readQuota } = require('./quota');
7
7
  const { fetchOauthUsage } = require('./usage');
8
8
  const { readRegistry } = require('./registry');
9
9
  const { refreshHistory, activityBuckets, weekHourHeat } = require('./history');
10
- const { scanAllTranscripts, scanFile, groupByProject, sessionTitle, combinedUsage, combinedDays, dailyCostSeries, subagentSummary, usageBreakdown, dayKey, reasonText, contextSummary, toolSummary } = require('./transcripts');
10
+ const { scanAllTranscripts, scanFile, groupByProject, sessionTitle, sessionKind, combinedUsage, combinedDays, dailyCostSeries, subagentSummary, usageBreakdown, dayKey, reasonText, contextSummary, toolSummary } = require('./transcripts');
11
11
  const { collectGitStatus } = require('./gitstatus');
12
12
  const { friendlyName } = require('./names');
13
13
  const { isIgnored } = require('./ignore');
@@ -277,6 +277,7 @@ class Collector {
277
277
  const liveByProject = new Set(
278
278
  this.raw.live.map((s) => worktreeRoot(s.cwd).root.toLowerCase())
279
279
  );
280
+ const liveIds = new Set(this.raw.live.map((s) => s.sessionId));
280
281
 
281
282
  const projects = [];
282
283
  for (const p of this.projectPaths()) {
@@ -287,6 +288,15 @@ class Collector {
287
288
  const git = this.raw.git.get(p) || null;
288
289
 
289
290
  const sessionMetas = group ? group.sessions : [];
291
+ // Automated and empty sessions still count toward spend and activity;
292
+ // they just don't take up rows in the card.
293
+ const shown = [];
294
+ const hidden = { automated: 0, empty: 0 };
295
+ for (const m of sessionMetas) {
296
+ const kind = sessionKind(m, liveIds.has(m.sessionId));
297
+ if (kind) hidden[kind]++;
298
+ else shown.push(m);
299
+ }
290
300
  const lastActivityAt = Math.max(
291
301
  sessionMetas[0] ? sessionMetas[0].lastActivityAt : 0,
292
302
  hist ? hist.lastTimestamp : 0,
@@ -309,7 +319,8 @@ class Collector {
309
319
  Date.now() - m.lastActivityAt < SPEND_WINDOW_MS ? sum + estimateCost(combinedUsage(m), pricing) : sum,
310
320
  0
311
321
  ),
312
- sessions: sessionMetas.slice(0, SESSIONS_PER_PROJECT).map((m) => {
322
+ hidden,
323
+ sessions: shown.slice(0, SESSIONS_PER_PROJECT).map((m) => {
313
324
  const { title, source } = sessionTitle(m, sessionNames);
314
325
  return {
315
326
  sessionId: m.sessionId,
@@ -508,7 +519,6 @@ class Collector {
508
519
  const forecast = this.limitForecast();
509
520
 
510
521
  // Background jobs, live or finished, with any worktree work at risk.
511
- const liveIds = new Set(this.raw.live.map((s) => s.sessionId));
512
522
  const jobWt = this.raw.jobWt || new Map();
513
523
  const jobsOut = jobs.map((j) => ({
514
524
  jobId: j.jobId,
@@ -543,6 +553,7 @@ class Collector {
543
553
  sessionId: m.sessionId,
544
554
  title,
545
555
  titleSource: source,
556
+ kind: sessionKind(m, this.raw.live.some((s) => s.sessionId === m.sessionId)),
546
557
  estCost: estimateCost(combinedUsage(m), pricing),
547
558
  assumed: costReport(combinedUsage(m), pricing).assumedModels.length > 0,
548
559
  awaySummary: m.awaySummary,
@@ -128,6 +128,7 @@ async function scanFile(abs, sessionIdFromName) {
128
128
  awaySummaryAt: null,
129
129
  aiTitle: null,
130
130
  waitingReason: null,
131
+ entrypoint: null,
131
132
  };
132
133
 
133
134
  // Head: first ~40 lines; the first line can be queue-operation without cwd.
@@ -148,6 +149,9 @@ async function scanFile(abs, sessionIdFromName) {
148
149
  const t = Date.parse(rec.timestamp);
149
150
  if (!Number.isNaN(t)) meta.startedAt = t;
150
151
  }
152
+ if (meta.entrypoint === null && rec.type === 'user' && typeof rec.entrypoint === 'string') {
153
+ meta.entrypoint = rec.entrypoint;
154
+ }
151
155
  if (meta.firstUserPrompt === null) {
152
156
  const text = extractUserText(rec);
153
157
  if (text) meta.firstUserPrompt = text.slice(0, 120);
@@ -573,6 +577,16 @@ function combinedUsage(meta) {
573
577
  return mergeUsage(mergeUsage({}, meta.usage), meta.subagentUsage);
574
578
  }
575
579
 
580
+ // Sessions nobody needs in a project list. 'automated': a one-shot claude -p
581
+ // run (recap and summarizer tools, plugin probes). 'empty': opened and closed
582
+ // without ever calling the model — only /clear, /exit and the like. A live
583
+ // session is never empty; it may just not have had its first reply yet.
584
+ function sessionKind(meta, isLive = false) {
585
+ if (meta.entrypoint === 'sdk-cli') return 'automated';
586
+ if (!isLive && !Object.keys(combinedUsage(meta)).length) return 'empty';
587
+ return null;
588
+ }
589
+
576
590
  // Cost breakdown for the transcript viewer, from the meta the collector
577
591
  // already holds — never re-reads the file. `context` and `tools` are
578
592
  // filled by the scanner once the context-pressure batch lands.
@@ -671,7 +685,7 @@ function groupByProject(sessions) {
671
685
  }
672
686
 
673
687
  module.exports = {
674
- scanAllTranscripts, scanFile, groupByProject, sessionTitle, combinedUsage, mergeUsage,
688
+ scanAllTranscripts, scanFile, groupByProject, sessionTitle, sessionKind, combinedUsage, mergeUsage,
675
689
  scanLine, mergeDays, combinedDays, dailyCostSeries, subagentSummary, usageBreakdown, dayKey, contextSummary, toolSummary,
676
690
  waitingReason, reasonText, shouldReadMeta, TOOL_RING,
677
691
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-mission-control",
3
- "version": "1.18.0",
3
+ "version": "1.20.0",
4
4
  "description": "Local dashboard for Claude Code: live sessions, transcripts, costs, git status — all your projects in one place.",
5
5
  "main": "server.js",
6
6
  "bin": {
package/public/index.html CHANGED
@@ -31,23 +31,25 @@
31
31
  src: url('/fonts/Oswald-Variable.woff2') format('woff2');
32
32
  font-weight: 200 700; font-style: normal; font-display: swap;
33
33
  }
34
- /* Departures board: blue-black enamel, ivory flap letters, amber is the
35
- loudest color (needs you), green is a small "boarding" lamp, red means
36
- broken. Oswald condensed caps carry names; JetBrains Mono carries data.
37
- Light theme is the same timetable, printed on paper. */
34
+ /* Departures board: blue-black enamel, amber is the loudest color (needs
35
+ you), green is a small "boarding" lamp, red means broken. Prose is the
36
+ system sans, data is JetBrains Mono, and the split-flap status lamp is
37
+ the one place Oswald caps survive. Light theme is the same board on paper. */
38
38
  :root {
39
39
  --disp-font: 'Oswald', 'Arial Narrow', sans-serif;
40
- --bg: #f5f4f0;
41
- --surface: #ecebe5;
42
- --surface2: #e0dfd8;
40
+ --ui-font: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Segoe UI', system-ui, sans-serif;
41
+ --mono-font: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;
42
+ --bg: #f1f0eb;
43
+ --surface: #fbfbf8;
44
+ --surface2: #e6e5df;
43
45
  --ink: #1b2022;
44
46
  --muted: #596264;
45
- --faint: #8d9496;
46
- --line: #d5d4cc;
47
+ --faint: #8a9193;
48
+ --line: #dddcd4;
47
49
  --accent: #1d7a44;
48
50
  --warn: #a66a00;
49
51
  --bad: #bd3524;
50
- --chip-bg: #e0dfd8;
52
+ --chip-bg: #ecebe5;
51
53
  --warn-bg: rgba(166, 106, 0, 0.10);
52
54
  --accent-bg: rgba(29, 122, 68, 0.08);
53
55
  --glow: none;
@@ -82,6 +84,7 @@
82
84
  so it overrides both light and dark palettes. */
83
85
  :root[data-theme="phosphor"] {
84
86
  --disp-font: 'JetBrains Mono', ui-monospace, monospace;
87
+ --ui-font: 'JetBrains Mono', ui-monospace, monospace;
85
88
  --bg: #050a06;
86
89
  --surface: #0a120c;
87
90
  --surface2: #122015;
@@ -106,6 +109,7 @@
106
109
  and "needs you" is the white-hot bright intensity. Red still means broken. */
107
110
  :root[data-theme="amber"] {
108
111
  --disp-font: 'JetBrains Mono', ui-monospace, monospace;
112
+ --ui-font: 'JetBrains Mono', ui-monospace, monospace;
109
113
  --bg: #0a0602;
110
114
  --surface: #140d04;
111
115
  --surface2: #1f1508;
@@ -179,10 +183,12 @@
179
183
  body {
180
184
  background: var(--bg);
181
185
  color: var(--ink);
182
- font: 12.5px/1.5 'JetBrains Mono', ui-monospace, "SF Mono", Menlo, monospace;
186
+ font: 13.5px/1.5 var(--ui-font);
183
187
  padding-bottom: 48px;
188
+ -webkit-font-smoothing: antialiased;
184
189
  }
185
- .mono { font-family: inherit; }
190
+ /* Numbers, costs, branches and commands stay monospaced; prose does not. */
191
+ .mono, code, pre, kbd, .badge, .num { font-family: var(--mono-font); font-variant-numeric: tabular-nums; }
186
192
  ::selection { background: var(--warn); color: var(--amber-ink); }
187
193
 
188
194
  /* Display face: condensed caps, the board's letter-painted voice. */
@@ -206,99 +212,100 @@
206
212
  .flap.flip { animation: flapflip 0.3s ease-out; transform-origin: 50% 50%; }
207
213
  @media (prefers-reduced-motion: reduce) { .flap.flip { animation: none; } }
208
214
 
209
- /* ---------- header: the station sign ---------- */
215
+ /* ---------- header ---------- */
210
216
  header {
211
- display: flex; align-items: center; gap: 16px;
217
+ display: flex; align-items: center; gap: 14px;
212
218
  flex-wrap: wrap; row-gap: 10px;
213
- padding: 12px 20px;
214
- border-bottom: 2px solid var(--line);
219
+ padding: 10px 24px;
220
+ border-bottom: 1px solid var(--line);
215
221
  background: var(--surface);
216
222
  }
217
223
  header h1 {
218
- font-family: var(--disp-font);
219
- font-size: 19px; font-weight: 600; letter-spacing: 0.10em;
220
- text-transform: uppercase; white-space: nowrap;
221
- }
222
- header h1 .tilde { color: var(--warn); }
223
- #clock {
224
- font-size: 14px; font-weight: 700; color: var(--ink);
225
- letter-spacing: 0.08em; font-variant-numeric: tabular-nums;
224
+ font-size: 15px; font-weight: 650; letter-spacing: -0.01em; white-space: nowrap;
226
225
  }
226
+ #clock { display: none; }
227
227
  #conn {
228
- width: 7px; height: 7px; border-radius: 50%;
228
+ width: 8px; height: 8px; border-radius: 50%;
229
229
  background: var(--faint); flex: none;
230
230
  }
231
231
  #conn.on { background: var(--accent); box-shadow: var(--glow) var(--accent); }
232
232
  #conn.retry { background: var(--warn); }
233
233
  .spacer { flex: 1; }
234
234
 
235
- #quota { display: flex; gap: 18px; align-items: center; font-size: 12px; color: var(--muted); }
235
+ #quota { display: flex; gap: 20px; align-items: center; font-size: 12.5px; color: var(--muted); }
236
236
  #quota.stale { opacity: 0.45; }
237
- .meter { display: flex; align-items: center; gap: 7px; }
237
+ #quota .q-spend { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; }
238
+ #quota .q-spend .num { color: var(--ink); }
239
+ .meter { display: flex; align-items: center; gap: 8px; white-space: nowrap; }
240
+ .meter .m-pct { color: var(--ink); font-family: var(--mono-font); font-size: 12px; min-width: 3ch; }
238
241
  .meter .bar {
239
- width: 72px; height: 5px; border-radius: 3px;
242
+ width: 64px; height: 6px; border-radius: 3px;
240
243
  background: var(--surface2); overflow: hidden;
241
244
  }
242
245
  .meter .fill { height: 100%; border-radius: 3px; background: var(--accent); }
243
246
  .meter .fill.hot { background: var(--warn); }
244
247
  .meter .fill.crit { background: var(--bad); }
245
- .meter .eta { color: var(--warn); white-space: nowrap; }
248
+ .meter .eta { color: var(--warn); white-space: nowrap; font-family: var(--mono-font); font-size: 12px; }
246
249
 
247
- /* ---------- live rail: the departures rows ---------- */
250
+ /* Section headings: plain sentence case, the count as a quiet number. */
251
+ .sec-head {
252
+ display: flex; align-items: baseline; gap: 8px;
253
+ font-size: 14px; font-weight: 600; color: var(--ink);
254
+ margin-bottom: 10px;
255
+ }
256
+ .sec-head .n { color: var(--faint); font-weight: 500; font-family: var(--mono-font); font-size: 12.5px; }
257
+
258
+ /* ---------- live rail ---------- */
248
259
  #live {
249
260
  position: sticky; top: 0; z-index: 5;
250
261
  background: var(--bg);
251
- padding: 14px 20px 14px;
252
- border-bottom: 2px solid var(--line);
262
+ padding: 18px 24px 16px;
263
+ border-bottom: 1px solid var(--line);
253
264
  }
254
- #live .rail-label {
255
- font-family: var(--disp-font);
256
- font-size: 12px; text-transform: uppercase; letter-spacing: 0.22em;
257
- color: var(--faint); margin-bottom: 10px;
258
- }
259
- #live .rows { display: flex; flex-direction: column; gap: 5px; }
265
+ #live .rows { display: flex; flex-direction: column; gap: 6px; }
260
266
  .dep-row {
261
267
  display: grid;
262
- grid-template-columns: 52px minmax(150px, 230px) 1fr max-content;
263
- gap: 2px 16px; align-items: center;
268
+ grid-template-columns: minmax(140px, 220px) 1fr max-content;
269
+ gap: 2px 18px; align-items: center;
264
270
  background: var(--surface);
265
- border-radius: 2px;
266
- padding: 8px 14px;
271
+ border: 1px solid var(--line);
272
+ border-radius: 8px;
273
+ padding: 10px 14px 10px 16px;
267
274
  }
268
- .dep-row.waiting { background: var(--warn-bg); box-shadow: inset 2px 0 0 var(--warn); }
269
- .dep-time { color: var(--faint); font-size: 12px; font-variant-numeric: tabular-nums; }
275
+ .dep-row.waiting { background: var(--warn-bg); border-color: var(--warn); }
276
+ .dep-time { display: none; }
270
277
  .dep-proj {
271
- font-size: 19px; line-height: 1.15; color: var(--ink);
278
+ font-size: 15px; font-weight: 600; line-height: 1.25; color: var(--ink);
272
279
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
273
280
  min-width: 0;
274
281
  }
275
- .dep-proj .badge { vertical-align: 3px; }
282
+ .dep-proj .badge { vertical-align: 1px; font-weight: 400; }
276
283
  .dep-mid { min-width: 0; }
277
284
  .dep-title {
278
- font-size: 12.5px; color: var(--muted); cursor: pointer;
285
+ font-size: 13.5px; color: var(--ink); cursor: pointer;
279
286
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
280
287
  }
281
- .dep-title:hover { color: var(--ink); }
288
+ .dep-title:hover { color: var(--accent); }
282
289
  .dep-task {
283
- margin-top: 2px; font-size: 11.5px; color: var(--ink);
290
+ margin-top: 2px; font-size: 12px; color: var(--muted);
284
291
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
285
292
  }
286
293
  .dep-task .prompt-ch { color: var(--accent); margin-right: 6px; }
287
294
  .dep-task .count { color: var(--faint); margin-left: 6px; }
288
295
  .dep-reason {
289
- margin-top: 2px; font-size: 11.5px; color: var(--muted);
296
+ margin-top: 2px; font-size: 12.5px; color: var(--ink);
290
297
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
291
298
  }
292
299
  .dep-reason .glyph { color: var(--warn); margin-right: 6px; font-weight: 600; }
293
- .dep-right { display: flex; align-items: center; gap: 10px; justify-self: end; }
294
- .dep-elapsed { color: var(--faint); font-size: 12px; }
300
+ .dep-right { display: flex; align-items: center; gap: 14px; justify-self: end; }
301
+ .dep-elapsed { color: var(--faint); font-size: 12px; font-family: var(--mono-font); }
295
302
  .dep-elapsed.overdue { color: var(--warn); font-weight: 600; }
296
303
  .unpriced { color: var(--warn); font-weight: 700; margin-left: 2px; cursor: help; }
297
- .dep-cost { display: flex; flex-direction: column; align-items: flex-end; line-height: 1.15; min-width: 52px; }
304
+ .dep-cost { display: flex; flex-direction: column; align-items: flex-end; line-height: 1.2; min-width: 52px; font-family: var(--mono-font); }
298
305
  .dep-cost-main { color: var(--muted); font-size: 12px; }
299
306
  .dep-burn { color: var(--faint); font-size: 10.5px; }
300
- .ctx { display: inline-flex; flex-direction: column; gap: 2px; min-width: 64px; }
301
- .ctx-label { font-size: 10.5px; color: var(--faint); text-align: right; white-space: nowrap; }
307
+ .ctx { display: inline-flex; flex-direction: column; gap: 3px; min-width: 64px; }
308
+ .ctx-label { font-size: 10.5px; color: var(--faint); text-align: right; white-space: nowrap; font-family: var(--mono-font); }
302
309
  .ctx-bar { height: 3px; background: var(--surface2); border-radius: 2px; overflow: hidden; }
303
310
  .ctx-fill { height: 100%; background: var(--accent); }
304
311
  .ctx.warn .ctx-fill { background: var(--warn); }
@@ -307,7 +314,11 @@
307
314
  .ctx.bad .ctx-label { color: var(--bad); }
308
315
  .mc-meta .failed { color: var(--warn); }
309
316
  .mc-meta.overdue { color: var(--warn); font-weight: 600; }
310
- .st { font-size: 11.5px; letter-spacing: 0.14em; padding: 3px 10px 2px; }
317
+ /* The status lamp is the one split-flap left: the loudest thing on screen. */
318
+ .st {
319
+ font-family: var(--disp-font); text-transform: uppercase; font-weight: 500;
320
+ font-size: 11.5px; letter-spacing: 0.12em; padding: 3px 10px 2px;
321
+ }
311
322
  .st.st-busy { color: var(--accent); }
312
323
  .st.st-waiting {
313
324
  background: var(--warn); color: var(--amber-ink); font-weight: 600;
@@ -315,105 +326,141 @@
315
326
  }
316
327
  .st.st-waiting::after { background: rgba(0, 0, 0, 0.22); }
317
328
  .st.st-quiet { color: var(--warn); }
318
- @media (max-width: 700px) {
319
- .dep-row { grid-template-columns: 44px 1fr max-content; }
320
- .dep-mid { grid-column: 1 / -1; }
321
- }
322
- .live-none { color: var(--faint); font-size: 13px; }
323
- .live-none code { color: var(--muted); }
329
+ .live-none { color: var(--muted); font-size: 13px; }
330
+ .live-none code { color: var(--ink); }
331
+
332
+ /* Row actions (rename / open / copy) wait for hover so titles get the room. */
333
+ .acts { display: inline-flex; align-items: center; gap: 2px; flex: none; }
334
+ @media (hover: hover) {
335
+ .acts { opacity: 0; transition: opacity 0.12s; }
336
+ li:hover > .acts, li:focus-within > .acts,
337
+ .dep-row:hover .acts, .dep-row:focus-within .acts,
338
+ .pin-row:hover .acts, .pin-row:focus-within .acts,
339
+ .digest-item:hover .acts, .digest-item:focus-within .acts { opacity: 1; }
340
+ }
341
+
342
+ /* In the narrow project cards the actions float over the row's right end. */
343
+ @media (hover: hover) {
344
+ .sessions li { position: relative; }
345
+ .sessions li > .acts {
346
+ position: absolute; right: 0; top: 50%; transform: translateY(-50%);
347
+ padding-left: 28px;
348
+ background: linear-gradient(to right, transparent, var(--surface) 24px);
349
+ pointer-events: none;
350
+ }
351
+ .sessions li:hover > .acts, .sessions li:focus-within > .acts { pointer-events: auto; }
352
+ }
353
+ .hidden-note {
354
+ align-self: flex-start; margin: 6px 0 4px; padding: 2px 0;
355
+ border: none; background: none; cursor: pointer;
356
+ font: 12px var(--ui-font); color: var(--faint);
357
+ }
358
+ .hidden-note:hover { color: var(--accent); }
359
+ .hidden-note:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }
360
+ .sessions + .hidden-note { border-top: 1px solid var(--line); align-self: stretch; text-align: left; padding-top: 8px; margin-top: 0; }
361
+ .d-noise { margin-top: 10px; }
362
+ .d-noise > summary { cursor: pointer; color: var(--faint); font-size: 12.5px; padding: 4px 0; }
363
+ .d-noise > summary:hover { color: var(--ink); }
364
+ .card .new-btn { font-size: 16px; line-height: 1; padding: 2px 7px; color: var(--faint); }
365
+ .card .new-btn:hover { color: var(--accent); }
324
366
 
325
367
  /* ---------- project grid ---------- */
326
- main { padding: 18px 20px; }
368
+ main { padding: 22px 24px; }
327
369
  #grid {
328
370
  display: grid; gap: 14px;
329
- grid-template-columns: repeat(auto-fill, minmax(330px, 1fr));
371
+ grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
330
372
  }
331
373
  .card {
332
374
  background: var(--surface);
333
- border-radius: 2px;
334
- border-top: 2px solid var(--line);
335
- padding: 12px 16px 10px;
375
+ border: 1px solid var(--line);
376
+ border-radius: 10px;
377
+ padding: 14px 16px 8px;
336
378
  display: flex; flex-direction: column;
379
+ min-width: 0;
337
380
  }
338
381
  .card .head { display: flex; align-items: center; gap: 8px; }
339
382
  .card .name {
340
- font-family: var(--disp-font);
341
- text-transform: uppercase; letter-spacing: 0.05em;
342
- font-weight: 500; font-size: 15px;
383
+ font-weight: 600; font-size: 15px; line-height: 1.3;
384
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
343
385
  }
344
- .card .live-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); flex: none; box-shadow: var(--glow) var(--accent); }
345
- .card .spark { margin-left: auto; }
346
- .card .meta { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin-top: 7px; font-size: 11px; }
386
+ .card .name:hover { color: var(--accent); }
387
+ .card .live-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); flex: none; box-shadow: var(--glow) var(--accent); }
388
+ .card .spark { margin-left: auto; opacity: 0.8; flex: none; }
389
+ .card .meta { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin-top: 6px; font-size: 11.5px; }
347
390
  .badge {
348
- padding: 0 6px; border-radius: 2px; background: var(--chip-bg); color: var(--muted);
349
- font-family: inherit; font-size: 11px;
391
+ padding: 1px 6px; border-radius: 4px; background: var(--chip-bg); color: var(--muted);
392
+ font-size: 11px; white-space: nowrap;
350
393
  }
351
394
  .badge.dirty { color: var(--warn); }
352
395
  .badge.ahead { color: var(--accent); }
353
396
  .badge.err { color: var(--bad); }
354
397
  .badge.bg { color: var(--accent); letter-spacing: 0.06em; }
355
398
  .badge.missing { color: var(--bad); }
399
+ .badge.changes { cursor: pointer; }
356
400
  .note-mark { color: var(--warn); cursor: default; font-size: 12px; flex: none; }
357
401
  #note-editor { margin: 10px 0 4px; display: flex; flex-direction: column; gap: 6px; }
358
402
  #note-editor textarea {
359
403
  width: 100%; min-height: 72px; resize: vertical; box-sizing: border-box;
360
- font: 12.5px 'JetBrains Mono', ui-monospace, monospace; color: var(--ink);
361
- background: var(--surface); border: 1px solid var(--line); border-radius: 3px; padding: 8px 10px;
404
+ font: 12.5px var(--mono-font); color: var(--ink);
405
+ background: var(--surface); border: 1px solid var(--line); border-radius: 6px; padding: 8px 10px;
362
406
  }
363
407
  #note-editor .hint { color: var(--faint); font-size: 11px; }
364
- .card .when { color: var(--faint); font-size: 11px; margin-left: auto; }
408
+ .card .when { color: var(--faint); font-size: 11.5px; margin-left: auto; }
365
409
  .sessions { list-style: none; padding: 0; margin: 10px 0 0; border-top: 1px solid var(--line); }
366
410
  .sessions li {
367
411
  display: flex; align-items: center; gap: 8px;
368
412
  padding: 7px 0; border-bottom: 1px solid var(--line);
369
- font-size: 12.5px;
413
+ font-size: 13px; min-height: 34px;
370
414
  }
371
415
  .sessions li:last-child { border-bottom: none; }
372
- .sessions .s-title {
416
+ .sessions .s-title, #pinned .s-title {
373
417
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
374
- color: var(--ink); flex: 1;
418
+ color: var(--ink); flex: 1; min-width: 0;
375
419
  }
376
- .sessions .s-title .wt { color: var(--accent); font-size: 11px; margin-right: 5px; }
377
- .sessions .s-when { color: var(--faint); font-size: 11px; flex: none; }
420
+ .sessions .s-title .wt { color: var(--accent); font-size: 11.5px; margin-right: 5px; }
421
+ .s-when { color: var(--faint); font-size: 11.5px; flex: none; white-space: nowrap; }
378
422
  .copy {
379
- flex: none; border: none; background: none; color: var(--faint);
380
- font-size: 11px; cursor: pointer; padding: 2px 6px; border-radius: 5px;
423
+ flex: none; border: none; background: none; color: var(--muted);
424
+ font-size: 12px; cursor: pointer; padding: 3px 7px; border-radius: 5px;
381
425
  font-family: inherit;
382
426
  }
383
427
  .copy:hover { background: var(--surface2); color: var(--ink); }
384
428
  .copy:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
385
429
  .open-btn { color: var(--accent); }
386
- #open-pref-wrap { font-size: 12px; color: var(--muted); display: flex; align-items: center; gap: 6px; white-space: nowrap; }
430
+ #open-pref-wrap { font-size: 12.5px; color: var(--muted); display: flex; align-items: center; gap: 6px; white-space: nowrap; }
431
+ #open-pref-wrap[hidden] { display: none; }
387
432
  #open-pref {
388
433
  font: inherit; color: var(--ink);
389
434
  background: var(--surface2); border: 1px solid var(--line);
390
- border-radius: 2px; padding: 2px 6px;
435
+ border-radius: 6px; padding: 3px 6px;
391
436
  }
392
437
  #search {
393
438
  font: inherit; color: var(--ink);
394
- background: var(--surface2); border: 1px solid var(--line);
395
- border-radius: 2px; padding: 4px 10px; width: 190px; margin-left: 14px;
439
+ background: var(--bg); border: 1px solid var(--line);
440
+ border-radius: 8px; padding: 6px 12px; width: 260px; margin-left: 10px;
396
441
  }
397
- #search:focus { outline: 2px solid var(--accent); outline-offset: 0; }
442
+ #search:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
398
443
  #search::placeholder { color: var(--faint); }
399
- /* A full set of meters needs ~1600px; tighten before the header wraps. */
400
- @media (max-width: 1600px) {
444
+ /* A full set of meters needs room; tighten before the header wraps. */
445
+ @media (max-width: 1500px) {
401
446
  header { gap: 12px; }
402
- #search { width: 120px; }
447
+ #search { width: 180px; }
403
448
  #open-pref-wrap .open-label { display: none; }
404
- #quota { gap: 12px; }
449
+ #quota { gap: 14px; }
405
450
  .meter .bar { width: 44px; }
451
+ #wk-chart { display: none; }
406
452
  }
407
- #gear, #mc-btn, #bell-btn {
453
+ .hd-btn {
408
454
  border: none; background: none; color: var(--muted); cursor: pointer;
409
- font-size: 15px; padding: 2px 6px; border-radius: 2px; margin-left: 4px;
455
+ font-size: 15px; line-height: 1; padding: 6px 8px; border-radius: 6px;
410
456
  }
411
- #gear:hover, #mc-btn:hover, #bell-btn:hover { background: var(--surface2); color: var(--ink); }
457
+ .hd-btn:hover { background: var(--surface2); color: var(--ink); }
458
+ .hd-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
412
459
  #bell-btn { position: relative; }
413
460
  #bell-badge {
414
- position: absolute; top: -3px; right: -3px;
461
+ position: absolute; top: -2px; right: -2px;
415
462
  background: var(--warn); color: var(--amber-ink);
416
- font: 700 9.5px 'JetBrains Mono', monospace;
463
+ font: 700 9.5px var(--mono-font);
417
464
  border-radius: 8px; padding: 1px 4px; min-width: 10px; text-align: center;
418
465
  }
419
466
 
@@ -429,11 +476,11 @@
429
476
  .set-row input[type="text"] {
430
477
  font: inherit; color: var(--ink);
431
478
  background: var(--surface2); border: 1px solid var(--line);
432
- border-radius: 2px; padding: 4px 8px; width: 170px;
479
+ border-radius: 6px; padding: 4px 8px; width: 170px;
433
480
  }
434
481
  .set-row select {
435
482
  font: inherit; color: var(--ink); background: var(--surface2);
436
- border: 1px solid var(--line); border-radius: 2px; padding: 3px 6px;
483
+ border: 1px solid var(--line); border-radius: 6px; padding: 3px 6px;
437
484
  }
438
485
  .set-row input[type="checkbox"] { accent-color: var(--accent); width: 15px; height: 15px; }
439
486
 
@@ -445,7 +492,7 @@
445
492
  .rename-input {
446
493
  font: inherit; font-size: 12.5px; color: var(--ink);
447
494
  background: var(--surface2); border: 1px solid var(--accent);
448
- border-radius: 2px; padding: 2px 6px; flex: 1; min-width: 0; width: 100%;
495
+ border-radius: 6px; padding: 2px 6px; flex: 1; min-width: 0; width: 100%;
449
496
  box-sizing: border-box;
450
497
  }
451
498
  .rename-input:focus { outline: none; }
@@ -457,8 +504,7 @@
457
504
  border-radius: 3px; padding: 8px 12px; font-size: 13px;
458
505
  white-space: pre-wrap; overflow-wrap: break-word;
459
506
  }
460
- .t-user .t-who { color: var(--accent); font-size: 10px; text-transform: uppercase;
461
- letter-spacing: 0.08em; display: block; margin-bottom: 3px; }
507
+ .t-user .t-who { color: var(--accent); font-size: 11.5px; font-weight: 600; display: block; margin-bottom: 3px; }
462
508
  .t-tool {
463
509
  font-family: inherit; font-size: 11.5px;
464
510
  color: var(--muted); padding: 1px 0 1px 10px;
@@ -476,16 +522,11 @@
476
522
  .none-yet { color: var(--faint); font-size: 12px; padding: 10px 0 4px; }
477
523
 
478
524
  /* ---------- pinned strip ---------- */
479
- #pinned { margin-bottom: 16px; }
480
- #pinned .pin-label {
481
- font-family: var(--disp-font);
482
- font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.22em;
483
- color: var(--faint); margin-bottom: 6px;
484
- }
525
+ #pinned { margin-bottom: 22px; }
485
526
  #pinned .pin-row {
486
- display: flex; align-items: baseline; gap: 10px;
487
- background: var(--surface); border-radius: 2px;
488
- padding: 7px 14px; margin-bottom: 4px;
527
+ display: flex; align-items: center; gap: 10px;
528
+ background: var(--surface); border: 1px solid var(--line); border-radius: 8px;
529
+ padding: 8px 14px; margin-bottom: 6px; min-height: 38px;
489
530
  }
490
531
  #pinned .pin-row .star { color: var(--warn); flex: none; }
491
532
 
@@ -509,78 +550,71 @@
509
550
 
510
551
  /* ---------- git attention strip ---------- */
511
552
  #git-attn {
512
- display: flex; align-items: baseline; flex-wrap: wrap; gap: 8px 14px;
513
- background: var(--warn-bg);
514
- border: 1px solid var(--line); border-radius: 3px;
515
- padding: 8px 14px; margin-bottom: 16px;
516
- font-size: 12.5px;
553
+ display: flex; align-items: center; flex-wrap: wrap; gap: 8px 16px;
554
+ border: 1px solid var(--line); border-left: 3px solid var(--warn); border-radius: 8px;
555
+ background: var(--surface);
556
+ padding: 10px 16px; margin-bottom: 22px;
557
+ font-size: 13px;
517
558
  }
518
- #git-attn .ga-label { color: var(--warn); font-weight: 600; white-space: nowrap; }
519
- #git-attn .ga-item { color: var(--ink); white-space: nowrap; }
520
- #git-attn .ga-item .badge { margin-left: 4px; }
559
+ #git-attn .ga-label { color: var(--ink); font-weight: 600; white-space: nowrap; }
560
+ #git-attn .ga-item { color: var(--muted); white-space: nowrap; }
561
+ #git-attn .ga-item .badge { margin-left: 5px; }
521
562
 
522
563
  /* ---------- digest ---------- */
523
- #digest { margin-bottom: 20px; }
524
- #digest summary {
564
+ #digest { margin-bottom: 28px; }
565
+ #digest summary, #dormant summary {
525
566
  cursor: pointer; user-select: none; list-style: none;
526
567
  display: flex; align-items: center; gap: 12px;
527
568
  }
528
- #digest summary::-webkit-details-marker { display: none; }
529
- #digest-label {
530
- font-family: var(--disp-font);
531
- font-size: 12px; text-transform: uppercase; letter-spacing: 0.22em;
532
- color: var(--faint);
569
+ #digest summary::-webkit-details-marker, #dormant summary::-webkit-details-marker { display: none; }
570
+ #digest summary .sec-head, #dormant summary .sec-head { margin-bottom: 0; }
571
+ .sec-head .caret {
572
+ display: inline-block; width: 10px; color: var(--faint); font-size: 10px;
573
+ transition: transform 0.12s;
533
574
  }
534
- #digest-label::before { content: '▸ '; }
535
- #digest[open] #digest-label::before { content: '▾ '; }
536
- #digest-range { display: none; gap: 2px; }
575
+ details[open] > summary .caret { transform: rotate(90deg); }
576
+ #digest-range { display: none; padding: 2px; gap: 2px; background: var(--surface); border: 1px solid var(--line); border-radius: 7px; }
537
577
  #digest[open] #digest-range { display: inline-flex; }
538
578
  #digest-range button {
539
- font: 11px 'JetBrains Mono', ui-monospace, monospace; color: var(--muted);
540
- background: none; border: 1px solid var(--line); cursor: pointer;
541
- padding: 2px 9px;
579
+ font: 12px var(--ui-font); color: var(--muted);
580
+ background: none; border: none; border-radius: 5px; cursor: pointer;
581
+ padding: 3px 10px;
542
582
  }
543
- #digest-range button:first-child { border-radius: 6px 0 0 6px; }
544
- #digest-range button:last-child { border-radius: 0 2px 2px 0; }
583
+ #digest-range button:hover { color: var(--ink); }
545
584
  #digest-range button.sel { background: var(--surface2); color: var(--ink); }
546
- #digest-body { margin-top: 10px; }
585
+ #digest-body { margin-top: 12px; }
547
586
  .digest-day {
548
- font-family: var(--disp-font);
549
- color: var(--faint); font-size: 11.5px; text-transform: uppercase;
550
- letter-spacing: 0.22em; margin: 16px 0 6px;
587
+ color: var(--muted); font-size: 12.5px; font-weight: 500;
588
+ margin: 16px 0 8px;
551
589
  }
590
+ .digest-day:first-child { margin-top: 0; }
552
591
  .digest-item {
553
- background: var(--surface); border-radius: 2px;
554
- padding: 9px 14px; margin-bottom: 5px;
592
+ background: var(--surface); border: 1px solid var(--line); border-radius: 8px;
593
+ padding: 10px 14px; margin-bottom: 6px;
555
594
  }
556
- .digest-item .di-top { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
595
+ .digest-item .di-top { display: flex; align-items: center; gap: 10px; min-width: 0; }
557
596
  .digest-item .di-proj {
558
- font-family: var(--disp-font);
559
- text-transform: uppercase; letter-spacing: 0.05em;
560
- font-weight: 500; font-size: 14.5px;
597
+ font-weight: 600; font-size: 13.5px; flex: none;
598
+ max-width: 30%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
561
599
  }
562
- .digest-item .di-title { color: var(--muted); font-size: 13px;
563
- overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; min-width: 140px; }
564
- .digest-item .di-when { color: var(--faint); font-size: 11px; }
600
+ .digest-item .di-title { color: var(--muted); font-size: 13.5px;
601
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; min-width: 0; }
602
+ .digest-item .di-when { color: var(--faint); font-size: 11.5px; font-family: var(--mono-font); white-space: nowrap; flex: none; }
565
603
  .digest-item .di-summary {
566
- margin-top: 6px; font-size: 12.5px; color: var(--ink); line-height: 1.5;
604
+ margin-top: 6px; font-size: 13px; color: var(--ink); line-height: 1.55; max-width: 110ch;
567
605
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
568
606
  cursor: pointer;
569
607
  }
570
608
  .digest-item .di-summary.expanded { display: block; }
571
- .digest-item .di-tasks { margin-top: 5px; font-size: 11px; color: var(--faint); }
609
+ .digest-item .di-tasks { margin-top: 5px; font-size: 12px; color: var(--faint); }
572
610
  .digest-item .di-tasks .done { color: var(--accent); }
573
- .digest-empty { color: var(--faint); font-size: 12.5px; padding: 6px 0; }
611
+ .digest-empty { color: var(--faint); font-size: 13px; padding: 6px 0; }
574
612
 
575
613
  /* ---------- dormant ---------- */
576
- #dormant { margin-top: 26px; }
577
- #dormant summary {
578
- font-family: var(--disp-font);
579
- cursor: pointer; color: var(--faint); font-size: 12px;
580
- text-transform: uppercase; letter-spacing: 0.22em; user-select: none;
581
- }
614
+ #dormant { margin-top: 32px; }
615
+ #grid + #dormant summary .sec-head { color: var(--muted); }
582
616
  #dormant .cards { margin-top: 12px; display: grid; gap: 14px;
583
- grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); }
617
+ grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); }
584
618
  #dormant .card { opacity: 0.75; }
585
619
 
586
620
  /* ---------- mission control ---------- */
@@ -592,13 +626,10 @@
592
626
  #mc[hidden] { display: none !important; }
593
627
  .mc-head {
594
628
  display: flex; align-items: center; gap: 14px;
595
- padding: 12px 20px; border-bottom: 2px solid var(--line);
629
+ padding: 12px 24px; border-bottom: 1px solid var(--line);
596
630
  background: var(--surface); flex: none;
597
631
  }
598
- .mc-title {
599
- font-family: var(--disp-font);
600
- font-size: 16px; font-weight: 600; letter-spacing: 0.18em; text-transform: uppercase;
601
- }
632
+ .mc-title { font-size: 15px; font-weight: 650; }
602
633
  .mc-sub { color: var(--faint); font-size: 12px; }
603
634
  #mc-grid {
604
635
  flex: 1; overflow-y: auto;
@@ -609,7 +640,7 @@
609
640
  #mc-grid .mc-empty { color: var(--faint); font-size: 13px; padding: 24px 0; }
610
641
  .mc-pane {
611
642
  display: flex; flex-direction: column;
612
- background: var(--surface); border-radius: 2px;
643
+ background: var(--surface); border: 1px solid var(--line); border-radius: 10px; overflow: hidden;
613
644
  height: min(46vh, 480px); min-width: 0;
614
645
  }
615
646
  .mc-pane.waiting { box-shadow: inset 0 2px 0 var(--warn); }
@@ -621,9 +652,7 @@
621
652
  .mc-pane-head .mc-proj { min-width: 0; flex: 0 1 auto; }
622
653
  .mc-stats { flex: 1 0 100%; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; min-width: 0; }
623
654
  .mc-pane-head .mc-proj {
624
- font-family: var(--disp-font);
625
- text-transform: uppercase; letter-spacing: 0.05em;
626
- font-weight: 500; font-size: 15px; cursor: pointer;
655
+ font-weight: 600; font-size: 14.5px; cursor: pointer;
627
656
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
628
657
  }
629
658
  .mc-pane-head .mc-proj:hover { color: var(--warn); }
@@ -649,8 +678,8 @@
649
678
  display: grid; gap: 10px; padding: 14px 20px 0;
650
679
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
651
680
  }
652
- .hv-tile { background: var(--surface); border-radius: 2px; padding: 9px 12px; min-width: 0; }
653
- .hv-tile .k { color: var(--faint); font-size: 10.5px; letter-spacing: 0.14em; text-transform: uppercase; }
681
+ .hv-tile { background: var(--surface); border: 1px solid var(--line); border-radius: 10px; padding: 9px 12px; min-width: 0; }
682
+ .hv-tile .k { color: var(--muted); font-size: 12px; font-weight: 500; }
654
683
  .hv-tile .v { font-family: var(--disp-font); font-size: 26px; font-weight: 500; line-height: 1.15; }
655
684
  .hv-tile .s { color: var(--faint); font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
656
685
  .hv-tile.warn .v, .hv-tile .eta { color: var(--warn); }
@@ -661,7 +690,7 @@
661
690
  }
662
691
  .hv-empty { color: var(--faint); font-size: 13px; padding: 24px 20px; }
663
692
  .hv-card {
664
- background: var(--surface); border-radius: 2px; min-width: 0;
693
+ background: var(--surface); border: 1px solid var(--line); border-radius: 10px; min-width: 0;
665
694
  border-top: 3px solid var(--hue); display: flex; flex-direction: column;
666
695
  }
667
696
  .hv-card.waiting { border-top-color: var(--warn); box-shadow: inset 0 0 0 1px var(--warn-bg); }
@@ -683,14 +712,14 @@
683
712
  .hv-money .big { font-family: var(--disp-font); font-size: 38px; font-weight: 500; line-height: 1; }
684
713
  .hv-money .rate { color: var(--muted); font-size: 12px; }
685
714
  .hv-state {
686
- margin: 0 14px; padding: 7px 10px; border-radius: 2px; background: var(--surface2);
715
+ margin: 0 14px; padding: 7px 10px; border-radius: 6px; background: var(--surface2);
687
716
  display: flex; gap: 10px; align-items: center; min-width: 0; font-size: 12px;
688
717
  }
689
718
  .hv-state .txt { color: var(--muted); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
690
719
  .hv-card.waiting .hv-state { background: var(--warn-bg); }
691
720
  .hv-card.waiting .hv-state .txt { color: var(--ink); }
692
721
  .hv-sec { padding: 12px 14px 0; }
693
- .hv-label { color: var(--faint); font-size: 10.5px; letter-spacing: 0.14em; text-transform: uppercase; display: flex; justify-content: space-between; gap: 8px; }
722
+ .hv-label { color: var(--muted); font-size: 12px; font-weight: 500; display: flex; justify-content: space-between; gap: 8px; }
694
723
  .hv-call { margin-top: 5px; font-size: 13px; display: flex; gap: 6px; min-width: 0; }
695
724
  .hv-call .verb { color: var(--muted); flex: none; }
696
725
  .hv-call .tgt { color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
@@ -702,7 +731,7 @@
702
731
  .hv-task { margin-top: 6px; font-size: 12px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
703
732
  .hv-task .prompt-ch { color: var(--accent); margin-right: 6px; }
704
733
  .hv-bars { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; padding: 14px 14px 0; }
705
- .hv-bar .row { display: flex; justify-content: space-between; color: var(--faint); font-size: 10.5px; letter-spacing: 0.06em; text-transform: uppercase; }
734
+ .hv-bar .row { display: flex; justify-content: space-between; color: var(--muted); font-size: 12px; }
706
735
  .hv-bar .row b { color: var(--muted); font-weight: 500; letter-spacing: 0; }
707
736
  .hv-bar .track { height: 4px; background: var(--surface2); margin-top: 5px; border-radius: 2px; overflow: hidden; }
708
737
  .hv-bar .fill { display: block; height: 100%; background: var(--hue); }
@@ -712,14 +741,14 @@
712
741
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px;
713
742
  padding: 12px 14px 12px; margin-top: 12px; border-top: 1px solid var(--line);
714
743
  }
715
- .hv-foot .k { color: var(--faint); font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; }
744
+ .hv-foot .k { color: var(--muted); font-size: 12px; }
716
745
  .hv-foot .v { font-size: 13px; margin-top: 2px; }
717
746
  .hv-foot .v.bad { color: var(--bad); }
718
747
  .hv-note { color: var(--faint); font-size: 11px; padding: 0 20px 24px; }
719
748
  .fm-sum { color: var(--muted); font-size: 12px; margin: 4px 0 14px; }
720
- .fm-award { margin: 0 0 18px; padding: 12px 14px; background: var(--surface); border-radius: 2px; }
749
+ .fm-award { margin: 0 0 18px; padding: 12px 14px; background: var(--surface); border: 1px solid var(--line); border-radius: 10px; }
721
750
  .fm-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
722
- .fm-title { font-family: var(--disp-font); font-size: 18px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; }
751
+ .fm-title { font-size: 16px; font-weight: 650; }
723
752
  .fm-blurb { color: var(--faint); font-size: 11.5px; flex: 1; min-width: 0; }
724
753
  .fm-row { display: flex; align-items: center; gap: 10px; padding: 7px 0; border-top: 1px solid var(--line); min-width: 0; }
725
754
  .fm-head + .fm-row { border-top: none; margin-top: 6px; }
@@ -742,7 +771,7 @@
742
771
 
743
772
  .mc-agents .agents { max-height: 160px; overflow-y: auto; }
744
773
  .agents { padding: 4px 12px 6px; border-bottom: 1px solid var(--line); font-size: 11.5px; flex: none; }
745
- .agents .team { color: var(--faint); text-transform: uppercase; letter-spacing: 0.08em; font-size: 10.5px; margin-bottom: 2px; }
774
+ .agents .team { color: var(--muted); font-size: 12px; font-weight: 500; margin-bottom: 2px; }
746
775
  .agents .agent { display: flex; align-items: center; gap: 7px; line-height: 1.6; }
747
776
  .agents .swatch { width: 7px; height: 7px; border-radius: 50%; flex: none; }
748
777
  .agents .aname { color: var(--ink); }
@@ -771,10 +800,7 @@
771
800
  display: flex; align-items: baseline; gap: 10px;
772
801
  padding: 16px 22px 12px; border-bottom: 1px solid var(--line);
773
802
  }
774
- #drawer .d-head h2 {
775
- font-family: var(--disp-font);
776
- font-size: 17px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em;
777
- }
803
+ #drawer .d-head h2 { font-size: 17px; font-weight: 650; }
778
804
  #drawer .d-head .d-path {
779
805
  color: var(--faint); font-size: 11px;
780
806
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1;
@@ -782,15 +808,15 @@
782
808
  #drawer .d-actions { display: flex; gap: 2px; align-items: center; flex: none; }
783
809
  #drawer .d-close {
784
810
  border: none; background: none; color: var(--muted); cursor: pointer;
785
- font-size: 18px; padding: 2px 8px; border-radius: 2px;
811
+ font-size: 18px; padding: 2px 8px; border-radius: 6px;
786
812
  }
787
813
  #drawer .d-close:hover { background: var(--surface2); color: var(--ink); }
788
814
  #drawer .d-body { overflow-y: auto; padding: 6px 22px 40px; }
789
815
  .d-section { margin-top: 18px; }
790
816
  .d-section > summary {
791
817
  cursor: pointer; user-select: none; list-style: none;
792
- font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em;
793
- color: var(--faint); padding: 4px 0;
818
+ font-size: 13px; font-weight: 600;
819
+ color: var(--muted); padding: 4px 0;
794
820
  }
795
821
  .d-section > summary::before { content: '▸ '; }
796
822
  .d-section[open] > summary::before { content: '▾ '; }
@@ -804,7 +830,11 @@
804
830
  font-size: 12px; color: var(--accent); margin-bottom: 6px;
805
831
  }
806
832
  .d-sessions li { display: flex; align-items: center; gap: 8px;
807
- padding: 6px 0; border-bottom: 1px solid var(--line); font-size: 12.5px; }
833
+ padding: 7px 0; border-bottom: 1px solid var(--line); font-size: 13px; min-height: 36px; }
834
+ .d-sessions .s-title {
835
+ flex: 1; min-width: 0; overflow: hidden;
836
+ display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
837
+ }
808
838
  .d-sessions li:last-child { border-bottom: none; }
809
839
  .d-sessions { list-style: none; padding: 0; margin: 6px 0 0; }
810
840
 
@@ -820,7 +850,7 @@
820
850
  background: var(--surface2); padding: 1px 4px; border-radius: 4px;
821
851
  }
822
852
  .md pre {
823
- background: var(--surface2); border-radius: 2px; padding: 10px 12px;
853
+ background: var(--surface2); border-radius: 6px; padding: 10px 12px;
824
854
  overflow-x: auto; margin: 8px 0;
825
855
  }
826
856
  .md pre code { background: none; padding: 0; }
@@ -833,7 +863,7 @@
833
863
  /* ---------- session changes panel ---------- */
834
864
  .badge.changes { color: var(--accent); cursor: pointer; }
835
865
  #changes-panel { margin-top: 10px; }
836
- .d-section-title { font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--faint); padding: 4px 0; }
866
+ .d-section-title { font-size: 13px; font-weight: 600; color: var(--muted); padding: 4px 0; }
837
867
  .chg-file { border: 1px solid var(--line); border-radius: 3px; margin-top: 8px; background: var(--surface); }
838
868
  .chg-head { display: flex; align-items: center; gap: 10px; padding: 8px 12px; cursor: pointer; font-size: 12.5px; }
839
869
  .chg-head .chg-path { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink); }
@@ -927,13 +957,13 @@
927
957
  }
928
958
  #welcome h2 { font-size: 14px; margin-bottom: 8px; }
929
959
  #welcome h2 .tilde { color: var(--accent); }
930
- #welcome code { background: var(--surface2); padding: 1px 5px; border-radius: 2px; }
960
+ #welcome code { background: var(--surface2); padding: 1px 5px; border-radius: 6px; }
931
961
  #welcome p { color: var(--muted); font-size: 12.5px; }
932
962
 
933
963
  #toast {
934
964
  position: fixed; bottom: 22px; left: 50%; transform: translateX(-50%);
935
965
  background: var(--ink); color: var(--bg);
936
- padding: 7px 14px; border-radius: 2px; font-size: 12.5px;
966
+ padding: 7px 14px; border-radius: 6px; font-size: 12.5px;
937
967
  opacity: 0; transition: opacity 0.18s; pointer-events: none;
938
968
  }
939
969
  #toast.show { opacity: 1; }
@@ -941,19 +971,22 @@
941
971
  /* ---------- phones: everything in one column, nothing scrolls sideways ---------- */
942
972
  @media (max-width: 700px) {
943
973
  header { padding: 10px 16px; gap: 8px 10px; }
944
- header h1 { font-size: 16px; }
945
- #search { flex: 1 1 90px; width: auto; min-width: 0; margin-left: 0; }
974
+ header h1 { font-size: 15px; margin-right: auto; }
975
+ #search { order: 9; flex: 1 1 100%; width: auto; min-width: 0; margin-left: 0; }
946
976
  header .spacer, #open-pref-wrap { display: none; } /* opening terminals is a desk thing; still in settings */
947
977
  #quota { order: 10; flex: 1 1 100%; flex-wrap: wrap; gap: 8px 14px; }
948
978
  .meter .bar { width: 52px; }
949
979
  #live { padding: 12px 16px; }
950
980
  main { padding: 14px 16px; }
951
- .dep-row { grid-template-columns: 44px 1fr; padding: 8px 12px; }
952
- .dep-proj { font-size: 17px; }
981
+ .dep-row { grid-template-columns: 1fr; padding: 10px 12px; }
982
+ .dep-proj { font-size: 15px; }
953
983
  .dep-mid, .dep-right { grid-column: 1 / -1; }
954
984
  .dep-right { justify-self: stretch; flex-wrap: wrap; gap: 6px 10px; }
955
985
  .dep-title, .dep-reason { white-space: normal; overflow-wrap: anywhere; }
956
986
  #pinned .pin-row { flex-wrap: wrap; padding: 7px 12px; }
987
+ .digest-item .di-top { flex-wrap: wrap; row-gap: 2px; }
988
+ .digest-item .di-proj { max-width: none; flex: 1 1 auto; }
989
+ .digest-item .di-title { order: 9; flex: 1 1 100%; }
957
990
  #git-attn .ga-item { white-space: normal; }
958
991
  #drawer { width: 100vw; border-left: none; }
959
992
  #drawer .d-head { flex-wrap: wrap; padding: 12px 16px 10px; }
@@ -980,8 +1013,8 @@
980
1013
  <body>
981
1014
  <header>
982
1015
  <div id="conn" title="connection"></div>
983
- <h1>Claude<span class="tilde"> · </span>Mission Control</h1>
984
- <input id="search" type="search" placeholder="Search… ⏎" autocomplete="off">
1016
+ <h1>Claude Mission Control</h1>
1017
+ <input id="search" type="search" placeholder="Search sessions" aria-label="Search sessions" autocomplete="off">
985
1018
  <div class="spacer"></div>
986
1019
  <label id="open-pref-wrap"><span class="open-label">Open in</span>
987
1020
  <select id="open-pref" aria-label="Open sessions in">
@@ -990,10 +1023,10 @@
990
1023
  </label>
991
1024
  <div id="quota"></div>
992
1025
  <span id="clock" class="flap" title="board time"></span>
993
- <button id="mc-btn" title="Mission Control — every live session at once">▦</button>
994
- <button id="hive-btn" title="Hive: every live session as a character card">⬢</button>
995
- <button id="bell-btn" title="Catch-up — what happened while you were away">🔔<span id="bell-badge" hidden></span></button>
996
- <button id="gear" title="Settings">⚙</button>
1026
+ <button id="mc-btn" class="hd-btn" title="Mission Control: every live session side by side" aria-label="Mission Control"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="2.5" width="5" height="5" rx="1"/><rect x="9" y="2.5" width="5" height="5" rx="1"/><rect x="2" y="9.5" width="5" height="5" rx="1"/><rect x="9" y="9.5" width="5" height="5" rx="1"/></svg></button>
1027
+ <button id="hive-btn" class="hd-btn" title="Hive: every live session as a character card" aria-label="Hive"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 1.8 13.4 4.9v6.2L8 14.2 2.6 11.1V4.9Z"/></svg></button>
1028
+ <button id="bell-btn" class="hd-btn" title="Catch up on what happened while you were away" aria-label="Catch up"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 11V7a4 4 0 0 1 8 0v4l1.2 1.5H2.8Z"/><path d="M6.5 14a1.6 1.6 0 0 0 3 0"/></svg><span id="bell-badge" hidden></span></button>
1029
+ <button id="gear" class="hd-btn" title="Settings" aria-label="Settings"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="8" cy="8" r="2.2"/><path d="M8 1.5v2M8 12.5v2M1.5 8h2M12.5 8h2M3.4 3.4l1.4 1.4M11.2 11.2l1.4 1.4M3.4 12.6l1.4-1.4M11.2 4.8l1.4-1.4"/></svg></button>
997
1030
  </header>
998
1031
 
999
1032
  <section id="live"></section>
@@ -1002,13 +1035,14 @@
1002
1035
  <div id="git-attn" hidden></div>
1003
1036
  <details id="digest" open>
1004
1037
  <summary>
1005
- <span id="digest-label">Digest</span>
1006
- <span id="digest-range" role="group" aria-label="digest window">
1007
- <button data-days="1">day</button><button data-days="3">3 days</button><button data-days="7">week</button>
1038
+ <span class="sec-head"><span class="caret">▶</span><span id="digest-label">Recent sessions</span><span class="n" id="digest-n"></span></span>
1039
+ <span id="digest-range" role="group" aria-label="How far back">
1040
+ <button data-days="1">Today</button><button data-days="3">3 days</button><button data-days="7">Week</button>
1008
1041
  </span>
1009
1042
  </summary>
1010
1043
  <div id="digest-body"></div>
1011
1044
  </details>
1045
+ <div class="sec-head" id="grid-head" hidden>Projects <span class="n"></span></div>
1012
1046
  <div id="grid"></div>
1013
1047
  <details id="dormant" hidden>
1014
1048
  <summary></summary>
@@ -1155,7 +1189,7 @@ function meter(label, pct, title, extra = '') {
1155
1189
  return `<div class="meter" title="${esc(title || '')}">
1156
1190
  <span>${esc(label)}</span>
1157
1191
  <span class="bar"><span class="fill ${cls}" style="width:${Math.min(100, pct)}%"></span></span>
1158
- <span>${Math.round(pct)}%</span>${extra}
1192
+ <span class="m-pct">${Math.round(pct)}%</span>${extra}
1159
1193
  </div>`;
1160
1194
  }
1161
1195
 
@@ -1218,6 +1252,11 @@ function modelShort(m) {
1218
1252
  return m.replace(/^claude-/, '').replace(/-\d{8}$/, '');
1219
1253
  }
1220
1254
 
1255
+ // Tooltip for a clickable session title; the model rides along instead of a chip.
1256
+ function titleTip(m, action = 'Click to read the transcript') {
1257
+ return m ? `${modelShort(m)}. ${action}` : action;
1258
+ }
1259
+
1221
1260
  function modelChip(m) {
1222
1261
  return m ? `<span class="badge" title="${esc(m)}">${esc(modelShort(m))}</span>` : '';
1223
1262
  }
@@ -1265,7 +1304,7 @@ function noteMark(sess) {
1265
1304
 
1266
1305
  function openBtn(sessionId) {
1267
1306
  if (liveIds.has(sessionId)) return '';
1268
- return `<button class="copy open-btn" data-open="${esc(sessionId)}" title="Open this session">open ⬈</button>`;
1307
+ return `<button class="copy open-btn" data-open="${esc(sessionId)}" title="Open this session">open</button>`;
1269
1308
  }
1270
1309
 
1271
1310
  // "Δ n" — how many files Claude edited in this session. Opens the changes panel.
@@ -1325,16 +1364,14 @@ function liveCard(s) {
1325
1364
  ? ` <span class="badge bg" title="${BG_TIP}">BG</span>`
1326
1365
  : s.isWorktree ? ' <span class="badge">worktree</span>' : '';
1327
1366
  return `<div class="dep-row ${s.status === 'waiting' ? 'waiting' : ''}">
1328
- <span class="dep-time" title="session started">${esc(dep)}</span>
1329
- <span class="dep-proj disp">${esc(s.projectName)}${kindBadge}</span>
1367
+ <span class="dep-proj" title="Started at ${esc(dep)}">${esc(s.projectName)}${kindBadge}</span>
1330
1368
  <div class="dep-mid">
1331
- <div class="dep-title" data-session="${esc(s.sessionId)}" title="Click to watch the transcript live">${esc(s.title || '')}</div>
1369
+ <div class="dep-title" data-session="${esc(s.sessionId)}" title="${esc(titleTip(s.model, 'Click to watch the transcript live'))}">${esc(s.title || '')}</div>
1332
1370
  ${task}
1333
1371
  ${reason}
1334
1372
  ${bgDetail}
1335
1373
  </div>
1336
1374
  <div class="dep-right">
1337
- ${modelChip(s.model)}
1338
1375
  ${s.estCost != null ? `<span class="dep-cost">${costChip(s.estCost, { cls: 'dep-cost-main', assumed: s.assumed })}${
1339
1376
  s.burnRate ? `<span class="dep-burn" title="Spend per hour over the last 30 minutes">$${s.burnRate}/h</span>` : ''}</span>` : ''}
1340
1377
  ${contextMeter(s.context)}
@@ -1342,39 +1379,47 @@ function liveCard(s) {
1342
1379
  ? `<span class="dep-elapsed overdue" title="No reply for ${s.waitingMin} minutes — session started ${elapsed(s.startedAt)} ago">${waitLabel(s.waitingMin)}</span>`
1343
1380
  : `<span class="dep-elapsed">${elapsed(s.startedAt)}</span>`}
1344
1381
  ${statusFlap(s)}
1345
- ${renameBtn(s.sessionId, s.title)}
1346
- ${copyBtn(s.resumeCommand)}
1382
+ <span class="acts">${renameBtn(s.sessionId, s.title)}${copyBtn(s.resumeCommand)}</span>
1347
1383
  </div>
1348
1384
  </div>`;
1349
1385
  }
1350
1386
 
1387
+ // "3 automated runs, 1 empty session" — the rows a card or drawer folds away.
1388
+ function hiddenLabel(h) {
1389
+ const plural = (n, w) => `${n} ${w}${n === 1 ? '' : 's'}`;
1390
+ return [h.automated && plural(h.automated, 'automated run'), h.empty && plural(h.empty, 'empty session')]
1391
+ .filter(Boolean).join(', ');
1392
+ }
1393
+
1351
1394
  function projectCard(p) {
1352
1395
  const sessions = p.sessions.length
1353
1396
  ? `<ul class="sessions">${p.sessions.map((sess) => `
1354
1397
  <li>
1355
- <span class="s-title" data-session="${esc(sess.sessionId)}" title="Click to read the transcript">${
1398
+ <span class="s-title" data-session="${esc(sess.sessionId)}" title="${esc(titleTip(sess.model))}">${
1356
1399
  sess.worktree ? `<span class="wt mono">⎇ ${esc(sess.worktree)}</span>` : ''}${esc(sess.title)}</span>${noteMark(sess)}
1357
- ${modelChip(sess.model)}
1358
1400
  ${changesChip(sess)}
1359
1401
  <span class="s-when">${relTime(sess.lastActivityAt)}</span>
1360
- ${renameBtn(sess.sessionId, sess.title)}
1361
- ${openBtn(sess.sessionId)}
1362
- ${copyBtn(sess.resumeCommand)}
1402
+ <span class="acts">${renameBtn(sess.sessionId, sess.title)}${openBtn(sess.sessionId)}${copyBtn(sess.resumeCommand)}</span>
1363
1403
  </li>`).join('')}</ul>`
1364
- : '<div class="none-yet">No sessions recorded.</div>';
1404
+ : '';
1405
+ const hiddenTxt = p.hidden ? hiddenLabel(p.hidden) : '';
1406
+ const hiddenNote = hiddenTxt
1407
+ ? `<button class="hidden-note" data-detail="${esc(p.path)}" data-detail-name="${esc(p.name)}" title="One-shot claude -p runs and sessions closed without a prompt. Open the project to see them.">${esc(hiddenTxt)} hidden</button>`
1408
+ : '';
1409
+ const empty = !p.sessions.length && !hiddenTxt ? '<div class="none-yet">No sessions recorded.</div>' : '';
1365
1410
  return `<div class="card">
1366
1411
  <div class="head">
1367
1412
  ${p.isLive ? '<span class="live-dot" title="session running"></span>' : ''}
1368
1413
  <span class="name" data-detail="${esc(p.path)}" style="cursor:pointer" title="Click for project details">${esc(p.name)}</span>
1369
- <button class="copy" data-new="${esc(p.path)}" title="Start a new Claude session here (Ghostty)">⊕</button>
1414
+ <button class="copy new-btn" data-new="${esc(p.path)}" title="Start a new Claude session here" aria-label="New session in ${esc(p.name)}">+</button>
1370
1415
  ${sparkSVG(p.activity.counts)}
1371
1416
  </div>
1372
1417
  <div class="meta">
1373
1418
  ${gitBadges(p.git, p.missing)}
1374
- ${costChip(p.spend7d, { cls: 'badge', after: '/7d' })}
1419
+ ${costChip(p.spend7d, { cls: 'badge', after: ' in 7d' })}
1375
1420
  <span class="when" title="${esc(p.path)}">${relTime(p.lastActivityAt)}</span>
1376
1421
  </div>
1377
- ${sessions}
1422
+ ${sessions}${empty}${hiddenNote}
1378
1423
  </div>`;
1379
1424
  }
1380
1425
 
@@ -1383,16 +1428,14 @@ function renderPinned(state) {
1383
1428
  const pins = state.pinned || [];
1384
1429
  el.hidden = !pins.length;
1385
1430
  if (!pins.length) { patch(el, ''); return; }
1386
- patch(el, `<div class="pin-label">Pinned</div>` + pins.map((p) => `
1431
+ patch(el, `<div class="sec-head">Pinned <span class="n">${pins.length}</span></div>` + pins.map((p) => `
1387
1432
  <div class="pin-row">
1388
1433
  <span class="star">★</span>
1389
- <span class="s-title" data-session="${esc(p.sessionId)}" title="Click to read the transcript">${esc(p.title)}</span>${noteMark(p)}
1390
- ${modelChip(p.model)}
1434
+ <span class="s-title" data-session="${esc(p.sessionId)}" title="${esc(titleTip(p.model))}">${esc(p.title)}</span>${noteMark(p)}
1391
1435
  ${changesChip(p)}
1392
1436
  <span class="s-when">${esc(p.projectName)}</span>
1393
1437
  <span class="s-when">${relTime(p.lastActivityAt)}</span>
1394
- ${renameBtn(p.sessionId, p.title)}
1395
- <button class="copy" data-pin="${esc(p.sessionId)}" title="Unpin">unpin</button>
1438
+ <span class="acts">${renameBtn(p.sessionId, p.title)}<button class="copy" data-pin="${esc(p.sessionId)}" title="Unpin">unpin</button></span>
1396
1439
  </div>`).join(''));
1397
1440
  }
1398
1441
 
@@ -1420,7 +1463,7 @@ function renderGitAttention(state) {
1420
1463
  const plural = (n, w) => `${n} ${w}${n === 1 ? '' : 's'}`;
1421
1464
  el.hidden = false;
1422
1465
  patch(el,
1423
- `<span class="ga-label">Unpushed work — ${[items.length && plural(items.length, 'repo'), atRisk.length && plural(atRisk.length, 'worktree')].filter(Boolean).join(', ')}</span>` +
1466
+ `<span class="ga-label">Unpushed work in ${[items.length && plural(items.length, 'repo'), atRisk.length && plural(atRisk.length, 'worktree')].filter(Boolean).join(', ')}</span>` +
1424
1467
  items.map((it) => `<span class="ga-item">${esc(it.name)}${
1425
1468
  it.changes ? `<span class="badge dirty" title="${it.changes} uncommitted/untracked files">●${it.changes}</span>` : ''}${
1426
1469
  it.ahead ? `<span class="badge ahead" title="${it.ahead} commits not pushed">↑${it.ahead}</span>` : ''}</span>`).join('') +
@@ -1469,14 +1512,13 @@ function digestItem(it) {
1469
1512
  return `<div class="digest-item">
1470
1513
  <div class="di-top">
1471
1514
  <span class="di-proj">${esc(it.project)}</span>
1472
- <span class="di-title" data-session="${esc(it.sessionId)}" title="Click to read the transcript">${it.worktree ? `<span class="wt mono">⎇</span> ` : ''}${esc(it.title)}</span>${noteMark(it)}
1515
+ <span class="di-title" data-session="${esc(it.sessionId)}" title="${esc(titleTip(it.model))}">${it.worktree ? `<span class="wt mono">⎇</span> ` : ''}${esc(it.title)}</span>${noteMark(it)}
1473
1516
  ${job ? `<span class="badge bg" title="${BG_TIP}">BG</span>` : ''}
1474
1517
  ${running ? '<span class="flap disp st st-busy">running</span>' : ''}
1475
- ${modelChip(it.model)}
1476
1518
  ${changesChip(it)}
1477
1519
  ${costChip(it.estCost, { cls: 'di-when', assumed: it.assumed })}
1478
1520
  <span class="di-when">${time}</span>
1479
- ${openBtn(it.sessionId)}
1521
+ <span class="acts">${openBtn(it.sessionId)}</span>
1480
1522
  </div>
1481
1523
  ${summary ? `<div class="di-summary" title="Click to expand">${esc(summary)}</div>` : ''}
1482
1524
  ${total ? `<div class="di-tasks"><span class="done">${t.completed}</span>/${total} tasks done${
@@ -1488,12 +1530,13 @@ function digestItem(it) {
1488
1530
  function renderDigest(state) {
1489
1531
  const items = digestEntries(state);
1490
1532
  const label = $('#digest-label');
1491
- label.textContent = `Digest — ${items.length} session${items.length === 1 ? '' : 's'}`;
1533
+ label.textContent = 'Recent sessions';
1534
+ $('#digest-n').textContent = items.length || '';
1492
1535
  for (const b of document.querySelectorAll('#digest-range button')) {
1493
1536
  b.classList.toggle('sel', Number(b.dataset.days) === digestDays);
1494
1537
  }
1495
1538
  if (!items.length) {
1496
- patch($('#digest-body'), `<div class="digest-empty">Quiet — no sessions in the last ${
1539
+ patch($('#digest-body'), `<div class="digest-empty">No sessions in the last ${
1497
1540
  digestDays === 1 ? 'day' : digestDays + ' days'}.</div>`);
1498
1541
  return;
1499
1542
  }
@@ -1536,17 +1579,17 @@ function render(state) {
1536
1579
  : '';
1537
1580
  const b = state.budget;
1538
1581
  const budgetMeter = b
1539
- ? meter('budget', (b.spent / b.limit) * 100, `≈$${b.spent.toFixed(0)} of your $${b.limit} weekly budget (7-day estimate)`)
1582
+ ? meter('Budget', (b.spent / b.limit) * 100, `≈$${b.spent.toFixed(0)} of your $${b.limit} weekly budget (7-day estimate)`)
1540
1583
  : '';
1541
1584
  const spendChip = planChip + (fmtCost(spend)
1542
- ? `${weeklyChart(state.weeklyCost)}${costChip(spend, { cls: '', before: '7d ' })}`
1585
+ ? `<span class="q-spend">${weeklyChart(state.weeklyCost)}${costChip(spend, { cls: 'num' })}<span>in 7 days</span></span>`
1543
1586
  : '') + budgetMeter;
1544
1587
  if (q) {
1545
1588
  qEl.className = q.stale ? 'stale' : '';
1546
1589
  patch(qEl, spendChip +
1547
- (q.utilization != null ? forecastMeter('session', q.utilization, q.resetsAt, state.forecast && state.forecast.session) : '') +
1548
- (q.weeklyUtilization != null ? forecastMeter('week', q.weeklyUtilization, q.weeklyResetsAt, state.forecast && state.forecast.week) : '') +
1549
- (q.costUsed != null && q.costLimit ? `<span title="extra usage">$${q.costUsed.toFixed(0)}/$${q.costLimit.toFixed(0)}</span>` : ''));
1590
+ (q.utilization != null ? forecastMeter('Session', q.utilization, q.resetsAt, state.forecast && state.forecast.session) : '') +
1591
+ (q.weeklyUtilization != null ? forecastMeter('Week', q.weeklyUtilization, q.weeklyResetsAt, state.forecast && state.forecast.week) : '') +
1592
+ (q.costUsed != null && q.costLimit ? `<span title="Extra usage spent this month, of your limit">Extra <span class="num" style="color:var(--ink)">$${q.costUsed.toFixed(0)}</span> of $${q.costLimit.toFixed(0)}</span>` : ''));
1550
1593
  } else {
1551
1594
  qEl.className = '';
1552
1595
  patch(qEl, spendChip);
@@ -1555,7 +1598,7 @@ function render(state) {
1555
1598
  // live rail
1556
1599
  const live = state.liveSessions || [];
1557
1600
  patch($('#live'),
1558
- `<div class="rail-label">Departures — ${live.length || 'none'} running</div>` +
1601
+ `<div class="sec-head">Running now${live.length ? ` <span class="n">${live.length}</span>` : ''}</div>` +
1559
1602
  (live.length
1560
1603
  ? `<div class="rows">${live.map(liveCard).join('')}</div>`
1561
1604
  : `<div class="live-none">No sessions running. Start one with <code class="mono">claude</code> in any project.</div>`));
@@ -1571,7 +1614,8 @@ function render(state) {
1571
1614
  const now = Date.now();
1572
1615
  const active = [], dormant = [];
1573
1616
  for (const p of state.projects || []) {
1574
- const isDormant = !p.isLive && (!p.lastActivityAt || now - p.lastActivityAt > DORMANT_MS);
1617
+ const noiseOnly = !p.sessions.length && p.hidden && (p.hidden.automated || p.hidden.empty);
1618
+ const isDormant = !p.isLive && (noiseOnly || !p.lastActivityAt || now - p.lastActivityAt > DORMANT_MS);
1575
1619
  (isDormant ? dormant : active).push(p);
1576
1620
  }
1577
1621
  patch($('#grid'), active.length || dormant.length
@@ -1583,7 +1627,10 @@ function render(state) {
1583
1627
  </div>`);
1584
1628
  const d = $('#dormant');
1585
1629
  d.hidden = dormant.length === 0;
1586
- $('summary', d).textContent = `Dormant — ${dormant.length} project${dormant.length === 1 ? '' : 's'}`;
1630
+ patch($('summary', d), `<span class="sec-head"><span class="caret">▶</span>Dormant projects <span class="n">${dormant.length}</span></span>`);
1631
+ const gh = $('#grid-head');
1632
+ gh.hidden = !active.length;
1633
+ $('.n', gh).textContent = active.length;
1587
1634
  patch($('.cards', d), dormant.map(projectCard).join(''));
1588
1635
 
1589
1636
  // tab title = attention signal
@@ -1618,6 +1665,7 @@ async function loadOpenPref() {
1618
1665
  const opts = [`<option value="terminal">${esc(c.resolvedTerminal.label)}</option>`];
1619
1666
  if (c.claudeApp) opts.push('<option value="app">Claude app</option>');
1620
1667
  prefEl.innerHTML = opts.join('');
1668
+ $('#open-pref-wrap').hidden = opts.length < 2;
1621
1669
  const stored = localStorage.getItem('openTarget') || 'terminal';
1622
1670
  prefEl.value = stored === 'app' && !c.claudeApp ? 'terminal' : stored;
1623
1671
  } catch {
@@ -1827,18 +1875,24 @@ function toolsetHtml(t, projectPath) {
1827
1875
  function renderDetail(d, name) {
1828
1876
  $('#d-title').textContent = name;
1829
1877
  $('#d-path').textContent = d.path;
1830
- const sessions = d.sessions.length
1831
- ? `<ul class="d-sessions">${d.sessions.map((s) => `
1878
+ const sessionRow = (s) => `
1832
1879
  <li>
1833
- <span class="s-title" data-session="${esc(s.sessionId)}" title="Click to read the transcript">${
1880
+ <span class="s-title" data-session="${esc(s.sessionId)}" title="${esc(titleTip(s.model))}">${
1834
1881
  s.worktree ? `<span class="wt mono">⎇ ${esc(s.worktree)}</span> ` : ''}${esc(s.title)}</span>${noteMark(s)}
1835
1882
  ${changesChip(s)}
1836
1883
  ${costChip(s.estCost, { assumed: s.assumed })}
1837
1884
  <span class="s-when">${relTime(s.lastActivityAt)}</span>
1838
- ${renameBtn(s.sessionId, s.title)}
1839
- ${openBtn(s.sessionId)}
1840
- ${copyBtn(s.resumeCommand)}
1841
- </li>`).join('')}</ul>`
1885
+ <span class="acts">${renameBtn(s.sessionId, s.title)}${openBtn(s.sessionId)}${copyBtn(s.resumeCommand)}</span>
1886
+ </li>`;
1887
+ const real = d.sessions.filter((s) => !s.kind);
1888
+ const noise = d.sessions.filter((s) => s.kind);
1889
+ const noiseTxt = hiddenLabel({
1890
+ automated: noise.filter((s) => s.kind === 'automated').length,
1891
+ empty: noise.filter((s) => s.kind === 'empty').length,
1892
+ });
1893
+ const sessions = d.sessions.length
1894
+ ? (real.length ? `<ul class="d-sessions">${real.map(sessionRow).join('')}</ul>` : '') +
1895
+ (noise.length ? `<details class="d-noise"><summary>${esc(noiseTxt)}</summary><ul class="d-sessions">${noise.map(sessionRow).join('')}</ul></details>` : '')
1842
1896
  : null;
1843
1897
  const caps = [...d.skills, ...d.agents.map((a) => ({ ...a, name: `agent: ${a.name}` })),
1844
1898
  ...d.commands.map((c) => ({ ...c, name: `/${c.name}` }))];
@@ -1875,7 +1929,7 @@ function renderDetail(d, name) {
1875
1929
  const tsOff = ts ? KINDS.reduce((n, k) => n + (ts[k] || []).filter((r) => !r.enabled).length, 0) : 0;
1876
1930
  $('#d-body').innerHTML =
1877
1931
  muteBtn +
1878
- dSection(`Sessions — ${d.sessions.length}`, sessions, 'No sessions recorded.') +
1932
+ dSection(`Sessions — ${d.sessions.filter((s) => !s.kind).length}`, sessions, 'No sessions recorded.') +
1879
1933
  dSection(`What changed — last ${(d.commits || []).length} commits`, commitRows ? commitSummaryLine(d.commitSummary) + `<ul class="d-sessions">${commitRows}</ul>` : null, 'No git history here.') +
1880
1934
  dSection('CLAUDE.md', d.claudeMd.length ? dFiles(d.claudeMd, 'name') : null, 'No CLAUDE.md — sessions here start without project instructions.') +
1881
1935
  dSection(`Memory — ${d.memory.length} file${d.memory.length === 1 ? '' : 's'}`, d.memory.length ? dFiles(d.memory, 'name') : null, 'No memory yet.') +
@@ -3125,7 +3179,7 @@ document.addEventListener('click', async (e) => {
3125
3179
 
3126
3180
  document.addEventListener('click', (e) => {
3127
3181
  const el = e.target.closest('[data-detail]');
3128
- if (el) openDetail(el.dataset.detail, el.textContent);
3182
+ if (el) openDetail(el.dataset.detail, el.dataset.detailName || el.textContent);
3129
3183
  });
3130
3184
  $('#d-close').addEventListener('click', closeDetail);
3131
3185
  $('#drawer-backdrop').addEventListener('click', closeDetail);