@rigour-labs/cli 2.11.0 → 2.12.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.
@@ -11,262 +11,282 @@ export const studioCommand = new Command('studio')
11
11
  .option('--dev', 'Run in development mode', true)
12
12
  .action(async (options) => {
13
13
  const cwd = process.cwd();
14
- // Calculate the workspace root where the studio package lives
15
- // This file is in packages/rigour-cli/src/commands/studio.ts (or dist/commands/studio.js)
16
- const workspaceRoot = path.join(path.dirname(new URL(import.meta.url).pathname), '../../../../');
14
+ const apiPort = parseInt(options.port) + 1;
15
+ const eventsPath = path.join(cwd, '.rigour/events.jsonl');
16
+ // Calculate the local dist path (where the pre-built Studio UI lives)
17
+ const __dirname = path.dirname(new URL(import.meta.url).pathname);
18
+ const localStudioDist = path.join(__dirname, '../studio-dist');
19
+ const workspaceRoot = path.join(__dirname, '../../../../');
17
20
  console.log(chalk.bold.cyan('\n🛡️ Launching Rigour Studio...'));
18
21
  console.log(chalk.gray(`Project Root: ${cwd}`));
22
+ // Pre-flight check: Is the project initialized?
23
+ const configPath = path.join(cwd, 'rigour.yml');
24
+ if (!(await fs.pathExists(configPath))) {
25
+ console.log(chalk.yellow('\n⚠️ Warning: rigour.yml not found.'));
26
+ console.log(chalk.dim('The Studio will be empty until you initialize the project.'));
27
+ console.log(chalk.cyan('Suggest: ') + chalk.bold('npx @rigour-labs/cli init') + '\n');
28
+ }
19
29
  console.log(chalk.gray(`Shadowing interactions in ${path.join(cwd, '.rigour/events.jsonl')}\n`));
20
- // For Phase 1, we start the studio in dev mode via pnpm
21
- // This ensures the user has a live, hot-reloading governance dashboard
22
- try {
23
- // Start the Studio dev server in the workspace root
24
- const studioProcess = execa('pnpm', ['--filter', '@rigour-labs/studio', 'dev', '--port', options.port], {
25
- stdio: 'inherit',
26
- shell: true,
27
- cwd: workspaceRoot
28
- });
29
- // Start a small API server for events on port + 1
30
- const apiPort = parseInt(options.port) + 1;
31
- const eventsPath = path.join(cwd, '.rigour/events.jsonl');
32
- const apiServer = http.createServer(async (req, res) => {
33
- const url = new URL(req.url || '', `http://${req.headers.host || 'localhost'}`);
34
- // Set global CORS headers
35
- res.setHeader('Access-Control-Allow-Origin', '*');
36
- res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
37
- res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
38
- if (req.method === 'OPTIONS') {
39
- res.writeHead(204);
40
- res.end();
41
- return;
30
+ // Check if we are in a monorepo development environment
31
+ const isMonorepo = await fs.pathExists(path.join(workspaceRoot, 'packages/rigour-studio'));
32
+ if (isMonorepo && options.dev) {
33
+ console.log(chalk.yellow('Monorepo detected: Launching Studio in Development Mode...'));
34
+ try {
35
+ // Start the Studio dev server in the workspace root
36
+ const studioProcess = execa('pnpm', ['--filter', '@rigour-labs/studio', 'dev', '--port', options.port], {
37
+ stdio: 'inherit',
38
+ shell: true,
39
+ cwd: workspaceRoot
40
+ });
41
+ await setupApiAndLaunch(apiPort, options.port, eventsPath, cwd, studioProcess);
42
+ return;
43
+ }
44
+ catch (e) {
45
+ console.log(chalk.dim('Development mode failed, falling back to standalone...'));
46
+ }
47
+ }
48
+ // Standalone Mode: Serve pre-built static files
49
+ console.log(chalk.green('Launching Studio in Standalone Mode...'));
50
+ if (!(await fs.pathExists(localStudioDist))) {
51
+ console.error(chalk.red(`\n❌ Error: Studio UI artifacts not found at ${localStudioDist}`));
52
+ console.log(chalk.yellow('If you are a developer, run "pnpm build" in the monorepo root first.\n'));
53
+ process.exit(1);
54
+ }
55
+ const staticServer = http.createServer(async (req, res) => {
56
+ const url = new URL(req.url || '', `http://${req.headers.host || 'localhost'}`);
57
+ let filePath = path.join(localStudioDist, url.pathname === '/' ? 'index.html' : url.pathname);
58
+ try {
59
+ if (!(await fs.pathExists(filePath)) || (await fs.stat(filePath)).isDirectory()) {
60
+ filePath = path.join(localStudioDist, 'index.html');
42
61
  }
43
- if (url.pathname === '/api/events') {
44
- res.writeHead(200, {
45
- 'Content-Type': 'text/event-stream',
46
- 'Cache-Control': 'no-cache',
47
- 'Connection': 'keep-alive'
48
- });
49
- // Send existing events first
50
- if (await fs.pathExists(eventsPath)) {
51
- const content = await fs.readFile(eventsPath, 'utf8');
52
- const lines = content.split('\n').filter(l => l.trim());
53
- for (const line of lines) {
54
- res.write(`data: ${line}\n\n`);
55
- }
62
+ const content = await fs.readFile(filePath);
63
+ const ext = path.extname(filePath);
64
+ const contentTypes = {
65
+ '.html': 'text/html',
66
+ '.js': 'application/javascript',
67
+ '.css': 'text/css',
68
+ '.json': 'application/json',
69
+ '.png': 'image/png',
70
+ '.jpg': 'image/jpeg',
71
+ '.svg': 'image/svg+xml',
72
+ '.ico': 'image/x-icon'
73
+ };
74
+ res.writeHead(200, { 'Content-Type': contentTypes[ext] || 'application/octet-stream' });
75
+ res.end(content);
76
+ }
77
+ catch (e) {
78
+ res.writeHead(404);
79
+ res.end('Not Found');
80
+ }
81
+ });
82
+ staticServer.listen(options.port, () => {
83
+ setupApiAndLaunch(apiPort, options.port, eventsPath, cwd);
84
+ });
85
+ });
86
+ async function setupApiAndLaunch(apiPort, studioPort, eventsPath, cwd, studioProcess) {
87
+ const apiServer = http.createServer(async (req, res) => {
88
+ const url = new URL(req.url || '', `http://${req.headers.host || 'localhost'}`);
89
+ res.setHeader('Access-Control-Allow-Origin', '*');
90
+ res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS, POST');
91
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
92
+ if (req.method === 'OPTIONS') {
93
+ res.writeHead(204);
94
+ res.end();
95
+ return;
96
+ }
97
+ if (url.pathname === '/api/events') {
98
+ res.writeHead(200, {
99
+ 'Content-Type': 'text/event-stream',
100
+ 'Cache-Control': 'no-cache',
101
+ 'Connection': 'keep-alive'
102
+ });
103
+ if (await fs.pathExists(eventsPath)) {
104
+ const content = await fs.readFile(eventsPath, 'utf8');
105
+ const lines = content.split('\n').filter(l => l.trim());
106
+ for (const line of lines) {
107
+ res.write(`data: ${line}\n\n`);
56
108
  }
57
- // Watch for new events
58
- await fs.ensureDir(path.dirname(eventsPath));
59
- const watcher = fs.watch(path.dirname(eventsPath), (eventType, filename) => {
60
- if (filename === 'events.jsonl') {
61
- fs.readFile(eventsPath, 'utf8').then(content => {
62
- const lines = content.split('\n').filter(l => l.trim());
63
- const lastLine = lines[lines.length - 1];
64
- if (lastLine) {
65
- res.write(`data: ${lastLine}\n\n`);
66
- }
67
- }).catch(() => { });
68
- }
69
- });
70
- req.on('close', () => watcher.close());
71
109
  }
72
- else if (url.pathname === '/api/file') {
73
- const filePath = url.searchParams.get('path');
74
- if (!filePath) {
75
- res.writeHead(400);
76
- res.end('Missing path parameter');
77
- return;
78
- }
79
- const absolutePath = path.resolve(cwd, filePath);
80
- if (!absolutePath.startsWith(cwd)) {
81
- res.writeHead(403);
82
- res.end('Forbidden: Path outside project root');
83
- return;
110
+ await fs.ensureDir(path.dirname(eventsPath));
111
+ const watcher = fs.watch(path.dirname(eventsPath), (eventType, filename) => {
112
+ if (filename === 'events.jsonl') {
113
+ fs.readFile(eventsPath, 'utf8').then(content => {
114
+ const lines = content.split('\n').filter(l => l.trim());
115
+ const lastLine = lines[lines.length - 1];
116
+ if (lastLine) {
117
+ res.write(`data: ${lastLine}\n\n`);
118
+ }
119
+ }).catch(() => { });
84
120
  }
85
- try {
86
- const stats = await fs.stat(absolutePath);
87
- if (!stats.isFile()) {
88
- res.writeHead(400);
89
- res.end('Path is not a file');
90
- return;
121
+ });
122
+ req.on('close', () => watcher.close());
123
+ }
124
+ else if (url.pathname === '/api/file') {
125
+ const filePath = url.searchParams.get('path');
126
+ if (!filePath) {
127
+ res.writeHead(400);
128
+ res.end('Missing path');
129
+ return;
130
+ }
131
+ const absolutePath = path.resolve(cwd, filePath);
132
+ if (!absolutePath.startsWith(cwd)) {
133
+ res.writeHead(403);
134
+ res.end('Forbidden');
135
+ return;
136
+ }
137
+ try {
138
+ const content = await fs.readFile(absolutePath, 'utf8');
139
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
140
+ res.end(content);
141
+ }
142
+ catch {
143
+ res.writeHead(404);
144
+ res.end('Not found');
145
+ }
146
+ }
147
+ else if (url.pathname === '/api/tree') {
148
+ try {
149
+ const getTree = async (dir) => {
150
+ const entries = await fs.readdir(dir, { withFileTypes: true });
151
+ let files = [];
152
+ const exclude = ['node_modules', '.git', '.rigour', '.venv', 'dist', 'build'];
153
+ for (const entry of entries) {
154
+ if (exclude.includes(entry.name) || entry.name.startsWith('.'))
155
+ continue;
156
+ const fullPath = path.join(dir, entry.name);
157
+ if (entry.isDirectory()) {
158
+ files = [...files, ...(await getTree(fullPath))];
159
+ }
160
+ else {
161
+ files.push(path.relative(cwd, fullPath));
162
+ }
91
163
  }
92
- const content = await fs.readFile(absolutePath, 'utf8');
164
+ return files;
165
+ };
166
+ res.writeHead(200, { 'Content-Type': 'application/json' });
167
+ res.end(JSON.stringify(await getTree(cwd)));
168
+ }
169
+ catch (e) {
170
+ res.writeHead(500);
171
+ res.end(e.message);
172
+ }
173
+ }
174
+ else if (url.pathname === '/api/config') {
175
+ try {
176
+ const configPath = path.join(cwd, 'rigour.yml');
177
+ if (await fs.pathExists(configPath)) {
93
178
  res.writeHead(200, { 'Content-Type': 'text/plain' });
94
- res.end(content);
179
+ res.end(await fs.readFile(configPath, 'utf8'));
95
180
  }
96
- catch (e) {
181
+ else {
97
182
  res.writeHead(404);
98
- res.end(`File not found: ${e.message}`);
183
+ res.end('Not found');
99
184
  }
100
185
  }
101
- else if (url.pathname === '/api/tree') {
102
- try {
103
- const getTree = async (dir) => {
104
- const entries = await fs.readdir(dir, { withFileTypes: true });
105
- let files = [];
106
- const exclude = [
107
- 'node_modules', '.git', '.rigour', '.github', '.vscode', '.cursor',
108
- 'venv', '.venv', 'dist', 'build', 'out', 'target', '__pycache__'
109
- ];
110
- for (const entry of entries) {
111
- if (exclude.includes(entry.name) || entry.name.startsWith('.')) {
112
- if (entry.name !== '.cursorrules' && entry.name !== '.cursor') {
113
- continue;
114
- }
115
- }
116
- const fullPath = path.join(dir, entry.name);
117
- if (entry.isDirectory()) {
118
- files = [...files, ...(await getTree(fullPath))];
119
- }
120
- else {
121
- files.push(path.relative(cwd, fullPath));
122
- }
123
- }
124
- return files;
125
- };
126
- const tree = await getTree(cwd);
186
+ catch (e) {
187
+ res.writeHead(500);
188
+ res.end(e.message);
189
+ }
190
+ }
191
+ else if (url.pathname === '/api/memory') {
192
+ try {
193
+ const memoryPath = path.join(cwd, '.rigour/memory.json');
194
+ if (await fs.pathExists(memoryPath)) {
127
195
  res.writeHead(200, { 'Content-Type': 'application/json' });
128
- res.end(JSON.stringify(tree));
196
+ res.end(await fs.readFile(memoryPath, 'utf8'));
129
197
  }
130
- catch (e) {
131
- res.writeHead(500);
132
- res.end(e.message);
198
+ else {
199
+ res.end(JSON.stringify({}));
133
200
  }
134
201
  }
135
- else if (url.pathname === '/api/config') {
136
- try {
137
- const configPath = path.join(cwd, 'rigour.yml');
138
- if (await fs.pathExists(configPath)) {
139
- const content = await fs.readFile(configPath, 'utf8');
140
- res.writeHead(200, { 'Content-Type': 'text/plain' });
141
- res.end(content);
142
- }
143
- else {
144
- res.writeHead(404);
145
- res.end('rigour.yml not found');
146
- }
147
- }
148
- catch (e) {
149
- res.writeHead(500);
150
- res.end(e.message);
151
- }
202
+ catch (e) {
203
+ res.writeHead(500);
204
+ res.end(e.message);
152
205
  }
153
- else if (url.pathname === '/api/memory') {
154
- try {
155
- const memoryPath = path.join(cwd, '.rigour/memory.json');
156
- if (await fs.pathExists(memoryPath)) {
157
- const content = await fs.readFile(memoryPath, 'utf8');
158
- res.writeHead(200, { 'Content-Type': 'application/json' });
159
- res.end(content);
160
- }
161
- else {
162
- res.end(JSON.stringify({}));
163
- }
206
+ }
207
+ else if (url.pathname === '/api/index-stats') {
208
+ try {
209
+ const indexPath = path.join(cwd, '.rigour/patterns.json');
210
+ if (await fs.pathExists(indexPath)) {
211
+ res.writeHead(200, { 'Content-Type': 'application/json' });
212
+ res.end(JSON.stringify(await fs.readJson(indexPath)));
164
213
  }
165
- catch (e) {
166
- res.writeHead(500);
167
- res.end(e.message);
214
+ else {
215
+ res.end(JSON.stringify({ patterns: [], stats: { totalPatterns: 0, totalFiles: 0, byType: {} } }));
168
216
  }
169
217
  }
170
- else if (url.pathname === '/api/index-stats') {
171
- try {
172
- const indexPath = path.join(cwd, '.rigour/patterns.json'); // Corrected path
173
- if (await fs.pathExists(indexPath)) {
174
- const content = await fs.readJson(indexPath);
175
- res.writeHead(200, { 'Content-Type': 'application/json' });
176
- res.end(JSON.stringify(content));
177
- }
178
- else {
179
- res.end(JSON.stringify({ patterns: [], stats: { totalPatterns: 0, totalFiles: 0, byType: {} } }));
180
- }
181
- }
182
- catch (e) {
183
- res.writeHead(500);
184
- res.end(e.message);
185
- }
218
+ catch (e) {
219
+ res.writeHead(500);
220
+ res.end(e.message);
186
221
  }
187
- else if (url.pathname === '/api/index-search') {
188
- const query = url.searchParams.get('q');
189
- if (!query) {
190
- res.writeHead(400);
191
- res.end('Missing query parameter');
192
- return;
193
- }
222
+ }
223
+ else if (url.pathname === '/api/index-search') {
224
+ const query = url.searchParams.get('q');
225
+ if (!query) {
226
+ res.writeHead(400);
227
+ res.end('Missing query');
228
+ return;
229
+ }
230
+ try {
231
+ const { generateEmbedding, semanticSearch } = await import('@rigour-labs/core/pattern-index');
232
+ const indexPath = path.join(cwd, '.rigour/patterns.json');
233
+ const indexData = await fs.readJson(indexPath);
234
+ const queryVector = await generateEmbedding(query);
235
+ const similarities = semanticSearch(queryVector, indexData.patterns);
236
+ const results = indexData.patterns.map((p, i) => ({ ...p, similarity: similarities[i] }))
237
+ .filter((p) => p.similarity > 0.3)
238
+ .sort((a, b) => b.similarity - a.similarity)
239
+ .slice(0, 20);
240
+ res.writeHead(200, { 'Content-Type': 'application/json' });
241
+ res.end(JSON.stringify(results));
242
+ }
243
+ catch (e) {
244
+ res.writeHead(500);
245
+ res.end(e.message);
246
+ }
247
+ }
248
+ else if (url.pathname === '/api/arbitrate' && req.method === 'POST') {
249
+ let body = '';
250
+ req.on('data', chunk => body += chunk);
251
+ req.on('end', async () => {
194
252
  try {
195
- const { generateEmbedding, semanticSearch } = await import('@rigour-labs/core/pattern-index');
196
- const indexPath = path.join(cwd, '.rigour/patterns.json');
197
- if (!(await fs.pathExists(indexPath))) {
198
- res.writeHead(404);
199
- res.end('Index not found');
200
- return;
201
- }
202
- const indexData = await fs.readJson(indexPath);
203
- const queryVector = await generateEmbedding(query);
204
- const similarities = semanticSearch(queryVector, indexData.patterns);
205
- // Attach similarity to patterns and sort
206
- const results = indexData.patterns.map((p, i) => ({
207
- ...p,
208
- similarity: similarities[i]
209
- }))
210
- .filter((p) => p.similarity > 0.3) // Threshold
211
- .sort((a, b) => b.similarity - a.similarity)
212
- .slice(0, 20);
213
- res.writeHead(200, { 'Content-Type': 'application/json' });
214
- res.end(JSON.stringify(results));
253
+ const decision = JSON.parse(body);
254
+ const logEntry = JSON.stringify({
255
+ id: randomUUID(),
256
+ timestamp: new Date().toISOString(),
257
+ tool: 'human_arbitration',
258
+ requestId: decision.requestId,
259
+ decision: decision.decision,
260
+ status: decision.decision === 'approve' ? 'success' : 'error',
261
+ arbitrated: true
262
+ }) + "\n";
263
+ await fs.appendFile(eventsPath, logEntry);
264
+ res.writeHead(200);
265
+ res.end(JSON.stringify({ success: true }));
215
266
  }
216
267
  catch (e) {
217
268
  res.writeHead(500);
218
269
  res.end(e.message);
219
270
  }
220
- }
221
- else if (url.pathname === '/api/arbitrate' && req.method === 'POST') {
222
- let body = '';
223
- req.on('data', chunk => body += chunk);
224
- req.on('end', async () => {
225
- try {
226
- const decision = JSON.parse(body);
227
- const logEntry = JSON.stringify({
228
- id: randomUUID(),
229
- timestamp: new Date().toISOString(),
230
- tool: 'human_arbitration',
231
- requestId: decision.requestId,
232
- decision: decision.decision,
233
- status: decision.decision === 'approve' ? 'success' : 'error',
234
- arbitrated: true
235
- }) + "\n";
236
- await fs.appendFile(eventsPath, logEntry);
237
- res.writeHead(200);
238
- res.end(JSON.stringify({ success: true }));
239
- }
240
- catch (e) {
241
- res.writeHead(500);
242
- res.end(e.message);
243
- }
244
- });
245
- return;
246
- }
247
- else {
248
- res.writeHead(404);
249
- res.end();
250
- }
251
- });
252
- apiServer.listen(apiPort, () => {
253
- console.log(chalk.gray(`API Streamer active on port ${apiPort}`));
254
- });
255
- // Open browser
256
- setTimeout(async () => {
257
- const url = `http://localhost:${options.port}`;
258
- console.log(chalk.green(`\n✅ Rigour Studio is live at ${chalk.bold(url)}`));
259
- try {
260
- await execa('open', [url]);
261
- }
262
- catch {
263
- }
264
- }, 2000);
271
+ });
272
+ }
273
+ else {
274
+ res.writeHead(404);
275
+ res.end();
276
+ }
277
+ });
278
+ apiServer.listen(apiPort, () => {
279
+ console.log(chalk.gray(`API Streamer active on port ${apiPort}`));
280
+ });
281
+ setTimeout(async () => {
282
+ const url = `http://localhost:${studioPort}`;
283
+ console.log(chalk.green(`\n✅ Rigour Studio is live at ${chalk.bold(url)}`));
284
+ try {
285
+ await execa('open', [url]);
286
+ }
287
+ catch { }
288
+ }, 1500);
289
+ if (studioProcess) {
265
290
  await studioProcess;
266
291
  }
267
- catch (error) {
268
- console.error(chalk.red(`\n❌ Failed to launch Rigour Studio: ${error.message}`));
269
- console.log(chalk.yellow('Make sure to run "pnpm install" in the root directory.\n'));
270
- process.exit(1);
271
- }
272
- });
292
+ }
package/package.json CHANGED
@@ -1,10 +1,15 @@
1
1
  {
2
2
  "name": "@rigour-labs/cli",
3
- "version": "2.11.0",
3
+ "version": "2.12.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "rigour": "dist/cli.js"
7
7
  },
8
+ "files": [
9
+ "dist",
10
+ "studio-dist",
11
+ "templates"
12
+ ],
8
13
  "repository": {
9
14
  "type": "git",
10
15
  "url": "https://github.com/rigour-labs/rigour"
@@ -23,7 +28,7 @@
23
28
  "inquirer": "9.2.16",
24
29
  "ora": "^8.0.1",
25
30
  "yaml": "^2.8.2",
26
- "@rigour-labs/core": "2.11.0"
31
+ "@rigour-labs/core": "2.12.0"
27
32
  },
28
33
  "devDependencies": {
29
34
  "@types/fs-extra": "^11.0.4",
@@ -31,7 +36,8 @@
31
36
  "@types/node": "^25.0.3"
32
37
  },
33
38
  "scripts": {
34
- "build": "tsc",
39
+ "build": "tsc && pnpm bundle-studio",
40
+ "bundle-studio": "node scripts/bundle-studio.js",
35
41
  "test": "vitest run"
36
42
  }
37
43
  }
@@ -0,0 +1 @@
1
+ :root{--bg-main: #fafbfc;--bg-sidebar: #ffffff;--bg-header: #ffffff;--bg-card: #ffffff;--bg-accent: #f4f6f8;--bg-surface: #fafbfc;--border-dim: #e8eaed;--border-med: #d0d7de;--border-color: #e8eaed;--text-main: #1f2328;--text-dim: #656d76;--text-vibrant: #000000;--text-primary: #1f2328;--accent-primary: #0891b2;--accent-secondary: #0d9488;--accent-tertiary: #7c3aed;--accent-glow: rgba(8, 145, 178, .1);--status-ready: #059669;--status-success: #059669;--status-error: #dc2626;--status-warning: #d97706;--code-bg: #f6f8fa;--gradient-accent: linear-gradient(135deg, #0891b2 0%, #06b6d4 100%)}[data-theme=dark]{--bg-main: #050505;--bg-sidebar: #0a0a0a;--bg-header: #0a0a0a;--bg-card: #0d0d0d;--bg-accent: #151515;--bg-surface: #0d0d0d;--border-dim: #1a1a1a;--border-med: #222222;--border-color: #1a1a1a;--text-main: #eeeeee;--text-dim: #888888;--text-vibrant: #ffffff;--text-primary: #eeeeee;--accent-primary: #22d3ee;--accent-secondary: #2dd4bf;--accent-tertiary: #a78bfa;--accent-glow: rgba(34, 211, 238, .1);--status-ready: #34d399;--status-success: #34d399;--status-error: #f87171;--status-warning: #fbbf24;--code-bg: #090909;--gradient-accent: linear-gradient(135deg, #06b6d4 0%, #22d3ee 100%)}*{box-sizing:border-box;margin:0;padding:0}body{font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;background-color:var(--bg-main);color:var(--text-main);-webkit-font-smoothing:antialiased;overflow:hidden}.studio{display:flex;height:100vh;width:100vw}.sidebar{width:240px;background-color:var(--bg-sidebar);border-right:1px solid var(--border-dim);display:flex;flex-direction:column;padding:16px;flex-shrink:0}.brand{display:flex;align-items:center;gap:12px;padding:8px 12px;margin-bottom:32px}.logo-icon{width:28px;height:28px;background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));border-radius:6px;display:flex;align-items:center;justify-content:center;font-weight:800;color:#fff;font-size:16px}.brand span{font-weight:700;font-size:18px;color:var(--text-vibrant);letter-spacing:-.5px}.version-pill{font-size:10px;background-color:var(--bg-accent);color:var(--text-dim);padding:2px 6px;border-radius:4px;font-family:JetBrains Mono,monospace;margin-left:auto}nav{display:flex;flex-direction:column;gap:4px;flex-grow:1}.nav-item{display:flex;align-items:center;gap:12px;padding:10px 12px;border-radius:8px;color:var(--text-dim);text-decoration:none;font-size:14px;font-weight:500;background:transparent;border:none;cursor:pointer;width:100%;text-align:left;transition:all .2s ease;position:relative}.nav-item:hover{background-color:var(--bg-accent);color:var(--text-main)}.nav-item.active{color:var(--text-vibrant)}.nav-glow{position:absolute;left:0;top:10%;bottom:10%;width:3px;background-color:var(--accent-primary);border-radius:0 4px 4px 0;box-shadow:0 0 10px var(--accent-primary)}.sidebar-footer{margin-top:auto;padding-top:16px;border-top:1px solid var(--border-dim);display:flex;align-items:center;gap:8px}.trust-indicator{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--text-dim);padding:6px 10px;background:#10b9810d;border:1px solid rgba(16,185,129,.1);border-radius:100px;margin-right:auto}.footer-item{background:transparent;border:none;color:var(--text-dim);cursor:pointer;padding:4px;border-radius:4px}.footer-item:hover{background-color:var(--bg-accent);color:var(--text-main)}.main-content{flex-grow:1;display:flex;flex-direction:column;overflow:hidden}header{height:56px;background-color:var(--bg-header);border-bottom:1px solid var(--border-dim);display:flex;align-items:center;justify-content:space-between;padding:0 24px;flex-shrink:0}.header-right{display:flex;align-items:center;gap:20px}.breadcrumb{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--text-dim)}.breadcrumb .current{color:var(--text-main);font-weight:500}.connection-status{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--text-dim);background:var(--bg-accent);padding:4px 12px;border-radius:6px;font-family:JetBrains Mono,monospace}.status-indicator{display:flex;align-items:center;gap:8px;font-size:12px;font-weight:600;color:var(--status-ready)}.pulse-emitter{width:8px;height:8px;background-color:var(--status-ready);border-radius:50%;position:relative}.pulse-emitter:after{content:"";position:absolute;width:100%;height:100%;background-color:var(--status-ready);border-radius:50%;animation:pulse 2s infinite}@keyframes pulse{0%{transform:scale(1);opacity:.8}to{transform:scale(3);opacity:0}}.view-container{flex-grow:1;padding:32px;overflow-y:auto}.view-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:24px}.view-title{display:flex;align-items:center;gap:12px;color:var(--text-vibrant)}.view-title h2{font-size:20px;font-weight:700;letter-spacing:-.5px}.view-actions{display:flex;gap:12px}.btn-primary{background-color:var(--accent-primary);color:#fff;border:none;padding:8px 16px;border-radius:6px;font-size:14px;font-weight:600;cursor:pointer;transition:background .2s}.btn-primary:hover{background-color:#4338ca}.btn-secondary{background-color:var(--bg-accent);color:var(--text-main);border:1px solid var(--border-med);padding:8px 16px;border-radius:6px;font-size:14px;font-weight:500;cursor:pointer}.btn-secondary:hover{background-color:var(--border-dim)}.log-container{min-height:400px;background-color:var(--bg-card);border:1px solid var(--border-dim);border-radius:12px;display:flex;align-items:center;justify-content:center;position:relative}.empty-state{text-align:center;display:flex;flex-direction:column;align-items:center;gap:16px;max-width:320px}.empty-state h3{font-weight:600;color:var(--text-vibrant)}.empty-state p{font-size:14px;color:var(--text-dim);line-height:1.5}.log-list{width:100%;height:100%;padding:16px;display:flex;flex-direction:column;gap:12px;overflow-y:auto}.log-entry{background-color:var(--bg-accent);border:1px solid var(--border-med);border-radius:8px;padding:12px;font-family:JetBrains Mono,monospace;font-size:13px;transition:border-color .2s}.log-entry:hover{border-color:var(--accent-primary)}.log-meta{display:flex;align-items:center;gap:12px;margin-bottom:8px;border-bottom:1px solid var(--border-dim);padding-bottom:8px}.log-time{color:var(--text-dim)}.log-request-id{color:var(--border-med);font-size:11px}.log-badge{padding:2px 6px;border-radius:4px;font-size:10px;font-weight:700;text-transform:uppercase}.log-badge.tool_call{background:#4f46e533;color:var(--accent-primary)}.log-badge.tool_response{background:#06b8d433;color:var(--accent-secondary)}.log-tool{font-weight:700;color:var(--text-vibrant);display:block;margin-bottom:4px}.log-args{background:#000;padding:8px;border-radius:4px;color:#a5d6ff;font-size:12px;margin:8px 0;white-space:pre-wrap;word-break:break-all}.log-status{display:inline-flex;align-items:center;gap:4px;font-weight:600;font-size:11px}.log-status.success{color:var(--status-ready)}.log-status.error{color:var(--status-error)}.governance-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:#000c;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);display:flex;align-items:center;justify-content:center;z-index:1000}.governance-window{width:90vw;height:85vh;background:var(--bg-card);border:1px solid var(--border-med);border-radius:12px;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 32px 64px #0009}.governance-header{height:52px;padding:0 20px;background:var(--bg-header);border-bottom:1px solid var(--border-dim);display:flex;align-items:center;justify-content:space-between}.governance-header .title{display:flex;align-items:center;gap:12px;color:var(--text-main);font-weight:600}.governance-body{flex:1;display:flex;overflow:hidden}.file-tree{width:250px;background:var(--bg-header);border-right:1px solid var(--border-dim);display:flex;flex-direction:column}.tree-header{padding:12px 16px;font-size:11px;color:var(--text-dim);text-transform:uppercase;font-weight:700;letter-spacing:.5px;display:flex;align-items:center;gap:8px}.tree-list{flex:1;overflow-y:auto}.tree-item{padding:8px 16px;font-size:13px;color:var(--text-main);cursor:pointer;display:flex;align-items:center;gap:10px;transition:all .2s}.tree-item:hover{background:#ffffff0d;color:var(--text-main)}.tree-item.active{background:var(--accent-glow);color:var(--accent-primary);border-right:2px solid var(--accent-primary)}.diff-view-area{flex:1;background:var(--code-bg);position:relative}.diff-placeholder{height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--text-dim);gap:16px}.inspectable{cursor:pointer;border-left:3px solid transparent}.inspectable:hover{background:#4f46e51a;border-left-color:var(--accent-primary)}.inspect-pill{margin-left:auto;font-size:10px;background:var(--accent-glow);color:var(--accent-secondary);padding:2px 8px;border-radius:100px;font-weight:500}.close-btn{background:transparent;border:none;color:var(--text-dim);cursor:pointer;padding:6px;border-radius:6px;transition:all .2s}.tree-item{display:flex;align-items:center;gap:8px;padding:6px 12px;cursor:pointer;border-radius:4px;transition:all .2s;font-size:13px;color:var(--text-dim);-webkit-user-select:none;user-select:none}.tree-item:hover{background:var(--bg-surface);color:var(--text-primary)}.tree-item.active{background:var(--accent-glow);color:var(--accent-secondary)}.tree-item.violated{color:var(--status-error);opacity:.9}.chevron{color:var(--text-dim);opacity:.6}.folder-icon{color:var(--accent-primary);opacity:.8}.file-icon{color:var(--text-dim);opacity:.6}.node-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.violation-icon{margin-left:auto;color:var(--status-error);opacity:.8}.diff-editor-container{flex:1;background:#1e1e1e;border-bottom-left-radius:12px;border-bottom-right-radius:12px;overflow:hidden;position:relative;min-height:0}::-webkit-scrollbar{width:8px}::-webkit-scrollbar-track{background:var(--bg-main)}::-webkit-scrollbar-thumb{background:var(--border-med);border-radius:10px}::-webkit-scrollbar-thumb:hover{background:var(--text-dim)}.theme-toggle{background:var(--bg-accent);border:1px solid var(--border-dim);border-radius:8px;padding:8px;cursor:pointer;color:var(--text-dim);display:flex;align-items:center;transition:all .2s}.theme-toggle:hover{color:var(--text-main);border-color:var(--border-med)}.memory-bank{display:flex;flex-direction:column;height:100%;background:var(--bg-main)}.memory-header{display:flex;justify-content:space-between;align-items:center;padding:20px 24px;border-bottom:1px solid var(--border-dim);background:var(--bg-card)}.memory-title{display:flex;align-items:center;gap:12px}.memory-title h2{font-size:18px;font-weight:600;color:var(--text-main)}.memory-count{background:var(--accent-glow);color:var(--accent-primary);padding:4px 10px;border-radius:12px;font-size:12px;font-weight:500}.memory-actions{display:flex;gap:12px;align-items:center}.search-box{display:flex;align-items:center;gap:8px;background:var(--bg-accent);border:1px solid var(--border-dim);border-radius:8px;padding:8px 12px}.search-box input{background:transparent;border:none;outline:none;color:var(--text-main);font-size:14px;width:200px}.search-box input::placeholder{color:var(--text-dim)}.refresh-btn{background:var(--bg-accent);border:1px solid var(--border-dim);border-radius:8px;padding:8px;cursor:pointer;color:var(--text-dim);transition:all .2s}.refresh-btn:hover{color:var(--accent-primary);border-color:var(--accent-primary)}.refresh-btn .spinning{animation:spin 1s linear infinite}.memory-content{display:grid;grid-template-columns:1fr 1.5fr;flex:1;overflow:hidden}.memory-list{border-right:1px solid var(--border-dim);overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:12px}.memory-loading,.memory-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--text-dim);text-align:center;gap:12px}.memory-empty h3{font-size:16px;color:var(--text-main)}.memory-empty code{background:var(--code-bg);padding:2px 6px;border-radius:4px;font-size:13px}.memory-item{background:var(--bg-card);border:1px solid var(--border-dim);border-radius:10px;padding:14px;cursor:pointer;transition:all .2s}.memory-item:hover{border-color:var(--border-med);background:var(--bg-accent)}.memory-item.active{border-color:var(--accent-primary);background:var(--accent-glow)}.memory-item-header{display:flex;align-items:center;gap:8px;margin-bottom:8px}.memory-key{font-weight:600;color:var(--accent-primary);flex:1;font-size:14px}.memory-item-header .chevron{color:var(--text-dim);opacity:0;transition:opacity .2s}.memory-item:hover .chevron,.memory-item.active .chevron{opacity:1}.memory-item-meta{display:flex;align-items:center;gap:6px;color:var(--text-dim);font-size:12px;margin-bottom:8px}.memory-item-preview{font-size:13px;color:var(--text-dim);line-height:1.5;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}.memory-detail{padding:24px;overflow-y:auto;background:var(--bg-card)}.detail-header{display:flex;align-items:center;gap:10px;margin-bottom:12px}.detail-header h3{font-size:18px;font-weight:600;color:var(--accent-primary)}.detail-meta{display:flex;align-items:center;gap:8px;color:var(--text-dim);font-size:13px;margin-bottom:20px;padding-bottom:16px;border-bottom:1px solid var(--border-dim)}.detail-content pre{background:var(--code-bg);border:1px solid var(--border-dim);border-radius:8px;padding:16px;font-family:JetBrains Mono,Fira Code,monospace;font-size:13px;line-height:1.6;white-space:pre-wrap;word-break:break-word;color:var(--text-main);overflow-x:auto}.detail-placeholder{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--text-dim);gap:12px}.pattern-index{display:flex;flex-direction:column;height:100%;background:var(--bg-main)}.pattern-header{display:flex;justify-content:space-between;align-items:center;padding:20px 24px;border-bottom:1px solid var(--border-dim);background:var(--bg-card)}.pattern-title{display:flex;align-items:center;gap:12px}.pattern-title h2{font-size:18px;font-weight:600;color:var(--text-main)}.pattern-count{background:var(--accent-glow);color:var(--accent-primary);padding:4px 10px;border-radius:12px;font-size:12px;font-weight:500}.pattern-actions{display:flex;gap:12px;align-items:center}.filter-box{display:flex;align-items:center;gap:8px;background:var(--bg-accent);border:1px solid var(--border-dim);border-radius:8px;padding:8px 12px}.filter-box select{background:transparent;border:none;outline:none;color:var(--text-main);font-size:14px;cursor:pointer}.pattern-stats{display:flex;gap:16px;padding:16px 24px;border-bottom:1px solid var(--border-dim);background:var(--bg-card)}.pattern-stats .stat-card{display:flex;align-items:center;gap:12px;background:var(--bg-accent);padding:12px 16px;border-radius:8px;border:1px solid var(--border-dim)}.stat-info{display:flex;flex-direction:column}.stat-info .stat-value{font-size:20px;font-weight:700;color:var(--text-main)}.stat-info .stat-label{font-size:12px;color:var(--text-dim)}.pattern-content{display:grid;grid-template-columns:1fr 1.5fr;flex:1;overflow:hidden}.pattern-list{border-right:1px solid var(--border-dim);overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:10px}.pattern-loading,.pattern-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--text-dim);text-align:center;gap:12px}.pattern-empty code{background:var(--code-bg);padding:2px 6px;border-radius:4px;font-size:13px}.pattern-item{background:var(--bg-card);border:1px solid var(--border-dim);border-radius:8px;padding:12px;cursor:pointer;transition:all .2s}.pattern-item:hover{border-color:var(--border-med);background:var(--bg-accent)}.pattern-item.active{border-color:var(--accent-primary);background:var(--accent-glow)}.pattern-item-header{display:flex;align-items:center;gap:8px;margin-bottom:6px}.pattern-type-badge{background:var(--accent-secondary);color:#fff;padding:2px 8px;border-radius:4px;font-size:11px;font-weight:600;text-transform:uppercase}.pattern-type-badge.large{padding:4px 12px;font-size:13px}.pattern-name{font-weight:600;color:var(--text-main);flex:1}.pattern-item-file{display:flex;align-items:center;gap:6px;color:var(--text-dim);font-size:12px;font-family:monospace}.exported-badge{background:var(--status-success);color:#fff;padding:2px 6px;border-radius:4px;font-size:10px;margin-top:6px;display:inline-block}.pattern-detail{padding:24px;overflow-y:auto;background:var(--bg-card)}.detail-section{margin-bottom:20px}.detail-section h4{font-size:12px;text-transform:uppercase;color:var(--text-dim);margin-bottom:8px;letter-spacing:.5px}.detail-section code{background:var(--code-bg);padding:8px 12px;border-radius:6px;font-family:monospace;font-size:13px;display:block;color:var(--accent-primary)}.detail-section pre{background:var(--code-bg);padding:12px;border-radius:6px;font-family:monospace;font-size:13px;overflow-x:auto;white-space:pre-wrap}.keywords-list{display:flex;flex-wrap:wrap;gap:6px}.keyword-tag{background:var(--bg-accent);color:var(--text-dim);padding:4px 10px;border-radius:12px;font-size:12px;border:1px solid var(--border-dim)}.quality-gates{display:flex;flex-direction:column;height:100%;background:var(--bg-main)}.gates-header{display:flex;justify-content:space-between;align-items:center;padding:20px 24px;border-bottom:1px solid var(--border-dim);background:var(--bg-card)}.gates-title{display:flex;align-items:center;gap:12px}.gates-title h2{font-size:18px;font-weight:600;color:var(--text-main)}.preset-badge{background:var(--accent-primary);color:#fff;padding:4px 12px;border-radius:12px;font-size:12px;font-weight:600;text-transform:uppercase}.gates-actions{display:flex;gap:12px;align-items:center}.view-toggle{display:flex;background:var(--bg-accent);border-radius:8px;padding:4px;border:1px solid var(--border-dim)}.view-toggle button{display:flex;align-items:center;gap:6px;padding:6px 12px;border:none;background:transparent;color:var(--text-dim);cursor:pointer;border-radius:6px;font-size:13px;transition:all .2s}.view-toggle button:hover{color:var(--text-main)}.view-toggle button.active{background:var(--bg-card);color:var(--accent-primary);box-shadow:0 1px 3px #0000001a}.gates-loading{display:flex;align-items:center;justify-content:center;height:100%;color:var(--text-dim)}.gates-raw{flex:1;overflow:auto;padding:24px}.gates-raw pre{background:var(--code-bg);padding:20px;border-radius:8px;border:1px solid var(--border-dim);font-family:JetBrains Mono,monospace;font-size:13px;line-height:1.6;color:var(--text-main);white-space:pre-wrap}.gates-content{flex:1;overflow-y:auto;padding:24px;display:flex;flex-direction:column;gap:24px}.gate-section{background:var(--bg-card);border:1px solid var(--border-dim);border-radius:12px;padding:20px}.section-header{display:flex;align-items:center;gap:10px;margin-bottom:16px;color:var(--text-main)}.section-header h3{font-size:15px;font-weight:600}.gate-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:12px}.gate-card{background:var(--bg-accent);border:1px solid var(--border-dim);border-radius:8px;padding:14px;display:flex;align-items:center;gap:12px}.gate-icon{color:var(--accent-primary)}.gate-info{display:flex;flex-direction:column}.gate-value{font-size:18px;font-weight:700;color:var(--text-main)}.gate-label{font-size:11px;color:var(--text-dim);text-transform:uppercase;letter-spacing:.5px}.protected-paths,.file-list{margin-top:16px}.protected-paths h4{font-size:12px;color:var(--text-dim);text-transform:uppercase;margin-bottom:10px}.path-list{display:flex;flex-wrap:wrap;gap:8px}.path-tag{background:var(--bg-accent);color:var(--accent-secondary);padding:4px 10px;border-radius:6px;font-family:monospace;font-size:12px;border:1px solid var(--border-dim)}.file-list{display:flex;flex-direction:column;gap:8px}.file-item{display:flex;align-items:center;gap:8px;color:var(--text-dim);font-size:13px;font-family:monospace}.audit-log{display:flex;flex-direction:column;height:100%;background:var(--bg-main)}.audit-header{display:flex;justify-content:space-between;align-items:center;padding:20px 24px;border-bottom:1px solid var(--border-dim);background:var(--bg-card)}.audit-title{display:flex;align-items:center;gap:12px}.audit-title h2{font-size:18px;font-weight:600;color:var(--text-main)}.log-count{background:var(--accent-glow);color:var(--accent-primary);padding:4px 10px;border-radius:12px;font-size:12px}.audit-actions{display:flex;gap:10px}.btn-secondary,.btn-primary{display:flex;align-items:center;gap:6px;padding:8px 14px;border-radius:8px;font-size:13px;cursor:pointer;transition:all .2s;border:none}.btn-secondary{background:var(--bg-accent);color:var(--text-dim);border:1px solid var(--border-dim)}.btn-secondary:hover{color:var(--text-main);border-color:var(--border-med)}.btn-primary{background:var(--accent-primary);color:#fff}.btn-primary:hover{opacity:.9}.audit-stats{display:flex;gap:12px;padding:16px 24px;border-bottom:1px solid var(--border-dim);background:var(--bg-card)}.stat-pill{display:flex;align-items:center;gap:6px;padding:6px 12px;background:var(--bg-accent);border-radius:20px;font-size:13px;color:var(--text-dim);border:1px solid var(--border-dim)}.stat-pill.success{color:var(--status-success)}.stat-pill.error{color:var(--status-error)}.stat-pill.pending{color:var(--status-warning)}.audit-filters{display:flex;gap:12px;padding:12px 24px;border-bottom:1px solid var(--border-dim)}.audit-content{display:grid;grid-template-columns:1fr 1.5fr;flex:1;overflow:hidden}.audit-content .log-list{border-right:1px solid var(--border-dim);overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:8px}.log-entry{background:var(--bg-card);border:1px solid var(--border-dim);border-radius:8px;padding:12px;cursor:pointer;transition:all .2s}.log-entry:hover{border-color:var(--border-med);background:var(--bg-accent)}.log-entry.active{border-color:var(--accent-primary);background:var(--accent-glow)}.log-entry.has-report{border-left:3px solid var(--accent-primary)}.log-header{display:flex;align-items:center;gap:8px}.status-dot{width:8px;height:8px;border-radius:50%;background:var(--text-dim)}.status-dot.success{background:var(--status-success)}.status-dot.error{background:var(--status-error)}.status-dot.pending{background:var(--status-warning)}.log-tool{font-weight:600;color:var(--text-main);flex:1;font-size:13px}.log-time{display:flex;align-items:center;gap:4px;color:var(--text-dim);font-size:11px}.report-badge{display:flex;align-items:center;gap:4px;background:var(--accent-glow);color:var(--accent-primary);padding:2px 8px;border-radius:12px;font-size:11px}.log-entry .chevron{color:var(--text-dim);opacity:0;transition:opacity .2s}.log-entry:hover .chevron,.log-entry.active .chevron{opacity:1}.log-request-id{font-size:11px;color:var(--text-dim);font-family:monospace;margin-top:6px}.log-detail{background:var(--bg-card);overflow-y:auto}.log-detail-view{height:100%;display:flex;flex-direction:column}.detail-header{display:flex;justify-content:space-between;align-items:center;padding:16px 20px;border-bottom:1px solid var(--border-dim)}.detail-info{display:flex;align-items:center;gap:12px}.detail-info h3{font-size:16px;font-weight:600;color:var(--text-main)}.status-badge{display:flex;align-items:center;gap:4px;padding:4px 10px;border-radius:6px;font-size:12px;font-weight:500;text-transform:capitalize}.status-badge.success{background:#22c55e26;color:var(--status-success)}.status-badge.error{background:#ef444426;color:var(--status-error)}.status-badge.pending{background:#eab30826;color:var(--status-warning)}.close-btn{background:none;border:none;color:var(--text-dim);cursor:pointer;padding:4px}.close-btn:hover{color:var(--text-main)}.detail-meta{display:flex;gap:24px;padding:12px 20px;border-bottom:1px solid var(--border-dim);background:var(--bg-accent)}.meta-item{display:flex;flex-direction:column;gap:2px}.meta-label{font-size:11px;text-transform:uppercase;color:var(--text-dim)}.meta-value{font-size:13px;color:var(--text-main)}.meta-value.mono{font-family:monospace}.detail-tabs{display:flex;padding:0 20px;border-bottom:1px solid var(--border-dim)}.detail-tabs button{padding:12px 16px;background:none;border:none;color:var(--text-dim);cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1px}.detail-tabs button:hover{color:var(--text-main)}.detail-tabs button.active{color:var(--accent-primary);border-bottom-color:var(--accent-primary)}.detail-body{flex:1;overflow-y:auto;padding:20px}.code-section,.error-section{margin-bottom:20px}.code-section h4,.error-section h4{font-size:12px;text-transform:uppercase;color:var(--text-dim);margin-bottom:8px}.code-section pre{background:var(--code-bg);padding:16px;border-radius:8px;font-family:JetBrains Mono,monospace;font-size:12px;line-height:1.6;overflow-x:auto;white-space:pre-wrap;color:var(--text-main)}.error-message{background:#ef44441a;border:1px solid rgba(239,68,68,.3);color:var(--status-error);padding:12px;border-radius:8px;font-size:13px}.diff-viewer-overlay{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;flex-direction:column;background:var(--bg-card)}.diff-viewer-window{flex:1;display:flex;flex-direction:column;overflow:hidden}.diff-viewer-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;background:var(--bg-sidebar);border-bottom:1px solid var(--border-dim)}.diff-viewer-header .title{display:flex;align-items:center;gap:10px;font-size:13px;font-weight:500;color:var(--text-main)}.diff-viewer-header .diff-badge{background:var(--accent-glow);color:var(--accent-primary);padding:2px 8px;border-radius:10px;font-size:11px}.diff-viewer-header .close-btn{background:transparent;border:none;color:var(--text-dim);cursor:pointer;padding:4px;border-radius:4px}.diff-viewer-header .close-btn:hover{color:var(--text-main);background:var(--bg-accent)}.diff-editor-container{flex:1;min-height:0;background:var(--code-bg)}.semantic-toggle{display:flex;align-items:center;gap:8px;background:var(--bg-accent);border:1px solid var(--border-dim);border-radius:8px;padding:8px 12px;font-size:13px;font-weight:500;color:var(--text-dim);cursor:pointer;transition:all .2s}.semantic-toggle:hover{border-color:var(--border-med);color:var(--text-main)}.semantic-toggle.active{background:var(--accent-glow);border-color:var(--accent-primary);color:var(--accent-primary);box-shadow:0 0 15px var(--accent-glow)}.semantic-toggle svg{color:var(--accent-tertiary)}.semantic-toggle.active svg{animation:rotate-spark 2s infinite ease-in-out}@keyframes rotate-spark{0%{transform:rotate(0) scale(1)}50%{transform:rotate(180deg) scale(1.2)}to{transform:rotate(360deg) scale(1)}}.similarity-badge{position:absolute;top:12px;right:12px;font-size:10px;font-weight:700;color:var(--accent-secondary);background:#2dd4bf1a;padding:2px 6px;border-radius:4px;border:1px solid rgba(45,212,191,.2)}.searching-indicator{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--accent-tertiary);margin:12px 0;font-weight:500}.spinning{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.hitl-actions{display:flex;align-items:center;gap:12px}.btn-approve,.btn-reject{display:flex;align-items:center;gap:8px;padding:6px 14px;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;transition:all .2s;border:1px solid transparent}.btn-approve{background:#10b9811a;color:#10b981;border-color:#10b98133}.btn-approve:hover{background:#10b98133;box-shadow:0 0 15px #10b98133}.btn-reject{background:#ef44441a;color:#ef4444;border-color:#ef444433}.btn-reject:hover{background:#ef444433;box-shadow:0 0 15px #ef444433}.hitl-actions .divider{width:1px;height:24px;background:var(--border-dim);margin:0 8px}.arbitrated-badge{display:inline-flex;align-items:center;gap:4px;font-size:10px;font-weight:800;text-transform:uppercase;padding:2px 8px;border-radius:100px;margin-left:8px}.arbitrated-badge.approve{background:#10b98126;color:#10b981;border:1px solid rgba(16,185,129,.3)}.arbitrated-badge.reject{background:#ef444426;color:#ef4444;border:1px solid rgba(239,68,68,.3)}