bangonit 0.3.4 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +24 -33
  2. package/app/desktopapp/dist/main/index.js +4 -2
  3. package/app/desktopapp/dist/main/ipc.js +96 -20
  4. package/app/desktopapp/dist/main/preload.js +4 -3
  5. package/app/desktopapp/dist/main/tabs.js +43 -3
  6. package/app/replay/dist/replay.css +1 -1
  7. package/app/replay/dist/replay.js +24 -24
  8. package/app/webapp/.next/standalone/app/webapp/.next/BUILD_ID +1 -1
  9. package/app/webapp/.next/standalone/app/webapp/.next/app-build-manifest.json +6 -6
  10. package/app/webapp/.next/standalone/app/webapp/.next/app-path-routes-manifest.json +1 -1
  11. package/app/webapp/.next/standalone/app/webapp/.next/build-manifest.json +2 -2
  12. package/app/webapp/.next/standalone/app/webapp/.next/prerender-manifest.json +1 -1
  13. package/app/webapp/.next/standalone/app/webapp/.next/server/app/_not-found.html +1 -1
  14. package/app/webapp/.next/standalone/app/webapp/.next/server/app/_not-found.rsc +1 -1
  15. package/app/webapp/.next/standalone/app/webapp/.next/server/app/app/page.js +3 -3
  16. package/app/webapp/.next/standalone/app/webapp/.next/server/app/app/page_client-reference-manifest.js +1 -1
  17. package/app/webapp/.next/standalone/app/webapp/.next/server/app/app.html +1 -1
  18. package/app/webapp/.next/standalone/app/webapp/.next/server/app/app.rsc +2 -2
  19. package/app/webapp/.next/standalone/app/webapp/.next/server/app/index.html +1 -1
  20. package/app/webapp/.next/standalone/app/webapp/.next/server/app/index.rsc +1 -1
  21. package/app/webapp/.next/standalone/app/webapp/.next/server/app-paths-manifest.json +3 -3
  22. package/app/webapp/.next/standalone/app/webapp/.next/server/chunks/708.js +4 -3
  23. package/app/webapp/.next/standalone/app/webapp/.next/server/pages/404.html +1 -1
  24. package/app/webapp/.next/standalone/app/webapp/.next/server/pages/500.html +1 -1
  25. package/app/webapp/.next/standalone/app/webapp/.next/server/pages-manifest.json +1 -1
  26. package/app/webapp/.next/standalone/app/webapp/.next/server/server-reference-manifest.json +1 -1
  27. package/app/webapp/.next/standalone/app/webapp/.next/static/chunks/app/app/page-d6a322343ab207d9.js +1 -0
  28. package/app/webapp/.next/standalone/package.json +5 -2
  29. package/app/webapp/.next/static/chunks/app/app/page-d6a322343ab207d9.js +1 -0
  30. package/app/webapp/src/shared/api/chat.ts +6 -3
  31. package/app/webapp/src/shared/components/AppShell.tsx +6 -5
  32. package/app/webapp/src/shared/components/SessionView.tsx +4 -3
  33. package/app/webapp/src/shared/lib/browser/index.ts +17 -27
  34. package/app/webapp/src/shared/lib/browser/recorder.ts +13 -4
  35. package/app/webapp/src/shared/lib/browser/snapshot.ts +19 -5
  36. package/app/webapp/src/shared/lib/browser/types.ts +21 -6
  37. package/app/webapp/src/shared/types/global.d.ts +6 -4
  38. package/bin/src/cli/bangonit.js +19 -11
  39. package/package.json +5 -2
  40. package/scripts/regen-replays.sh +101 -0
  41. package/app/webapp/.next/standalone/app/webapp/.next/static/chunks/app/app/page-df00a757f649931a.js +0 -1
  42. package/app/webapp/.next/static/chunks/app/app/page-df00a757f649931a.js +0 -1
  43. /package/app/webapp/.next/standalone/app/webapp/.next/static/{bBZVP6sUilA3wyt0vYqDc → GYX27NS_2uYjr9ksgGxIo}/_buildManifest.js +0 -0
  44. /package/app/webapp/.next/standalone/app/webapp/.next/static/{bBZVP6sUilA3wyt0vYqDc → GYX27NS_2uYjr9ksgGxIo}/_ssgManifest.js +0 -0
  45. /package/app/webapp/.next/static/{bBZVP6sUilA3wyt0vYqDc → GYX27NS_2uYjr9ksgGxIo}/_buildManifest.js +0 -0
  46. /package/app/webapp/.next/static/{bBZVP6sUilA3wyt0vYqDc → GYX27NS_2uYjr9ksgGxIo}/_ssgManifest.js +0 -0
package/README.md CHANGED
@@ -9,9 +9,9 @@ Bang On It! replaces annoying manual QA and flakey end-to-end tests with a CLI-f
9
9
  ```bash
10
10
 
11
11
  # Run a single test
12
- npx bangonit run \
13
- --plan "Go to localhost:3000, login as test@test.com (password: 12345), \
14
- and click all the buttons in the dashboard and make sure they work"
12
+ npx bangonit run --plan \
13
+ "Go to localhost:3000, login as test@test.com (password: 12345), \
14
+ and click all the buttons in the dashboard and make sure they work"
15
15
 
16
16
  # Run a suite of test plans
17
17
  npx bangonit run testplans/*.md --concurrency 3
@@ -47,7 +47,7 @@ The problem is that traditional E2E testing is its own bottleneck. Writing Selen
47
47
 
48
48
  Bang On It! removes that bottleneck. You describe what to test in plain English. An AI agent launches a real browser and executes the test — clicking buttons, filling forms, navigating pages, and verifying results. No selectors, no page objects, no flaky waits. Tests that take minutes to write instead of hours, and that don't break when you rename a CSS class because the agent interprets the website just like a real user would.
49
49
 
50
- The loop is simple: agent writes code, agent tests code, human reviews the test plans. Bang On It! is the testing layer for that loop.
50
+ The loop is simple: agent writes code, agent tests code, human reviews the test plans. And because test plans are plain English, anyone on the team — PMs, designers, QA — can write and review them, not just engineers. Bang On It! is the testing layer for that loop.
51
51
 
52
52
  ## Bang On It vs...
53
53
 
@@ -55,53 +55,46 @@ The loop is simple: agent writes code, agent tests code, human reviews the test
55
55
 
56
56
  Claude Code can drive a browser via Anthropic's computer use or third-party browser MCP servers. The approach is similar in spirit — an AI agent interacting with a real browser using natural language instructions. But Claude Code is a general-purpose coding agent, not a testing tool. It can _do_ browser testing, the same way Playwright can, but it wasn't built for it.
57
57
 
58
- **Key technical differences:**
59
-
60
58
  - **Purpose-built browser tooling.** Claude Code's computer use takes full-screen screenshots and uses pixel coordinates for every interaction — it's controlling a generic desktop, not a browser specifically. Bang On It! uses the browser's accessibility tree for element identification (fast, text-based, no image processing needed) and only falls back to screenshots when visual verification is required (charts, colors, layout). This hybrid approach is dramatically faster — a text snapshot is returned in milliseconds vs. capturing, transmitting, and processing a full screenshot on every single action.
61
59
  - **Batched actions.** Claude Code executes one browser action per tool call — click, wait for response, screenshot, next action. Bang On It! batches multiple actions into a single tool call (navigate + click + type + observe) and only captures page state at the end. Fewer round-trips to the model means faster test execution.
62
- - **Parallel test execution.** Claude Code runs one browser session at a time. Bang On It! runs N agents in parallel (`--concurrency N`), each with its own isolated browser partition — separate cookies, localStorage, and session state. A 10-test suite runs in the time of 2 tests, not 10.
60
+ - **Parallel test execution.** Claude Code runs one browser session at a time. Bang On It! is a real test runner that can run N agents in parallel (`--concurrency N`), each with its own isolated browser partition — separate cookies, localStorage, and session state. A 10-test suite runs in the time of 2 tests, not 10.
63
61
  - **Session recordings.** Bang On It! records every test run as a self-contained HTML replay with a multi-agent timeline view — video clips, console logs, and tool invocations all synced together. Share a link, not a screenshot. Claude Code has no recording capability.
64
62
  - **Real-time UI.** Bang On It! includes a live observation UI where you can watch agents execute in real time — see the cursor move, watch pages load, monitor progress across parallel agents. Claude Code outputs text to a terminal.
65
63
  - **CI-native.** Bang On It! generates GitHub Actions workflows out of the box, handles headless execution, uploads results as artifacts, and optionally pushes recordings to S3. Wiring Claude Code into CI for browser testing is a DIY project.
64
+ - **Realistic input simulation.** Claude Code's computer use moves the mouse in straight lines and types text as a single string. Bang On It! drives the browser through CDP `Input.dispatchMouseEvent` and Electron `sendInputEvent` — mouse movements follow eased Bézier-style curves, keystrokes fire individual `keyDown`/`char`/`keyUp` events with randomized 30–100ms delays. This catches hover states, drag interactions, debounced inputs, and event listeners that only trigger on real input events.
66
65
 
67
66
  Claude Code is an excellent coding agent. But for the specific job of testing a web app in a browser — fast, in parallel, with recordings and a UI — Bang On It! is purpose-built for exactly that.
68
67
 
69
- ### vs Selenium / Playwright
68
+ ### vs OSS browser automation (Playwright, Selenium, Cypress)
70
69
 
71
- Selenium and Playwright are powerful browser automation libraries — but they're _libraries_, not testing tools. You write code: selectors, page objects, explicit waits, retry logic, assertion helpers. When the UI changes, your selectors break and you're back in the maintenance treadmill.
70
+ These are powerful browser automation libraries — but they're _libraries_, not testing tools. You write code: selectors, page objects, explicit waits, retry logic, assertion helpers. When the UI changes, your selectors break and you're back in the maintenance treadmill.
72
71
 
73
- **Key technical differences:**
72
+ **Development speed:**
74
73
 
75
- - **No selectors.** Bang On It! uses the browser's accessibility tree to identify elements, the same way a screen reader does. There are no CSS selectors or XPath expressions to break when you rename a class or restructure your markup.
74
+ - **No test code.** Test plans are plain English Markdown files. A checkout flow test is 5 lines of English, not 50–100 lines of code. PMs, designers, and QA testers can write and review test plans without learning a framework or writing a line of code.
75
+ - **No selectors.** Bang On It! uses the browser's accessibility tree to identify elements, the same way a screen reader does. No CSS selectors or XPath expressions to break when you rename a class or restructure your markup.
76
76
  - **No page objects.** The AI agent interprets the page semantically on every interaction. There's no abstraction layer to keep in sync with your UI.
77
- - **No explicit waits.** Bang On It! tracks real network activity via Chrome DevTools Protocol events. It knows when the page is idle — no `waitForSelector`, no `sleep(2000)`, no polling.
78
- - **No test code at all.** Test plans are plain English Markdown files. A non-engineer can write and review them.
79
- - **Self-correcting execution.** If a button moves, changes label, or is behind a modal, the agent adapts. A Playwright script just fails.
80
-
81
- Playwright and Selenium dispatch actions programmatically — they call `element.click()` directly in the DOM, bypassing the browser's input pipeline entirely. Bang On It! drives the browser through real OS-level input events via Chrome DevTools Protocol. Mouse movements follow eased curves with realistic interpolation. Keystrokes fire individual `keyDown`, `char`, and `keyUp` events with randomized inter-key delays (30–100ms). This matters because many modern apps have hover states, drag interactions, debounced inputs, and event listeners that only trigger on real input events — not synthetic DOM calls.
82
77
 
83
- Playwright and Selenium are great for building custom browser automation. But if the goal is _testing_, you're writing and maintaining a lot of infrastructure that Bang On It! eliminates entirely.
78
+ **Debuggability:**
84
79
 
85
- ### vs Cypress
80
+ - **Session recordings.** Every test run can be recorded as a self-contained HTML replay with video clips, console logs, and tool invocations all synced on a timeline. Share a link, not a log file.
81
+ - **Real-time UI.** Watch agents execute live — see the cursor move, watch pages load, monitor progress across parallel agents. Playwright gives you a trace viewer after the fact; Bang On It! lets you watch in real time.
82
+ - **Real browser.** Bang On It! launches actual Chromium via Electron. Multi-tab, cross-origin, OAuth popups, file downloads — all work naturally. Cypress runs in an iframe and can't test these natively.
86
83
 
87
- Cypress improved DX over Selenium with automatic waiting, time-travel debugging, and a built-in test runner. But it's still selector-based test code, and it runs in a synthetic environment — Cypress injects itself into the browser via an iframe, which means it can't test multi-tab flows, OAuth redirects, or cross-origin interactions natively.
84
+ **De-flaking:**
88
85
 
89
- **Key technical differences:**
90
-
91
- - **Real browser, not an iframe.** Bang On It! launches actual Chromium via Electron. Multi-tab, cross-origin, OAuth popups, file downloads all work naturally because it's a real browser session.
92
- - **No selector coupling.** Cypress tests break when `data-cy` attributes are removed or DOM structure changes. Bang On It! reads the page like a user does — through its semantic structure, not its implementation details.
93
- - **Parallel isolation.** Each Bang On It! agent runs in its own browser partition with fully isolated cookies, localStorage, and session state. Cypress parallelization requires Dashboard (paid) and splits at the file level.
94
- - **Minutes to write vs hours.** A Cypress test for a checkout flow is 50-100 lines of code. The equivalent Bang On It! test plan is 5 lines of English.
95
- - **Realistic input simulation.** Cypress uses `cy.click()` and `cy.type()` which dispatch synthetic jQuery-style events. Bang On It! sends real input events through Chrome DevTools Protocol — eased mouse curves, per-character keystroke events with natural timing — catching bugs that only surface with real user interaction patterns.
86
+ - **No explicit waits.** Bang On It! tracks real network activity via Chrome DevTools Protocol. It knows when the page is idle — no `waitForSelector`, no `sleep(2000)`, no polling.
87
+ - **Self-correcting execution.** If a button moves, changes label, or is behind a modal, the agent adapts. A Playwright script just fails.
88
+ - **Realistic input simulation.** These tools dispatch events synthetically — `element.click()` and `element.value = "text"` bypass the browser's input pipeline entirely. Bang On It! sends real input events through CDP — eased mouse curves, per-character keystroke events with natural timing, real `mouseWheel` events for scrolling. This catches hover menus that don't open, inputs that don't validate on blur, drag-and-drop that doesn't work, and custom components that listen for native events.
96
89
 
97
- ### vs QA Wolf / Testim / Mabl (AI-assisted record-and-replay)
90
+ These tools are great for building custom browser automation. But if the goal is _testing_, you're writing and maintaining a lot of infrastructure that Bang On It! eliminates entirely.
98
91
 
99
- These tools use AI to _help_ you create and maintain traditional selector-based tests — typically through record-and-replay with smart locators that auto-heal when elements move. They reduce maintenance, but the underlying model is still the same: a recorded script of UI interactions replayed deterministically.
92
+ ### vs Commercial AI record-and-replay
100
93
 
101
- **Key technical differences:**
94
+ These tools use AI to help you create and maintain traditional selector-based tests — typically through record-and-replay with smart locators that auto-heal when elements move. They reduce maintenance, but the underlying model is still the same: a recorded script of UI interactions replayed deterministically.
102
95
 
103
96
  - **Generative, not recorded.** Bang On It! doesn't record and replay a fixed script. The AI agent reads your test plan and _decides_ how to execute it on each run. If the UI changes, the agent figures out the new path — it doesn't try to heal a stale recording.
104
- - **No vendor lock-in.** Test plans are Markdown files in your repo. There's no proprietary test format, no cloud dashboard required, no per-seat pricing for test authoring. You own your tests.
97
+ - **No vendor lock-in.** Test plans are Markdown files in your repo. No proprietary test format, no cloud dashboard required, no per-seat pricing. You own your tests.
105
98
  - **Runs locally and in CI.** These tools are typically cloud-hosted services. Bang On It! runs on your machine or in your own CI pipeline. Your app never leaves your network.
106
99
  - **Understands intent, not just actions.** A recorded test replays "click the third button in the sidebar." Bang On It! executes "verify the user can navigate to settings" — if the settings link moves from the sidebar to a top nav, the test still passes.
107
100
 
@@ -109,7 +102,7 @@ These tools use AI to _help_ you create and maintain traditional selector-based
109
102
 
110
103
  Manual QA catches things automated tests miss — but it doesn't scale, it's slow, and humans get tired. The same tester clicking through the same flow for the 50th time will miss things.
111
104
 
112
- Bang On It! runs the same tests with the same thoroughness every time, in parallel, in seconds. Write the test plan once, run it on every PR. Keep manual QA for exploratory testing where human judgment matters let Bang On It! handle the repetitive verification.
105
+ Bang On It! runs the same tests with the same thoroughness every time, in parallel, in seconds. Write the test plan once, run it on every PR. And because test plans are plain English, your QA team can write them directly — translating their domain knowledge into automated tests without waiting on engineering. Keep manual QA for exploratory testing where human judgment matters; let Bang On It! handle the repetitive verification.
113
106
 
114
107
  ### Performance and realistic simulation
115
108
 
@@ -202,8 +195,6 @@ You write test plans in plain English (or Markdown). Bang On It! spins up a real
202
195
  4. Observes the results via DOM snapshots and screenshots
203
196
  5. Reports pass/fail with a summary of what happened
204
197
 
205
- No selectors. No page objects. No flaky waits. Just describe what to test and let it bang on your app.
206
-
207
198
  ## Test Plans
208
199
 
209
200
  Test plans are Markdown files. `boi init` creates a recommended two-tier structure:
@@ -187,8 +187,6 @@ async function createWindow() {
187
187
  const recordDir = path.join(baseDir, timestamp);
188
188
  fs.mkdirSync(recordDir, { recursive: true });
189
189
  (0, ipc_1.setRunDir)(recordDir);
190
- if (!cliArgs.json)
191
- console.log(`Recording to: ${recordDir}`);
192
190
  }
193
191
  mainWindow.loadURL((0, ipc_1.getWebappUrl)());
194
192
  // Once webapp is loaded, send test plans
@@ -198,16 +196,19 @@ async function createWindow() {
198
196
  if (cliArgs.testPlanFiles.length > 0 || cliArgs.plan) {
199
197
  const plans = [];
200
198
  const names = [];
199
+ const planDirs = [];
201
200
  // Inline plan from --plan flag
202
201
  if (cliArgs.plan) {
203
202
  plans.push(cliArgs.plan);
204
203
  names.push("inline");
204
+ planDirs.push(undefined);
205
205
  }
206
206
  // Plans from files
207
207
  for (const file of cliArgs.testPlanFiles) {
208
208
  const absPath = path.isAbsolute(file) ? file : path.join(cliArgs.cwd, file);
209
209
  try {
210
210
  plans.push(fs.readFileSync(absPath, "utf-8"));
211
+ planDirs.push(path.dirname(absPath));
211
212
  }
212
213
  catch (err) {
213
214
  console.error(`Error reading test plan ${file}: ${err.message}`);
@@ -227,6 +228,7 @@ async function createWindow() {
227
228
  retries: parsed[i].frontmatter.retries ?? cliArgs.retries,
228
229
  extraPrompt: cliArgs.prompt || undefined,
229
230
  record: cliArgs.record || undefined,
231
+ planDir: planDirs[i] || undefined,
230
232
  });
231
233
  }
232
234
  }
@@ -50,11 +50,21 @@ const path = __importStar(require("path"));
50
50
  const fs = __importStar(require("fs"));
51
51
  const electron_2 = require("electron");
52
52
  const electron_store_1 = __importDefault(require("electron-store"));
53
+ const child_process_1 = require("child_process");
53
54
  const tabs_1 = require("./tabs");
54
55
  const store = new electron_store_1.default();
55
56
  let mainWindow = null;
56
57
  let bangerCwd = process.cwd();
57
58
  let runDir = null;
59
+ function findGitRoot(from) {
60
+ try {
61
+ return (0, child_process_1.execSync)("git rev-parse --show-toplevel", { cwd: from, stdio: ["ignore", "pipe", "ignore"] })
62
+ .toString().trim();
63
+ }
64
+ catch {
65
+ return null;
66
+ }
67
+ }
58
68
  function clearStore() {
59
69
  store.clear();
60
70
  }
@@ -78,9 +88,11 @@ function getWebappUrl() {
78
88
  }
79
89
  function registerIpcHandlers() {
80
90
  // --- Tab management ---
81
- electron_1.ipcMain.handle("register-tab", (_, { agentId, tabId, wcId, initialUrl }) => {
91
+ electron_1.ipcMain.handle("register-tab", (_, { agentId, tabId, wcId, initialUrl, planDir }) => {
82
92
  const ctx = (0, tabs_1.getOrCreateContext)(agentId);
83
93
  (0, tabs_1.setDataDir)(ctx, path.join(electron_2.app.getPath("userData"), "browser", "shared"));
94
+ if (planDir)
95
+ ctx.planDir = planDir;
84
96
  (0, tabs_1.addTab)(ctx, tabId, wcId, sendToRenderer, agentId, initialUrl);
85
97
  });
86
98
  electron_1.ipcMain.handle("set-active-tab", (_, { agentId, tabId }) => {
@@ -196,6 +208,35 @@ function registerIpcHandlers() {
196
208
  const ctx = (0, tabs_1.getContext)(agentId);
197
209
  return ctx?.downloads || [];
198
210
  });
211
+ // --- File chooser ---
212
+ electron_1.ipcMain.handle("handle-file-chooser", (_, { agentId, action, files }) => {
213
+ const { handleFileChooser } = require("./tabs");
214
+ if (action === "cancel") {
215
+ return handleFileChooser(agentId, "cancel");
216
+ }
217
+ const ctx = (0, tabs_1.getContext)(agentId);
218
+ const baseDir = ctx?.planDir || bangerCwd;
219
+ const sandboxRoot = findGitRoot(baseDir) || baseDir;
220
+ const resolved = files?.map((f) => {
221
+ const abs = path.isAbsolute(f) ? path.resolve(f) : path.resolve(baseDir, f);
222
+ let real;
223
+ try {
224
+ real = fs.realpathSync(abs);
225
+ }
226
+ catch (e) {
227
+ throw new Error(`Upload file not found: ${abs}`);
228
+ }
229
+ if (!real.startsWith(sandboxRoot + path.sep) && real !== sandboxRoot) {
230
+ throw new Error(`Upload path "${f}" escapes sandbox root ${sandboxRoot}`);
231
+ }
232
+ return real;
233
+ });
234
+ return handleFileChooser(agentId, "accept", resolved);
235
+ });
236
+ electron_1.ipcMain.handle("get-file-chooser-state", (_, agentId) => {
237
+ const { getFileChooserState } = require("./tabs");
238
+ return getFileChooserState(agentId);
239
+ });
199
240
  // --- Agent CRUD ---
200
241
  electron_1.ipcMain.handle("get-agents", () => store.get("agents", []));
201
242
  electron_1.ipcMain.handle("set-agents", (_, agents) => store.set("agents", agents));
@@ -236,22 +277,28 @@ function registerIpcHandlers() {
236
277
  sendToRenderer(data.type, data);
237
278
  });
238
279
  // --- Recording ---
239
- electron_1.ipcMain.handle("save-recording-clip", async (_, opts) => {
240
- const clipDir = path.join(opts.runDir, "clips", opts.agentId);
241
- fs.mkdirSync(clipDir, { recursive: true });
242
- const clipPath = path.join(clipDir, `clip-${opts.clipIndex}-tab-${opts.tabId}.webm`);
243
- fs.writeFileSync(clipPath, Buffer.from(opts.data));
244
- });
245
- electron_1.ipcMain.handle("save-replay-data", async (_, opts) => {
246
- fs.mkdirSync(opts.runDir, { recursive: true });
247
- fs.writeFileSync(path.join(opts.runDir, "data.json"), opts.data);
248
- });
280
+ // Generates one HTML file per agent + an index.html redirect.
281
+ // Each agent HTML is self-contained with base64-encoded video clips.
249
282
  electron_1.ipcMain.handle("generate-replay-html", async (_, opts) => {
250
- const dataPath = path.join(opts.runDir, "data.json");
251
- if (!fs.existsSync(dataPath))
252
- return;
253
- const replayData = fs.readFileSync(dataPath, "utf-8");
254
- // Try to load pre-built replay viewer bundle (dev: relative to compiled output, prod: next to app)
283
+ fs.mkdirSync(opts.runDir, { recursive: true });
284
+ const parsed = JSON.parse(opts.data);
285
+ const agents = parsed.agents || [];
286
+ // Build slug map: agent id → filename
287
+ const slugify = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "agent";
288
+ const usedSlugs = new Set();
289
+ const agentFiles = {};
290
+ for (const agent of agents) {
291
+ let slug = slugify(agent.name);
292
+ if (usedSlugs.has(slug)) {
293
+ let i = 2;
294
+ while (usedSlugs.has(`${slug}-${i}`))
295
+ i++;
296
+ slug = `${slug}-${i}`;
297
+ }
298
+ usedSlugs.add(slug);
299
+ agentFiles[agent.id] = `${slug}.html`;
300
+ }
301
+ // Load pre-built replay viewer bundle
255
302
  let replayJs = "";
256
303
  let replayCss = "";
257
304
  const candidates = [
@@ -268,21 +315,50 @@ function registerIpcHandlers() {
268
315
  if (fs.existsSync(replayCssPath)) {
269
316
  replayCss = fs.readFileSync(replayCssPath, "utf-8");
270
317
  }
271
- const html = `<!DOCTYPE html>
318
+ // Generate one HTML per agent
319
+ for (const agent of agents) {
320
+ const agentData = {
321
+ ...parsed,
322
+ focusAgentId: agent.id,
323
+ agentFiles,
324
+ // Only include this agent's clips (with base64 data)
325
+ clips: (parsed.clips || []).filter((c) => c.agentId === agent.id),
326
+ // Only include this agent's messages and console logs
327
+ messages: { [agent.id]: parsed.messages?.[agent.id] || [] },
328
+ consoleLogs: { [agent.id]: parsed.consoleLogs?.[agent.id] || [] },
329
+ // Keep full timeline for time range, but filter to this agent
330
+ timeline: (parsed.timeline || []).filter((e) => e.agentId === agent.id),
331
+ };
332
+ const html = `<!DOCTYPE html>
272
333
  <html lang="en">
273
334
  <head>
274
335
  <meta charset="UTF-8">
275
336
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
276
- <title>Session Replay</title>
337
+ <title>${agent.name} - Session Replay</title>
277
338
  <style>${replayCss}</style>
278
339
  </head>
279
340
  <body>
280
341
  <div id="root"></div>
281
- <script>window.__REPLAY_DATA__ = ${replayData};</script>
342
+ <script>window.__REPLAY_DATA__ = ${JSON.stringify(agentData)};</script>
282
343
  <script>${replayJs}</script>
283
344
  </body>
284
345
  </html>`;
285
- fs.writeFileSync(path.join(opts.runDir, "index.html"), html);
346
+ fs.writeFileSync(path.join(opts.runDir, agentFiles[agent.id]), html);
347
+ }
348
+ // index.html redirects to first agent
349
+ const firstFile = agents.length > 0 ? agentFiles[agents[0].id] : "";
350
+ const indexHtml = `<!DOCTYPE html>
351
+ <html lang="en">
352
+ <head>
353
+ <meta charset="UTF-8">
354
+ <meta http-equiv="refresh" content="0; url=${firstFile}">
355
+ <title>Session Replay</title>
356
+ </head>
357
+ <body>
358
+ <a href="${firstFile}">Open replay</a>
359
+ </body>
360
+ </html>`;
361
+ fs.writeFileSync(path.join(opts.runDir, "index.html"), indexHtml);
286
362
  });
287
363
  electron_1.ipcMain.handle("get-run-dir", () => runDir);
288
364
  }
@@ -27,7 +27,7 @@ contextBridge.exposeInMainWorld("bangonit", {
27
27
  goForward: (agentId) => ipcRenderer.invoke("go-forward", agentId),
28
28
  getURL: (agentId) => ipcRenderer.invoke("get-url", agentId),
29
29
  // Tab management
30
- registerTab: (agentId, tabId, wcId, initialUrl) => ipcRenderer.invoke("register-tab", { agentId, tabId, wcId, initialUrl }),
30
+ registerTab: (agentId, tabId, wcId, initialUrl, planDir) => ipcRenderer.invoke("register-tab", { agentId, tabId, wcId, initialUrl, planDir }),
31
31
  setActiveTab: (agentId, tabId) => ipcRenderer.invoke("set-active-tab", { agentId, tabId }),
32
32
  getTabInfo: (agentId) => ipcRenderer.invoke("get-tab-info", agentId),
33
33
  requestNewTab: (agentId, url) => ipcRenderer.invoke("request-new-tab", { agentId, url }),
@@ -35,6 +35,9 @@ contextBridge.exposeInMainWorld("bangonit", {
35
35
  requestSelectTab: (agentId, tabId) => ipcRenderer.invoke("request-select-tab", { agentId, tabId }),
36
36
  // Downloads
37
37
  getDownloads: (agentId) => ipcRenderer.invoke("get-downloads", agentId),
38
+ // File chooser
39
+ handleFileChooser: (agentId, action, files) => ipcRenderer.invoke("handle-file-chooser", { agentId, action, files }),
40
+ getFileChooserState: (agentId) => ipcRenderer.invoke("get-file-chooser-state", agentId),
38
41
  // Events from main process
39
42
  onTabUpdated: (cb) => onEvent("tab-updated", cb),
40
43
  onOpenNewTab: (cb) => onEvent("open-new-tab", cb),
@@ -70,8 +73,6 @@ contextBridge.exposeInMainWorld("bangonit", {
70
73
  // Bang On It!: forward browser console messages to main
71
74
  emitConsoleMessage: (data) => ipcRenderer.send("console-message", data),
72
75
  // Recording
73
- saveRecordingClip: (opts) => ipcRenderer.invoke("save-recording-clip", opts),
74
- saveReplayData: (opts) => ipcRenderer.invoke("save-replay-data", opts),
75
76
  generateReplayHtml: (opts) => ipcRenderer.invoke("generate-replay-html", opts),
76
77
  getRunDir: () => ipcRenderer.invoke("get-run-dir"),
77
78
  });
@@ -41,6 +41,8 @@ exports.destroyContext = destroyContext;
41
41
  exports.allContexts = allContexts;
42
42
  exports.setDataDir = setDataDir;
43
43
  exports.addTab = addTab;
44
+ exports.handleFileChooser = handleFileChooser;
45
+ exports.getFileChooserState = getFileChooserState;
44
46
  exports.getActiveWc = getActiveWc;
45
47
  exports.getActiveTab = getActiveTab;
46
48
  const electron_1 = require("electron");
@@ -150,10 +152,12 @@ function setupCdp(wc, sendToRenderer, agentId, tabId) {
150
152
  method === "Page.screencastFrame") {
151
153
  sendToRenderer("cdp-event", { agentId, tabId, method, params });
152
154
  }
153
- // Auto-cancel file chooser dialogs
155
+ // Track file chooser dialogs — agent can accept via browser_upload tool
154
156
  if (method === "Page.fileChooserOpened") {
155
- wc.debugger.sendCommand("Page.handleFileChooser", { action: "cancel" })
156
- .catch((e) => console.error("[tabs] Page.handleFileChooser failed:", e.message));
157
+ const ctx = agentContexts.get(agentId);
158
+ if (ctx) {
159
+ ctx.pendingFileChooser = { tabId, mode: params.mode, backendNodeId: params.backendNodeId };
160
+ }
157
161
  }
158
162
  });
159
163
  }
@@ -305,6 +309,42 @@ function addTab(ctx, tabId, wcId, sendToRenderer, agentId, initialUrl) {
305
309
  wc.loadURL(initialUrl);
306
310
  }
307
311
  }
312
+ async function handleFileChooser(agentId, action, files) {
313
+ const ctx = agentContexts.get(agentId);
314
+ if (!ctx)
315
+ return { ok: false, error: "No agent context" };
316
+ if (!ctx.pendingFileChooser)
317
+ return { ok: false, error: "No file picker is open" };
318
+ const tab = ctx.tabs.get(ctx.pendingFileChooser.tabId);
319
+ if (!tab)
320
+ return { ok: false, error: "Tab no longer exists" };
321
+ if (action === "cancel") {
322
+ ctx.pendingFileChooser = null;
323
+ return { ok: true };
324
+ }
325
+ if (!files?.length) {
326
+ return { ok: false, error: "No files provided" };
327
+ }
328
+ const { backendNodeId } = ctx.pendingFileChooser;
329
+ try {
330
+ await tab.wc.debugger.sendCommand("DOM.setFileInputFiles", {
331
+ files,
332
+ backendNodeId,
333
+ });
334
+ }
335
+ catch (e) {
336
+ console.error("[tabs] DOM.setFileInputFiles failed:", e.message);
337
+ return { ok: false, error: e.message };
338
+ }
339
+ ctx.pendingFileChooser = null;
340
+ return { ok: true };
341
+ }
342
+ function getFileChooserState(agentId) {
343
+ const ctx = agentContexts.get(agentId);
344
+ if (!ctx?.pendingFileChooser)
345
+ return { open: false };
346
+ return { open: true, mode: ctx.pendingFileChooser.mode };
347
+ }
308
348
  function getActiveWc(agentId) {
309
349
  const ctx = agentContexts.get(agentId);
310
350
  if (!ctx || ctx.activeTabId === null)
@@ -1 +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:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";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:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,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}.prose{color:var(--tw-prose-body);max-width:65ch}.prose :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-lead);font-size:1.25em;line-height:1.6;margin-top:1.2em;margin-bottom:1.2em}.prose :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-links);text-decoration:underline;font-weight:500}.prose :where(strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.prose :where(a strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol[type=A]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=A s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=I]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type=I s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type="1"]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal}.prose :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:disc;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{font-weight:400;color:var(--tw-prose-counters)}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-bullets)}.prose :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.25em}.prose :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-top:3em;margin-bottom:3em}.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-style:italic;color:var(--tw-prose-quotes);border-inline-start-width:.25rem;border-inline-start-color:var(--tw-prose-quote-borders);quotes:"“""”""‘""’";margin-top:1.6em;margin-bottom:1.6em;padding-inline-start:1em}.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:open-quote}.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:close-quote}.prose :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:800;font-size:2.25em;margin-top:0;margin-bottom:.8888889em;line-height:1.1111111}.prose :where(h1 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:900;color:inherit}.prose :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:700;font-size:1.5em;margin-top:2em;margin-bottom:1em;line-height:1.3333333}.prose :where(h2 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:800;color:inherit}.prose :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;font-size:1.25em;margin-top:1.6em;margin-bottom:.6em;line-height:1.6}.prose :where(h3 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.5em;margin-bottom:.5em;line-height:1.5}.prose :where(h4 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){display:block;margin-top:2em;margin-bottom:2em}.prose :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-family:inherit;color:var(--tw-prose-kbd);box-shadow:0 0 0 1px var(--tw-prose-kbd-shadows),0 3px 0 var(--tw-prose-kbd-shadows);font-size:.875em;border-radius:.3125rem;padding-top:.1875em;padding-inline-end:.375em;padding-bottom:.1875em;padding-inline-start:.375em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-code);font-weight:600;font-size:.875em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:"`"}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:"`"}.prose :where(a code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h1 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.875em}.prose :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.9em}.prose :where(h4 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-pre-code);background-color:var(--tw-prose-pre-bg);overflow-x:auto;font-weight:400;font-size:.875em;line-height:1.7142857;margin-top:1.7142857em;margin-bottom:1.7142857em;border-radius:.375rem;padding-top:.8571429em;padding-inline-end:1.1428571em;padding-bottom:.8571429em;padding-inline-start:1.1428571em}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:transparent;border-width:0;border-radius:0;padding:0;font-weight:inherit;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:none}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:none}.prose :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){width:100%;table-layout:auto;margin-top:2em;margin-bottom:2em;font-size:.875em;line-height:1.7142857}.prose :where(thead):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-th-borders)}.prose :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;vertical-align:bottom;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody tr):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-td-borders)}.prose :where(tbody tr:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:0}.prose :where(tbody td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:baseline}.prose :where(tfoot):not(:where([class~=not-prose],[class~=not-prose] *)){border-top-width:1px;border-top-color:var(--tw-prose-th-borders)}.prose :where(tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:top}.prose :where(th,td):not(:where([class~=not-prose],[class~=not-prose] *)){text-align:start}.prose :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-captions);font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.prose{--tw-prose-body: #374151;--tw-prose-headings: #111827;--tw-prose-lead: #4b5563;--tw-prose-links: #111827;--tw-prose-bold: #111827;--tw-prose-counters: #6b7280;--tw-prose-bullets: #d1d5db;--tw-prose-hr: #e5e7eb;--tw-prose-quotes: #111827;--tw-prose-quote-borders: #e5e7eb;--tw-prose-captions: #6b7280;--tw-prose-kbd: #111827;--tw-prose-kbd-shadows: rgb(17 24 39 / 10%);--tw-prose-code: #111827;--tw-prose-pre-code: #e5e7eb;--tw-prose-pre-bg: #1f2937;--tw-prose-th-borders: #d1d5db;--tw-prose-td-borders: #e5e7eb;--tw-prose-invert-body: #d1d5db;--tw-prose-invert-headings: #fff;--tw-prose-invert-lead: #9ca3af;--tw-prose-invert-links: #fff;--tw-prose-invert-bold: #fff;--tw-prose-invert-counters: #9ca3af;--tw-prose-invert-bullets: #4b5563;--tw-prose-invert-hr: #374151;--tw-prose-invert-quotes: #f3f4f6;--tw-prose-invert-quote-borders: #374151;--tw-prose-invert-captions: #9ca3af;--tw-prose-invert-kbd: #fff;--tw-prose-invert-kbd-shadows: rgb(255 255 255 / 10%);--tw-prose-invert-code: #fff;--tw-prose-invert-pre-code: #d1d5db;--tw-prose-invert-pre-bg: rgb(0 0 0 / 50%);--tw-prose-invert-th-borders: #4b5563;--tw-prose-invert-td-borders: #374151;font-size:1rem;line-height:1.75}.prose :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(.prose>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(.prose>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(.prose>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.prose :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.5714286em;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(.prose>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(.prose>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.prose-sm{font-size:.875rem;line-height:1.7142857}.prose-sm :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em}.prose-sm :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.2857143em;line-height:1.5555556;margin-top:.8888889em;margin-bottom:.8888889em}.prose-sm :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em;margin-bottom:1.3333333em;padding-inline-start:1.1111111em}.prose-sm :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:2.1428571em;margin-top:0;margin-bottom:.8em;line-height:1.2}.prose-sm :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.4285714em;margin-top:1.6em;margin-bottom:.8em;line-height:1.4}.prose-sm :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.2857143em;margin-top:1.5555556em;margin-bottom:.4444444em;line-height:1.5555556}.prose-sm :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.4285714em;margin-bottom:.5714286em;line-height:1.4285714}.prose-sm :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose-sm :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;border-radius:.3125rem;padding-top:.1428571em;padding-inline-end:.3571429em;padding-bottom:.1428571em;padding-inline-start:.3571429em}.prose-sm :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em}.prose-sm :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.9em}.prose-sm :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em}.prose-sm :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.6666667;margin-top:1.6666667em;margin-bottom:1.6666667em;border-radius:.25rem;padding-top:.6666667em;padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em;padding-inline-start:1.5714286em}.prose-sm :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em;padding-inline-start:1.5714286em}.prose-sm :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.2857143em;margin-bottom:.2857143em}.prose-sm :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.4285714em}.prose-sm :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.4285714em}.prose-sm :where(.prose-sm>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5714286em;margin-bottom:.5714286em}.prose-sm :where(.prose-sm>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(.prose-sm>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.1428571em}.prose-sm :where(.prose-sm>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(.prose-sm>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.1428571em}.prose-sm :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5714286em;margin-bottom:.5714286em}.prose-sm :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em}.prose-sm :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.2857143em;padding-inline-start:1.5714286em}.prose-sm :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2.8571429em;margin-bottom:2.8571429em}.prose-sm :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.5}.prose-sm :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose-sm :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose-sm :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.6666667em;padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose-sm :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose-sm :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose-sm :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.3333333;margin-top:.6666667em}.prose-sm :where(.prose-sm>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(.prose-sm>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.prose-invert{--tw-prose-body: var(--tw-prose-invert-body);--tw-prose-headings: var(--tw-prose-invert-headings);--tw-prose-lead: var(--tw-prose-invert-lead);--tw-prose-links: var(--tw-prose-invert-links);--tw-prose-bold: var(--tw-prose-invert-bold);--tw-prose-counters: var(--tw-prose-invert-counters);--tw-prose-bullets: var(--tw-prose-invert-bullets);--tw-prose-hr: var(--tw-prose-invert-hr);--tw-prose-quotes: var(--tw-prose-invert-quotes);--tw-prose-quote-borders: var(--tw-prose-invert-quote-borders);--tw-prose-captions: var(--tw-prose-invert-captions);--tw-prose-kbd: var(--tw-prose-invert-kbd);--tw-prose-kbd-shadows: var(--tw-prose-invert-kbd-shadows);--tw-prose-code: var(--tw-prose-invert-code);--tw-prose-pre-code: var(--tw-prose-invert-pre-code);--tw-prose-pre-bg: var(--tw-prose-invert-pre-bg);--tw-prose-th-borders: var(--tw-prose-invert-th-borders);--tw-prose-td-borders: var(--tw-prose-invert-td-borders)}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.bottom-0{bottom:0}.top-0{top:0}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.ml-1\.5{margin-left:.375rem}.ml-auto{margin-left:auto}.mt-1{margin-top:.25rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.hidden{display:none}.h-2{height:.5rem}.h-6{height:1.5rem}.h-full{height:100%}.h-screen{height:100vh}.max-h-32{max-height:8rem}.max-h-60{max-height:15rem}.max-h-full{max-height:100%}.min-h-0{min-height:0px}.w-0\.5{width:.125rem}.w-2{width:.5rem}.w-48{width:12rem}.w-full{width:100%}.min-w-0{min-width:0px}.max-w-full{max-width:100%}.max-w-none{max-width:none}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.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))}.cursor-pointer{cursor:pointer}.select-all{-webkit-user-select:all;-moz-user-select:all;user-select:all}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.gap-1{gap:.25rem}.gap-2{gap:.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-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))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-blue-500{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.border-blue-600{--tw-border-opacity: 1;border-color:rgb(37 99 235 / var(--tw-border-opacity, 1))}.border-zinc-500{--tw-border-opacity: 1;border-color:rgb(113 113 122 / var(--tw-border-opacity, 1))}.border-zinc-700{--tw-border-opacity: 1;border-color:rgb(63 63 70 / var(--tw-border-opacity, 1))}.border-zinc-800{--tw-border-opacity: 1;border-color:rgb(39 39 42 / var(--tw-border-opacity, 1))}.bg-amber-500{--tw-bg-opacity: 1;background-color:rgb(245 158 11 / var(--tw-bg-opacity, 1))}.bg-amber-950\/50{background-color:#451a0380}.bg-blue-400{--tw-bg-opacity: 1;background-color:rgb(96 165 250 / var(--tw-bg-opacity, 1))}.bg-blue-600\/30{background-color:#2563eb4d}.bg-blue-900\/60{background-color:#1e3a8a99}.bg-blue-950\/30{background-color:#1725544d}.bg-green-950\/50{background-color:#052e1680}.bg-red-950\/50{background-color:#450a0a80}.bg-zinc-700{--tw-bg-opacity: 1;background-color:rgb(63 63 70 / var(--tw-bg-opacity, 1))}.bg-zinc-800{--tw-bg-opacity: 1;background-color:rgb(39 39 42 / var(--tw-bg-opacity, 1))}.bg-zinc-900{--tw-bg-opacity: 1;background-color:rgb(24 24 27 / var(--tw-bg-opacity, 1))}.bg-zinc-900\/50{background-color:#18181b80}.bg-zinc-950{--tw-bg-opacity: 1;background-color:rgb(9 9 11 / var(--tw-bg-opacity, 1))}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.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-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.pb-2{padding-bottom:.5rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-\[10px\]{font-size:10px}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.tracking-wider{letter-spacing:.05em}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-zinc-200{--tw-text-opacity: 1;color:rgb(228 228 231 / var(--tw-text-opacity, 1))}.text-zinc-300{--tw-text-opacity: 1;color:rgb(212 212 216 / var(--tw-text-opacity, 1))}.text-zinc-400{--tw-text-opacity: 1;color:rgb(161 161 170 / var(--tw-text-opacity, 1))}.text-zinc-500{--tw-text-opacity: 1;color:rgb(113 113 122 / var(--tw-text-opacity, 1))}.text-zinc-600{--tw-text-opacity: 1;color:rgb(82 82 91 / var(--tw-text-opacity, 1))}.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)}.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-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}body{margin:0;background:#09090b;color:#d4d4d8;font-family:ui-sans-serif,system-ui,sans-serif}*{box-sizing:border-box}.hover\:border-zinc-500:hover{--tw-border-opacity: 1;border-color:rgb(113 113 122 / var(--tw-border-opacity, 1))}.hover\:border-zinc-700:hover{--tw-border-opacity: 1;border-color:rgb(63 63 70 / var(--tw-border-opacity, 1))}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.hover\:text-zinc-300:hover{--tw-text-opacity: 1;color:rgb(212 212 216 / var(--tw-text-opacity, 1))}.hover\:text-zinc-400:hover{--tw-text-opacity: 1;color:rgb(161 161 170 / var(--tw-text-opacity, 1))}@media (min-width: 768px){.md\:block{display:block}.md\:flex{display:flex}.md\:hidden{display:none}.md\:w-\[420px\]{width:420px}.md\:shrink-0{flex-shrink:0}.md\:gap-3{gap:.75rem}.md\:border-r{border-right-width:1px}.md\:px-2{padding-left:.5rem;padding-right:.5rem}.md\:px-4{padding-left:1rem;padding-right:1rem}.md\:py-1{padding-top:.25rem;padding-bottom:.25rem}.md\:py-3{padding-top:.75rem;padding-bottom:.75rem}}
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:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";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:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,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}.prose{color:var(--tw-prose-body);max-width:65ch}.prose :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-lead);font-size:1.25em;line-height:1.6;margin-top:1.2em;margin-bottom:1.2em}.prose :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-links);text-decoration:underline;font-weight:500}.prose :where(strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.prose :where(a strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol[type=A]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=A s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=I]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type=I s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type="1"]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal}.prose :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:disc;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{font-weight:400;color:var(--tw-prose-counters)}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-bullets)}.prose :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.25em}.prose :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-top:3em;margin-bottom:3em}.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-style:italic;color:var(--tw-prose-quotes);border-inline-start-width:.25rem;border-inline-start-color:var(--tw-prose-quote-borders);quotes:"“""”""‘""’";margin-top:1.6em;margin-bottom:1.6em;padding-inline-start:1em}.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:open-quote}.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:close-quote}.prose :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:800;font-size:2.25em;margin-top:0;margin-bottom:.8888889em;line-height:1.1111111}.prose :where(h1 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:900;color:inherit}.prose :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:700;font-size:1.5em;margin-top:2em;margin-bottom:1em;line-height:1.3333333}.prose :where(h2 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:800;color:inherit}.prose :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;font-size:1.25em;margin-top:1.6em;margin-bottom:.6em;line-height:1.6}.prose :where(h3 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.5em;margin-bottom:.5em;line-height:1.5}.prose :where(h4 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){display:block;margin-top:2em;margin-bottom:2em}.prose :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-family:inherit;color:var(--tw-prose-kbd);box-shadow:0 0 0 1px var(--tw-prose-kbd-shadows),0 3px 0 var(--tw-prose-kbd-shadows);font-size:.875em;border-radius:.3125rem;padding-top:.1875em;padding-inline-end:.375em;padding-bottom:.1875em;padding-inline-start:.375em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-code);font-weight:600;font-size:.875em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:"`"}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:"`"}.prose :where(a code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h1 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.875em}.prose :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.9em}.prose :where(h4 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-pre-code);background-color:var(--tw-prose-pre-bg);overflow-x:auto;font-weight:400;font-size:.875em;line-height:1.7142857;margin-top:1.7142857em;margin-bottom:1.7142857em;border-radius:.375rem;padding-top:.8571429em;padding-inline-end:1.1428571em;padding-bottom:.8571429em;padding-inline-start:1.1428571em}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:transparent;border-width:0;border-radius:0;padding:0;font-weight:inherit;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:none}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:none}.prose :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){width:100%;table-layout:auto;margin-top:2em;margin-bottom:2em;font-size:.875em;line-height:1.7142857}.prose :where(thead):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-th-borders)}.prose :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;vertical-align:bottom;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody tr):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-td-borders)}.prose :where(tbody tr:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:0}.prose :where(tbody td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:baseline}.prose :where(tfoot):not(:where([class~=not-prose],[class~=not-prose] *)){border-top-width:1px;border-top-color:var(--tw-prose-th-borders)}.prose :where(tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:top}.prose :where(th,td):not(:where([class~=not-prose],[class~=not-prose] *)){text-align:start}.prose :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-captions);font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.prose{--tw-prose-body: #374151;--tw-prose-headings: #111827;--tw-prose-lead: #4b5563;--tw-prose-links: #111827;--tw-prose-bold: #111827;--tw-prose-counters: #6b7280;--tw-prose-bullets: #d1d5db;--tw-prose-hr: #e5e7eb;--tw-prose-quotes: #111827;--tw-prose-quote-borders: #e5e7eb;--tw-prose-captions: #6b7280;--tw-prose-kbd: #111827;--tw-prose-kbd-shadows: rgb(17 24 39 / 10%);--tw-prose-code: #111827;--tw-prose-pre-code: #e5e7eb;--tw-prose-pre-bg: #1f2937;--tw-prose-th-borders: #d1d5db;--tw-prose-td-borders: #e5e7eb;--tw-prose-invert-body: #d1d5db;--tw-prose-invert-headings: #fff;--tw-prose-invert-lead: #9ca3af;--tw-prose-invert-links: #fff;--tw-prose-invert-bold: #fff;--tw-prose-invert-counters: #9ca3af;--tw-prose-invert-bullets: #4b5563;--tw-prose-invert-hr: #374151;--tw-prose-invert-quotes: #f3f4f6;--tw-prose-invert-quote-borders: #374151;--tw-prose-invert-captions: #9ca3af;--tw-prose-invert-kbd: #fff;--tw-prose-invert-kbd-shadows: rgb(255 255 255 / 10%);--tw-prose-invert-code: #fff;--tw-prose-invert-pre-code: #d1d5db;--tw-prose-invert-pre-bg: rgb(0 0 0 / 50%);--tw-prose-invert-th-borders: #4b5563;--tw-prose-invert-td-borders: #374151;font-size:1rem;line-height:1.75}.prose :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(.prose>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(.prose>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(.prose>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.prose :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.5714286em;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(.prose>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(.prose>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.prose-sm{font-size:.875rem;line-height:1.7142857}.prose-sm :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em}.prose-sm :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.2857143em;line-height:1.5555556;margin-top:.8888889em;margin-bottom:.8888889em}.prose-sm :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em;margin-bottom:1.3333333em;padding-inline-start:1.1111111em}.prose-sm :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:2.1428571em;margin-top:0;margin-bottom:.8em;line-height:1.2}.prose-sm :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.4285714em;margin-top:1.6em;margin-bottom:.8em;line-height:1.4}.prose-sm :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.2857143em;margin-top:1.5555556em;margin-bottom:.4444444em;line-height:1.5555556}.prose-sm :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.4285714em;margin-bottom:.5714286em;line-height:1.4285714}.prose-sm :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose-sm :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;border-radius:.3125rem;padding-top:.1428571em;padding-inline-end:.3571429em;padding-bottom:.1428571em;padding-inline-start:.3571429em}.prose-sm :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em}.prose-sm :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.9em}.prose-sm :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em}.prose-sm :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.6666667;margin-top:1.6666667em;margin-bottom:1.6666667em;border-radius:.25rem;padding-top:.6666667em;padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em;padding-inline-start:1.5714286em}.prose-sm :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em;padding-inline-start:1.5714286em}.prose-sm :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.2857143em;margin-bottom:.2857143em}.prose-sm :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.4285714em}.prose-sm :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.4285714em}.prose-sm :where(.prose-sm>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5714286em;margin-bottom:.5714286em}.prose-sm :where(.prose-sm>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(.prose-sm>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.1428571em}.prose-sm :where(.prose-sm>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(.prose-sm>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.1428571em}.prose-sm :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5714286em;margin-bottom:.5714286em}.prose-sm :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em}.prose-sm :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.2857143em;padding-inline-start:1.5714286em}.prose-sm :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2.8571429em;margin-bottom:2.8571429em}.prose-sm :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.5}.prose-sm :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose-sm :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose-sm :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.6666667em;padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose-sm :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose-sm :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose-sm :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.3333333;margin-top:.6666667em}.prose-sm :where(.prose-sm>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(.prose-sm>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.prose-invert{--tw-prose-body: var(--tw-prose-invert-body);--tw-prose-headings: var(--tw-prose-invert-headings);--tw-prose-lead: var(--tw-prose-invert-lead);--tw-prose-links: var(--tw-prose-invert-links);--tw-prose-bold: var(--tw-prose-invert-bold);--tw-prose-counters: var(--tw-prose-invert-counters);--tw-prose-bullets: var(--tw-prose-invert-bullets);--tw-prose-hr: var(--tw-prose-invert-hr);--tw-prose-quotes: var(--tw-prose-invert-quotes);--tw-prose-quote-borders: var(--tw-prose-invert-quote-borders);--tw-prose-captions: var(--tw-prose-invert-captions);--tw-prose-kbd: var(--tw-prose-invert-kbd);--tw-prose-kbd-shadows: var(--tw-prose-invert-kbd-shadows);--tw-prose-code: var(--tw-prose-invert-code);--tw-prose-pre-code: var(--tw-prose-invert-pre-code);--tw-prose-pre-bg: var(--tw-prose-invert-pre-bg);--tw-prose-th-borders: var(--tw-prose-invert-th-borders);--tw-prose-td-borders: var(--tw-prose-invert-td-borders)}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.bottom-0{bottom:0}.top-0{top:0}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.ml-1\.5{margin-left:.375rem}.ml-auto{margin-left:auto}.mt-1{margin-top:.25rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.hidden{display:none}.h-2{height:.5rem}.h-6{height:1.5rem}.h-full{height:100%}.h-screen{height:100vh}.max-h-32{max-height:8rem}.max-h-60{max-height:15rem}.max-h-full{max-height:100%}.min-h-0{min-height:0px}.w-0\.5{width:.125rem}.w-2{width:.5rem}.w-48{width:12rem}.w-full{width:100%}.min-w-0{min-width:0px}.max-w-full{max-width:100%}.max-w-none{max-width:none}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.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))}.cursor-pointer{cursor:pointer}.select-all{-webkit-user-select:all;-moz-user-select:all;user-select:all}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.gap-1{gap:.25rem}.gap-2{gap:.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-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))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-blue-500{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.border-blue-600{--tw-border-opacity: 1;border-color:rgb(37 99 235 / var(--tw-border-opacity, 1))}.border-zinc-500{--tw-border-opacity: 1;border-color:rgb(113 113 122 / var(--tw-border-opacity, 1))}.border-zinc-700{--tw-border-opacity: 1;border-color:rgb(63 63 70 / var(--tw-border-opacity, 1))}.border-zinc-800{--tw-border-opacity: 1;border-color:rgb(39 39 42 / var(--tw-border-opacity, 1))}.bg-amber-500{--tw-bg-opacity: 1;background-color:rgb(245 158 11 / var(--tw-bg-opacity, 1))}.bg-amber-950\/50{background-color:#451a0380}.bg-blue-400{--tw-bg-opacity: 1;background-color:rgb(96 165 250 / var(--tw-bg-opacity, 1))}.bg-blue-600\/30{background-color:#2563eb4d}.bg-blue-900\/60{background-color:#1e3a8a99}.bg-blue-950\/30{background-color:#1725544d}.bg-green-950\/50{background-color:#052e1680}.bg-red-950\/50{background-color:#450a0a80}.bg-zinc-700{--tw-bg-opacity: 1;background-color:rgb(63 63 70 / var(--tw-bg-opacity, 1))}.bg-zinc-800{--tw-bg-opacity: 1;background-color:rgb(39 39 42 / var(--tw-bg-opacity, 1))}.bg-zinc-900{--tw-bg-opacity: 1;background-color:rgb(24 24 27 / var(--tw-bg-opacity, 1))}.bg-zinc-900\/50{background-color:#18181b80}.bg-zinc-950{--tw-bg-opacity: 1;background-color:rgb(9 9 11 / var(--tw-bg-opacity, 1))}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.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-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.pb-2{padding-bottom:.5rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-\[10px\]{font-size:10px}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.tracking-wider{letter-spacing:.05em}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-zinc-200{--tw-text-opacity: 1;color:rgb(228 228 231 / var(--tw-text-opacity, 1))}.text-zinc-300{--tw-text-opacity: 1;color:rgb(212 212 216 / var(--tw-text-opacity, 1))}.text-zinc-400{--tw-text-opacity: 1;color:rgb(161 161 170 / var(--tw-text-opacity, 1))}.text-zinc-500{--tw-text-opacity: 1;color:rgb(113 113 122 / var(--tw-text-opacity, 1))}.text-zinc-600{--tw-text-opacity: 1;color:rgb(82 82 91 / var(--tw-text-opacity, 1))}.no-underline{text-decoration-line:none}.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)}.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-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}body{margin:0;background:#09090b;color:#d4d4d8;font-family:ui-sans-serif,system-ui,sans-serif}*{box-sizing:border-box}.hover\:border-zinc-500:hover{--tw-border-opacity: 1;border-color:rgb(113 113 122 / var(--tw-border-opacity, 1))}.hover\:border-zinc-700:hover{--tw-border-opacity: 1;border-color:rgb(63 63 70 / var(--tw-border-opacity, 1))}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.hover\:text-zinc-300:hover{--tw-text-opacity: 1;color:rgb(212 212 216 / var(--tw-text-opacity, 1))}.hover\:text-zinc-400:hover{--tw-text-opacity: 1;color:rgb(161 161 170 / var(--tw-text-opacity, 1))}@media (min-width: 768px){.md\:block{display:block}.md\:flex{display:flex}.md\:hidden{display:none}.md\:w-\[420px\]{width:420px}.md\:shrink-0{flex-shrink:0}.md\:gap-3{gap:.75rem}.md\:border-r{border-right-width:1px}.md\:px-2{padding-left:.5rem;padding-right:.5rem}.md\:px-4{padding-left:1rem;padding-right:1rem}.md\:py-1{padding-top:.25rem;padding-bottom:.25rem}.md\:py-3{padding-top:.75rem;padding-bottom:.75rem}}