agent-state-machine 2.4.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +7 -7
- package/lib/llm.js +14 -3
- package/lib/remote/client.js +19 -6
- package/lib/runtime/agent.js +29 -2
- package/lib/runtime/prompt.js +1 -1
- package/lib/runtime/runtime.js +48 -4
- package/lib/runtime/track-changes.js +84 -0
- package/package.json +1 -1
- package/templates/project-builder/agents/{code-writer.md → code-write.md} +18 -12
- package/templates/project-builder/agents/{assumptions-clarifier.md → intake-assumptions.md} +1 -0
- package/templates/project-builder/agents/{requirements-clarifier.md → intake-requirements.md} +1 -0
- package/templates/project-builder/agents/{scope-clarifier.md → intake-scope.md} +1 -0
- package/templates/project-builder/agents/{security-clarifier.md → intake-security.md} +1 -0
- package/templates/project-builder/agents/{roadmap-generator.md → plan-roadmap.md} +1 -0
- package/templates/project-builder/agents/{task-planner.md → plan-tasks.md} +1 -0
- package/templates/project-builder/agents/post-code-fix.md +59 -0
- package/templates/project-builder/agents/{code-reviewer.md → post-code-review.md} +10 -0
- package/templates/project-builder/agents/post-code-security.md +55 -0
- package/templates/project-builder/agents/{security-reviewer.md → pre-code-security.md} +8 -11
- package/templates/project-builder/agents/{test-planner.md → pre-code-tests.md} +1 -0
- package/templates/project-builder/agents/response-interpreter.md +1 -0
- package/templates/project-builder/agents/verify-commit-msg.md +64 -0
- package/templates/project-builder/agents/{sanity-checker.md → verify-sanity.md} +1 -12
- package/templates/project-builder/config.js +15 -4
- package/templates/project-builder/scripts/safeguard-recovery.js +40 -0
- package/templates/project-builder/scripts/validate-changes.js +61 -0
- package/templates/project-builder/scripts/workflow-helpers.js +87 -35
- package/templates/project-builder/workflow.js +231 -93
- package/vercel-server/api/config/[token].js +76 -0
- package/vercel-server/api/history/[token].js +1 -0
- package/vercel-server/api/ws/cli.js +39 -20
- package/vercel-server/local-server.js +98 -11
- package/vercel-server/public/remote/assets/index-BHvHkNOe.css +1 -0
- package/vercel-server/public/remote/assets/index-BnuR91vD.js +188 -0
- package/vercel-server/public/remote/index.html +2 -2
- package/vercel-server/ui/src/App.jsx +35 -0
- package/vercel-server/ui/src/components/ContentCard.jsx +183 -5
- package/vercel-server/ui/src/components/Header.jsx +59 -11
- package/vercel-server/ui/src/components/SettingsModal.jsx +145 -0
- package/templates/project-builder/agents/code-fixer.md +0 -50
- package/vercel-server/public/remote/assets/index-Bnvi3AUu.js +0 -173
- package/vercel-server/public/remote/assets/index-DH2uv4Ll.css +0 -1
- /package/templates/project-builder/{agents → scripts}/sanity-runner.js +0 -0
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
setCLIConnected,
|
|
15
15
|
addEvent,
|
|
16
16
|
setEvents,
|
|
17
|
+
peekConfigUpdate,
|
|
18
|
+
popConfigUpdate,
|
|
17
19
|
redis,
|
|
18
20
|
KEYS,
|
|
19
21
|
} from '../../lib/redis.js';
|
|
@@ -59,10 +61,14 @@ async function handlePost(req, res) {
|
|
|
59
61
|
try {
|
|
60
62
|
switch (action) {
|
|
61
63
|
case 'session_init': {
|
|
62
|
-
const { workflowName, history } = body;
|
|
64
|
+
const { workflowName, history, config } = body;
|
|
63
65
|
|
|
64
|
-
// Create session
|
|
65
|
-
await createSession(sessionToken, {
|
|
66
|
+
// Create session with initial config
|
|
67
|
+
await createSession(sessionToken, {
|
|
68
|
+
workflowName,
|
|
69
|
+
cliConnected: true,
|
|
70
|
+
config: config || null,
|
|
71
|
+
});
|
|
66
72
|
|
|
67
73
|
// Replace events with the provided history snapshot (single source of truth)
|
|
68
74
|
await setEvents(sessionToken, history || []);
|
|
@@ -140,7 +146,7 @@ async function handlePost(req, res) {
|
|
|
140
146
|
}
|
|
141
147
|
|
|
142
148
|
/**
|
|
143
|
-
* Handle GET requests - long-poll for interaction responses
|
|
149
|
+
* Handle GET requests - long-poll for interaction responses and config updates
|
|
144
150
|
* Uses efficient polling with 5-second intervals (Upstash doesn't support BLPOP)
|
|
145
151
|
*/
|
|
146
152
|
async function handleGet(req, res) {
|
|
@@ -165,8 +171,7 @@ async function handleGet(req, res) {
|
|
|
165
171
|
|
|
166
172
|
// Poll every 5 seconds (10 calls per 50s timeout vs 50 calls before)
|
|
167
173
|
while (Date.now() - startTime < timeoutMs) {
|
|
168
|
-
//
|
|
169
|
-
// We only remove AFTER CLI confirms receipt via DELETE request
|
|
174
|
+
// Check for pending interactions first (higher priority)
|
|
170
175
|
const pending = await redis.lindex(pendingKey, 0);
|
|
171
176
|
|
|
172
177
|
if (pending) {
|
|
@@ -175,9 +180,6 @@ async function handleGet(req, res) {
|
|
|
175
180
|
// Generate a receipt ID so CLI can confirm
|
|
176
181
|
const receiptId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
177
182
|
|
|
178
|
-
// DON'T remove yet - CLI will confirm with DELETE request
|
|
179
|
-
// This prevents data loss if response doesn't reach CLI
|
|
180
|
-
|
|
181
183
|
return res.status(200).json({
|
|
182
184
|
type: 'interaction_response',
|
|
183
185
|
receiptId,
|
|
@@ -185,11 +187,24 @@ async function handleGet(req, res) {
|
|
|
185
187
|
});
|
|
186
188
|
}
|
|
187
189
|
|
|
188
|
-
//
|
|
190
|
+
// Check for pending config updates
|
|
191
|
+
const configUpdate = await peekConfigUpdate(token);
|
|
192
|
+
|
|
193
|
+
if (configUpdate) {
|
|
194
|
+
const receiptId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
195
|
+
|
|
196
|
+
return res.status(200).json({
|
|
197
|
+
type: 'config_update',
|
|
198
|
+
receiptId,
|
|
199
|
+
...configUpdate,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Wait 5 seconds before checking again
|
|
189
204
|
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
190
205
|
}
|
|
191
206
|
|
|
192
|
-
// Timeout - no interaction received
|
|
207
|
+
// Timeout - no interaction or config update received
|
|
193
208
|
return res.status(204).end();
|
|
194
209
|
} catch (err) {
|
|
195
210
|
console.error('Error polling for interactions:', err);
|
|
@@ -198,25 +213,29 @@ async function handleGet(req, res) {
|
|
|
198
213
|
}
|
|
199
214
|
|
|
200
215
|
/**
|
|
201
|
-
* Handle DELETE requests - CLI confirms receipt of interaction
|
|
202
|
-
* This removes the
|
|
216
|
+
* Handle DELETE requests - CLI confirms receipt of interaction or config update
|
|
217
|
+
* This removes the item from the pending queue
|
|
203
218
|
*/
|
|
204
219
|
async function handleDelete(req, res) {
|
|
205
|
-
const { token } = req.query;
|
|
220
|
+
const { token, type = 'interaction' } = req.query;
|
|
206
221
|
|
|
207
222
|
if (!token) {
|
|
208
223
|
return res.status(400).json({ error: 'Missing token parameter' });
|
|
209
224
|
}
|
|
210
225
|
|
|
211
|
-
const channel = KEYS.interactions(token);
|
|
212
|
-
const pendingKey = `${channel}:pending`;
|
|
213
|
-
|
|
214
226
|
try {
|
|
215
|
-
|
|
216
|
-
|
|
227
|
+
if (type === 'config') {
|
|
228
|
+
// Remove pending config update
|
|
229
|
+
await popConfigUpdate(token);
|
|
230
|
+
} else {
|
|
231
|
+
// Remove pending interaction (default)
|
|
232
|
+
const channel = KEYS.interactions(token);
|
|
233
|
+
const pendingKey = `${channel}:pending`;
|
|
234
|
+
await redis.lpop(pendingKey);
|
|
235
|
+
}
|
|
217
236
|
return res.status(200).json({ success: true });
|
|
218
237
|
} catch (err) {
|
|
219
|
-
console.error('Error confirming
|
|
238
|
+
console.error('Error confirming receipt:', err);
|
|
220
239
|
return res.status(500).json({ error: err.message });
|
|
221
240
|
}
|
|
222
241
|
}
|
|
@@ -42,6 +42,8 @@ function createSession(token, data) {
|
|
|
42
42
|
cliConnected: true,
|
|
43
43
|
history: data.history || [],
|
|
44
44
|
pendingInteractions: [],
|
|
45
|
+
pendingConfigUpdates: [],
|
|
46
|
+
config: data.config || { fullAuto: false, autoSelectDelay: 20 },
|
|
45
47
|
createdAt: Date.now(),
|
|
46
48
|
};
|
|
47
49
|
sessions.set(token, session);
|
|
@@ -66,6 +68,23 @@ function broadcastToSession(token, event) {
|
|
|
66
68
|
}
|
|
67
69
|
}
|
|
68
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Broadcast "update" to trigger browser refetch
|
|
73
|
+
* The browser SSE handler listens for this string to call fetchData()
|
|
74
|
+
*/
|
|
75
|
+
function broadcastUpdate(token) {
|
|
76
|
+
const clients = sseClients.get(token);
|
|
77
|
+
if (!clients) return;
|
|
78
|
+
|
|
79
|
+
for (const client of clients) {
|
|
80
|
+
try {
|
|
81
|
+
client.write('data: update\n\n');
|
|
82
|
+
} catch (e) {
|
|
83
|
+
clients.delete(client);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
69
88
|
/**
|
|
70
89
|
* Parse request body
|
|
71
90
|
*/
|
|
@@ -122,8 +141,8 @@ async function handleCliPost(req, res) {
|
|
|
122
141
|
|
|
123
142
|
switch (action) {
|
|
124
143
|
case 'session_init': {
|
|
125
|
-
const { workflowName, history } = body;
|
|
126
|
-
createSession(sessionToken, { workflowName, history });
|
|
144
|
+
const { workflowName, history, config } = body;
|
|
145
|
+
createSession(sessionToken, { workflowName, history, config });
|
|
127
146
|
|
|
128
147
|
broadcastToSession(sessionToken, {
|
|
129
148
|
type: 'cli_connected',
|
|
@@ -138,6 +157,9 @@ async function handleCliPost(req, res) {
|
|
|
138
157
|
});
|
|
139
158
|
}
|
|
140
159
|
|
|
160
|
+
// Trigger browser refetch to pick up config and latest state
|
|
161
|
+
broadcastUpdate(sessionToken);
|
|
162
|
+
|
|
141
163
|
return sendJson(res, 200, { success: true });
|
|
142
164
|
}
|
|
143
165
|
|
|
@@ -186,7 +208,7 @@ async function handleCliPost(req, res) {
|
|
|
186
208
|
}
|
|
187
209
|
|
|
188
210
|
/**
|
|
189
|
-
* Handle CLI GET (long-poll for interactions)
|
|
211
|
+
* Handle CLI GET (long-poll for interactions and config updates)
|
|
190
212
|
* Peeks at first item without removing - CLI confirms via DELETE
|
|
191
213
|
*/
|
|
192
214
|
async function handleCliGet(req, res, query) {
|
|
@@ -204,11 +226,11 @@ async function handleCliGet(req, res, query) {
|
|
|
204
226
|
const timeoutMs = Math.min(parseInt(timeout, 10), 55000);
|
|
205
227
|
const startTime = Date.now();
|
|
206
228
|
|
|
207
|
-
// Poll for pending interactions
|
|
229
|
+
// Poll for pending interactions and config updates
|
|
208
230
|
const checkInterval = setInterval(() => {
|
|
231
|
+
// Check interactions first (higher priority)
|
|
209
232
|
if (session.pendingInteractions.length > 0) {
|
|
210
233
|
clearInterval(checkInterval);
|
|
211
|
-
// Peek at first item WITHOUT removing - CLI will confirm via DELETE
|
|
212
234
|
const interaction = session.pendingInteractions[0];
|
|
213
235
|
return sendJson(res, 200, {
|
|
214
236
|
type: 'interaction_response',
|
|
@@ -216,6 +238,16 @@ async function handleCliGet(req, res, query) {
|
|
|
216
238
|
});
|
|
217
239
|
}
|
|
218
240
|
|
|
241
|
+
// Check config updates
|
|
242
|
+
if (session.pendingConfigUpdates && session.pendingConfigUpdates.length > 0) {
|
|
243
|
+
clearInterval(checkInterval);
|
|
244
|
+
const configUpdate = session.pendingConfigUpdates[0];
|
|
245
|
+
return sendJson(res, 200, {
|
|
246
|
+
type: 'config_update',
|
|
247
|
+
...configUpdate,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
219
251
|
if (Date.now() - startTime >= timeoutMs) {
|
|
220
252
|
clearInterval(checkInterval);
|
|
221
253
|
res.writeHead(204);
|
|
@@ -230,11 +262,11 @@ async function handleCliGet(req, res, query) {
|
|
|
230
262
|
}
|
|
231
263
|
|
|
232
264
|
/**
|
|
233
|
-
* Handle CLI DELETE (confirm receipt of interaction)
|
|
234
|
-
* Removes the first pending
|
|
265
|
+
* Handle CLI DELETE (confirm receipt of interaction or config update)
|
|
266
|
+
* Removes the first pending item after CLI confirms receipt
|
|
235
267
|
*/
|
|
236
268
|
function handleCliDelete(req, res, query) {
|
|
237
|
-
const { token } = query;
|
|
269
|
+
const { token, type = 'interaction' } = query;
|
|
238
270
|
|
|
239
271
|
if (!token) {
|
|
240
272
|
return sendJson(res, 400, { error: 'Missing token' });
|
|
@@ -245,9 +277,16 @@ function handleCliDelete(req, res, query) {
|
|
|
245
277
|
return sendJson(res, 404, { error: 'Session not found' });
|
|
246
278
|
}
|
|
247
279
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
session.
|
|
280
|
+
if (type === 'config') {
|
|
281
|
+
// Remove the first pending config update
|
|
282
|
+
if (session.pendingConfigUpdates && session.pendingConfigUpdates.length > 0) {
|
|
283
|
+
session.pendingConfigUpdates.shift();
|
|
284
|
+
}
|
|
285
|
+
} else {
|
|
286
|
+
// Remove the first pending interaction (default)
|
|
287
|
+
if (session.pendingInteractions.length > 0) {
|
|
288
|
+
session.pendingInteractions.shift();
|
|
289
|
+
}
|
|
251
290
|
}
|
|
252
291
|
|
|
253
292
|
return sendJson(res, 200, { success: true });
|
|
@@ -318,6 +357,7 @@ function handleHistoryGet(res, token) {
|
|
|
318
357
|
return sendJson(res, 200, {
|
|
319
358
|
workflowName: session.workflowName,
|
|
320
359
|
cliConnected: session.cliConnected,
|
|
360
|
+
config: session.config || { fullAuto: false, autoSelectDelay: 20 },
|
|
321
361
|
entries: session.history,
|
|
322
362
|
});
|
|
323
363
|
}
|
|
@@ -352,6 +392,47 @@ async function handleSubmitPost(req, res, token) {
|
|
|
352
392
|
return sendJson(res, 200, { success: true });
|
|
353
393
|
}
|
|
354
394
|
|
|
395
|
+
/**
|
|
396
|
+
* Handle config update POST from browser
|
|
397
|
+
*/
|
|
398
|
+
async function handleConfigPost(req, res, token) {
|
|
399
|
+
const session = getSession(token);
|
|
400
|
+
if (!session) {
|
|
401
|
+
return sendJson(res, 404, { error: 'Session not found' });
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (!session.cliConnected) {
|
|
405
|
+
return sendJson(res, 503, { error: 'CLI is disconnected' });
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const body = await parseBody(req);
|
|
409
|
+
const { fullAuto, autoSelectDelay, stop } = body;
|
|
410
|
+
|
|
411
|
+
// Build config update object
|
|
412
|
+
const configUpdate = {};
|
|
413
|
+
if (fullAuto !== undefined) configUpdate.fullAuto = fullAuto;
|
|
414
|
+
if (autoSelectDelay !== undefined) configUpdate.autoSelectDelay = autoSelectDelay;
|
|
415
|
+
if (stop !== undefined) configUpdate.stop = stop;
|
|
416
|
+
|
|
417
|
+
if (Object.keys(configUpdate).length === 0) {
|
|
418
|
+
return sendJson(res, 400, { error: 'No config fields provided' });
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Add to pending config updates for CLI to pick up
|
|
422
|
+
if (!session.pendingConfigUpdates) {
|
|
423
|
+
session.pendingConfigUpdates = [];
|
|
424
|
+
}
|
|
425
|
+
session.pendingConfigUpdates.push(configUpdate);
|
|
426
|
+
|
|
427
|
+
// Update session config (except stop which is transient)
|
|
428
|
+
if (!stop) {
|
|
429
|
+
if (fullAuto !== undefined) session.config.fullAuto = fullAuto;
|
|
430
|
+
if (autoSelectDelay !== undefined) session.config.autoSelectDelay = autoSelectDelay;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return sendJson(res, 200, { success: true });
|
|
434
|
+
}
|
|
435
|
+
|
|
355
436
|
/**
|
|
356
437
|
* Serve session UI
|
|
357
438
|
*/
|
|
@@ -496,6 +577,12 @@ async function handleRequest(req, res) {
|
|
|
496
577
|
return handleSubmitPost(req, res, submitMatch[1]);
|
|
497
578
|
}
|
|
498
579
|
|
|
580
|
+
// Route: Config
|
|
581
|
+
const configMatch = pathname.match(/^\/api\/config\/([^/]+)$/);
|
|
582
|
+
if (configMatch && req.method === 'POST') {
|
|
583
|
+
return handleConfigPost(req, res, configMatch[1]);
|
|
584
|
+
}
|
|
585
|
+
|
|
499
586
|
// Route: Static files
|
|
500
587
|
if (pathname === '/') {
|
|
501
588
|
const defaultToken = getDefaultSessionToken();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Inter,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:JetBrains Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.-inset-4{top:-1rem;right:-1rem;bottom:-1rem;left:-1rem}.inset-0{top:0;right:0;bottom:0;left:0}.inset-x-0{left:0;right:0}.left-0{left:0}.left-1{left:.25rem}.left-4{left:1rem}.left-6{left:1.5rem}.right-3{right:.75rem}.top-0{top:0}.top-1{top:.25rem}.top-1\/2{top:50%}.top-full{top:100%}.z-10{z-index:10}.z-40{z-index:40}.z-50{z-index:50}.m-0{margin:0}.mx-auto{margin-left:auto;margin-right:auto}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.ml-2{margin-left:.5rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-6{margin-top:1.5rem}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.h-10{height:2.5rem}.h-12{height:3rem}.h-14{height:3.5rem}.h-16{height:4rem}.h-2\.5{height:.625rem}.h-20{height:5rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-32{height:8rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-64{height:16rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\[100dvh\]{height:100dvh}.h-\[40vh\]{height:40vh}.h-full{height:100%}.max-h-\[60vh\]{max-height:60vh}.min-h-\[50vh\]{min-height:50vh}.w-10{width:2.5rem}.w-12{width:3rem}.w-14{width:3.5rem}.w-16{width:4rem}.w-2\.5{width:.625rem}.w-20{width:5rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-\[90vw\]{width:90vw}.w-full{width:100%}.min-w-0{min-width:0px}.min-w-\[200px\]{min-width:200px}.max-w-2xl{max-width:42rem}.max-w-3xl{max-width:48rem}.max-w-4xl{max-width:56rem}.max-w-5xl{max-width:64rem}.max-w-\[300px\]{max-width:300px}.max-w-md{max-width:28rem}.max-w-xl{max-width:36rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-180{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes ping{75%,to{transform:scale(2);opacity:0}}.animate-ping{animation:ping 1s cubic-bezier(0,0,.2,1) infinite}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.items-stretch{align-items:stretch}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-10>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(2.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2.5rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(2rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-balance{text-wrap:balance}.break-words{overflow-wrap:break-word}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-3xl{border-radius:1.5rem}.rounded-\[20px\]{border-radius:20px}.rounded-\[22px\]{border-radius:22px}.rounded-\[24px\]{border-radius:24px}.rounded-\[28px\]{border-radius:28px}.rounded-\[32px\]{border-radius:32px}.rounded-\[36px\]{border-radius:36px}.rounded-full{border-radius:9999px}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-black{--tw-border-opacity: 1;border-color:rgb(0 0 0 / var(--tw-border-opacity, 1))}.border-black\/10{border-color:#0000001a}.border-black\/20{border-color:#0003}.border-black\/30{border-color:#0000004d}.border-transparent{border-color:transparent}.bg-bg{background-color:var(--bg)}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.bg-black\/10{background-color:#0000001a}.bg-black\/20{background-color:#0003}.bg-black\/5{background-color:#0000000d}.bg-black\/50{background-color:#00000080}.bg-black\/\[0\.02\]{background-color:#00000005}.bg-black\/\[0\.03\]{background-color:#00000008}.bg-fg{background-color:var(--fg)}.bg-red-500\/10{background-color:#ef44441a}.bg-red-600{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity, 1))}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-white\/20{background-color:#fff3}.bg-yellow-500{--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity, 1))}.bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}.from-bg{--tw-gradient-from: var(--bg) var(--tw-gradient-from-position);--tw-gradient-to: rgb(255 255 255 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.via-bg{--tw-gradient-to: rgb(255 255 255 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--bg) var(--tw-gradient-via-position), var(--tw-gradient-to)}.to-transparent{--tw-gradient-to: transparent var(--tw-gradient-to-position)}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-10{padding:2.5rem}.p-2{padding:.5rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-12{padding-left:3rem;padding-right:3rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-16{padding-top:4rem;padding-bottom:4rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-20{padding-top:5rem;padding-bottom:5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.pb-6{padding-bottom:1.5rem}.pl-11{padding-left:2.75rem}.pr-10{padding-right:2.5rem}.pt-0\.5{padding-top:.125rem}.pt-1{padding-top:.25rem}.pt-3{padding-top:.75rem}.pt-4{padding-top:1rem}.pt-6{padding-top:1.5rem}.pt-8{padding-top:2rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:JetBrains Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-black{font-weight:900}.font-bold{font-weight:700}.font-extrabold{font-weight:800}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.italic{font-style:italic}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.leading-tight{line-height:1.25}.tracking-\[0\.16em\]{letter-spacing:.16em}.tracking-\[0\.24em\]{letter-spacing:.24em}.tracking-\[0\.28em\]{letter-spacing:.28em}.tracking-\[0\.2em\]{letter-spacing:.2em}.tracking-\[0\.32em\]{letter-spacing:.32em}.tracking-\[0\.35em\]{letter-spacing:.35em}.tracking-\[0\.4em\]{letter-spacing:.4em}.tracking-tight{letter-spacing:-.025em}.tracking-tighter{letter-spacing:-.05em}.tracking-widest{letter-spacing:.1em}.text-bg{color:var(--bg)}.text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1))}.text-black\/30{color:#0000004d}.text-black\/40{color:#0006}.text-black\/50{color:#00000080}.text-black\/60{color:#0009}.text-black\/70{color:#000000b3}.text-black\/80{color:#000c}.text-black\/85{color:#000000d9}.text-black\/90{color:#000000e6}.text-fg{color:var(--fg)}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-subtle{color:var(--subtle)}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-white\/50{color:#ffffff80}.text-white\/70{color:#ffffffb3}.text-white\/90{color:#ffffffe6}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-20{opacity:.2}.opacity-30{opacity:.3}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.opacity-90{opacity:.9}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_10px_30px_rgba\(0\,0\,0\,0\.2\)\]{--tw-shadow: 0 10px 30px rgba(0,0,0,.2);--tw-shadow-colored: 0 10px 30px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-black\/10{--tw-shadow-color: rgb(0 0 0 / .1);--tw-shadow: var(--tw-shadow-colored)}.shadow-black\/20{--tw-shadow-color: rgb(0 0 0 / .2);--tw-shadow: var(--tw-shadow-colored)}.blur{--tw-blur: blur(8px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.blur-2xl{--tw-blur: blur(40px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-3xl{--tw-backdrop-blur: blur(64px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-sm{--tw-backdrop-blur: blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-300{transition-duration:.3s}:root{--bg: #ffffff;--fg: #000000;--subtle: rgba(0, 0, 0, .45);--border: rgba(0, 0, 0, .12);--accent: #000000}.dark{--bg: #000000;--fg: #ffffff;--subtle: rgba(255, 255, 255, .45);--border: rgba(255, 255, 255, .18);--accent: #ffffff}body{background-color:var(--bg);color:var(--fg);font-family:Inter,sans-serif;transition:background-color .4s ease,color .4s ease;overflow:hidden;margin:0;height:100vh;height:100dvh;width:100vw}.custom-scroll::-webkit-scrollbar{width:4px}.custom-scroll::-webkit-scrollbar-track{background:transparent}.custom-scroll::-webkit-scrollbar-thumb{background:var(--subtle);border-radius:4px;opacity:.2}.markdown-body pre{background:#0000000d;padding:1.5rem;border-radius:12px;overflow-x:auto;font-size:.85rem;line-height:1.6}.dark .markdown-body pre{background:#ffffff14}.content-width{width:100%;max-width:900px;margin:0 auto}.main-container{height:100vh;width:100vw;display:flex;flex-direction:column}.main-stage{flex:1;position:relative;z-index:10;padding-top:calc(80px + env(safe-area-inset-top))}.jumper-input{background:transparent;border:none;color:inherit;font-family:inherit;font-weight:700;text-align:center;width:2.2rem;outline:none;padding:0;margin:0;font-size:inherit}.jumper-input::-webkit-inner-spin-button,.jumper-input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.nav-footer{height:80px;display:flex;align-items:center;justify-content:center;z-index:50}.footer-control{display:flex;align-items:center;gap:1.5rem;padding:.5rem 1.2rem;background:#0000000a;-webkit-backdrop-filter:blur(20px);backdrop-filter:blur(20px);border-radius:99px;border:1px solid var(--border)}.dark .footer-control{background:#ffffff14}.tooltip{position:relative}.tooltip:after{content:attr(data-tooltip);position:absolute;bottom:-2.2rem;left:50%;transform:translate(-50%) translateY(4px);background:var(--fg);color:var(--bg);padding:.35rem .55rem;border-radius:999px;font-size:10px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .12s ease,transform .12s ease;box-shadow:0 12px 30px #00000026;z-index:40}.tooltip:hover:after,.tooltip:focus-visible:after{opacity:1;transform:translate(-50%) translateY(0)}.raw-json-block{margin:0;padding:1.5rem;border-radius:24px;border:1px solid var(--border);background:#0000000a;color:#000000d9;font-family:JetBrains Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"monospace";font-size:.85rem;line-height:1.6;white-space:pre-wrap;word-break:break-word;overflow:auto;max-height:70vh}.raw-json-block--full{max-height:none;overflow:visible}.dark .raw-json-block{background:#ffffff14;color:#ffffffe6}.json-token-key{color:#000000bf;font-weight:600}.json-token-string{color:#0009}.json-token-number,.json-token-boolean{color:#000000d9}.json-token-null{color:#00000073}@keyframes check-pop{0%{transform:scale(.7);opacity:0}50%{opacity:1}to{transform:scale(1);opacity:1}}@keyframes check-draw{0%{stroke-dashoffset:30;opacity:0}40%{opacity:1}to{stroke-dashoffset:0;opacity:1}}@keyframes ring-pop{0%{transform:scale(.7);opacity:.5}to{transform:scale(1.2);opacity:0}}.checkmark-pop{animation:check-pop .45s ease-out both}.checkmark-draw{stroke-dasharray:30;stroke-dashoffset:30;animation:check-draw .65s ease-out .15s forwards}.success-ring{animation:ring-pop .8s ease-out both}.dark .json-token-key{color:#fffc}.dark .json-token-string{color:#ffffffa6}.dark .json-token-number,.dark .json-token-boolean{color:#ffffffe6}.dark .json-token-null{color:#ffffff8c}.placeholder\:text-black\/40::-moz-placeholder{color:#0006}.placeholder\:text-black\/40::placeholder{color:#0006}.placeholder\:opacity-20::-moz-placeholder{opacity:.2}.placeholder\:opacity-20::placeholder{opacity:.2}.hover\:scale-105:hover{--tw-scale-x: 1.05;--tw-scale-y: 1.05;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:scale-\[1\.02\]:hover{--tw-scale-x: 1.02;--tw-scale-y: 1.02;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:border-black\/30:hover{border-color:#0000004d}.hover\:border-black\/40:hover{border-color:#0006}.hover\:bg-black\/10:hover{background-color:#0000001a}.hover\:bg-black\/5:hover{background-color:#0000000d}.hover\:bg-black\/\[0\.03\]:hover{background-color:#00000008}.hover\:bg-red-500\/20:hover{background-color:#ef444433}.hover\:bg-red-700:hover{--tw-bg-opacity: 1;background-color:rgb(185 28 28 / var(--tw-bg-opacity, 1))}.hover\:text-black:hover{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1))}.hover\:opacity-100:hover{opacity:1}.focus\:border-black:focus{--tw-border-opacity: 1;border-color:rgb(0 0 0 / var(--tw-border-opacity, 1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-black\/30:focus{--tw-ring-color: rgb(0 0 0 / .3)}.active\:scale-95:active{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.disabled\:opacity-0:disabled{opacity:0}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-50:disabled{opacity:.5}.group[open] .group-open\:rotate-90{--tw-rotate: 90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1))}.group:hover .group-hover\:opacity-60{opacity:.6}.group:hover .group-hover\:opacity-70{opacity:.7}.dark\:border-white:is(.dark *){--tw-border-opacity: 1;border-color:rgb(255 255 255 / var(--tw-border-opacity, 1))}.dark\:border-white\/10:is(.dark *){border-color:#ffffff1a}.dark\:border-white\/15:is(.dark *){border-color:#ffffff26}.dark\:border-white\/20:is(.dark *){border-color:#fff3}.dark\:border-white\/30:is(.dark *){border-color:#ffffff4d}.dark\:bg-black:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.dark\:bg-black\/20:is(.dark *){background-color:#0003}.dark\:bg-white:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.dark\:bg-white\/10:is(.dark *){background-color:#ffffff1a}.dark\:bg-white\/20:is(.dark *){background-color:#fff3}.dark\:bg-white\/5:is(.dark *){background-color:#ffffff0d}.dark\:bg-white\/\[0\.03\]:is(.dark *){background-color:#ffffff08}.dark\:bg-white\/\[0\.04\]:is(.dark *){background-color:#ffffff0a}.dark\:text-black:is(.dark *){--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1))}.dark\:text-black\/70:is(.dark *){color:#000000b3}.dark\:text-black\/90:is(.dark *){color:#000000e6}.dark\:text-white:is(.dark *){--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.dark\:text-white\/30:is(.dark *){color:#ffffff4d}.dark\:text-white\/40:is(.dark *){color:#fff6}.dark\:text-white\/50:is(.dark *){color:#ffffff80}.dark\:text-white\/60:is(.dark *){color:#fff9}.dark\:text-white\/70:is(.dark *){color:#ffffffb3}.dark\:text-white\/80:is(.dark *){color:#fffc}.dark\:text-white\/85:is(.dark *){color:#ffffffd9}.dark\:text-white\/90:is(.dark *){color:#ffffffe6}.dark\:shadow-\[0_10px_30px_rgba\(255\,255\,255\,0\.1\)\]:is(.dark *){--tw-shadow: 0 10px 30px rgba(255,255,255,.1);--tw-shadow-colored: 0 10px 30px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.dark\:shadow-white\/10:is(.dark *){--tw-shadow-color: rgb(255 255 255 / .1);--tw-shadow: var(--tw-shadow-colored)}.dark\:shadow-white\/5:is(.dark *){--tw-shadow-color: rgb(255 255 255 / .05);--tw-shadow: var(--tw-shadow-colored)}.dark\:placeholder\:text-white\/40:is(.dark *)::-moz-placeholder{color:#fff6}.dark\:placeholder\:text-white\/40:is(.dark *)::placeholder{color:#fff6}.dark\:hover\:border-white\/30:hover:is(.dark *){border-color:#ffffff4d}.dark\:hover\:border-white\/40:hover:is(.dark *){border-color:#fff6}.dark\:hover\:bg-white\/10:hover:is(.dark *){background-color:#ffffff1a}.dark\:hover\:bg-white\/15:hover:is(.dark *){background-color:#ffffff26}.dark\:hover\:bg-white\/\[0\.04\]:hover:is(.dark *){background-color:#ffffff0a}.dark\:hover\:text-white:hover:is(.dark *){--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.dark\:focus\:border-white:focus:is(.dark *){--tw-border-opacity: 1;border-color:rgb(255 255 255 / var(--tw-border-opacity, 1))}.dark\:focus\:ring-white\/40:focus:is(.dark *){--tw-ring-color: rgb(255 255 255 / .4)}.group:hover .dark\:group-hover\:text-white:is(.dark *){--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}@media (min-width: 640px){.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.sm\:grid-cols-\[160px_1fr\]{grid-template-columns:160px 1fr}.sm\:items-start{align-items:flex-start}.sm\:p-8{padding:2rem}.sm\:px-10{padding-left:2.5rem;padding-right:2.5rem}.sm\:px-8{padding-left:2rem;padding-right:2rem}.sm\:py-14{padding-top:3.5rem;padding-bottom:3.5rem}.sm\:pt-10{padding-top:2.5rem}.sm\:text-2xl{font-size:1.5rem;line-height:2rem}.sm\:text-3xl{font-size:1.875rem;line-height:2.25rem}.sm\:text-4xl{font-size:2.25rem;line-height:2.5rem}.sm\:text-6xl{font-size:3.75rem;line-height:1}.sm\:text-lg{font-size:1.125rem;line-height:1.75rem}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}.sm\:text-xl{font-size:1.25rem;line-height:1.75rem}}@media (min-width: 768px){.md\:text-5xl{font-size:3rem;line-height:1}.md\:text-7xl{font-size:4.5rem;line-height:1}}@media (min-width: 1024px){.lg\:px-10{padding-left:2.5rem;padding-right:2.5rem}.lg\:px-12{padding-left:3rem;padding-right:3rem}}.\[\&\:\:-webkit-details-marker\]\:hidden::-webkit-details-marker{display:none}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/remote/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2) format("woff2-variations");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/remote/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2) format("woff2-variations");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/remote/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2) format("woff2-variations");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/remote/assets/inter-greek-wght-normal-CkhJZR-_.woff2) format("woff2-variations");unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/remote/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2) format("woff2-variations");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/remote/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2) format("woff2-variations");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/remote/assets/inter-latin-wght-normal-Dx4kXJAl.woff2) format("woff2-variations");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(data:font/woff2;base64,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) format("woff2-variations");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/remote/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2) format("woff2-variations");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/remote/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2) format("woff2-variations");unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/remote/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2) format("woff2-variations");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/remote/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2) format("woff2-variations");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/remote/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2) format("woff2-variations");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|