awel 0.1.0 → 0.1.1

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
@@ -1,5 +1,7 @@
1
1
  # Awel
2
2
 
3
+ English | [中文](./README.zh-CN.md)
4
+
3
5
  AI-powered development overlay for Next.js. Awel runs a proxy in front of your dev server, injects a floating chat button into your app, and lets you talk to an AI agent that can read, write, and edit files in your project — all from an embedded dashboard.
4
6
 
5
7
  ![Awel dashboard overlay](docs/screenshot.png)
@@ -71,6 +73,13 @@ The AI agent has access to:
71
73
  ## Features
72
74
 
73
75
  - **Element inspector** — click the crosshair icon to select an element in your app and attach it as context to your prompt
76
+
77
+ ![Element inspector](docs/Screenshot-Inspector.png)
78
+
79
+ - **Screenshot annotator** — annotate screenshots with shapes, arrows, and text before sending to the agent
80
+
81
+ ![Screenshot annotator](docs/Screenshot-annotator.png)
82
+
74
83
  - **Image attachments** — attach screenshots or reference images
75
84
  - **Plan approval** — the agent can propose plans for you to review before making changes
76
85
  - **Undo** — roll back all file changes from an agent session in one click
@@ -0,0 +1,107 @@
1
+ # Awel
2
+
3
+ [English](./README.md) | 中文
4
+
5
+ 为 Next.js 打造的 AI 开发助手。Awel 在你的开发服务器前运行一个代理,向页面注入一个悬浮聊天按钮,让你通过内嵌的面板与 AI 智能体对话——它可以读取、编写和编辑项目中的文件。
6
+
7
+ ![Awel 面板](docs/screenshot.png)
8
+
9
+ ## 快速开始
10
+
11
+ ```bash
12
+ # 如果你已经在一个 Next.js 项目中,可以跳过这一步
13
+ npx create-next-app@latest my-app && cd my-app
14
+
15
+ npx awel dev
16
+ ```
17
+
18
+ Awel 会在端口 3001 启动,并代理运行在端口 3000 的 Next.js 开发服务器。打开 `http://localhost:3001` 即可看到带有 Awel 浮层的应用。
19
+
20
+ ### 选项
21
+
22
+ ```
23
+ awel dev [options]
24
+
25
+ -p, --port <port> 目标应用端口(默认:3000)
26
+ -v, --verbose 将 LLM 流式事件输出到 stderr
27
+ ```
28
+
29
+ ## 工作原理
30
+
31
+ Awel 位于浏览器和开发服务器之间:
32
+
33
+ ```
34
+ 浏览器 → Awel(代理 :3001)→ 你的应用(开发服务器 :3000)
35
+ ```
36
+
37
+ 1. Awel 拦截 HTML 响应,注入一段脚本
38
+ 2. 脚本在页面右下角渲染一个悬浮按钮(通过 Shadow DOM 隔离)
39
+ 3. 点击按钮会打开一个全屏聊天面板(iframe)
40
+ 4. 描述你的需求——AI 智能体会读取代码、进行编辑、执行命令,并实时流式返回结果
41
+
42
+ HMR / WebSocket 流量会透明代理,在智能体编辑文件期间暂停,以防止热重载干扰。
43
+
44
+ ## 支持的模型
45
+
46
+ Awel 使用 [Vercel AI SDK](https://sdk.vercel.ai),支持多个服务商。设置对应的环境变量即可启用:
47
+
48
+ | 服务商 | 环境变量 | 示例模型 |
49
+ |--------|----------|----------|
50
+ | Claude Code | PATH 中有 Claude CLI | sonnet, opus, haiku |
51
+ | Anthropic API | `ANTHROPIC_API_KEY` | claude-sonnet-4-5, claude-opus-4-5 |
52
+ | OpenAI | `OPENAI_API_KEY` | gpt-5.2-codex, gpt-5.1-codex |
53
+ | Google AI | `GOOGLE_GENERATIVE_AI_API_KEY` | gemini-3-pro-preview, gemini-2.5-pro |
54
+ | 通义千问 | `DASHSCOPE_API_KEY` | qwen-max, qwen-plus-latest |
55
+ | MiniMax | `MINIMAX_API_KEY` | MiniMax-M2 |
56
+ | Vercel Gateway | `AI_GATEWAY_API_KEY` | 通过网关访问多种模型 |
57
+
58
+ 可随时在面板顶部的下拉菜单中切换模型。
59
+
60
+ ## 智能体工具
61
+
62
+ AI 智能体可使用以下工具:
63
+
64
+ - **Read** / **Write** / **Edit** / **MultiEdit** — 文件操作
65
+ - **Bash** — 执行 Shell 命令
66
+ - **Glob** / **Grep** / **CodeSearch** — 查找文件和搜索代码
67
+ - **WebSearch** / **WebFetch** — 网络搜索
68
+ - **ProposePlan** — 提出多步骤实施计划,等待你审批后再执行
69
+ - **AskUser** — 在执行过程中向你提问
70
+ - **RestartDevServer** — 配置变更后重启开发服务器
71
+ - **TodoRead** / **TodoWrite** — 跨对话的任务管理
72
+
73
+ ## 功能特性
74
+
75
+ - **元素检查器** — 点击十字准星图标,在应用中选择一个元素,自动作为上下文附加到提示中
76
+
77
+ ![元素检查器](docs/Screenshot-Inspector.png)
78
+
79
+ - **截图标注** — 用图形、箭头和文字标注截图后发送给智能体
80
+
81
+ ![截图标注](docs/Screenshot-annotator.png)
82
+
83
+ - **图片附件** — 附加截图或参考图片
84
+ - **计划审批** — 智能体可以提出计划,由你审核后再执行变更
85
+ - **撤销** — 一键回滚整个智能体会话的所有文件变更
86
+ - **Diff 审查** — 在接受变更前查看所有文件修改的摘要
87
+ - **深色模式** — 跟随系统偏好
88
+ - **国际化** — 支持英文和中文
89
+
90
+ ## 开发
91
+
92
+ ```bash
93
+ npm run build # 构建所有模块
94
+ npm run dev # 监听模式(仅 CLI)
95
+ ```
96
+
97
+ 单独构建:
98
+
99
+ ```bash
100
+ npm run build:cli # TypeScript → dist/cli/
101
+ npm run build:dashboard # Vite → dist/dashboard/
102
+ npm run build:host # esbuild → dist/host/host.js
103
+ ```
104
+
105
+ ## 许可证
106
+
107
+ MIT
package/dist/cli/agent.js CHANGED
@@ -164,6 +164,13 @@ export function createAgentRoute(projectCwd, targetPort) {
164
164
  appendUserMessage(userContent);
165
165
  appendResponseMessages(responseMessages);
166
166
  }
167
+ })
168
+ .catch((err) => {
169
+ // Swallow provider-level rejections (e.g. API 400 from tool-use
170
+ // concurrency in Claude Code). The error has already been surfaced
171
+ // as an SSE error event inside streamResponse.
172
+ const msg = err instanceof Error ? err.message : String(err);
173
+ console.error(`[awel] streamResponse rejected: ${msg}`);
167
174
  })
168
175
  .finally(() => {
169
176
  if (!signal.aborted) {
@@ -262,5 +269,9 @@ export function createAgentRoute(projectCwd, targetPort) {
262
269
  agent.get('/api/dev-server/status', (c) => {
263
270
  return c.json(getDevServerStatus());
264
271
  });
272
+ // ─── Project Info ───────────────────────────────────────
273
+ agent.get('/api/project-info', (c) => {
274
+ return c.json({ projectCwd });
275
+ });
265
276
  return agent;
266
277
  }
@@ -1,15 +1,36 @@
1
1
  import { Hono } from 'hono';
2
- const commentPopupHtml = `<!DOCTYPE html>
3
- <html lang="en">
2
+ const i18n = {
3
+ en: { placeholder: 'Describe what you want to change...', cancel: 'Cancel', send: 'Send' },
4
+ zh: { placeholder: '描述您想进行的更改...', cancel: '取消', send: '发送' },
5
+ };
6
+ function getCommentPopupHtml(lang, theme) {
7
+ const t = i18n[lang] ?? i18n.en;
8
+ const isDark = theme !== 'light';
9
+ return `<!DOCTYPE html>
10
+ <html lang="en" data-theme="${isDark ? 'dark' : 'light'}">
4
11
  <head>
5
12
  <meta charset="UTF-8">
6
13
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
14
  <style>
15
+ :root[data-theme="dark"] {
16
+ --bg: #18181b; --fg: #fafafa;
17
+ --title: #e4e4e7; --muted: #a1a1aa; --dim: #71717a; --sep: #52525b;
18
+ --input-bg: #09090b; --input-border: #27272a; --input-focus: #a1a1aa;
19
+ --btn-bg: #27272a; --btn-fg: #a1a1aa; --btn-hover-bg: #3f3f46; --btn-hover-fg: #fafafa;
20
+ --primary-bg: #fafafa; --primary-border: #fafafa; --primary-fg: #18181b; --primary-hover: #e4e4e7;
21
+ }
22
+ :root[data-theme="light"] {
23
+ --bg: #ffffff; --fg: #18181b;
24
+ --title: #27272a; --muted: #71717a; --dim: #a1a1aa; --sep: #d4d4d8;
25
+ --input-bg: #f4f4f5; --input-border: #d4d4d8; --input-focus: #71717a;
26
+ --btn-bg: #e4e4e7; --btn-fg: #71717a; --btn-hover-bg: #d4d4d8; --btn-hover-fg: #18181b;
27
+ --primary-bg: #18181b; --primary-border: #18181b; --primary-fg: #fafafa; --primary-hover: #27272a;
28
+ }
8
29
  * { box-sizing: border-box; margin: 0; padding: 0; }
9
30
  body {
10
31
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
11
- background: #18181b;
12
- color: #fafafa;
32
+ background: var(--bg);
33
+ color: var(--fg);
13
34
  padding: 0 12px 12px;
14
35
  height: 100vh;
15
36
  display: flex;
@@ -30,7 +51,7 @@ const commentPopupHtml = `<!DOCTYPE html>
30
51
  .header-title {
31
52
  font-size: 12px;
32
53
  font-weight: 600;
33
- color: #e4e4e7;
54
+ color: var(--title);
34
55
  }
35
56
  .element-info {
36
57
  margin-left: auto;
@@ -38,7 +59,7 @@ const commentPopupHtml = `<!DOCTYPE html>
38
59
  align-items: center;
39
60
  gap: 4px;
40
61
  font-size: 11px;
41
- color: #a1a1aa;
62
+ color: var(--muted);
42
63
  overflow: hidden;
43
64
  }
44
65
  .element-name {
@@ -49,10 +70,10 @@ const commentPopupHtml = `<!DOCTYPE html>
49
70
  max-width: 100px;
50
71
  }
51
72
  .element-sep {
52
- color: #52525b;
73
+ color: var(--sep);
53
74
  }
54
75
  .element-file {
55
- color: #71717a;
76
+ color: var(--dim);
56
77
  font-family: ui-monospace, SFMono-Regular, monospace;
57
78
  font-size: 10px;
58
79
  white-space: nowrap;
@@ -63,10 +84,10 @@ const commentPopupHtml = `<!DOCTYPE html>
63
84
  textarea {
64
85
  flex: 1;
65
86
  width: 100%;
66
- background: #09090b;
67
- border: 1px solid #27272a;
87
+ background: var(--input-bg);
88
+ border: 1px solid var(--input-border);
68
89
  border-radius: 6px;
69
- color: #fafafa;
90
+ color: var(--fg);
70
91
  font-family: inherit;
71
92
  font-size: 13px;
72
93
  padding: 8px 10px;
@@ -75,10 +96,10 @@ const commentPopupHtml = `<!DOCTYPE html>
75
96
  transition: border-color 0.15s ease;
76
97
  }
77
98
  textarea:focus {
78
- border-color: #a1a1aa;
99
+ border-color: var(--input-focus);
79
100
  }
80
101
  textarea::placeholder {
81
- color: #71717a;
102
+ color: var(--dim);
82
103
  }
83
104
  .buttons {
84
105
  display: flex;
@@ -92,26 +113,26 @@ const commentPopupHtml = `<!DOCTYPE html>
92
113
  font-weight: 500;
93
114
  padding: 6px 14px;
94
115
  border-radius: 6px;
95
- border: 1px solid #27272a;
116
+ border: 1px solid var(--input-border);
96
117
  cursor: pointer;
97
118
  transition: all 0.15s ease;
98
119
  }
99
120
  button:active { transform: scale(0.97); }
100
121
  .btn-close {
101
- background: #27272a;
102
- color: #a1a1aa;
122
+ background: var(--btn-bg);
123
+ color: var(--btn-fg);
103
124
  }
104
125
  .btn-close:hover {
105
- background: #3f3f46;
106
- color: #fafafa;
126
+ background: var(--btn-hover-bg);
127
+ color: var(--btn-hover-fg);
107
128
  }
108
129
  .btn-submit {
109
- background: #fafafa;
110
- border-color: #fafafa;
111
- color: #18181b;
130
+ background: var(--primary-bg);
131
+ border-color: var(--primary-border);
132
+ color: var(--primary-fg);
112
133
  }
113
134
  .btn-submit:hover {
114
- background: #e4e4e7;
135
+ background: var(--primary-hover);
115
136
  }
116
137
  .btn-submit:disabled {
117
138
  opacity: 0.4;
@@ -135,10 +156,10 @@ const commentPopupHtml = `<!DOCTYPE html>
135
156
  </div>
136
157
  <div class="element-info" id="elementInfo"></div>
137
158
  </div>
138
- <textarea id="comment" placeholder="Describe what you want to change..." autofocus></textarea>
159
+ <textarea id="comment" placeholder="${t.placeholder}" autofocus></textarea>
139
160
  <div class="buttons">
140
- <button class="btn-close" id="closeBtn">Cancel</button>
141
- <button class="btn-submit" id="submitBtn" disabled>Send<kbd id="shortcutHint"></kbd></button>
161
+ <button class="btn-close" id="closeBtn">${t.cancel}</button>
162
+ <button class="btn-submit" id="submitBtn" disabled>${t.send}<kbd id="shortcutHint"></kbd></button>
142
163
  </div>
143
164
  <script>
144
165
  const params = new URLSearchParams(window.location.search);
@@ -197,10 +218,13 @@ const commentPopupHtml = `<!DOCTYPE html>
197
218
  </script>
198
219
  </body>
199
220
  </html>`;
221
+ }
200
222
  export function createCommentPopupRoute() {
201
223
  const app = new Hono();
202
224
  app.get('/_awel/comment-popup', (c) => {
203
- return c.html(commentPopupHtml);
225
+ const lang = c.req.query('lang') ?? 'en';
226
+ const theme = c.req.query('theme') ?? 'dark';
227
+ return c.html(getCommentPopupHtml(lang, theme));
204
228
  });
205
229
  return app;
206
230
  }
@@ -63,7 +63,10 @@ Inspector Context:
63
63
  - CRITICAL: Focus your changes on the specific selected tag, NOT the entire parent component. The rendered HTML attributes help you locate the exact JSX element in the source code.
64
64
  - Use the parent component source code only as context to find and modify the specific tag.
65
65
  - Prioritize addressing what the user sees: if props are undefined/null, investigate why.
66
- - Reference the specific line numbers from the context when making edits.`;
66
+ - Reference the specific line numbers from the context when making edits.
67
+
68
+ Language:
69
+ - IMPORTANT: Always respond in the same language the user writes in. If the user writes in Chinese, respond in Chinese. If the user writes in English, respond in English. Match the user's language throughout the conversation.`;
67
70
  /** Detects files Claude Code uses for plan output (.claude/plans/*.md, plan.md) */
68
71
  function isPlanFile(filePath) {
69
72
  const normalized = filePath.replace(/\\/g, '/');
@@ -96,6 +99,7 @@ function createModel(modelId, providerType, cwd) {
96
99
  permissionMode: 'acceptEdits',
97
100
  streamingInput: 'always',
98
101
  maxTurns: 25,
102
+ appendSystemPrompt: 'IMPORTANT: Always respond in the same language the user writes in. If the user writes in Chinese, respond in Chinese. If the user writes in English, respond in English. Match the user\'s language throughout the conversation.',
99
103
  });
100
104
  }
101
105
  else if (providerType === 'anthropic') {
@@ -427,10 +431,20 @@ export function createVercelProvider(modelId, providerType) {
427
431
  }
428
432
  }
429
433
  catch (err) {
430
- // Ignore abort errors from user-input pauses or external cancellation
434
+ // Ignore abort errors from user-input pauses or external cancellation.
435
+ // For other errors (e.g. transient API 400s from tool-use concurrency),
436
+ // log and surface them as SSE error events instead of killing the stream.
431
437
  const externallyAborted = config.signal?.aborted;
432
- if (!waitingForUserInput && !externallyAborted)
433
- throw err;
438
+ if (!waitingForUserInput && !externallyAborted) {
439
+ const errorMsg = err instanceof Error ? err.message : String(err);
440
+ logEvent('error', `stream error (non-fatal): ${errorMsg}`);
441
+ const errorData = JSON.stringify({
442
+ type: 'error',
443
+ message: errorMsg
444
+ });
445
+ addToHistory('error', errorData);
446
+ await stream.writeSSE({ event: 'error', data: errorData });
447
+ }
434
448
  }
435
449
  // Capture response messages for multi-turn accumulation.
436
450
  // Skip only when externally aborted (new request cancelled this one).
@@ -3,6 +3,7 @@
3
3
  * Handles spawning, restarting, health checks, and auto-restart on crash.
4
4
  */
5
5
  import { execa } from 'execa';
6
+ import { watch } from 'node:fs';
6
7
  import { awel, pipeChildOutput } from './logger.js';
7
8
  const state = {
8
9
  process: null,
@@ -14,6 +15,59 @@ const state = {
14
15
  lastError: null,
15
16
  };
16
17
  let autoRestartEnabled = true;
18
+ let crashWatcher = null;
19
+ let crashDebounceTimer = null;
20
+ const SOURCE_EXT_RE = /\.(ts|tsx|js|jsx|json|css|scss|html|mdx?)$/;
21
+ const IGNORE_RE = /(?:^|[/\\])(?:node_modules|\.next|\.git|dist|build)[/\\]/;
22
+ /**
23
+ * Start watching the project directory for file changes while the server is
24
+ * crashed. When a source file is modified (e.g. the agent fixed the bug),
25
+ * restart immediately instead of waiting for the next backoff tick.
26
+ */
27
+ function startCrashWatcher() {
28
+ if (crashWatcher)
29
+ return;
30
+ crashWatcher = watch(state.cwd, { recursive: true }, (_event, filename) => {
31
+ if (!filename)
32
+ return;
33
+ if (IGNORE_RE.test(filename))
34
+ return;
35
+ if (!SOURCE_EXT_RE.test(filename))
36
+ return;
37
+ if (state.status !== 'crashed')
38
+ return;
39
+ // Debounce — agents often write multiple files in quick succession
40
+ if (crashDebounceTimer)
41
+ clearTimeout(crashDebounceTimer);
42
+ crashDebounceTimer = setTimeout(async () => {
43
+ if (state.status !== 'crashed')
44
+ return;
45
+ awel.log(`File changed (${filename}), restarting dev server...`);
46
+ stopCrashWatcher();
47
+ try {
48
+ state.restartCount = 0;
49
+ await doSpawn();
50
+ awel.log('Dev server restarted after file change.');
51
+ }
52
+ catch (err) {
53
+ const msg = err instanceof Error ? err.message : String(err);
54
+ awel.error(`Restart after file change failed: ${msg}`);
55
+ // Re-enable watcher for the next change
56
+ startCrashWatcher();
57
+ }
58
+ }, 500);
59
+ });
60
+ }
61
+ function stopCrashWatcher() {
62
+ if (crashDebounceTimer) {
63
+ clearTimeout(crashDebounceTimer);
64
+ crashDebounceTimer = null;
65
+ }
66
+ if (crashWatcher) {
67
+ crashWatcher.close();
68
+ crashWatcher = null;
69
+ }
70
+ }
17
71
  /**
18
72
  * Wait for the dev server to respond on its port.
19
73
  * Polls with 500ms intervals up to the timeout.
@@ -59,7 +113,8 @@ function getBackoffDelay(restartCount) {
59
113
  return delay;
60
114
  }
61
115
  /**
62
- * Handle process exit — auto-restart with exponential backoff if enabled.
116
+ * Handle process exit — attempt one auto-restart, then switch to file-watch
117
+ * mode so the server restarts as soon as the agent (or user) saves a fix.
63
118
  */
64
119
  function attachExitHandler(child) {
65
120
  child.catch(async (error) => {
@@ -72,26 +127,33 @@ function attachExitHandler(child) {
72
127
  if (!autoRestartEnabled)
73
128
  return;
74
129
  state.restartCount++;
75
- const delay = getBackoffDelay(state.restartCount);
76
- awel.log(`Auto-restarting dev server in ${delay}ms (attempt ${state.restartCount})...`);
77
- await new Promise(r => setTimeout(r, delay));
78
- // Check we're still in crashed state (user may have manually restarted)
79
- if (state.status !== 'crashed')
80
- return;
81
- try {
82
- await doSpawn();
83
- awel.log('Dev server auto-restarted successfully.');
84
- }
85
- catch (err) {
86
- const msg = err instanceof Error ? err.message : String(err);
87
- awel.error(`Auto-restart failed: ${msg}`);
130
+ // First crash: try one immediate auto-restart (could be a transient issue)
131
+ if (state.restartCount <= 1) {
132
+ const delay = getBackoffDelay(state.restartCount);
133
+ awel.log(`Auto-restarting dev server in ${delay}ms (attempt ${state.restartCount})...`);
134
+ await new Promise(r => setTimeout(r, delay));
135
+ if (state.status !== 'crashed')
136
+ return;
137
+ try {
138
+ await doSpawn();
139
+ awel.log('Dev server auto-restarted successfully.');
140
+ return;
141
+ }
142
+ catch (err) {
143
+ const msg = err instanceof Error ? err.message : String(err);
144
+ awel.error(`Auto-restart failed: ${msg}`);
145
+ }
88
146
  }
147
+ // Repeated crashes: stop retrying blindly, watch for file changes instead
148
+ awel.log('Watching for file changes to restart dev server...');
149
+ startCrashWatcher();
89
150
  });
90
151
  }
91
152
  /**
92
153
  * Internal spawn + health-check routine.
93
154
  */
94
155
  async function doSpawn() {
156
+ stopCrashWatcher();
95
157
  state.status = 'starting';
96
158
  const child = spawn(state.port, state.cwd);
97
159
  state.process = child;
@@ -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: 0 0% 4%;--card: 0 0% 100%;--card-foreground: 0 0% 4%;--primary: 0 0% 9%;--primary-foreground: 0 0% 98%;--secondary: 0 0% 96%;--secondary-foreground: 0 0% 9%;--muted: 0 0% 96%;--muted-foreground: 0 0% 45%;--accent: 0 0% 96%;--accent-foreground: 0 0% 9%;--border: 0 0% 90%;--input: 0 0% 90%;--ring: 0 0% 64%;--radius: .5rem;--scrollbar-thumb: rgba(0, 0, 0, .15);--scrollbar-thumb-hover: rgba(0, 0, 0, .25)}.dark{--background: 0 0% 4%;--foreground: 0 0% 98%;--card: 0 0% 7%;--card-foreground: 0 0% 98%;--primary: 0 0% 98%;--primary-foreground: 0 0% 9%;--secondary: 0 0% 15%;--secondary-foreground: 0 0% 98%;--muted: 0 0% 15%;--muted-foreground: 0 0% 64%;--accent: 0 0% 15%;--accent-foreground: 0 0% 98%;--border: 0 0% 15%;--input: 0 0% 15%;--ring: 0 0% 80%;--scrollbar-thumb: rgba(255, 255, 255, .15);--scrollbar-thumb-hover: rgba(255, 255, 255, .25)}*{--tw-border-opacity: 1;border-color:hsl(var(--border) / var(--tw-border-opacity, 1))}html,body,#root{width:100%;height:100%;margin:0;padding:0;overflow:hidden}body{background-color:transparent;--tw-text-opacity: 1;color:hsl(var(--foreground) / var(--tw-text-opacity, 1));-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,sans-serif}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--scrollbar-thumb);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-thumb-hover)}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.invisible{visibility:hidden}.collapse{visibility:collapse}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.-right-1\.5{right:-.375rem}.-top-1\.5{top:-.375rem}.bottom-20{bottom:5rem}.bottom-4{bottom:1rem}.bottom-full{bottom:100%}.left-0{left:0}.left-1\/2{left:50%}.left-4{left:1rem}.left-6{left:1.5rem}.right-0{right:0}.right-2{right:.5rem}.right-4{right:1rem}.right-6{right:1.5rem}.top-0{top:0}.top-1\/2{top:50%}.top-2{top:.5rem}.top-4{top:1rem}.top-6{top:1.5rem}.top-full{top:100%}.z-50{z-index:50}.z-\[60\]{z-index:60}.z-\[9999\]{z-index:9999}.mx-0\.5{margin-left:.125rem;margin-right:.125rem}.mx-4{margin-left:1rem;margin-right:1rem}.my-0\.5{margin-top:.125rem;margin-bottom:.125rem}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-1\.5{margin-top:.375rem;margin-bottom:.375rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.mb-0\.5{margin-bottom:.125rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.ml-0\.5{margin-left:.125rem}.ml-1\.5{margin-left:.375rem}.ml-2{margin-left:.5rem}.ml-auto{margin-left:auto}.mr-4{margin-right:1rem}.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}.block{display:block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.hidden{display:none}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-11{height:2.75rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-full{height:100%}.max-h-32{max-height:8rem}.max-h-48{max-height:12rem}.max-h-64{max-height:16rem}.max-h-\[140px\]{max-height:140px}.max-h-\[200px\]{max-height:200px}.max-h-\[70vh\]{max-height:70vh}.max-h-\[80vh\]{max-height:80vh}.max-h-\[90vh\]{max-height:90vh}.min-h-0{min-height:0px}.min-h-\[36px\]{min-height:36px}.min-h-full{min-height:100%}.w-1\.5{width:.375rem}.w-10{width:2.5rem}.w-2\.5{width:.625rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-7{width:1.75rem}.w-72{width:18rem}.w-8{width:2rem}.w-\[380px\]{width:380px}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.w-max{width:-moz-max-content;width:max-content}.min-w-0{min-width:0px}.max-w-2xl{max-width:42rem}.max-w-\[10rem\]{max-width:10rem}.max-w-\[160px\]{max-width:160px}.max-w-\[180px\]{max-width:180px}.max-w-\[200px\]{max-width:200px}.max-w-\[220px\]{max-width:220px}.max-w-\[250px\]{max-width:250px}.max-w-\[85\%\]{max-width:85%}.max-w-\[90vw\]{max-width:90vw}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.grow{flex-grow:1}.-translate-x-1\/2{--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-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-180{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes 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-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.list-inside{list-style-position:inside}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-reverse))}.space-y-1\.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))}.self-end{align-self:flex-end}.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{white-space:pre}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.rounded{border-radius:.25rem}.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-xl{border-radius:.75rem}.border{border-width:1px}.border-y{border-top-width:1px;border-bottom-width:1px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-amber-500\/50{border-color:#f59e0b80}.border-blue-500\/50{border-color:#3b82f680}.border-border{--tw-border-opacity: 1;border-color:hsl(var(--border) / var(--tw-border-opacity, 1))}.border-border\/50{border-color:hsl(var(--border) / .5)}.border-border\/60{border-color:hsl(var(--border) / .6)}.border-cyan-500\/50{border-color:#06b6d480}.border-input{--tw-border-opacity: 1;border-color:hsl(var(--input) / var(--tw-border-opacity, 1))}.border-muted\/50{border-color:hsl(var(--muted) / .5)}.border-orange-500\/50{border-color:#f9731680}.border-purple-500\/50{border-color:#a855f780}.border-red-300\/60{border-color:#fca5a599}.border-red-500\/50{border-color:#ef444480}.border-violet-400{--tw-border-opacity: 1;border-color:rgb(167 139 250 / var(--tw-border-opacity, 1))}.border-violet-500{--tw-border-opacity: 1;border-color:rgb(139 92 246 / var(--tw-border-opacity, 1))}.border-violet-500\/70{border-color:#8b5cf6b3}.border-violet-600\/60{border-color:#7c3aed99}.border-violet-700\/50{border-color:#6d28d980}.border-yellow-300\/60{border-color:#fde04799}.bg-amber-100\/60{background-color:#fef3c799}.bg-amber-600{--tw-bg-opacity: 1;background-color:rgb(217 119 6 / var(--tw-bg-opacity, 1))}.bg-background{--tw-bg-opacity: 1;background-color:hsl(var(--background) / var(--tw-bg-opacity, 1))}.bg-black\/60{background-color:#0009}.bg-black\/80{background-color:#000c}.bg-blue-100\/60{background-color:#dbeafe99}.bg-card{--tw-bg-opacity: 1;background-color:hsl(var(--card) / var(--tw-bg-opacity, 1))}.bg-card\/50{background-color:hsl(var(--card) / .5)}.bg-card\/60{background-color:hsl(var(--card) / .6)}.bg-card\/80{background-color:hsl(var(--card) / .8)}.bg-emerald-400{--tw-bg-opacity: 1;background-color:rgb(52 211 153 / var(--tw-bg-opacity, 1))}.bg-green-100\/50{background-color:#dcfce780}.bg-green-100\/60{background-color:#dcfce799}.bg-muted{--tw-bg-opacity: 1;background-color:hsl(var(--muted) / var(--tw-bg-opacity, 1))}.bg-muted\/30{background-color:hsl(var(--muted) / .3)}.bg-muted\/40{background-color:hsl(var(--muted) / .4)}.bg-muted\/50{background-color:hsl(var(--muted) / .5)}.bg-muted\/60{background-color:hsl(var(--muted) / .6)}.bg-muted\/80{background-color:hsl(var(--muted) / .8)}.bg-primary{--tw-bg-opacity: 1;background-color:hsl(var(--primary) / var(--tw-bg-opacity, 1))}.bg-red-100{--tw-bg-opacity: 1;background-color:rgb(254 226 226 / var(--tw-bg-opacity, 1))}.bg-red-100\/50{background-color:#fee2e280}.bg-red-100\/60{background-color:#fee2e299}.bg-red-200\/60{background-color:#fecaca99}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-red-600{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity, 1))}.bg-rose-100{--tw-bg-opacity: 1;background-color:rgb(255 228 230 / var(--tw-bg-opacity, 1))}.bg-secondary{--tw-bg-opacity: 1;background-color:hsl(var(--secondary) / var(--tw-bg-opacity, 1))}.bg-violet-500{--tw-bg-opacity: 1;background-color:rgb(139 92 246 / var(--tw-bg-opacity, 1))}.bg-violet-600{--tw-bg-opacity: 1;background-color:rgb(124 58 237 / var(--tw-bg-opacity, 1))}.bg-violet-900\/50{background-color:#4c1d9580}.bg-violet-950\/40{background-color:#2e106566}.bg-violet-950\/50{background-color:#2e106580}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-yellow-100{--tw-bg-opacity: 1;background-color:rgb(254 249 195 / var(--tw-bg-opacity, 1))}.bg-yellow-100\/60{background-color:#fef9c399}.bg-yellow-200\/60{background-color:#fef08a99}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.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-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.pb-0{padding-bottom:0}.pb-1{padding-bottom:.25rem}.pl-2{padding-left:.5rem}.pl-3{padding-left:.75rem}.pr-1{padding-right:.25rem}.pr-7{padding-right:1.75rem}.pt-0{padding-top:0}.pt-1{padding-top:.25rem}.pt-1\.5{padding-top:.375rem}.pt-2{padding-top:.5rem}.pt-3{padding-top:.75rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-baseline{vertical-align:baseline}.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-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-base{font-size:1rem;line-height:1.5rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.italic{font-style:italic}.leading-5{line-height:1.25rem}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.leading-snug{line-height:1.375}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.text-amber-700{--tw-text-opacity: 1;color:rgb(180 83 9 / var(--tw-text-opacity, 1))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.text-blue-700{--tw-text-opacity: 1;color:rgb(29 78 216 / var(--tw-text-opacity, 1))}.text-card-foreground{--tw-text-opacity: 1;color:hsl(var(--card-foreground) / var(--tw-text-opacity, 1))}.text-current{color:currentColor}.text-cyan-400{--tw-text-opacity: 1;color:rgb(34 211 238 / var(--tw-text-opacity, 1))}.text-cyan-600{--tw-text-opacity: 1;color:rgb(8 145 178 / var(--tw-text-opacity, 1))}.text-emerald-600{--tw-text-opacity: 1;color:rgb(5 150 105 / var(--tw-text-opacity, 1))}.text-foreground{--tw-text-opacity: 1;color:hsl(var(--foreground) / 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-green-700{--tw-text-opacity: 1;color:rgb(21 128 61 / var(--tw-text-opacity, 1))}.text-muted-foreground{--tw-text-opacity: 1;color:hsl(var(--muted-foreground) / var(--tw-text-opacity, 1))}.text-orange-400{--tw-text-opacity: 1;color:rgb(251 146 60 / var(--tw-text-opacity, 1))}.text-orange-600{--tw-text-opacity: 1;color:rgb(234 88 12 / var(--tw-text-opacity, 1))}.text-pink-600{--tw-text-opacity: 1;color:rgb(219 39 119 / var(--tw-text-opacity, 1))}.text-primary{--tw-text-opacity: 1;color:hsl(var(--primary) / var(--tw-text-opacity, 1))}.text-primary-foreground{--tw-text-opacity: 1;color:hsl(var(--primary-foreground) / var(--tw-text-opacity, 1))}.text-purple-400{--tw-text-opacity: 1;color:rgb(192 132 252 / var(--tw-text-opacity, 1))}.text-purple-600{--tw-text-opacity: 1;color:rgb(147 51 234 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity, 1))}.text-red-600\/60{color:#dc262699}.text-red-700{--tw-text-opacity: 1;color:rgb(185 28 28 / var(--tw-text-opacity, 1))}.text-red-800{--tw-text-opacity: 1;color:rgb(153 27 27 / var(--tw-text-opacity, 1))}.text-rose-600{--tw-text-opacity: 1;color:rgb(225 29 72 / var(--tw-text-opacity, 1))}.text-rose-800{--tw-text-opacity: 1;color:rgb(159 18 57 / var(--tw-text-opacity, 1))}.text-secondary-foreground{--tw-text-opacity: 1;color:hsl(var(--secondary-foreground) / var(--tw-text-opacity, 1))}.text-violet-200{--tw-text-opacity: 1;color:rgb(221 214 254 / var(--tw-text-opacity, 1))}.text-violet-300{--tw-text-opacity: 1;color:rgb(196 181 253 / var(--tw-text-opacity, 1))}.text-violet-400{--tw-text-opacity: 1;color:rgb(167 139 250 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-white\/70{color:#ffffffb3}.text-yellow-600{--tw-text-opacity: 1;color:rgb(202 138 4 / var(--tw-text-opacity, 1))}.text-yellow-800{--tw-text-opacity: 1;color:rgb(133 77 14 / var(--tw-text-opacity, 1))}.underline{text-decoration-line:underline}.underline-offset-4{text-underline-offset:4px}.placeholder-muted-foreground::-moz-placeholder{--tw-placeholder-opacity: 1;color:hsl(var(--muted-foreground) / var(--tw-placeholder-opacity, 1))}.placeholder-muted-foreground::placeholder{--tw-placeholder-opacity: 1;color:hsl(var(--muted-foreground) / var(--tw-placeholder-opacity, 1))}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-80{opacity:.8}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-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-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)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.ring-offset-background{--tw-ring-offset-color: hsl(var(--background) / 1)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-100{transition-duration:.1s}.placeholder\:text-muted-foreground::-moz-placeholder{--tw-text-opacity: 1;color:hsl(var(--muted-foreground) / var(--tw-text-opacity, 1))}.placeholder\:text-muted-foreground::placeholder{--tw-text-opacity: 1;color:hsl(var(--muted-foreground) / var(--tw-text-opacity, 1))}.focus-within\:border-ring:focus-within{--tw-border-opacity: 1;border-color:hsl(var(--ring) / var(--tw-border-opacity, 1))}.focus-within\:ring-2:focus-within{--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-within\:ring-ring\/50:focus-within{--tw-ring-color: hsl(var(--ring) / .5)}.hover\:border-ring:hover{--tw-border-opacity: 1;border-color:hsl(var(--ring) / var(--tw-border-opacity, 1))}.hover\:bg-accent:hover{--tw-bg-opacity: 1;background-color:hsl(var(--accent) / var(--tw-bg-opacity, 1))}.hover\:bg-accent\/80:hover{background-color:hsl(var(--accent) / .8)}.hover\:bg-amber-200\/60:hover{background-color:#fde68a99}.hover\:bg-amber-500:hover{--tw-bg-opacity: 1;background-color:rgb(245 158 11 / var(--tw-bg-opacity, 1))}.hover\:bg-blue-200\/60:hover{background-color:#bfdbfe99}.hover\:bg-card:hover{--tw-bg-opacity: 1;background-color:hsl(var(--card) / var(--tw-bg-opacity, 1))}.hover\:bg-muted:hover{--tw-bg-opacity: 1;background-color:hsl(var(--muted) / var(--tw-bg-opacity, 1))}.hover\:bg-muted\/50:hover{background-color:hsl(var(--muted) / .5)}.hover\:bg-muted\/60:hover{background-color:hsl(var(--muted) / .6)}.hover\:bg-primary\/90:hover{background-color:hsl(var(--primary) / .9)}.hover\:bg-red-200:hover{--tw-bg-opacity: 1;background-color:rgb(254 202 202 / var(--tw-bg-opacity, 1))}.hover\:bg-red-200\/60:hover{background-color:#fecaca99}.hover\:bg-red-500:hover{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.hover\:bg-red-500\/90:hover{background-color:#ef4444e6}.hover\:bg-red-700:hover{--tw-bg-opacity: 1;background-color:rgb(185 28 28 / var(--tw-bg-opacity, 1))}.hover\:bg-secondary\/80:hover{background-color:hsl(var(--secondary) / .8)}.hover\:bg-violet-500:hover{--tw-bg-opacity: 1;background-color:rgb(139 92 246 / var(--tw-bg-opacity, 1))}.hover\:bg-yellow-200:hover{--tw-bg-opacity: 1;background-color:rgb(254 240 138 / var(--tw-bg-opacity, 1))}.hover\:bg-yellow-200\/60:hover{background-color:#fef08a99}.hover\:text-accent-foreground:hover{--tw-text-opacity: 1;color:hsl(var(--accent-foreground) / var(--tw-text-opacity, 1))}.hover\:text-amber-800:hover{--tw-text-opacity: 1;color:rgb(146 64 14 / var(--tw-text-opacity, 1))}.hover\:text-blue-800:hover{--tw-text-opacity: 1;color:rgb(30 64 175 / var(--tw-text-opacity, 1))}.hover\:text-foreground:hover{--tw-text-opacity: 1;color:hsl(var(--foreground) / var(--tw-text-opacity, 1))}.hover\:text-red-700:hover{--tw-text-opacity: 1;color:rgb(185 28 28 / var(--tw-text-opacity, 1))}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.hover\:opacity-80:hover{opacity:.8}.hover\:brightness-125:hover{--tw-brightness: brightness(1.25);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)}.focus\:border-ring:focus{--tw-border-opacity: 1;border-color:hsl(var(--ring) / var(--tw-border-opacity, 1))}.focus\:border-violet-500\/70:focus{border-color:#8b5cf6b3}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset: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-opacity: 1;--tw-ring-color: hsl(var(--ring) / var(--tw-ring-opacity, 1))}.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-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.group\/tip:hover .group-hover\/tip\:visible{visibility:visible}.group:hover .group-hover\:flex{display:flex}.group\/tip:hover .group-hover\/tip\:opacity-100,.group:hover .group-hover\:opacity-100{opacity:1}.dark\:border-red-800\/60:is(.dark *){border-color:#991b1b99}.dark\:border-yellow-800\/60:is(.dark *){border-color:#854d0e99}.dark\:bg-amber-900\/40:is(.dark *){background-color:#78350f66}.dark\:bg-blue-900\/40:is(.dark *){background-color:#1e3a8a66}.dark\:bg-green-900\/20:is(.dark *){background-color:#14532d33}.dark\:bg-green-900\/40:is(.dark *){background-color:#14532d66}.dark\:bg-red-800:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(153 27 27 / var(--tw-bg-opacity, 1))}.dark\:bg-red-800\/60:is(.dark *){background-color:#991b1b99}.dark\:bg-red-800\/80:is(.dark *){background-color:#991b1bcc}.dark\:bg-red-900\/20:is(.dark *){background-color:#7f1d1d33}.dark\:bg-red-950\/30:is(.dark *){background-color:#450a0a4d}.dark\:bg-red-950\/40:is(.dark *){background-color:#450a0a66}.dark\:bg-rose-800\/80:is(.dark *){background-color:#9f1239cc}.dark\:bg-yellow-800:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(133 77 14 / var(--tw-bg-opacity, 1))}.dark\:bg-yellow-800\/60:is(.dark *){background-color:#854d0e99}.dark\:bg-yellow-800\/80:is(.dark *){background-color:#854d0ecc}.dark\:bg-yellow-950\/40:is(.dark *){background-color:#42200666}.dark\:text-amber-300:is(.dark *){--tw-text-opacity: 1;color:rgb(252 211 77 / var(--tw-text-opacity, 1))}.dark\:text-blue-300:is(.dark *){--tw-text-opacity: 1;color:rgb(147 197 253 / 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-cyan-400:is(.dark *){--tw-text-opacity: 1;color:rgb(34 211 238 / var(--tw-text-opacity, 1))}.dark\:text-emerald-400:is(.dark *){--tw-text-opacity: 1;color:rgb(52 211 153 / var(--tw-text-opacity, 1))}.dark\:text-green-300:is(.dark *){--tw-text-opacity: 1;color:rgb(134 239 172 / 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-orange-400:is(.dark *){--tw-text-opacity: 1;color:rgb(251 146 60 / var(--tw-text-opacity, 1))}.dark\:text-pink-400:is(.dark *){--tw-text-opacity: 1;color:rgb(244 114 182 / var(--tw-text-opacity, 1))}.dark\:text-purple-400:is(.dark *){--tw-text-opacity: 1;color:rgb(192 132 252 / var(--tw-text-opacity, 1))}.dark\:text-red-100:is(.dark *){--tw-text-opacity: 1;color:rgb(254 226 226 / var(--tw-text-opacity, 1))}.dark\:text-red-200:is(.dark *){--tw-text-opacity: 1;color:rgb(254 202 202 / var(--tw-text-opacity, 1))}.dark\:text-red-300:is(.dark *){--tw-text-opacity: 1;color:rgb(252 165 165 / 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-red-400\/60:is(.dark *){color:#f8717199}.dark\:text-rose-100:is(.dark *){--tw-text-opacity: 1;color:rgb(255 228 230 / var(--tw-text-opacity, 1))}.dark\:text-rose-200:is(.dark *){--tw-text-opacity: 1;color:rgb(254 205 211 / var(--tw-text-opacity, 1))}.dark\:text-rose-400:is(.dark *){--tw-text-opacity: 1;color:rgb(251 113 133 / var(--tw-text-opacity, 1))}.dark\:text-yellow-100:is(.dark *){--tw-text-opacity: 1;color:rgb(254 249 195 / var(--tw-text-opacity, 1))}.dark\:text-yellow-200:is(.dark *){--tw-text-opacity: 1;color:rgb(254 240 138 / 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))}.dark\:text-yellow-400\/60:is(.dark *){color:#facc1599}.dark\:hover\:bg-amber-800\/50:hover:is(.dark *){background-color:#92400e80}.dark\:hover\:bg-blue-800\/50:hover:is(.dark *){background-color:#1e40af80}.dark\:hover\:bg-red-700\/80:hover:is(.dark *){background-color:#b91c1ccc}.dark\:hover\:bg-red-900\/40:hover:is(.dark *){background-color:#7f1d1d66}.dark\:hover\:bg-yellow-700\/80:hover:is(.dark *){background-color:#a16207cc}.dark\:hover\:bg-yellow-900\/40:hover:is(.dark *){background-color:#713f1266}.dark\:hover\:text-amber-200:hover:is(.dark *){--tw-text-opacity: 1;color:rgb(253 230 138 / var(--tw-text-opacity, 1))}.dark\:hover\:text-blue-200:hover:is(.dark *){--tw-text-opacity: 1;color:rgb(191 219 254 / var(--tw-text-opacity, 1))}.dark\:hover\:text-red-300:hover:is(.dark *){--tw-text-opacity: 1;color:rgb(252 165 165 / var(--tw-text-opacity, 1))}