@njeybe/nativ 2.6.0 → 2.8.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.
Files changed (80) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +71 -1
  3. package/dist/commands/design-check.d.ts +4 -0
  4. package/dist/commands/design-check.js +76 -0
  5. package/dist/commands/init.d.ts +1 -0
  6. package/dist/commands/init.js +53 -1
  7. package/dist/commands/update.js +46 -0
  8. package/dist/core/design-rules.d.ts +23 -0
  9. package/dist/core/design-rules.js +115 -0
  10. package/dist/core/harness-integrity.js +26 -7
  11. package/dist/core/setup/pm-playbook.d.ts +1 -1
  12. package/dist/core/setup/pm-playbook.js +1 -1
  13. package/dist/index.js +11 -0
  14. package/dist/server/studio-ui/pages/benchmarks/script.js +9 -8
  15. package/dist/server/studio-ui/pages/benchmarks/styles.js +11 -6
  16. package/dist/server/studio-ui/pages/database/browser.js +2 -2
  17. package/dist/server/studio-ui/pages/database/connection.js +6 -5
  18. package/dist/server/studio-ui/pages/database/data-shell.js +12 -17
  19. package/dist/server/studio-ui/pages/database/drift.js +2 -2
  20. package/dist/server/studio-ui/pages/database/events.js +1 -2
  21. package/dist/server/studio-ui/pages/database/explorer.js +6 -6
  22. package/dist/server/studio-ui/pages/database/styles.js +5 -5
  23. package/dist/server/studio-ui/pages/flow/layout.js +1 -1
  24. package/dist/server/studio-ui/pages/flow/styles.js +4 -4
  25. package/dist/server/studio-ui/pages/flow/timeline-view.js +1 -1
  26. package/dist/server/studio-ui/pages/home/script.js +108 -92
  27. package/dist/server/studio-ui/pages/home/styles.js +29 -45
  28. package/dist/server/studio-ui/pages/tasks/script.js +146 -14
  29. package/dist/server/studio-ui/pages/tasks/styles.js +63 -6
  30. package/dist/server/studio-ui/pages/team/script.js +69 -53
  31. package/dist/server/studio-ui/pages/team/styles.js +20 -20
  32. package/dist/server/studio-ui/pages/worktrees/script.js +47 -35
  33. package/dist/server/studio-ui/pages/worktrees/styles.js +12 -10
  34. package/dist/server/studio-ui/shell/brand.d.ts +4 -0
  35. package/dist/server/studio-ui/shell/brand.js +16 -0
  36. package/dist/server/studio-ui/shell/console.js +71 -6
  37. package/dist/server/studio-ui/shell/core.js +8 -8
  38. package/dist/server/studio-ui/shell/detail-view.js +4 -4
  39. package/dist/server/studio-ui/shell/dispatch.js +23 -4
  40. package/dist/server/studio-ui/shell/events-tools.js +16 -1
  41. package/dist/server/studio-ui/shell/format.js +36 -8
  42. package/dist/server/studio-ui/shell/icons.js +23 -15
  43. package/dist/server/studio-ui/shell/markup-head.js +11 -6
  44. package/dist/server/studio-ui/shell/markup-tools.js +29 -15
  45. package/dist/server/studio-ui/shell/model.js +8 -2
  46. package/dist/server/studio-ui/shell/paint.js +4 -3
  47. package/dist/server/studio-ui/shell/pipeline-state.js +2 -11
  48. package/dist/server/studio-ui/shell/proposals.js +6 -2
  49. package/dist/server/studio-ui/shell/pulse.d.ts +1 -0
  50. package/dist/server/studio-ui/shell/pulse.js +68 -0
  51. package/dist/server/studio-ui/shell/router.js +25 -0
  52. package/dist/server/studio-ui/shell/sidebar-view.js +7 -5
  53. package/dist/server/studio-ui/shell/toasts-format.js +4 -4
  54. package/dist/server/studio-ui/shell/triage.js +1 -1
  55. package/dist/server/studio-ui/styles/console.js +36 -13
  56. package/dist/server/studio-ui/styles/dialogs.js +4 -4
  57. package/dist/server/studio-ui/styles/responsive.js +8 -0
  58. package/dist/server/studio-ui/styles/shell.js +23 -6
  59. package/dist/server/studio-ui/styles/strategist.js +10 -10
  60. package/dist/server/studio-ui/styles/tokens-base.js +23 -23
  61. package/dist/server/studio-ui.js +2 -0
  62. package/package.json +1 -1
  63. package/plugin/.claude-plugin/plugin.json +1 -1
  64. package/plugin/agents/architect.md +11 -12
  65. package/plugin/agents/verifier.md +4 -1
  66. package/plugin/skills/nativ/SKILL.md +7 -7
  67. package/templates/AGENTS.md +7 -7
  68. package/templates/GEMINI.md +4 -3
  69. package/templates/claude-agents/architect.md +11 -12
  70. package/templates/claude-agents/verifier.md +4 -1
  71. package/templates/design/taste/LICENSE +21 -0
  72. package/templates/design/taste/SOURCE.md +28 -0
  73. package/templates/design/taste/v1/DIGEST.md +57 -0
  74. package/templates/design/taste/v1/SKILL.md +226 -0
  75. package/templates/design/taste/v2/DIGEST.md +62 -0
  76. package/templates/design/taste/v2/SKILL.md +1206 -0
  77. package/templates/dot-ai/subagents/architect.md +8 -2
  78. package/templates/dot-ai/subagents/flutter-developer.md +2 -11
  79. package/templates/dot-ai/subagents/frontend.md +47 -44
  80. package/templates/dot-ai/ui_specs.md +15 -36
@@ -2,6 +2,7 @@
2
2
  export const modelScript = String.raw ` // ─── Studio client model: buildModel(api) is pure ──────────────────────────
3
3
  var RUN_ACTIVE = { spawning_worktree: 1, running: 1, verifying: 1, merging: 1 };
4
4
  function isRunActive(run) { return !!run && !!RUN_ACTIVE[run.status]; }
5
+ var STALE_MS = 4 * 3600 * 1000;
5
6
 
6
7
  /** Text before the first colon, at most 46 characters. */
7
8
  function shortName(name) {
@@ -46,11 +47,14 @@ export const modelScript = String.raw ` // ─── Studio client model: build
46
47
  if (startT == null && run && isRunActive(run)) startT = toTime(run.startedAt);
47
48
  var cost = b && b.actual ? positive(b.actual.costUsd) : null;
48
49
  if (cost == null && b && b.estimated) cost = positive(b.estimated.costUsd);
50
+ var claimT = toTime(t.claimedAt);
51
+ var stale = running && claimT != null && Studio.now() - claimT > STALE_MS && !isRunActive(run);
49
52
  return {
50
53
  id: t.id,
51
54
  title: t.title || t.id,
52
55
  description: t.description || '',
53
- status: t.status,
56
+ status: stale ? 'pending' : t.status,
57
+ claimedStatus: t.status,
54
58
  agent: t.assignedSubagent || null,
55
59
  deps: t.dependencies || [],
56
60
  files: t.targetFiles || [],
@@ -62,6 +66,8 @@ export const modelScript = String.raw ` // ─── Studio client model: build
62
66
  reason: t.status === 'blocked' && t.notes ? String(t.notes).replace(/^\s+|\s+$/g, '') || null : null,
63
67
  attempt: attemptsOf(t, running),
64
68
  maxAttempts: (t.circuitBreaker && t.circuitBreaker.maxThreshold) || 3,
69
+ claimT: claimT,
70
+ stale: stale,
65
71
  startT: startT,
66
72
  endT: t.status === 'completed' && b ? toTime(b.completedAt) : null,
67
73
  durationMs: b ? positive(b.durationMs) : null,
@@ -175,7 +181,7 @@ export const modelScript = String.raw ` // ─── Studio client model: build
175
181
  tasks: tasks,
176
182
  byId: byId,
177
183
  activeMilestoneId: active,
178
- running: tasks.filter(function (t) { return t.status === 'in_progress'; }),
184
+ running: tasks.filter(function (t) { return t.status === 'in_progress' && !t.stale; }),
179
185
  stuck: tasks.filter(function (t) { return t.status === 'blocked'; }),
180
186
  needsYou: needsYouItems(tasks, byId, api.escalations),
181
187
  summary: summaryOf(api, milestones, tasks)
@@ -67,11 +67,9 @@ export const paintScript = String.raw ` // ─── Painting ─────
67
67
  var side = $('side-nav');
68
68
  var main = mainWorktree();
69
69
  var agents = pipe.worktrees.filter(function (w) { return w.isAgentWorktree; }).length;
70
- var s = pipe.report && pipe.report.summary;
71
70
  var navHtml = Studio.sidebarHtml({
72
71
  model: Studio.model, view: Studio.state.view,
73
- worktreeCount: pipe.loaded.worktrees && !pipe.errors.worktrees ? agents : null,
74
- benchmarkText: s ? compactNum(Number(s.averageThroughputOpsPerSec) || 0) : ''
72
+ worktreeCount: pipe.loaded.worktrees && !pipe.errors.worktrees ? agents : null
75
73
  });
76
74
  if (side.__html !== navHtml) { side.innerHTML = navHtml; side.__html = navHtml; }
77
75
  var foot = $('side-foot');
@@ -79,7 +77,10 @@ export const paintScript = String.raw ` // ─── Painting ─────
79
77
  if (foot.__html !== footHtml) { foot.innerHTML = footHtml; foot.__html = footHtml; }
80
78
  var lost = !!(pipe.errors.tasks || pipe.errors.status) || live.mode === 'offline';
81
79
  $('banner').hidden = !lost;
80
+ Studio.afterPaint.forEach(function (fn) { try { fn(); } catch (e) { /* one hook must not break painting */ } });
82
81
  }
82
+ Studio.afterPaint = Studio.afterPaint || [];
83
+ Studio.onPaint = function (fn) { Studio.afterPaint.push(fn); };
83
84
 
84
85
  /** One entry point after data or state changes: model, sidebar, current page, panel, palette. */
85
86
  function refreshUi(parts) {
@@ -117,18 +117,9 @@ export const pipelineStateScript = String.raw ` // ─── Pipeline state ─
117
117
  return main;
118
118
  }
119
119
 
120
- /** 1234 becomes "1.2k" so the sidebar badge stays small. */
121
- function compactNum(n) {
122
- if (!isFinite(n)) return '–';
123
- if (n >= 1e6) return (n / 1e6).toFixed(1).replace(/\.0$/, '') + 'M';
124
- if (n >= 1e3) return (n / 1e3).toFixed(1).replace(/\.0$/, '') + 'k';
125
- return String(Math.round(n));
126
- }
127
-
128
- /** Newest Just-finished style helpers live in pages; here only what the runner tools need. */
129
120
  var RUN_LABEL = {
130
- spawning_worktree: 'Worktree', running: 'Claude Running', verifying: 'Verifying', merging: 'Merging',
131
- completed: 'Run completed', failed: 'Run failed', aborted: 'Run aborted'
121
+ spawning_worktree: 'Running', running: 'Running', verifying: 'Running', merging: 'Running',
122
+ completed: 'Done', failed: 'Stuck', aborted: 'Up next'
132
123
  };
133
124
  function runFor(taskId) {
134
125
  var found = null;
@@ -111,6 +111,7 @@ export const proposalsScript = String.raw ` // ─── Console tab 3: self-he
111
111
 
112
112
  function openConsole(taskId, quiet, tab) {
113
113
  var drawer = $('runner-console-drawer');
114
+ rememberInspectorOpener();
114
115
  if (rc.taskId !== taskId) {
115
116
  rc.taskId = taskId;
116
117
  rc.backfilled = false;
@@ -130,10 +131,11 @@ export const proposalsScript = String.raw ` // ─── Console tab 3: self-he
130
131
  rc.minimized = false;
131
132
  drawer.hidden = false;
132
133
  drawer.classList.remove('is-min');
133
- $('rc-minimize').textContent = 'Minimize';
134
+ $('rc-minimize').textContent = 'Collapse';
134
135
  $('rc-minimize').setAttribute('aria-expanded', 'true');
135
136
  document.body.classList.add('has-console');
136
137
  document.body.classList.remove('has-console-min');
138
+ applyInspectorMode();
137
139
  syncConsole();
138
140
  syncHealTab();
139
141
  rcTick();
@@ -149,6 +151,8 @@ export const proposalsScript = String.raw ` // ─── Console tab 3: self-he
149
151
  rc.diffTimer = null;
150
152
  $('runner-console-drawer').hidden = true;
151
153
  document.body.classList.remove('has-console', 'has-console-min');
154
+ applyInspectorMode();
155
+ restoreInspectorFocus();
152
156
  }
153
157
 
154
158
  function toggleConsoleMinimized() {
@@ -158,7 +162,7 @@ export const proposalsScript = String.raw ` // ─── Console tab 3: self-he
158
162
  document.body.classList.toggle('has-console', !rc.minimized);
159
163
  document.body.classList.toggle('has-console-min', rc.minimized);
160
164
  var btn = $('rc-minimize');
161
- btn.textContent = rc.minimized ? 'Expand' : 'Minimize';
165
+ btn.textContent = rc.minimized ? 'Expand' : 'Collapse';
162
166
  btn.setAttribute('aria-expanded', String(!rc.minimized));
163
167
  if (!rc.minimized && rc.tab === 'diff' && rc.taskId) loadConsoleDiff(rc.taskId);
164
168
  }
@@ -0,0 +1 @@
1
+ export declare const pulseScript: string;
@@ -0,0 +1,68 @@
1
+ // Pulse strip: active milestone plus running, blocked and decision counts under the header.
2
+ export const pulseScript = String.raw ` // ─── Pulse strip ───────────────────────────────────────────────────────────
3
+ /** Pure: turns raw counts into the strip's parts. in = { loaded, offline, ms, running, blocked, decisions }. */
4
+ function pulseParts(s) {
5
+ if (s.offline) return { kind: 'offline', text: 'Offline, retrying' };
6
+ if (!s.loaded) return { kind: 'loading', text: '-- -- -- --' };
7
+ var counts = [
8
+ { key: 'running', label: 'running', n: s.running, tone: 'run' },
9
+ { key: 'stuck', label: 'stuck', n: s.blocked, tone: 'bad' },
10
+ { key: 'decisions', label: s.decisions === 1 ? 'decision' : 'decisions', n: s.decisions, tone: 'warn' }
11
+ ];
12
+ counts = counts.filter(function (c) { return c.n > 0; });
13
+ counts.forEach(function (c) { c.zero = false; });
14
+ return { kind: 'ready', ms: s.ms, counts: counts };
15
+ }
16
+
17
+ function pulseHtml(p) {
18
+ if (p.kind !== 'ready') {
19
+ return '<span class="pulse-note ' + p.kind + '">' + esc(p.text) + '</span>';
20
+ }
21
+ if (!p.counts.length) return '';
22
+ var out = '<span class="pulse-ms num">' + esc(p.ms) + '</span>';
23
+ p.counts.forEach(function (c) {
24
+ out += '<button type="button" class="pulse-item ' + c.tone + '" data-pulse="' + c.key + '">' +
25
+ '<span class="num">' + c.n + '</span> ' + c.label + '</button>';
26
+ });
27
+ return out;
28
+ }
29
+
30
+ function pulseInput() {
31
+ var m = Studio.model;
32
+ var ms = null;
33
+ if (m) m.milestones.forEach(function (x) { if (x.id === m.activeMilestoneId) ms = x; });
34
+ // Same source as the Home decision queue, so the two counts agree.
35
+ var decisions = m && m.needsYou ? m.needsYou : [];
36
+ return {
37
+ loaded: !!(m && pipe.loaded.tasks),
38
+ offline: live.mode === 'offline',
39
+ ms: ms ? ms.id + ' ' + ms.done + '/' + ms.total : '',
40
+ running: m ? m.running.length : 0,
41
+ blocked: m ? m.stuck.length : 0,
42
+ decisions: decisions.length
43
+ };
44
+ }
45
+
46
+ var pulseEl = $('pulse');
47
+ function paintPulse() {
48
+ if (!pulseEl) return;
49
+ var html = pulseHtml(pulseParts(pulseInput()));
50
+ if (pulseEl.__html === html) return;
51
+ pulseEl.innerHTML = html;
52
+ pulseEl.hidden = !html;
53
+ pulseEl.__html = html;
54
+ }
55
+
56
+ // Studio.taskFilter ('running' or 'stuck') is read by the Tasks page when it opens.
57
+ if (pulseEl) pulseEl.addEventListener('click', function (e) {
58
+ var b = e.target.closest ? e.target.closest('[data-pulse]') : null;
59
+ if (!b) return;
60
+ var key = b.getAttribute('data-pulse');
61
+ if (key === 'decisions') { location.hash = '#home'; return; }
62
+ Studio.taskFilter = key;
63
+ location.hash = '#tasks';
64
+ });
65
+ Studio.onPaint(paintPulse);
66
+ paintPulse();
67
+
68
+ `;
@@ -88,6 +88,31 @@ export const routerScript = String.raw ` // ─── Router and shell-wide eve
88
88
  });
89
89
  }, 1000);
90
90
 
91
+ // Active milestone: the one with running tasks, else the highest-index one.
92
+ Studio.activeMilestone = function (model) {
93
+ var list = (model && model.milestones) || [];
94
+ var run = list.filter(function (m) {
95
+ return m.tasks.some(function (t) { return t.status === 'in_progress'; });
96
+ });
97
+ var pool = run.length ? run : list;
98
+ return pool.length ? pool[pool.length - 1] : null;
99
+ };
100
+
101
+ var autoNavDone = false;
102
+ Studio.autoNav = function () {
103
+ if (autoNavDone || !pipe.loaded.tasks || !Studio.model) return;
104
+ var ms = Studio.activeMilestone(Studio.model);
105
+ if (!ms) return;
106
+ autoNavDone = true;
107
+ var S = Studio.state;
108
+ var opts = { milestoneId: ms.id, keepDetail: true, scroll: false };
109
+ if (!S.view) Studio.go('tasks', opts);
110
+ else { S.milestoneId = ms.id; paintPage(S.view); }
111
+ var el = document.querySelector('[data-milestone="' + cssEsc(ms.id) + '"], [data-ms-group="' + cssEsc(ms.id) + '"]');
112
+ if (el && el.scrollIntoView) el.scrollIntoView({ block: 'start' });
113
+ };
114
+ if (typeof Studio.onPaint === 'function') { Studio.onPaint(function () { Studio.autoNav(); }); }
115
+
91
116
  Studio.api = api;
92
117
  Studio.postJson = postJson;
93
118
  Studio.toast = function (message, kind) { toast(message, kind); };
@@ -5,19 +5,21 @@ export const sidebarViewScript = String.raw ` // ─── Sidebar markup (pure
5
5
  function navBadges(env) {
6
6
  var badges = {}, model = env.model;
7
7
  if (model && model.needsYou.length) badges.home = ['warn', model.needsYou.length];
8
- if (model && model.running.length) badges.tasks = ['run', model.running.length];
8
+ var ready = model ? model.tasks.filter(Studio.status.isReady).length : 0;
9
+ var active = model ? model.running.length + ready : 0;
10
+ if (active) badges.tasks = [model.running.length ? 'run' : '', active];
9
11
  if (env.worktreeCount != null) badges.worktrees = ['', env.worktreeCount];
10
- if (env.benchmarkText) badges.benchmarks = ['', env.benchmarkText];
11
12
  return badges;
12
13
  }
13
14
 
14
- /** env = { model, view, worktreeCount, benchmarkText }. */
15
+ /** env = { model, view, worktreeCount }. */
15
16
  Studio.sidebarHtml = function (env) {
16
17
  var badges = navBadges(env);
17
18
  var item = function (p) {
18
19
  var b = badges[p.id];
19
20
  return '<button type="button" class="nav-item" data-go="' + esc(p.id) + '"' +
20
- (env.view === p.id ? ' aria-current="page"' : '') + '>' + ui.svg(p.icon) +
21
+ (env.view === p.id ? ' aria-current="page"' : '') +
22
+ ' aria-label="' + esc(p.label) + '" title="' + esc(p.label) + '"' + '>' + ui.svg(p.icon) +
21
23
  '<span class="nav-text">' + esc(p.label) + '</span>' +
22
24
  (b ? '<span class="nav-badge' + (b[0] ? ' ' + b[0] : '') + '">' + esc(b[1]) + '</span>' : '') + '</button>';
23
25
  };
@@ -32,7 +34,7 @@ export const sidebarViewScript = String.raw ` // ─── Sidebar markup (pure
32
34
  /** env = { root, branch }. */
33
35
  Studio.sideFootHtml = function (env) {
34
36
  return '<span>Workspace</span><span class="mono" id="sb-root" title="' + esc(env.root || '') + '">' +
35
- esc(env.root || '–') + '</span><span>branch <span class="mono" id="sb-branch-name">' + esc(env.branch || '–') + '</span></span>';
37
+ esc(env.root || '-') + '</span><span>branch <span class="mono" id="sb-branch-name">' + esc(env.branch || '-') + '</span></span>';
36
38
  };
37
39
 
38
40
  `;
@@ -48,16 +48,16 @@ export const toastsFormatScript = String.raw ` // ─── Toasts ────
48
48
 
49
49
  // ─── Formatting ────────────────────────────────────────────────────────────
50
50
  function num(n) {
51
- if (n == null || n === '' || isNaN(n)) return '–';
51
+ if (n == null || n === '' || isNaN(n)) return '-';
52
52
  return Number(n).toLocaleString('en-US', { maximumFractionDigits: 3 });
53
53
  }
54
54
  function usd(n) {
55
- if (n == null || isNaN(n)) return '–';
55
+ if (n == null || isNaN(n)) return '-';
56
56
  n = Number(n);
57
57
  return '$' + (n >= 100 ? n.toFixed(0) : n >= 1 ? n.toFixed(2) : n.toFixed(3));
58
58
  }
59
59
  function fmtMs(ms) {
60
- if (ms == null || isNaN(ms)) return '–';
60
+ if (ms == null || isNaN(ms)) return '-';
61
61
  if (ms < 1000) return Math.round(ms) + ' ms';
62
62
  if (ms < 60000) return (ms / 1000).toFixed(2) + ' s';
63
63
  return Math.floor(ms / 60000) + 'm ' + Math.round((ms % 60000) / 1000) + 's';
@@ -83,7 +83,7 @@ export const toastsFormatScript = String.raw ` // ─── Toasts ────
83
83
  if (typeof v === 'number') return num(v);
84
84
  if (Array.isArray(v)) return v.map(String).join(', ');
85
85
  if (v && typeof v === 'object') return JSON.stringify(v);
86
- return String(v == null ? '–' : v);
86
+ return String(v == null ? '-' : v);
87
87
  }
88
88
 
89
89
  `;
@@ -35,7 +35,7 @@ export const triageScript = String.raw ` // ─── Tier 1 AI Strategist (/ap
35
35
  $('t1-chip-model').textContent = modelName(model);
36
36
  $('t1-model').textContent = model;
37
37
  var last = lastTriage();
38
- $('t1-latency').textContent = last ? fmtMs(last.latencyMs) + ' · ' + modelName(last.model || model) : 'No evaluations yet';
38
+ $('t1-latency').textContent = last ? fmtMs(last.latencyMs) + ', ' + modelName(last.model || model) : 'No evaluations yet';
39
39
  $('t1-key').textContent = pipe.errors.triage && !t ? 'Status unavailable' : !t ? 'Checking' : t.hasApiKey ? 'Configured' : 'Not set (offline rules)';
40
40
  var sw = $('t1-auto');
41
41
  var on = !!(t && t.autoTriageEnabled);
@@ -1,20 +1,40 @@
1
1
  // Runner console drawer and proposal review styles.
2
- export const consoleCss = String.raw `/* Live runner console drawer */
3
- body.has-console { padding-bottom: min(58vh, 520px); }
4
- body.has-console-min { padding-bottom: 56px; }
5
- #runner-console-drawer { position: fixed; inset: auto 0 0 var(--sidebar-w); z-index: 60; display: flex; flex-direction: column; max-height: min(58vh, 520px); background: var(--term-bg); color: var(--term-text); border-top: 1px solid var(--chip-border); box-shadow: var(--shadow-float); animation: rc-slide-up .28s cubic-bezier(.2, .8, .2, 1); }
2
+ export const consoleCss = String.raw `/* Runner inspector: right pane at 1024px and up, modal sheet below. */
3
+ #runner-console-drawer { position: fixed; top: var(--inspector-top, 96px); right: 0; bottom: 0; z-index: 30; width: 320px; display: flex; flex-direction: column; background: var(--term-bg); color: var(--term-text); border-left: 1px solid var(--line); transition: width .2s ease; }
6
4
  #runner-console-drawer[hidden] { display: none; }
7
- #runner-console-drawer.is-min .rc-body, #runner-console-drawer.is-min .rc-foot { display: none; }
8
- @keyframes rc-slide-up { from { transform: translateY(100%); } to { transform: translateY(0); } }
9
- .rc-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 8px 14px; border-bottom: 1px solid var(--term-line); }
5
+ /* Content reflows beside the pane instead of sitting under it. */
6
+ body.has-console main, body:has(#runner-console-drawer:not([hidden])) main { margin-right: 320px; }
7
+ body.has-console-min main { margin-right: 56px; }
8
+ @media (min-width: 1280px) {
9
+ #runner-console-drawer { width: 360px; }
10
+ body.has-console main, body:has(#runner-console-drawer:not([hidden])) main { margin-right: 360px; }
11
+ }
12
+ #runner-console-drawer.is-min { width: 56px; overflow: hidden; }
13
+ #runner-console-drawer.is-min .rc-body, #runner-console-drawer.is-min .rc-foot, #runner-console-drawer.is-min .rc-title, #runner-console-drawer.is-min .rc-steps, #runner-console-drawer.is-min .rc-status, #runner-console-drawer.is-min .rc-clock { display: none; }
14
+ @media (max-width: 1023px) {
15
+ #runner-console-drawer { top: 0; width: min(420px, 92vw); max-width: 92vw; z-index: 40; border-radius: 6px 0 0 6px; box-shadow: var(--shadow); }
16
+ body.has-console main, body.has-console-min main, body:has(#runner-console-drawer:not([hidden])) main { margin-right: 0; }
17
+ }
18
+ /* The task detail dialog stays modal above the inspector. */
19
+ #task-drawer { z-index: 45; }
20
+ .rc-id-row { display: flex; align-items: center; gap: 10px; width: 100%; }
21
+ .rc-status { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--term-muted); }
22
+ .rc-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--term-muted); }
23
+ .rc-status[data-tone="ok"] .rc-dot { background: var(--ok); }
24
+ .rc-status[data-tone="err"] .rc-dot { background: var(--danger); }
25
+ .rc-status[data-tone="run"] .rc-dot { background: var(--warn); }
26
+ .rc-clock { margin-left: auto; font-family: var(--mono); font-size: 12px; font-variant-numeric: tabular-nums; color: var(--term-muted); }
10
27
  .rc-task { font-family: var(--mono); font-size: 12.5px; font-weight: 600; }
11
- .rc-title { font-size: 12.5px; color: var(--term-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 34ch; }
12
- .rc-steps { display: flex; align-items: center; gap: 6px; margin: 0 0 0 auto; padding: 0; list-style: none; }
28
+ .rc-title { font-size: 12.5px; color: var(--term-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; flex: 1 1 100%; }
29
+ .rc-steps { display: flex; align-items: center; gap: 6px; margin: 0; padding: 0; list-style: none; flex-wrap: wrap; }
13
30
  .rc-step { display: inline-flex; align-items: center; gap: 5px; padding: 2px 8px; font-size: 11px; color: var(--term-muted); border: 1px solid var(--term-line); border-radius: var(--r-control); }
14
31
  .rc-step.is-done { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 55%, transparent); }
15
32
  .rc-step.is-active { color: var(--term-text); background: color-mix(in srgb, var(--primary) 32%, transparent); border-color: var(--primary); }
16
33
  .rc-step.is-failed { color: var(--term-text); background: color-mix(in srgb, var(--danger) 32%, transparent); border-color: var(--danger); }
17
- .rc-controls { display: flex; gap: 6px; flex: none; }
34
+ .rc-controls { display: flex; gap: 6px; flex-wrap: wrap; }
35
+ .rc-more { position: relative; }
36
+ .rc-menu { position: absolute; top: calc(100% + 4px); left: 0; z-index: 2; display: flex; flex-direction: column; gap: 4px; padding: 6px; background: var(--term-bg); border: 1px solid var(--term-line); border-radius: var(--r-control); }
37
+ .rc-menu[hidden], .rc-btn[hidden], .rc-steps[hidden], .rc-foot[hidden] { display: none; }
18
38
  .rc-btn { padding: 5px 9px; font: 500 11px/1 var(--font); color: var(--term-text); background: transparent; border: 1px solid var(--term-line); border-radius: var(--r-control); cursor: pointer; }
19
39
  .rc-btn:hover { background: color-mix(in srgb, var(--term-muted) 22%, transparent); }
20
40
  .rc-btn:disabled { opacity: .45; cursor: not-allowed; }
@@ -22,7 +42,7 @@ body.has-console-min { padding-bottom: 56px; }
22
42
  .rc-btn.danger { color: var(--term-text); border-color: var(--danger); }
23
43
  .rc-btn.danger:hover { background: color-mix(in srgb, var(--danger) 30%, transparent); }
24
44
  .rc-btn:focus-visible { outline: 2px solid var(--term-text); outline-offset: 2px; }
25
- .rc-body { flex: 1 1 auto; min-height: 120px; margin: 0; padding: 10px 14px; overflow: auto; font-family: var(--mono); font-size: 12.5px; line-height: 1.55; white-space: pre-wrap; overflow-wrap: anywhere; }
45
+ .rc-body { flex: 1 1 auto; min-height: 120px; margin: 0; padding: 10px 14px; overflow: auto; font-family: var(--mono); font-size: 12.5px; font-variant-numeric: tabular-nums; line-height: 1.55; white-space: pre-wrap; overflow-wrap: anywhere; }
26
46
  .rc-body:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
27
47
  .rc-empty { color: var(--term-muted); }
28
48
  .rc-fg-muted { color: var(--term-muted); }
@@ -31,10 +51,10 @@ body.has-console-min { padding-bottom: 56px; }
31
51
  .rc-fg-info { color: var(--accent); }
32
52
  .rc-fg-danger { color: color-mix(in srgb, var(--danger) 55%, #fff); }
33
53
  .rc-bold { font-weight: 700; }
34
- .rc-foot { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; padding: 7px 14px; font-size: 11.5px; color: var(--term-muted); border-top: 1px solid var(--term-line); }
54
+ .rc-foot { font-variant-numeric: tabular-nums; display: flex; align-items: center; gap: 16px; flex-wrap: wrap; padding: 7px 14px; font-size: 11.5px; color: var(--term-muted); border-top: 1px solid var(--term-line); }
35
55
  .rc-foot strong { font-family: var(--mono); font-weight: 600; color: var(--term-text); }
36
56
  #runner-console-drawer.is-min .rc-tabs { display: none; }
37
- .rc-tabs { display: flex; gap: 2px; padding: 0 14px; border-bottom: 1px solid var(--term-line); }
57
+ .rc-tabs { display: flex; gap: 2px; padding: 0 10px; overflow-x: auto; border-bottom: 1px solid var(--line); }
38
58
  .rc-tab { display: inline-flex; align-items: center; gap: 6px; padding: 8px 10px; font: 500 12px/1 var(--font); color: var(--term-muted); background: none; border: none; border-bottom: 2px solid transparent; margin-bottom: -1px; cursor: pointer; }
39
59
  .rc-tab:hover { color: var(--term-text); }
40
60
  .rc-tab[aria-selected="true"] { color: var(--term-text); border-bottom-color: var(--primary); }
@@ -80,4 +100,7 @@ body.has-console-min { padding-bottom: 56px; }
80
100
  .heal-result.is-error { color: color-mix(in srgb, var(--danger) 55%, #fff); border-color: color-mix(in srgb, var(--danger) 45%, transparent); }
81
101
  .heal-skel { height: 12px; margin: 8px 0; border-radius: 4px; background: var(--term-line); animation: blink 1.4s ease-in-out infinite; }
82
102
  @media (max-width: 768px) { .heal-grid { grid-template-columns: 1fr; } }
103
+ @media (prefers-reduced-motion: reduce) {
104
+ #runner-console-drawer, #runner-console-drawer * { transition: none !important; animation: none !important; }
105
+ }
83
106
  `;
@@ -2,7 +2,7 @@
2
2
  export const dialogsCss = String.raw `/* Toasts */
3
3
  /* Bottom-right so notifications never cover the sticky header and navigation. */
4
4
  .toast-host { position: fixed; right: 16px; bottom: 16px; z-index: 100; display: flex; flex-direction: column; gap: 8px; width: min(440px, calc(100vw - 32px)); pointer-events: none; }
5
- .toast { pointer-events: auto; display: flex; gap: 10px; align-items: flex-start; padding: 12px 14px; font-size: 13.5px; background: var(--surface); border: 1px solid var(--border); border-left: 3px solid var(--danger); border-radius: var(--r-card); box-shadow: var(--shadow-float); animation: toast-in .18s ease-out; }
5
+ .toast { pointer-events: auto; display: flex; gap: 10px; align-items: flex-start; padding: 12px 14px; font-size: 13.5px; background: var(--surface); border: 1px solid var(--line-2); border-left: 3px solid var(--danger); border-radius: var(--r-card); box-shadow: var(--shadow); animation: toast-in .18s ease-out; }
6
6
  .toast.ok { border-left-color: var(--success); }
7
7
  .toast .t-body { flex: 1; min-width: 0; overflow-wrap: anywhere; }
8
8
  .toast .t-hint { color: var(--muted); font-size: 12.5px; }
@@ -12,8 +12,8 @@ export const dialogsCss = String.raw `/* Toasts */
12
12
  /* Dialogs */
13
13
  dialog { border: none; padding: 0; background: transparent; color: var(--text); width: min(640px, calc(100vw - 32px)); max-height: calc(100vh - 32px); }
14
14
  dialog.narrow { width: min(460px, calc(100vw - 32px)); }
15
- dialog::backdrop { background: rgb(10 10 20 / 0.4); backdrop-filter: blur(2px); }
16
- .modal { padding: 22px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-card); box-shadow: var(--shadow-float); }
15
+ dialog::backdrop { background: color-mix(in srgb, var(--text) 40%, transparent); backdrop-filter: blur(2px); }
16
+ .modal { padding: 22px; background: var(--surface); border: 1px solid var(--line-2); border-radius: var(--r-card); box-shadow: var(--shadow); }
17
17
  .modal h2 { margin: 0 0 4px; font-size: 18px; font-weight: 600; }
18
18
  .modal .lead { color: var(--muted); margin: 0 0 16px; font-size: 14px; line-height: 1.55; }
19
19
  .field { margin-bottom: 16px; }
@@ -112,7 +112,7 @@ textarea.cmd-preview { min-height: 92px; font-family: var(--mono); font-size: 12
112
112
  .switch-text { display: flex; flex-direction: column; font-size: 13.5px; }
113
113
  .switch-text b { font-weight: 600; }
114
114
  .switch-text span { font-size: 12.5px; color: var(--muted); }
115
- kbd { padding: 0 5px; font-family: var(--mono); font-size: 11px; color: var(--muted); background: var(--chip); border: 1px solid var(--chip-border); border-radius: 4px; }
115
+ kbd { padding: 0 5px; font-family: var(--mono); font-size: 11px; color: var(--muted); background: var(--chip); border: 1px solid var(--chip-border); border-radius: var(--r-control); }
116
116
  .btn.primary kbd { color: var(--accent-fg); background: transparent; border-color: color-mix(in srgb, var(--accent-fg) 45%, transparent); }
117
117
 
118
118
  `;
@@ -1,5 +1,13 @@
1
1
  // Responsive rules for the kept pages and dialogs.
2
2
  export const responsiveCss = String.raw `/* Responsive */
3
+ @media (max-width: 1023px) and (min-width: 761px) {
4
+ :root { --sidebar-w: 56px; }
5
+ .side { padding: 16px 8px; align-items: center; }
6
+ .brand { padding: 0; justify-content: center; }
7
+ .brand .wordmark { height: 16px; width: 16px; overflow: hidden; }
8
+ .brand b, .brand .ver, .nav-glabel, .nav-text, .nav-badge, .side-foot { display: none; }
9
+ .nav-item { justify-content: center; padding: 6px; width: 40px; }
10
+ }
3
11
  @media (max-width: 900px) {
4
12
  #runner-console-drawer { inset: auto 0 0 0; }
5
13
  .panes { grid-template-columns: 1fr; }
@@ -3,22 +3,23 @@ export const shellCss = String.raw `/* Layout */
3
3
  .app { display: grid; grid-template-columns: var(--sidebar-w) minmax(0, 1fr); min-height: 100vh; }
4
4
  .side { border-right: 1px solid var(--line); background: var(--surface); padding: 16px 12px; display: flex; flex-direction: column; gap: 20px; position: sticky; top: 0; height: 100vh; overflow-y: auto; }
5
5
  .brand { display: flex; align-items: center; gap: 10px; padding: 0 6px; }
6
- .logo { width: 26px; height: 26px; border-radius: 7px; background: var(--accent); color: var(--accent-fg); display: grid; place-items: center; font-weight: 700; font-size: 14px; flex: none; }
6
+ .wordmark { height: 18px; width: auto; color: var(--fg); display: block; flex: none; }
7
+ .wordmark.is-rail { height: 16px; }
7
8
  .brand b { font-weight: 600; white-space: nowrap; }
8
- .brand .ver { margin-left: auto; font: 11px var(--mono); color: var(--fg-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
9
+ .side-foot .ver { font: 11px var(--mono); color: var(--fg-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
9
10
  .nav { display: flex; flex-direction: column; gap: 14px; }
10
11
  .nav-group { display: flex; flex-direction: column; gap: 2px; }
11
12
  .nav-glabel { font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--fg-3); padding: 0 8px 4px; }
12
13
  .nav-item { display: flex; align-items: center; gap: 10px; min-height: 32px; padding: 7px 8px; border-radius: 7px; border: 0; background: none; text-align: left; color: var(--fg-2); width: 100%; cursor: pointer; transition: background .15s, color .15s; }
13
- .nav-item svg { width: 16px; height: 16px; flex: none; }
14
+ .nav-item svg { width: 20px; height: 20px; flex: none; }
14
15
  .nav-item:hover { background: var(--surface-2); color: var(--fg); }
15
- .nav-item[aria-current="page"] { background: var(--accent-soft); color: var(--accent); font-weight: 500; }
16
+ .nav-item[aria-current="page"] { background: var(--accent-soft); color: var(--fg); font-weight: 600; }
16
17
  .nav-badge { margin-left: auto; font: 500 11px var(--font); font-variant-numeric: tabular-nums; min-width: 20px; padding: 1px 6px; border-radius: 999px; background: var(--surface-2); color: var(--fg-2); text-align: center; }
17
18
  .nav-badge.warn { background: var(--warn-soft); color: var(--warn); }
18
19
  .nav-badge.run { background: var(--run-soft); color: var(--run); }
19
20
  .side-foot { margin-top: auto; font-size: 12px; color: var(--fg-3); padding: 0 8px; display: flex; flex-direction: column; gap: 4px; }
20
21
  .side-foot > div { display: flex; flex-direction: column; gap: 4px; }
21
- .side-foot .mono { color: var(--fg-2); font-size: 11px; word-break: break-all; }
22
+ .side-foot .mono { color: var(--fg-2); font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
22
23
  .foot-project { color: var(--fg); font-weight: 500; }
23
24
  .main { min-width: 0; display: flex; flex-direction: column; }
24
25
  .top { position: sticky; top: env(safe-area-inset-top, 0px); z-index: 20; background: color-mix(in srgb, var(--bg) 88%, transparent); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); border-bottom: 1px solid var(--line); display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 10px 24px; }
@@ -27,6 +28,22 @@ export const shellCss = String.raw `/* Layout */
27
28
  .top-right { margin-left: auto; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
28
29
  .btn.ghost { background: none; border-color: transparent; color: var(--fg-2); box-shadow: none; }
29
30
  .btn.ghost:hover { background: var(--surface-2); }
31
+ .spacer { flex: 1; }
32
+ .ic-sun, .ic-moon { display: inline-flex; }
33
+ .ic-moon { display: none; }
34
+ :root[data-theme="light"] .ic-sun { display: none; }
35
+ :root[data-theme="light"] .ic-moon { display: inline-flex; }
36
+ @media (prefers-color-scheme: light) { :root:not([data-theme="dark"]) .ic-sun { display: none; } :root:not([data-theme="dark"]) .ic-moon { display: inline-flex; } }
37
+ #pulse[hidden] { display: none; }
38
+ .pulse-strip { min-height: 32px; height: auto; width: auto; border-radius: 0; background: none; box-shadow: none; animation: none; display: flex; flex-wrap: wrap; align-items: center; gap: 0 20px; padding: 0 24px; border-bottom: 1px solid var(--line); font: 12.5px var(--mono); color: var(--fg-2); }
39
+ #pulse .num { font-variant-numeric: tabular-nums; }
40
+ #pulse .pulse-ms { color: var(--fg); padding-right: 20px; border-right: 1px solid var(--line); }
41
+ #pulse .pulse-item { display: inline-flex; align-items: center; gap: 6px; min-height: 32px; padding: 0; border: 0; background: none; font: inherit; cursor: pointer; }
42
+ #pulse .pulse-note.loading { color: var(--fg-3); }
43
+ #pulse .pulse-item.run { color: var(--run); } #pulse .pulse-item.bad { color: var(--bad); } #pulse .pulse-item.warn, #pulse .pulse-note.offline { color: var(--warn); }
44
+ @media (max-width: 1023px) { .pulse-strip { padding-top: 4px; padding-bottom: 4px; gap: 0 16px; } #pulse .pulse-ms { flex-basis: 100%; border-right: 0; padding-right: 0; } }
45
+ .wordmark.is-rail { display: none; }
46
+ @media (max-width: 1023px) { .wordmark { display: none; } .wordmark.is-rail { display: block; } }
30
47
  .btn svg { width: 14px; height: 14px; }
31
48
  .kbd { font: 11px var(--mono); padding: 0 5px; border: 1px solid var(--line); border-radius: 4px; color: var(--fg-3); }
32
49
  .live { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--fg-2); }
@@ -113,5 +130,5 @@ export const shellCss = String.raw `/* Layout */
113
130
  .drawer { width: 100%; }
114
131
  #runner-console-drawer { inset: auto 0 0 0; }
115
132
  }
116
- @media (prefers-reduced-motion: reduce) { .pulse, .live-dot.flash { animation: none; } * { transition: none !important; } }
133
+ @media (prefers-reduced-motion: reduce) { .pulse, .live-dot.flash { animation: none !important; } * { transition: none !important; } }
117
134
  `;
@@ -12,30 +12,30 @@ export const strategistCss = String.raw `/* Tier 1 AI Strategist (ui_specs.md §
12
12
  .t1-chip-label { color: var(--muted); }
13
13
  .t1-caret { color: var(--muted); transition: transform .15s; }
14
14
  .t1-chip[aria-expanded="true"] .t1-caret { transform: rotate(180deg); }
15
- .t1-menu { position: absolute; top: calc(100% + 6px); right: 0; z-index: 30; width: 300px; padding: 12px; font-size: 12.5px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-card); box-shadow: var(--shadow-float); }
15
+ .t1-menu { position: absolute; top: calc(100% + 6px); right: 0; z-index: 30; width: 300px; padding: 12px; font-size: 12.5px; background: var(--surface); border: 1px solid var(--line-2); border-radius: var(--r-card); box-shadow: var(--shadow); }
16
16
  .t1-menu:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
17
17
  .t1-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 28px; color: var(--muted); }
18
18
  .t1-row b, .t1-row code { color: var(--text); font-weight: 500; font-variant-numeric: tabular-nums; }
19
19
  .t1-row code { font-family: var(--mono); font-size: 12px; }
20
- .t1-switch { position: relative; width: 54px; height: 24px; padding: 0 8px; font: 600 10.5px/22px var(--mono); color: var(--muted); text-align: right; background: var(--chip); border: 1px solid var(--chip-border); border-radius: 12px; cursor: pointer; transition: background .15s, border-color .15s; }
20
+ .t1-switch { position: relative; width: 54px; height: 24px; padding: 0 8px; font: 600 10.5px/22px var(--mono); color: var(--muted); text-align: right; background: var(--chip); border: 1px solid var(--chip-border); border-radius: var(--r-control); cursor: pointer; transition: background .15s, border-color .15s; }
21
21
  .t1-switch::before { content: ""; position: absolute; top: 3px; left: 3px; width: 16px; height: 16px; border-radius: 50%; background: var(--surface); box-shadow: var(--shadow); transition: transform .15s; }
22
- .t1-switch[aria-checked="true"] { color: #fff; text-align: left; background: var(--success); border-color: var(--success); }
22
+ .t1-switch[aria-checked="true"] { color: var(--accent-fg); text-align: left; background: var(--success); border-color: var(--success); }
23
23
  .t1-switch[aria-checked="true"]::before { transform: translateX(30px); }
24
24
  .t1-switch:disabled { opacity: .5; cursor: not-allowed; }
25
25
  .t1-policy { display: flex; gap: 6px; margin: 8px 0; padding: 8px; line-height: 1.45; color: var(--muted); background: var(--bg); border: 1px solid var(--border); border-radius: var(--r-control); }
26
26
  .t1-policy .icon { flex: none; margin-top: 1px; color: var(--success); }
27
27
  .t1-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; margin: 0; }
28
- .t1-stats div { min-width: 0; padding: 6px 8px; border: 1px solid var(--border); border-radius: var(--r-control); }
28
+ .t1-stats div { min-width: 0; padding: 6px 8px; border: 0; border-left: 1px solid var(--line); border-radius: 0; font-variant-numeric: tabular-nums; }
29
29
  .t1-stats dt { font-size: 11px; color: var(--muted); }
30
30
  .t1-stats dd { margin: 2px 0 0; font: 600 15px/1.2 var(--mono); font-variant-numeric: tabular-nums; color: var(--text); }
31
31
  .t1-strip { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; margin: 0 0 10px; padding: 8px 10px; color: var(--term-muted); border: 1px solid var(--term-line); border-radius: var(--r-control); }
32
32
  .t1-strip > span { display: inline-flex; align-items: center; gap: 6px; }
33
33
  .t1-strip strong { color: var(--term-text); font-weight: 600; }
34
34
  .t1-strip.is-triaging { border-color: color-mix(in srgb, var(--active) 60%, transparent); background: color-mix(in srgb, var(--active) 14%, transparent); }
35
- .rc-btn.t1-run { color: #fff; background: var(--active); border-color: var(--active); }
36
- .rc-btn.t1-run:hover:not(:disabled) { background: color-mix(in srgb, var(--active) 88%, #000); }
35
+ .rc-btn.t1-run { color: var(--accent-fg); background: var(--active); border-color: var(--active); }
36
+ .rc-btn.t1-run:hover:not(:disabled) { background: color-mix(in srgb, var(--active) 88%, var(--text)); }
37
37
  .t1-banner { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; padding: 8px 10px; font-weight: 600; border-radius: var(--r-control); }
38
- .t1-banner.is-resolved { color: #fff; background: var(--success); }
38
+ .t1-banner.is-resolved { color: var(--accent-fg); background: var(--success); }
39
39
  .t1-banner.is-safe { color: var(--ok); border: 1px solid color-mix(in srgb, var(--ok) 45%, transparent); }
40
40
  .t1-card { margin: 0 0 12px; padding: 12px 12px 12px 16px; border: 1px solid var(--term-line); border-left: 4px solid var(--proposal); border-radius: var(--r-card); }
41
41
  .t1-card-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
@@ -53,10 +53,10 @@ export const strategistCss = String.raw `/* Tier 1 AI Strategist (ui_specs.md §
53
53
  .t1-opt:hover { border-color: var(--term-text); }
54
54
  .t1-opt.is-recommended { background: var(--primary); border-color: var(--primary); }
55
55
  .t1-opt.is-recommended:hover { background: var(--primary-hover); border-color: var(--primary-hover); }
56
- .t1-opt.is-recommended span { color: color-mix(in srgb, #fff 82%, var(--primary)); }
56
+ .t1-opt.is-recommended span { color: color-mix(in srgb, var(--accent-fg) 82%, var(--primary)); }
57
57
  .t1-opt[aria-checked="true"] { box-shadow: 0 0 0 2px var(--term-bg), 0 0 0 4px var(--proposal); }
58
58
  .t1-custom { display: flex; flex-direction: column; gap: 4px; margin-top: 8px; font-size: 12px; color: var(--term-muted); }
59
- .t1-custom textarea { width: 100%; box-sizing: border-box; min-height: 52px; padding: 6px 8px; font: 12.5px/1.45 var(--font); color: var(--term-text); background: rgb(148 163 184 / 0.08); border: 1px solid var(--term-line); border-radius: var(--r-control); resize: vertical; }
59
+ .t1-custom textarea { width: 100%; box-sizing: border-box; min-height: 52px; padding: 6px 8px; font: 12.5px/1.45 var(--font); color: var(--term-text); background: color-mix(in srgb, var(--term-text) 8%, transparent); border: 1px solid var(--term-line); border-radius: var(--r-control); resize: vertical; }
60
60
  .t1-custom textarea:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
61
61
  .t1-decided { margin: 8px 0 0; font-size: 12px; color: var(--term-muted); }
62
62
  @media (prefers-reduced-motion: reduce) { .t1-chip[data-state="triaging"] .t1-spark { animation: none; } }
@@ -65,7 +65,7 @@ export const strategistCss = String.raw `/* Tier 1 AI Strategist (ui_specs.md §
65
65
  .t1-menu { position: fixed; top: 56px; left: 16px; right: 16px; width: auto; }
66
66
  }
67
67
  .df-add { display: block; color: var(--ok); background: color-mix(in srgb, var(--ok) 12%, transparent); }
68
- .df-del { display: block; color: color-mix(in srgb, var(--danger) 55%, #fff); background: color-mix(in srgb, var(--danger) 14%, transparent); }
68
+ .df-del { display: block; color: color-mix(in srgb, var(--danger) 55%, var(--accent-fg)); background: color-mix(in srgb, var(--danger) 14%, transparent); }
69
69
  .df-hunk { display: block; color: var(--accent); }
70
70
  .df-file { display: block; margin-top: 8px; font-weight: 700; color: var(--term-text); }
71
71
  .df-meta { display: block; color: var(--term-muted); }