better-symphony 1.0.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 (63) hide show
  1. package/CLAUDE.md +60 -0
  2. package/LICENSE +21 -0
  3. package/README.md +292 -0
  4. package/dist/web/app.css +2 -0
  5. package/dist/web/index.html +13 -0
  6. package/dist/web/main.js +235 -0
  7. package/package.json +62 -0
  8. package/src/agent/claude-runner.ts +576 -0
  9. package/src/agent/protocol.ts +2 -0
  10. package/src/agent/runner.ts +2 -0
  11. package/src/agent/session.ts +113 -0
  12. package/src/cli.ts +354 -0
  13. package/src/config/loader.ts +379 -0
  14. package/src/config/types.ts +382 -0
  15. package/src/index.ts +53 -0
  16. package/src/linear-cli.ts +414 -0
  17. package/src/logging/logger.ts +143 -0
  18. package/src/orchestrator/multi-orchestrator.ts +266 -0
  19. package/src/orchestrator/orchestrator.ts +1357 -0
  20. package/src/orchestrator/scheduler.ts +195 -0
  21. package/src/orchestrator/state.ts +201 -0
  22. package/src/prompts/github-system-prompt.md +51 -0
  23. package/src/prompts/linear-system-prompt.md +44 -0
  24. package/src/tracker/client.ts +577 -0
  25. package/src/tracker/github-issues-tracker.ts +280 -0
  26. package/src/tracker/github-pr-tracker.ts +298 -0
  27. package/src/tracker/index.ts +9 -0
  28. package/src/tracker/interface.ts +76 -0
  29. package/src/tracker/linear-tracker.ts +147 -0
  30. package/src/tracker/queries.ts +281 -0
  31. package/src/tracker/types.ts +125 -0
  32. package/src/tui/App.tsx +157 -0
  33. package/src/tui/LogView.tsx +120 -0
  34. package/src/tui/StatusBar.tsx +72 -0
  35. package/src/tui/TabBar.tsx +55 -0
  36. package/src/tui/sink.ts +47 -0
  37. package/src/tui/types.ts +6 -0
  38. package/src/tui/useOrchestrator.ts +244 -0
  39. package/src/web/server.ts +182 -0
  40. package/src/web/sink.ts +67 -0
  41. package/src/web-ui/App.tsx +60 -0
  42. package/src/web-ui/components/agent-table.tsx +57 -0
  43. package/src/web-ui/components/header.tsx +72 -0
  44. package/src/web-ui/components/log-stream.tsx +111 -0
  45. package/src/web-ui/components/retry-table.tsx +58 -0
  46. package/src/web-ui/components/stats-cards.tsx +142 -0
  47. package/src/web-ui/components/ui/badge.tsx +30 -0
  48. package/src/web-ui/components/ui/button.tsx +39 -0
  49. package/src/web-ui/components/ui/card.tsx +32 -0
  50. package/src/web-ui/globals.css +27 -0
  51. package/src/web-ui/index.html +13 -0
  52. package/src/web-ui/lib/use-sse.ts +98 -0
  53. package/src/web-ui/lib/utils.ts +25 -0
  54. package/src/web-ui/main.tsx +4 -0
  55. package/src/workspace/hooks.ts +97 -0
  56. package/src/workspace/manager.ts +211 -0
  57. package/src/workspace/render-hook.ts +13 -0
  58. package/workflows/dev.md +127 -0
  59. package/workflows/github-issues.md +107 -0
  60. package/workflows/pr-review.md +89 -0
  61. package/workflows/prd.md +170 -0
  62. package/workflows/ralph.md +95 -0
  63. package/workflows/smoke.md +66 -0
package/CLAUDE.md ADDED
@@ -0,0 +1,60 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## What is Symphony
6
+
7
+ Symphony is a headless coding agent orchestrator. It polls issue trackers (Linear, GitHub Issues, GitHub PRs) for work items, dispatches Claude Code agents to complete them, and manages the full lifecycle from task selection through completion.
8
+
9
+ Workflows are defined as Markdown files with YAML frontmatter (config) + Liquid templates (agent prompts). Multiple workflows can run concurrently in one process.
10
+
11
+ ## Commands
12
+
13
+ ```bash
14
+ bun install # Install dependencies
15
+ bun run src/cli.ts # Start with TUI (auto-detects workflows/*.md)
16
+ bun run src/cli.ts -w workflows/dev.md # Run specific workflow(s)
17
+ bun run src/cli.ts --headless # Run without TUI
18
+ bun run src/cli.ts --web # Run with web dashboard (implies --headless)
19
+ bun run src/cli.ts --web --web-port 8080 # Web dashboard on custom port (default: 3000)
20
+ bun run src/cli.ts --dry-run # Preview rendered prompts, no agent launched
21
+ bun run --watch src/cli.ts # Dev mode with file watching
22
+ bun run src/linear-cli.ts # Standalone Linear CLI tool
23
+ tsc --noEmit # Type check (no build step needed; Bun runs TS directly)
24
+ ```
25
+
26
+ ## Architecture
27
+
28
+ ### Core Flow
29
+
30
+ 1. **Tracker** polls for issues matching configured labels/states
31
+ 2. **Orchestrator** claims an issue (atomic, prevents duplicates) and creates a per-issue **Workspace** via hooks (e.g., git clone)
32
+ 3. **Config loader** renders the Liquid template with issue context to produce the agent prompt
33
+ 4. **Claude runner** spawns `claude` CLI in the workspace, streaming `--output-format stream-json` events
34
+ 5. On completion, labels are swapped to reflect status (`agent:dev` → `agent:dev:done` or `agent:dev:error`)
35
+
36
+ ### Key Abstractions
37
+
38
+ - **Tracker interface** (`src/tracker/interface.ts`): Polymorphic abstraction over Linear (GraphQL), GitHub Issues (`gh` CLI), and GitHub PRs (`gh` CLI). Factory in `src/tracker/index.ts`.
39
+ - **Orchestrator** (`src/orchestrator/orchestrator.ts`): Single-workflow poll loop with concurrency control, retry queue, and token tracking. **MultiOrchestrator** coordinates multiple workflows sharing one Linear client.
40
+ - **Scheduler** (`src/orchestrator/scheduler.ts`): Manages poll intervals and `max_concurrent_agents` / `max_concurrent_agents_by_state` limits.
41
+ - **State** (`src/orchestrator/state.ts`): Tracks claims, running sessions, retries, and aggregate token usage.
42
+ - **Workspace manager** (`src/workspace/manager.ts`): Creates per-issue directories, runs `after_create`/`before_run` shell hooks with Liquid template support.
43
+ - **Claude runner** (`src/agent/claude-runner.ts`): Spawns Claude CLI, parses stream-json events for real-time status and token counts.
44
+
45
+ ### Workflow Modes
46
+
47
+ - **default**: One agent per issue, runs to completion
48
+ - **ralph_loop**: Loops through subtasks, spawning a fresh agent per subtask with clean context
49
+
50
+ ### Environment Variables
51
+
52
+ Agents receive `SYMPHONY_LINEAR` (path to Linear CLI), `SYMPHONY_WORKSPACE` (workspace path), `SYMPHONY_ISSUE_IDENTIFIER` (e.g., `SYM-123`), and `GH_REPO` (for GitHub trackers) in their environment.
53
+
54
+ ## Tech Stack
55
+
56
+ - **Runtime**: Bun (executes TypeScript directly, no build step)
57
+ - **Language**: TypeScript 5.8 with strict mode
58
+ - **TUI**: React + Ink (terminal UI framework)
59
+ - **Templates**: LiquidJS for rendering workflow prompts with issue context
60
+ - **Config**: YAML frontmatter parsed from workflow Markdown files
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Sabatino Masala
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,292 @@
1
+ # Better Symphony
2
+
3
+ A headless coding agent orchestrator that polls issue trackers (Linear, GitHub Issues, GitHub PRs) for work items, dispatches AI agents (Claude Code), and manages the full development lifecycle.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ # Clone and install Symphony
9
+ git clone https://github.com/AugmentCo/better-symphony.git
10
+ cd better-symphony
11
+ bun install
12
+
13
+ # Optional: create a global alias so you can run `symphony` from anywhere
14
+ alias symphony="bun run $(pwd)/src/cli.ts"
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ > **Important:** Symphony is run from **your project's directory**, not from inside the `better-symphony` repo. Your project should have a `workflows/` folder containing your workflow `.md` files. Symphony auto-detects `workflows/*.md` in the current working directory.
20
+
21
+ ```bash
22
+ cd ~/your-project # Your project with a workflows/ directory
23
+
24
+ # Set your Linear API key
25
+ export LINEAR_API_KEY=lin_api_xxxxx
26
+
27
+ # Run all workflows in workflows/
28
+ symphony
29
+
30
+ # Or run specific workflow(s)
31
+ symphony -w workflows/dev.md
32
+ symphony -w workflows/prd.md workflows/dev.md workflows/ralph.md
33
+
34
+ # If you didn't set up the alias, use the full path:
35
+ bun run ~/path/to/better-symphony/src/cli.ts -w workflows/dev.md
36
+ ```
37
+
38
+ ### CLI Flags
39
+
40
+ | Flag | Description |
41
+ |------|-------------|
42
+ | `-w <files>` | Run specific workflow file(s) |
43
+ | `--headless` | Run without the TUI |
44
+ | `--web` | Start web dashboard (implies `--headless`) |
45
+ | `--web-port <port>` | Web dashboard port (default: `3000`) |
46
+ | `--web-host <host>` | Web dashboard bind address (default: `0.0.0.0`) |
47
+ | `--dry-run` | Preview rendered prompts without launching agents |
48
+
49
+ ### Project structure
50
+
51
+ ```
52
+ your-project/
53
+ ├── workflows/
54
+ │ ├── dev.md # Your workflow files
55
+ │ ├── prd.md
56
+ │ └── pr-review.md
57
+ ├── src/ # Your project source code
58
+ └── ...
59
+ ```
60
+
61
+ ## How It Works
62
+
63
+ Better Symphony uses **workflow files** (`workflows/*.md`) to define what the orchestrator does. Each workflow is a Markdown file with YAML frontmatter for configuration and a Liquid template for the agent prompt.
64
+
65
+ ### Workflow Files
66
+
67
+ This repo includes example workflows you can copy into your project's `workflows/` directory:
68
+
69
+ - **`workflows/prd.md`** - PRD agent: analyzes issues and breaks complex ones into subtasks
70
+ - **`workflows/dev.md`** - Dev agent: implements tasks directly
71
+ - **`workflows/ralph.md`** - Ralph agent: loops through subtasks with fresh context per subtask
72
+ - **`workflows/pr-review.md`** - PR review agent: reviews GitHub PRs, runs tests, and posts review comments
73
+ - **`workflows/github-issues.md`** - GitHub Issues agent: implements tasks from GitHub Issues
74
+
75
+ Each workflow specifies which labels to watch for (e.g., `agent:dev`), so multiple workflows can run in parallel without conflicts.
76
+
77
+ ### Source Code
78
+
79
+ - **`src/cli.ts`** - Entry point and argument parsing
80
+ - **`src/orchestrator/`** - Poll loop, scheduling, concurrency control, and multi-workflow coordination
81
+ - **`src/tracker/`** - Tracker implementations (Linear GraphQL, GitHub Issues, GitHub PRs via `gh` CLI)
82
+ - **`src/workspace/`** - Per-issue workspace creation/cleanup and shell hooks
83
+ - **`src/agent/`** - Agent harness (spawns Claude CLI, parses stream-json output)
84
+ - **`src/config/`** - YAML frontmatter + Liquid template parsing
85
+ - **`src/logging/`** - Structured logging
86
+
87
+ ### Linear CLI
88
+
89
+ Better Symphony injects a `SYMPHONY_LINEAR` env var into every agent process, pointing to a bundled Linear CLI (`src/linear-cli.ts`). Agents use it to update issues, swap labels, create subtasks, and post comments without needing separate API keys.
90
+
91
+ ```bash
92
+ bun $SYMPHONY_LINEAR get-issue SYM-123
93
+ bun $SYMPHONY_LINEAR update-issue SYM-123 --state "In Progress"
94
+ bun $SYMPHONY_LINEAR swap-label SYM-123 --remove "agent:dev" --add "agent:dev:done"
95
+ bun $SYMPHONY_LINEAR create-issue --parent SYM-123 --title "Implement feature X"
96
+ bun $SYMPHONY_LINEAR create-comment SYM-123 "Done implementing"
97
+ ```
98
+
99
+ ### GitHub CLI
100
+
101
+ For GitHub Issues integration, agents use the standard `gh` CLI directly. Better Symphony sets the `GH_REPO` environment variable automatically.
102
+
103
+ ```bash
104
+ gh issue view 123 --json number,title,body,state,labels,comments
105
+ gh issue create --title "Fix bug" --label "bug"
106
+ gh issue edit 123 --add-label "agent:dev:progress"
107
+ gh issue edit 123 --remove-label "agent:dev"
108
+ gh issue comment 123 --body "Done implementing"
109
+ gh issue close 123
110
+ ```
111
+
112
+ ## Workflow File Format
113
+
114
+ ```yaml
115
+ ---
116
+ tracker:
117
+ kind: linear
118
+ api_key: $LINEAR_API_KEY
119
+ project_slug: my-project
120
+ active_states: [Todo, In Progress]
121
+ terminal_states: [Done, Cancelled]
122
+ required_labels: [agent:dev]
123
+ excluded_labels: [agent:prd]
124
+
125
+ polling:
126
+ interval_ms: 30000
127
+
128
+ workspace:
129
+ root: ~/.symphony/workspaces
130
+
131
+ hooks:
132
+ after_create: |
133
+ git clone git@github.com:yourorg/repo.git .
134
+ bun install
135
+ before_run: |
136
+ git fetch origin main
137
+ git reset --hard origin/main
138
+
139
+ agent:
140
+ harness: claude
141
+ max_concurrent_agents: 2
142
+ max_turns: 20
143
+ ---
144
+
145
+ You are working on **{{ issue.identifier }}**: {{ issue.title }}
146
+
147
+ ## Description
148
+ {{ issue.description | default: "No description provided" }}
149
+
150
+ {% if issue.children.size > 0 %}
151
+ ## Subtasks
152
+ {% for child in issue.children %}
153
+ - {{ child.identifier }}: {{ child.title }} ({{ child.state }})
154
+ {% endfor %}
155
+ {% endif %}
156
+ ```
157
+
158
+ ### GitHub Issues Tracker
159
+
160
+ For GitHub Issues, use `kind: github-issues`:
161
+
162
+ ```yaml
163
+ ---
164
+ tracker:
165
+ kind: github-issues
166
+ repo: owner/repo
167
+ active_states: [open]
168
+ terminal_states: [closed]
169
+ required_labels: [agent:dev]
170
+ excluded_labels: [agent:dev:done]
171
+
172
+ polling:
173
+ interval_ms: 30000
174
+
175
+ workspace:
176
+ root: ~/.symphony/workspaces
177
+
178
+ hooks:
179
+ after_create: |
180
+ git clone git@github.com:owner/repo.git .
181
+ bun install
182
+ before_run: |
183
+ git fetch origin main
184
+ git reset --hard origin/main
185
+
186
+ agent:
187
+ harness: claude
188
+ max_concurrent_agents: 2
189
+ max_turns: 20
190
+ ---
191
+
192
+ You are working on **{{ issue.identifier }}** (#{{ issue.number }}): {{ issue.title }}
193
+
194
+ ## Description
195
+ {{ issue.description | default: "No description provided" }}
196
+
197
+ When done, use `gh issue edit {{ issue.number }} --add-label "agent:dev:done"` to mark completion.
198
+ ```
199
+
200
+ ### GitHub PR Tracker
201
+
202
+ For GitHub Pull Requests, use `kind: github-pr`:
203
+
204
+ ```yaml
205
+ ---
206
+ tracker:
207
+ kind: github-pr
208
+ repo: owner/repo
209
+ active_states: [open]
210
+ terminal_states: [closed, merged]
211
+ excluded_labels: [review:complete]
212
+
213
+ workspace:
214
+ root: ~/.symphony/workspaces
215
+
216
+ hooks:
217
+ after_create: |
218
+ git clone git@github.com:owner/repo.git .
219
+ before_run: |
220
+ git fetch origin
221
+ git checkout {{ issue.branch_name }}
222
+ git merge origin/main --no-edit || true
223
+
224
+ agent:
225
+ harness: claude
226
+ max_concurrent_agents: 1
227
+ ---
228
+
229
+ You are reviewing **PR #{{ issue.number }}**: {{ issue.title }}
230
+
231
+ **Branch:** `{{ issue.branch_name }}` → `{{ issue.base_branch }}`
232
+ **Author:** {{ issue.author }}
233
+ **Files changed:** {{ issue.files_changed }}
234
+
235
+ ## Description
236
+ {{ issue.body | default: "No description provided" }}
237
+
238
+ When done, use `gh pr edit {{ issue.number }} --add-label "review:complete"` to mark completion.
239
+ ```
240
+
241
+ The GitHub PR tracker exposes additional template variables: `issue.branch_name`, `issue.base_branch`, `issue.author`, `issue.files_changed`, and `issue.comments`.
242
+
243
+ ## Yolobox Support
244
+
245
+ Better Symphony has first-class support for [Yolobox](https://github.com/finbarr/yolobox), a Docker-based sandbox for running agents. When enabled, the agent binary is launched inside a Yolobox container.
246
+
247
+ ```yaml
248
+ agent:
249
+ harness: claude # which agent to run: claude, codex, opencode
250
+ yolobox: true
251
+ yolobox_arguments: ["--claude-config"] # extra args passed to yolobox before the agent flags
252
+ ```
253
+
254
+ This produces: `yolobox claude --claude-config -- -p "..." --output-format stream-json --verbose ...`
255
+
256
+ When yolobox is enabled, Symphony automatically:
257
+ - **Mounts** the Symphony source directory into the container (so `$SYMPHONY_LINEAR` resolves correctly)
258
+ - **Forwards** environment variables via `--env`: `SYMPHONY_LINEAR`, `SYMPHONY_WORKSPACE`, `SYMPHONY_ISSUE_ID`, `SYMPHONY_ISSUE_IDENTIFIER`, and `LINEAR_API_KEY`
259
+
260
+ Without `yolobox: true`, the harness binary is invoked directly.
261
+
262
+ ## Labels
263
+
264
+ Each workflow watches for a specific label and adds status suffixes as it progresses:
265
+
266
+ | Label | Purpose |
267
+ |-------|---------|
268
+ | `agent:prd` | Break down issues into subtasks |
269
+ | `agent:dev` | Implement tasks directly |
270
+ | `agent:ralph` | Loop through subtasks with fresh context |
271
+
272
+ Status flow: `agent:dev` → `agent:dev:progress` → `agent:dev:done` (or `agent:dev:error`)
273
+
274
+ To retry a failed issue: remove the `:error` label and re-add the base label.
275
+
276
+ ## Environment Variables
277
+
278
+ | Variable | Description |
279
+ |----------|-------------|
280
+ | `LINEAR_API_KEY` | Required for Linear tracker. Your Linear API key |
281
+ | `GH_REPO` | Required for GitHub trackers. Repository in `owner/repo` format |
282
+ | `SYMPHONY_LINEAR` | Injected into agents. Path to the Linear CLI |
283
+ | `SYMPHONY_WORKSPACE` | Injected into agents. Path to the issue workspace |
284
+ | `SYMPHONY_ISSUE_IDENTIFIER` | Injected into agents. e.g., `SYM-123` or `ISSUE-123` |
285
+
286
+ ## License
287
+
288
+ MIT — see [LICENSE](LICENSE) for details.
289
+
290
+ ---
291
+
292
+ Inspired by [openai/symphony](https://github.com/openai/symphony).
@@ -0,0 +1,2 @@
1
+ /*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */
2
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-pan-x:initial;--tw-pan-y:initial;--tw-pinch-zoom:initial;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-divide-x-reverse:0;--tw-border-style:solid;--tw-divide-y-reverse:0;--tw-leading:initial;--tw-font-weight:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-blue-300:oklch(80.9% .105 251.813);--color-blue-400:oklch(70.7% .165 254.624);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-lg:32rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--radius-md:.375rem;--radius-lg:.5rem;--animate-spin:spin 1s linear infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--radius:.5rem;--color-background:oklch(14.5% 0 0);--color-foreground:oklch(98.5% 0 0);--color-card:oklch(17% 0 0);--color-card-foreground:oklch(98.5% 0 0);--color-muted:oklch(26.9% 0 0);--color-muted-foreground:oklch(70.8% 0 0);--color-border:oklch(30% 0 0);--color-accent:oklch(26.9% 0 0);--color-accent-foreground:oklch(98.5% 0 0);--color-destructive:oklch(57.7% .245 27.325);--color-primary:oklch(98.5% 0 0);--color-primary-foreground:oklch(20.5% 0 0);--color-secondary:oklch(26.9% 0 0);--color-secondary-foreground:oklch(98.5% 0 0);--color-ring:oklch(55.6% 0 0);--color-success:oklch(70% .2 145);--color-warning:oklch(75% .18 85)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}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;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.visible\!{visibility:visible!important}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.not-sr-only{clip-path:none;white-space:normal;width:auto;height:auto;margin:0;padding:0;position:static;overflow:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing) * 0)}.start{inset-inline-start:var(--spacing)}.end{inset-inline-end:var(--spacing)}.isolate{isolation:isolate}.isolation-auto{isolation:auto}.z-50{z-index:50}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-4{margin-inline:calc(var(--spacing) * 4)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mr-2{margin-right:calc(var(--spacing) * 2)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.ml-1{margin-left:calc(var(--spacing) * 1)}.ml-2{margin-left:calc(var(--spacing) * 2)}.block{display:block}.contents{display:contents}.flex{display:flex}.flow-root{display:flow-root}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.inline-grid{display:inline-grid}.inline-table{display:inline-table}.list-item{display:list-item}.table{display:table}.table-caption{display:table-caption}.table-cell{display:table-cell}.table-column{display:table-column}.table-column-group{display:table-column-group}.table-footer-group{display:table-footer-group}.table-header-group{display:table-header-group}.table-row{display:table-row}.table-row-group{display:table-row-group}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2{height:calc(var(--spacing) * 2)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.h-screen{height:100vh}.min-h-0{min-height:calc(var(--spacing) * 0)}.min-h-screen{min-height:100vh}.w-2{width:calc(var(--spacing) * 2)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-9{width:calc(var(--spacing) * 9)}.w-\[70px\]{width:70px}.w-full{width:100%}.max-w-lg{max-width:var(--container-lg)}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:calc(var(--spacing) * 0)}.flex-1{flex:1}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.border-collapse{border-collapse:collapse}.translate-none{translate:none}.scale-3d{scale:var(--tw-scale-x) var(--tw-scale-y) var(--tw-scale-z)}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-spin{animation:var(--animate-spin)}.cursor-pointer{cursor:pointer}.touch-pinch-zoom{--tw-pinch-zoom:pinch-zoom;touch-action:var(--tw-pan-x,) var(--tw-pan-y,) var(--tw-pinch-zoom,)}.resize{resize:both}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-reverse>:not(:last-child)){--tw-space-y-reverse:1}:where(.space-x-reverse>:not(:last-child)){--tw-space-x-reverse:1}:where(.divide-x>:not(:last-child)){--tw-divide-x-reverse:0;border-inline-style:var(--tw-border-style);border-inline-start-width:calc(1px * var(--tw-divide-x-reverse));border-inline-end-width:calc(1px * calc(1 - var(--tw-divide-x-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-y-reverse>:not(:last-child)){--tw-divide-y-reverse:1}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:var(--radius)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-s{border-start-start-radius:var(--radius);border-end-start-radius:var(--radius)}.rounded-ss{border-start-start-radius:var(--radius)}.rounded-e{border-start-end-radius:var(--radius);border-end-end-radius:var(--radius)}.rounded-se{border-start-end-radius:var(--radius)}.rounded-ee{border-end-end-radius:var(--radius)}.rounded-es{border-end-start-radius:var(--radius)}.rounded-t{border-top-left-radius:var(--radius);border-top-right-radius:var(--radius)}.rounded-l{border-top-left-radius:var(--radius);border-bottom-left-radius:var(--radius)}.rounded-tl{border-top-left-radius:var(--radius)}.rounded-r{border-top-right-radius:var(--radius);border-bottom-right-radius:var(--radius)}.rounded-tr{border-top-right-radius:var(--radius)}.rounded-b{border-bottom-right-radius:var(--radius);border-bottom-left-radius:var(--radius)}.rounded-br{border-bottom-right-radius:var(--radius)}.rounded-bl{border-bottom-left-radius:var(--radius)}.border{border-style:var(--tw-border-style);border-width:1px}.border-x{border-inline-style:var(--tw-border-style);border-inline-width:1px}.border-y{border-block-style:var(--tw-border-style);border-block-width:1px}.border-s{border-inline-start-style:var(--tw-border-style);border-inline-start-width:1px}.border-e{border-inline-end-style:var(--tw-border-style);border-inline-end-width:1px}.border-bs{border-block-start-style:var(--tw-border-style);border-block-start-width:1px}.border-be{border-block-end-style:var(--tw-border-style);border-block-end-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-border{border-color:var(--color-border)}.border-transparent{border-color:#0000}.bg-background{background-color:var(--color-background)}.bg-black\/30{background-color:#0000004d}@supports (color:color-mix(in lab, red, red)){.bg-black\/30{background-color:color-mix(in oklab, var(--color-black) 30%, transparent)}}.bg-black\/60{background-color:#0009}@supports (color:color-mix(in lab, red, red)){.bg-black\/60{background-color:color-mix(in oklab, var(--color-black) 60%, transparent)}}.bg-card{background-color:var(--color-card)}.bg-destructive{background-color:var(--color-destructive)}.bg-muted{background-color:var(--color-muted)}.bg-muted\/20{background-color:#26262633}@supports (color:color-mix(in lab, red, red)){.bg-muted\/20{background-color:color-mix(in oklab, var(--color-muted) 20%, transparent)}}.bg-muted\/50{background-color:#26262680}@supports (color:color-mix(in lab, red, red)){.bg-muted\/50{background-color:color-mix(in oklab, var(--color-muted) 50%, transparent)}}.bg-primary{background-color:var(--color-primary)}.bg-secondary{background-color:var(--color-secondary)}.bg-success{background-color:var(--color-success)}.bg-success\/20{background-color:#30bd4433}@supports (color:color-mix(in lab, red, red)){.bg-success\/20{background-color:color-mix(in oklab, var(--color-success) 20%, transparent)}}.bg-transparent{background-color:#0000}.bg-warning\/20{background-color:#dba40033}@supports (color:color-mix(in lab, red, red)){.bg-warning\/20{background-color:color-mix(in oklab, var(--color-warning) 20%, transparent)}}.bg-repeat{background-repeat:repeat}.mask-no-clip{-webkit-mask-clip:no-clip;mask-clip:no-clip}.mask-repeat{-webkit-mask-repeat:repeat;mask-repeat:repeat}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-8{padding:calc(var(--spacing) * 8)}.px-1{padding-inline:calc(var(--spacing) * 1)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-0{padding-block:calc(var(--spacing) * 0)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-8{padding-block:calc(var(--spacing) * 8)}.pt-0{padding-top:calc(var(--spacing) * 0)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.leading-5{--tw-leading:calc(var(--spacing) * 5);line-height:calc(var(--spacing) * 5)}.leading-none{--tw-leading:1;line-height:1}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.text-wrap{text-wrap:wrap}.break-all{word-break:break-all}.text-clip{text-overflow:clip}.text-ellipsis{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.text-blue-400{color:var(--color-blue-400)}.text-card-foreground{color:var(--color-card-foreground)}.text-destructive{color:var(--color-destructive)}.text-foreground{color:var(--color-foreground)}.text-muted-foreground{color:var(--color-muted-foreground)}.text-primary-foreground{color:var(--color-primary-foreground)}.text-secondary-foreground{color:var(--color-secondary-foreground)}.text-success{color:var(--color-success)}.text-warning{color:var(--color-warning)}.text-white{color:var(--color-white)}.capitalize{text-transform:capitalize}.lowercase{text-transform:lowercase}.normal-case{text-transform:none}.uppercase{text-transform:uppercase}.italic{font-style:italic}.not-italic{font-style:normal}.diagonal-fractions{--tw-numeric-fraction:diagonal-fractions;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.lining-nums{--tw-numeric-figure:lining-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.oldstyle-nums{--tw-numeric-figure:oldstyle-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.ordinal{--tw-ordinal:ordinal;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.proportional-nums{--tw-numeric-spacing:proportional-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.slashed-zero{--tw-slashed-zero:slashed-zero;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.stacked-fractions{--tw-numeric-fraction:stacked-fractions;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.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,)}.normal-nums{font-variant-numeric:normal}.line-through{text-decoration-line:line-through}.no-underline{text-decoration-line:none}.overline{text-decoration-line:overline}.underline{text-decoration-line:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.subpixel-antialiased{-webkit-font-smoothing:auto;-moz-osx-font-smoothing:auto}.shadow,.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.inset-ring{--tw-inset-ring-shadow:inset 0 0 0 1px var(--tw-inset-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-offset-background{--tw-ring-offset-color:var(--color-background)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.drop-shadow{--tw-drop-shadow-size:drop-shadow(0 1px 2px var(--tw-drop-shadow-color,#0000001a)) drop-shadow(0 1px 1px var(--tw-drop-shadow-color,#0000000f));--tw-drop-shadow:drop-shadow(0 1px 2px #0000001a) drop-shadow(0 1px 1px #0000000f);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur{--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-grayscale{--tw-backdrop-grayscale:grayscale(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-invert{--tw-backdrop-invert:invert(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-sepia{--tw-backdrop-sepia:sepia(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-filter{-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.\[agent\:dev\]{agent:dev}.\[agent\:prd\]{agent:prd}.\[review\:complete\]{review:complete}:where(.divide-x-reverse>:not(:last-child)){--tw-divide-x-reverse:1}.ring-inset{--tw-ring-inset:inset}.last\:border-0:last-child{border-style:var(--tw-border-style);border-width:0}@media (hover:hover){.hover\:border-muted-foreground:hover{border-color:var(--color-muted-foreground)}.hover\:bg-accent:hover{background-color:var(--color-accent)}.hover\:bg-destructive\/90:hover{background-color:#e40014e6}@supports (color:color-mix(in lab, red, red)){.hover\:bg-destructive\/90:hover{background-color:color-mix(in oklab, var(--color-destructive) 90%, transparent)}}.hover\:bg-muted\/20:hover{background-color:#26262633}@supports (color:color-mix(in lab, red, red)){.hover\:bg-muted\/20:hover{background-color:color-mix(in oklab, var(--color-muted) 20%, transparent)}}.hover\:bg-muted\/30:hover{background-color:#2626264d}@supports (color:color-mix(in lab, red, red)){.hover\:bg-muted\/30:hover{background-color:color-mix(in oklab, var(--color-muted) 30%, transparent)}}.hover\:bg-muted\/40:hover{background-color:#26262666}@supports (color:color-mix(in lab, red, red)){.hover\:bg-muted\/40:hover{background-color:color-mix(in oklab, var(--color-muted) 40%, transparent)}}.hover\:bg-primary\/90:hover{background-color:#fafafae6}@supports (color:color-mix(in lab, red, red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab, var(--color-primary) 90%, transparent)}}.hover\:bg-secondary\/80:hover{background-color:#262626cc}@supports (color:color-mix(in lab, red, red)){.hover\:bg-secondary\/80:hover{background-color:color-mix(in oklab, var(--color-secondary) 80%, transparent)}}.hover\:text-accent-foreground:hover{color:var(--color-accent-foreground)}.hover\:text-blue-300:hover{color:var(--color-blue-300)}.hover\:text-foreground:hover{color:var(--color-foreground)}}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color:var(--color-ring)}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:opacity-50:disabled{opacity:.5}@media (min-width:40rem){.sm\:gap-4{gap:calc(var(--spacing) * 4)}.sm\:p-6{padding:calc(var(--spacing) * 6)}.sm\:px-6{padding-inline:calc(var(--spacing) * 6)}}}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,sans-serif}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-pan-x{syntax:"*";inherits:false}@property --tw-pan-y{syntax:"*";inherits:false}@property --tw-pinch-zoom{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}
@@ -0,0 +1,13 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Better Symphony</title>
7
+ <link rel="stylesheet" href="/app.css">
8
+ </head>
9
+ <body class="bg-background text-foreground min-h-screen">
10
+ <div id="root"></div>
11
+ <script src="/main.js" type="module"></script>
12
+ </body>
13
+ </html>