@canivel/ralph 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,6 +8,16 @@ Ralph is a minimal, file-based agent loop for autonomous coding. Each iteration
8
8
 
9
9
  ## What's New in This Fork
10
10
 
11
+ ### Latest Updates
12
+
13
+ - **Web Dashboard** - Real-time monitoring UI for managing multiple projects from a single interface (`ralph dashboard`) -- see [Dashboard](#dashboard)
14
+ - **Automatic Retry** - Transient errors (API timeouts, rate limits) trigger automatic retries with exponential backoff
15
+ - **Story Blocking** - Stories that fail repeatedly are automatically blocked to prevent infinite loops
16
+ - **Git Cleanup** - Failed iterations automatically clean up uncommitted changes
17
+ - **Lean Progress Log** - Detailed run info moved to `.ralph/runs/`, keeping progress.md concise
18
+
19
+ ### Original Fork Features
20
+
11
21
  - **First-run agent selection** - Prompted to choose your default agent on first use
12
22
  - **`ralph config` command** - Reconfigure your default agent anytime
13
23
  - **Improved Claude support** - Uses stdin mode (`-p -`) for reliable prompt passing
@@ -88,6 +98,7 @@ Ralph Configuration
88
98
  | `ralph overview` | Generate human-readable overview from PRD |
89
99
  | `ralph ping` | Health check for agent connectivity |
90
100
  | `ralph log "<message>"` | Append to activity log |
101
+ | `ralph dashboard` | Start the Ralph dashboard web server |
91
102
  | `ralph help` | Show help message |
92
103
 
93
104
  ### Options
@@ -199,9 +210,70 @@ The build loop automatically updates story status:
199
210
  | `open` | Available for selection |
200
211
  | `in_progress` | Currently being worked on (with `startedAt`) |
201
212
  | `done` | Completed (with `completedAt`) |
213
+ | `blocked` | Too many failures (with `blockedReason`) |
202
214
 
203
215
  If a loop crashes while a story is `in_progress`, set `STALE_SECONDS` in config to auto-reopen stalled stories.
204
216
 
217
+ ## Error Handling & Retry
218
+
219
+ Ralph includes robust error handling for transient failures:
220
+
221
+ ### Automatic Retry
222
+
223
+ When the agent fails with a transient error (API timeouts, rate limits, connection resets), Ralph automatically retries:
224
+
225
+ ```
226
+ ╔═══════════════════════════════════════════════════════════╗
227
+ ║ Transient error detected, retrying (1/3)...
228
+ ╚═══════════════════════════════════════════════════════════╝
229
+ ```
230
+
231
+ Detected transient errors:
232
+ - "No messages returned" (Claude CLI)
233
+ - Rate limit errors
234
+ - API overload errors
235
+ - Connection resets (ECONNRESET, ETIMEDOUT)
236
+ - Socket hang up
237
+
238
+ ### Story Blocking
239
+
240
+ If a story fails repeatedly (default: 3 times), it's automatically marked as `blocked`:
241
+
242
+ ```json
243
+ {
244
+ "id": "US-001",
245
+ "status": "blocked",
246
+ "blockedReason": "Failed 3 times"
247
+ }
248
+ ```
249
+
250
+ Blocked stories are skipped in subsequent iterations. To retry a blocked story, manually change its status back to `open` in the PRD JSON.
251
+
252
+ ### Git Cleanup
253
+
254
+ When an iteration fails, Ralph automatically cleans up uncommitted changes to prevent polluting the next iteration:
255
+
256
+ ```bash
257
+ # Automatic cleanup on failure
258
+ git checkout -- .
259
+ git clean -fd
260
+ ```
261
+
262
+ ### Configuration
263
+
264
+ Control retry behavior in `.agents/ralph/config.sh`:
265
+
266
+ ```bash
267
+ # Number of retries per iteration for transient errors (default: 3)
268
+ MAX_RETRIES=3
269
+
270
+ # Delay between retries in seconds (default: 5)
271
+ RETRY_DELAY=5
272
+
273
+ # Max failures before blocking a story (default: 3)
274
+ MAX_STORY_FAILURES=3
275
+ ```
276
+
205
277
  ## State Files
206
278
 
207
279
  All state is stored in `.ralph/` in your project:
@@ -214,6 +286,95 @@ All state is stored in `.ralph/` in your project:
214
286
  | `errors.log` | Repeated failures and notes |
215
287
  | `runs/` | Raw run logs and summaries |
216
288
 
289
+ ## Dashboard
290
+
291
+ Ralph includes a real-time web dashboard for monitoring and managing multiple projects from a single interface. Run several autonomous builds in parallel and track all of them from one place.
292
+
293
+ ```bash
294
+ ralph dashboard --open
295
+ ```
296
+
297
+ ### Managing Multiple Projects
298
+
299
+ The dashboard landing page shows every registered project as a card. Each card displays the project name, path, story completion progress, and last activity time. Click any card to drill into that project's full detail view.
300
+
301
+ ![Ralph Dashboard — Projects](images/dash-projects.png)
302
+
303
+ **Adding projects:**
304
+
305
+ There are three ways to register projects with the dashboard:
306
+
307
+ 1. **Auto-detect** -- If the current directory contains a `.ralph/` folder, it is registered automatically when you start the dashboard.
308
+ 2. **CLI flag** -- Pass multiple project paths at launch:
309
+ ```bash
310
+ ralph dashboard --projects ~/project-a,~/project-b,~/project-c
311
+ ```
312
+ 3. **From the UI** -- Click the **Add Project** button in the top-right corner, browse to a directory that contains a `.ralph/` folder, and add it. Projects can be added at any time without restarting the server.
313
+
314
+ Any directory with a `.ralph/` folder is a valid Ralph project. You can mix and match projects that use different agents or PRDs.
315
+
316
+ ### Project Detail View
317
+
318
+ Click a project card to open its detail view. The header shows the project name, status (Idle / Running), path, story progress bar, and run count. Below that, seven tabs give you full visibility into the build.
319
+
320
+ ![Ralph Dashboard — Story Board](images/Screenshot%202026-01-31%20094653.png)
321
+
322
+ | Tab | What It Shows |
323
+ |-----|---------------|
324
+ | **Overview** | Story progress bar, quality gates, recent activity feed, current iteration status |
325
+ | **Stories** | Kanban board with **Open**, **In Progress**, and **Done** columns. Cards show story ID, title, blocked/dependency status, and elapsed time |
326
+ | **Runs** | Timeline of every iteration with duration, success/failure status, and expandable raw output |
327
+ | **Logs** | Browsable activity log, error log, and per-run logs with search and filtering |
328
+ | **Progress** | Rendered `progress.md` with syntax highlighting |
329
+ | **Guardrails** | Rendered `guardrails.md` with color-coded Sign entries |
330
+ | **Metrics** | Charts for completion rate, iteration durations, and success/failure rates over time |
331
+
332
+ ### Key Capabilities
333
+
334
+ - **Real-time Updates** -- WebSocket-based live updates as files change on disk; no manual refresh needed
335
+ - **Multi-project Support** -- Switch between projects from the landing page or the sidebar to monitor parallel builds
336
+ - **Story Dependency Tracking** -- Blocked stories show which dependency they are waiting on (e.g. "Waiting on: US-005")
337
+ - **Elapsed Time Tracking** -- In-progress and completed stories display elapsed time
338
+ - **Connection Status** -- A live indicator in the header shows the WebSocket connection state
339
+ - **Dark / Light Mode** -- Toggle between themes
340
+ - **Responsive Design** -- Works on desktop and mobile
341
+
342
+ ### Dashboard Options
343
+
344
+ | Option | Description |
345
+ |--------|-------------|
346
+ | `--port <number>` | Port to run the server on (default: 4242) |
347
+ | `--host <hostname>` | Host to bind to (default: localhost) |
348
+ | `--open` | Open browser automatically after starting |
349
+ | `--projects <paths>` | Comma-separated project paths to register |
350
+
351
+ ```bash
352
+ # Examples
353
+ ralph dashboard # default port 4242
354
+ ralph dashboard --port 8080 # custom port
355
+ ralph dashboard --open # auto-open browser
356
+ ralph dashboard --host 0.0.0.0 # expose on all interfaces
357
+ ralph dashboard --projects ~/app-a,~/app-b,~/app-c # register three projects
358
+ ```
359
+
360
+ ### Tech Stack
361
+
362
+ - **Frontend**: React 19, TypeScript, Vite, Tailwind CSS, shadcn/ui, Recharts
363
+ - **Backend**: Express, WebSocket (ws), chokidar (file watching)
364
+ - **State**: Zustand for client state management
365
+
366
+ ### Running Dashboard Tests
367
+
368
+ ```bash
369
+ # Run all dashboard tests (113 tests)
370
+ npm run test:dashboard
371
+
372
+ # Tests cover:
373
+ # - Data parsers (PRD, activity log, errors log, progress.md, guardrails.md)
374
+ # - Project manager service
375
+ # - API endpoints
376
+ ```
377
+
217
378
  ## Advanced
218
379
 
219
380
  ### Multiple PRD Files
package/bin/ralph CHANGED
@@ -17,6 +17,10 @@ let installForce = false;
17
17
  let prdPath = null;
18
18
  let progressPath = null;
19
19
  let prdOutPath = null;
20
+ let dashboardPort = 4242;
21
+ let dashboardHost = "localhost";
22
+ let dashboardOpen = false;
23
+ let dashboardProjects = null;
20
24
 
21
25
  function exists(p) {
22
26
  try {
@@ -78,6 +82,7 @@ Commands:
78
82
  build [n] [--no-commit] Run build loop (default)
79
83
  overview Render a human overview from PRD JSON
80
84
  config Configure default agent
85
+ dashboard Start the Ralph dashboard web server
81
86
  help Show this message
82
87
 
83
88
  Options:
@@ -86,6 +91,12 @@ Options:
86
91
  --progress <path> Override progress log path
87
92
  --agent <codex|claude|droid|opencode> Override agent runner
88
93
 
94
+ Dashboard options:
95
+ --port <number> Port to run on (default: 4242)
96
+ --host <hostname> Host to bind to (default: localhost)
97
+ --open Open browser after starting
98
+ --projects <paths> Comma-separated project paths to register
99
+
89
100
  Notes:
90
101
  - Uses local .agents/ralph if present; otherwise uses bundled defaults.
91
102
  - State and logs are written to .ralph/ in the project.
@@ -93,6 +104,43 @@ Notes:
93
104
  `);
94
105
  }
95
106
 
107
+ /**
108
+ * Show detailed help for the dashboard command
109
+ */
110
+ function dashboardUsage() {
111
+ console.log(`ralph dashboard - Start the Ralph dashboard web server
112
+
113
+ Usage:
114
+ ralph dashboard [options]
115
+
116
+ Options:
117
+ --port <number> Port to run the server on (default: 4242)
118
+ --host <hostname> Host to bind to (default: localhost)
119
+ --open Open browser automatically after starting
120
+ --projects <paths> Comma-separated project paths to register
121
+
122
+ Description:
123
+ Starts a web-based dashboard for monitoring Ralph projects in real-time.
124
+ The dashboard provides:
125
+
126
+ - Project Overview: See all registered projects with story progress
127
+ - Kanban Board: Visualize stories by status (Open/In Progress/Done)
128
+ - Run History: View all iteration runs with duration and status
129
+ - Log Viewer: Browse activity logs, error logs, and run logs
130
+ - Progress Tracking: View progress.md with collapsible story sections
131
+ - Guardrails: View guardrails.md with highlighted Sign entries
132
+ - Metrics: Charts showing completion rate, iteration durations, and more
133
+
134
+ The current directory is auto-registered if it contains a .ralph/ folder.
135
+
136
+ Examples:
137
+ ralph dashboard # Start on port 4242
138
+ ralph dashboard --port 8080 # Start on custom port
139
+ ralph dashboard --open # Start and open browser
140
+ ralph dashboard --projects /a,/b # Register multiple projects
141
+ `);
142
+ }
143
+
96
144
  function getGlobalConfigPath() {
97
145
  return path.join(os.homedir(), ".ralph", "config.json");
98
146
  }
@@ -248,6 +296,37 @@ for (let i = 0; i < rawArgs.length; i += 1) {
248
296
  i += 1;
249
297
  continue;
250
298
  }
299
+ if (arg.startsWith("--port=")) {
300
+ dashboardPort = parseInt(arg.split("=").slice(1).join("="), 10);
301
+ continue;
302
+ }
303
+ if (arg === "--port") {
304
+ dashboardPort = parseInt(rawArgs[i + 1], 10);
305
+ i += 1;
306
+ continue;
307
+ }
308
+ if (arg.startsWith("--host=")) {
309
+ dashboardHost = arg.split("=").slice(1).join("=");
310
+ continue;
311
+ }
312
+ if (arg === "--host") {
313
+ dashboardHost = rawArgs[i + 1];
314
+ i += 1;
315
+ continue;
316
+ }
317
+ if (arg === "--open") {
318
+ dashboardOpen = true;
319
+ continue;
320
+ }
321
+ if (arg.startsWith("--projects=")) {
322
+ dashboardProjects = arg.split("=").slice(1).join("=");
323
+ continue;
324
+ }
325
+ if (arg === "--projects") {
326
+ dashboardProjects = rawArgs[i + 1];
327
+ i += 1;
328
+ continue;
329
+ }
251
330
  args.push(arg);
252
331
  }
253
332
 
@@ -577,6 +656,92 @@ async function main() {
577
656
  process.exit(0);
578
657
  }
579
658
 
659
+ if (cmd === "dashboard") {
660
+ // Handle dashboard --help
661
+ if (args.includes("--help") || args.includes("-h")) {
662
+ dashboardUsage();
663
+ process.exit(0);
664
+ }
665
+
666
+ const dashboardServerPath = path.join(repoRoot, "dashboard", "server", "dist", "index.js");
667
+ const projectManagerPath = path.join(repoRoot, "dashboard", "server", "dist", "services", "projectManager.js");
668
+
669
+ if (!exists(dashboardServerPath)) {
670
+ console.error("Dashboard server not found. Run 'npm run build:dashboard:server' first.");
671
+ process.exit(1);
672
+ }
673
+
674
+ // Set environment variable for the server to find client dist
675
+ process.env.RALPH_REPO_ROOT = repoRoot;
676
+
677
+ const { startServer, shutdown, server } = require(dashboardServerPath);
678
+ const { registerProject, isValidRalphProject } = require(projectManagerPath);
679
+
680
+ // Auto-register cwd if it's a Ralph project (.ralph/ exists)
681
+ const cwdRalphDir = path.join(cwd, ".ralph");
682
+ if (exists(cwdRalphDir)) {
683
+ registerProject(cwd).then((result) => {
684
+ if (result.success) {
685
+ console.log(`Auto-registered current directory: ${cwd}`);
686
+ }
687
+ // If already registered, that's fine - no error message needed
688
+ }).catch(() => {
689
+ // Ignore registration errors during startup
690
+ });
691
+ }
692
+
693
+ // Register projects from --projects option
694
+ if (dashboardProjects) {
695
+ const projectPaths = dashboardProjects.split(",").map((p) => p.trim()).filter(Boolean);
696
+ for (const projectPath of projectPaths) {
697
+ const absolutePath = path.isAbsolute(projectPath) ? projectPath : path.resolve(cwd, projectPath);
698
+ registerProject(absolutePath).then((result) => {
699
+ if (result.success) {
700
+ console.log(`Registered project: ${absolutePath}`);
701
+ } else {
702
+ console.warn(`Failed to register project: ${result.error}`);
703
+ }
704
+ }).catch((err) => {
705
+ console.warn(`Failed to register project ${absolutePath}: ${err.message}`);
706
+ });
707
+ }
708
+ }
709
+
710
+ // Start the server
711
+ const port = dashboardPort || 4242;
712
+ const host = dashboardHost || "localhost";
713
+ const serverUrl = `http://${host}:${port}`;
714
+
715
+ server.listen(port, host, () => {
716
+ console.log(`Ralph Dashboard server running at ${serverUrl}`);
717
+ console.log("Press Ctrl+C to stop the server.");
718
+
719
+ // Open browser if --open flag is set
720
+ if (dashboardOpen) {
721
+ const openCommand =
722
+ process.platform === "darwin" ? "open" :
723
+ process.platform === "win32" ? "start" : "xdg-open";
724
+ spawnSync(openCommand, [serverUrl], { shell: true, stdio: "ignore" });
725
+ }
726
+ });
727
+
728
+ // Handle graceful shutdown on SIGINT
729
+ process.on("SIGINT", () => {
730
+ console.log("\nShutting down gracefully...");
731
+ shutdown();
732
+ process.exit(0);
733
+ });
734
+
735
+ process.on("SIGTERM", () => {
736
+ console.log("Received SIGTERM, shutting down...");
737
+ shutdown();
738
+ process.exit(0);
739
+ });
740
+
741
+ // Keep the process running
742
+ return;
743
+ }
744
+
580
745
  if (cmd === "prd") {
581
746
  let request = args.slice(1).join(" ").trim();
582
747
  if (!request) {
@@ -0,0 +1 @@
1
+ *,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family: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}:root{--background: 0 0% 100%;--foreground: 222.2 84% 4.9%;--card: 0 0% 100%;--card-foreground: 222.2 84% 4.9%;--popover: 0 0% 100%;--popover-foreground: 222.2 84% 4.9%;--primary: 222.2 47.4% 11.2%;--primary-foreground: 210 40% 98%;--secondary: 210 40% 96.1%;--secondary-foreground: 222.2 47.4% 11.2%;--muted: 210 40% 96.1%;--muted-foreground: 215.4 16.3% 46.9%;--accent: 210 40% 96.1%;--accent-foreground: 222.2 47.4% 11.2%;--destructive: 0 84.2% 60.2%;--destructive-foreground: 210 40% 98%;--border: 214.3 31.8% 91.4%;--input: 214.3 31.8% 91.4%;--ring: 222.2 84% 4.9%;--radius: .5rem}.dark{--background: 222.2 84% 4.9%;--foreground: 210 40% 98%;--card: 222.2 84% 4.9%;--card-foreground: 210 40% 98%;--popover: 222.2 84% 4.9%;--popover-foreground: 210 40% 98%;--primary: 210 40% 98%;--primary-foreground: 222.2 47.4% 11.2%;--secondary: 217.2 32.6% 17.5%;--secondary-foreground: 210 40% 98%;--muted: 217.2 32.6% 17.5%;--muted-foreground: 215 20.2% 65.1%;--accent: 217.2 32.6% 17.5%;--accent-foreground: 210 40% 98%;--destructive: 0 62.8% 30.6%;--destructive-foreground: 210 40% 98%;--border: 217.2 32.6% 17.5%;--input: 217.2 32.6% 17.5%;--ring: 212.7 26.8% 83.9%}*{border-color:hsl(var(--border))}body{background-color:hsl(var(--background));color:hsl(var(--foreground))}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.visible{visibility:visible}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.bottom-0{bottom:0}.left-0{left:0}.left-3{left:.75rem}.left-\[50\%\]{left:50%}.right-0{right:0}.right-4{right:1rem}.top-0{top:0}.top-1\/2{top:50%}.top-4{top:1rem}.top-\[50\%\]{top:50%}.top-full{top:100%}.z-40{z-index:40}.z-50{z-index:50}.col-span-full{grid-column:1 / -1}.mx-auto{margin-left:auto;margin-right:auto}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-3{margin-top:.75rem;margin-bottom:.75rem}.my-4{margin-top:1rem;margin-bottom:1rem}.my-6{margin-top:1.5rem;margin-bottom:1.5rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.ml-2{margin-left:.5rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.block{display:block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.contents{display:contents}.hidden{display:none}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-11{height:2.75rem}.h-12{height:3rem}.h-14{height:3.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\[200px\]{height:200px}.h-\[280px\]{height:280px}.h-full{height:100%}.h-screen{height:100vh}.max-h-64{max-height:16rem}.max-h-80{max-height:20rem}.max-h-\[600px\]{max-height:600px}.max-h-\[80vh\]{max-height:80vh}.max-h-\[calc\(100vh-300px\)\]{max-height:calc(100vh - 300px)}.min-h-\[400px\]{min-height:400px}.w-1\.5{width:.375rem}.w-10{width:2.5rem}.w-12{width:3rem}.w-14{width:3.5rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-20{width:5rem}.w-24{width:6rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-3\/4{width:75%}.w-32{width:8rem}.w-4{width:1rem}.w-40{width:10rem}.w-48{width:12rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-8{width:2rem}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.min-w-0{min-width:0px}.min-w-\[120px\]{min-width:120px}.max-w-2xl{max-width:42rem}.max-w-\[200px\]{max-width:200px}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-none{max-width:none}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-\[-50\%\]{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-\[-50\%\]{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes ping{75%,to{transform:scale(2);opacity:0}}.animate-ping{animation:ping 1s cubic-bezier(0,0,.2,1) infinite}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-\[auto_1fr_auto_auto_auto_auto\]{grid-template-columns:auto 1fr auto auto auto auto}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(0px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px * var(--tw-space-y-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.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-\[inherit\]{border-radius:inherit}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.rounded-b-lg{border-bottom-right-radius:var(--radius);border-bottom-left-radius:var(--radius)}.rounded-t-lg{border-top-left-radius:var(--radius);border-top-right-radius:var(--radius)}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-l-2{border-left-width:2px}.border-l-4{border-left-width:4px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-amber-500\/30{border-color:#f59e0b4d}.border-blue-500\/30{border-color:#3b82f64d}.border-blue-500\/50{border-color:#3b82f680}.border-border{border-color:hsl(var(--border))}.border-destructive\/50{border-color:hsl(var(--destructive) / .5)}.border-input{border-color:hsl(var(--input))}.border-primary\/20{border-color:hsl(var(--primary) / .2)}.border-primary\/50{border-color:hsl(var(--primary) / .5)}.border-yellow-500\/30{border-color:#eab3084d}.border-l-blue-500{--tw-border-opacity: 1;border-left-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.border-l-gray-400{--tw-border-opacity: 1;border-left-color:rgb(156 163 175 / var(--tw-border-opacity, 1))}.border-l-green-500{--tw-border-opacity: 1;border-left-color:rgb(34 197 94 / var(--tw-border-opacity, 1))}.border-l-red-500{--tw-border-opacity: 1;border-left-color:rgb(239 68 68 / var(--tw-border-opacity, 1))}.border-l-transparent{border-left-color:transparent}.border-l-yellow-500{--tw-border-opacity: 1;border-left-color:rgb(234 179 8 / var(--tw-border-opacity, 1))}.border-t-transparent{border-top-color:transparent}.bg-accent{background-color:hsl(var(--accent))}.bg-amber-500\/10{background-color:#f59e0b1a}.bg-background{background-color:hsl(var(--background))}.bg-black\/80{background-color:#000c}.bg-blue-400{--tw-bg-opacity: 1;background-color:rgb(96 165 250 / var(--tw-bg-opacity, 1))}.bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.bg-blue-500\/10{background-color:#3b82f61a}.bg-blue-500\/5{background-color:#3b82f60d}.bg-border{background-color:hsl(var(--border))}.bg-card{background-color:hsl(var(--card))}.bg-destructive{background-color:hsl(var(--destructive))}.bg-destructive\/10{background-color:hsl(var(--destructive) / .1)}.bg-destructive\/5{background-color:hsl(var(--destructive) / .05)}.bg-gray-400{--tw-bg-opacity: 1;background-color:rgb(156 163 175 / var(--tw-bg-opacity, 1))}.bg-gray-500{--tw-bg-opacity: 1;background-color:rgb(107 114 128 / var(--tw-bg-opacity, 1))}.bg-gray-500\/10{background-color:#6b72801a}.bg-green-400{--tw-bg-opacity: 1;background-color:rgb(74 222 128 / var(--tw-bg-opacity, 1))}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-green-500\/10{background-color:#22c55e1a}.bg-green-500\/5{background-color:#22c55e0d}.bg-muted{background-color:hsl(var(--muted))}.bg-muted\/10{background-color:hsl(var(--muted) / .1)}.bg-muted\/20{background-color:hsl(var(--muted) / .2)}.bg-muted\/30{background-color:hsl(var(--muted) / .3)}.bg-muted\/50{background-color:hsl(var(--muted) / .5)}.bg-popover{background-color:hsl(var(--popover))}.bg-primary{background-color:hsl(var(--primary))}.bg-primary\/5{background-color:hsl(var(--primary) / .05)}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-red-500\/10{background-color:#ef44441a}.bg-red-500\/5{background-color:#ef44440d}.bg-secondary{background-color:hsl(var(--secondary))}.bg-yellow-300{--tw-bg-opacity: 1;background-color:rgb(253 224 71 / var(--tw-bg-opacity, 1))}.bg-yellow-500\/10{background-color:#eab3081a}.stroke-muted{stroke:hsl(var(--muted))}.p-0{padding:0}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-\[1px\]{padding:1px}.px-0\.5{padding-left:.125rem;padding-right:.125rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-20{padding-top:5rem;padding-bottom:5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-2{padding-bottom:.5rem}.pb-3{padding-bottom:.75rem}.pb-4{padding-bottom:1rem}.pl-4{padding-left:1rem}.pl-6{padding-left:1.5rem}.pl-7{padding-left:1.75rem}.pl-9{padding-left:2.25rem}.pr-2{padding-right:.5rem}.pt-0{padding-top:0}.pt-0\.5{padding-top:.125rem}.pt-2{padding-top:.5rem}.pt-4{padding-top:1rem}.pt-6{padding-top:1.5rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.capitalize{text-transform:capitalize}.italic{font-style:italic}.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)}.leading-none{line-height:1}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.text-accent-foreground{color:hsl(var(--accent-foreground))}.text-amber-500{--tw-text-opacity: 1;color:rgb(245 158 11 / var(--tw-text-opacity, 1))}.text-amber-600{--tw-text-opacity: 1;color:rgb(217 119 6 / var(--tw-text-opacity, 1))}.text-blue-500{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity, 1))}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.text-card-foreground{color:hsl(var(--card-foreground))}.text-destructive{color:hsl(var(--destructive))}.text-destructive-foreground{color:hsl(var(--destructive-foreground))}.text-destructive\/50{color:hsl(var(--destructive) / .5)}.text-foreground{color:hsl(var(--foreground))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-green-600{--tw-text-opacity: 1;color:rgb(22 163 74 / var(--tw-text-opacity, 1))}.text-muted-foreground{color:hsl(var(--muted-foreground))}.text-muted-foreground\/30{color:hsl(var(--muted-foreground) / .3)}.text-muted-foreground\/50{color:hsl(var(--muted-foreground) / .5)}.text-primary{color:hsl(var(--primary))}.text-primary-foreground{color:hsl(var(--primary-foreground))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-red-500\/50{color:#ef444480}.text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity, 1))}.text-secondary-foreground{color:hsl(var(--secondary-foreground))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity, 1))}.text-yellow-600{--tw-text-opacity: 1;color:rgb(202 138 4 / var(--tw-text-opacity, 1))}.underline-offset-4{text-underline-offset:4px}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline{outline-style:solid}.ring-offset-background{--tw-ring-offset-color: hsl(var(--background))}.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-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.file\:border-0::file-selector-button{border-width:0px}.file\:bg-transparent::file-selector-button{background-color:transparent}.file\:text-sm::file-selector-button{font-size:.875rem;line-height:1.25rem}.file\:font-medium::file-selector-button{font-weight:500}.placeholder\:text-muted-foreground::-moz-placeholder{color:hsl(var(--muted-foreground))}.placeholder\:text-muted-foreground::placeholder{color:hsl(var(--muted-foreground))}.first\:mt-0:first-child{margin-top:0}.last\:mb-0:last-child{margin-bottom:0}.last\:border-b-0:last-child{border-bottom-width:0px}.hover\:border-primary\/50:hover{border-color:hsl(var(--primary) / .5)}.hover\:bg-accent:hover{background-color:hsl(var(--accent))}.hover\:bg-destructive\/90:hover{background-color:hsl(var(--destructive) / .9)}.hover\:bg-muted\/50:hover{background-color:hsl(var(--muted) / .5)}.hover\:bg-primary\/10:hover{background-color:hsl(var(--primary) / .1)}.hover\:bg-primary\/90:hover{background-color:hsl(var(--primary) / .9)}.hover\:bg-secondary\/80:hover{background-color:hsl(var(--secondary) / .8)}.hover\:text-accent-foreground:hover{color:hsl(var(--accent-foreground))}.hover\:text-foreground:hover{color:hsl(var(--foreground))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.hover\:shadow-md:hover{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-ring:focus{--tw-ring-color: hsl(var(--ring))}.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color: hsl(var(--ring))}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width: 2px}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group.toaster .group-\[\.toaster\]\:border-border{border-color:hsl(var(--border))}.group.toast .group-\[\.toast\]\:bg-muted{background-color:hsl(var(--muted))}.group.toast .group-\[\.toast\]\:bg-primary{background-color:hsl(var(--primary))}.group.toaster .group-\[\.toaster\]\:bg-background{background-color:hsl(var(--background))}.group.toast .group-\[\.toast\]\:text-muted-foreground{color:hsl(var(--muted-foreground))}.group.toast .group-\[\.toast\]\:text-primary-foreground{color:hsl(var(--primary-foreground))}.group.toaster .group-\[\.toaster\]\:text-foreground{color:hsl(var(--foreground))}.group.toaster .group-\[\.toaster\]\:shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.peer:disabled~.peer-disabled\:cursor-not-allowed{cursor:not-allowed}.peer:disabled~.peer-disabled\:opacity-70{opacity:.7}.data-\[state\=active\]\:bg-background[data-state=active]{background-color:hsl(var(--background))}.data-\[state\=open\]\:bg-accent[data-state=open]{background-color:hsl(var(--accent))}.data-\[state\=active\]\:text-foreground[data-state=active]{color:hsl(var(--foreground))}.data-\[state\=open\]\:text-muted-foreground[data-state=open]{color:hsl(var(--muted-foreground))}.data-\[state\=active\]\:shadow-sm[data-state=active]{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.dark\:bg-yellow-700:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(161 98 7 / var(--tw-bg-opacity, 1))}.dark\:text-amber-400:is(.dark *){--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.dark\:text-blue-400:is(.dark *){--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.dark\:text-gray-400:is(.dark *){--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.dark\:text-green-400:is(.dark *){--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.dark\:text-red-400:is(.dark *){--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.dark\:text-yellow-400:is(.dark *){--tw-text-opacity: 1;color:rgb(250 204 21 / var(--tw-text-opacity, 1))}@media (min-width: 640px){.sm\:block{display:block}.sm\:max-w-xs{max-width:20rem}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:justify-end{justify-content:flex-end}.sm\:space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.sm\:rounded-lg{border-radius:var(--radius)}.sm\:text-left{text-align:left}}@media (min-width: 768px){.md\:block{display:block}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width: 1024px){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}