autosnippet 1.4.8 → 1.5.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
@@ -39,7 +39,7 @@ asd ui # 启动 Dashboard(建议常驻)
39
39
 
40
40
  1. **组建知识库**:`asd ais <Target>` 或 `asd ais --all` → Dashboard Candidates 审核 → Recipe 入库
41
41
  2. **依赖关系**:`asd spm-map` 或 Dashboard 刷新
42
- 3. **Cursor 集成**:`asd install:cursor-skill --mcp`(安装 Skills + MCP,需 `asd ui` 运行)
42
+ 3. **Cursor 集成**:`asd install:cursor-skill --mcp`(安装 Skills + Cursor 规则 `.cursor/rules/` + MCP,需 `asd ui` 运行)
43
43
  4. **语义索引**:`asd ui` 启动时自动 embed;也可手动 `asd embed`
44
44
 
45
45
  ### 闭环
@@ -57,7 +57,7 @@ asd ui # 启动 Dashboard(建议常驻)
57
57
  | 指令 | 作用 |
58
58
  |------|------|
59
59
  | `// as:create` / `// as:c` | 无选项时只打开 Dashboard(路径已填),由用户点 Scan File 或 Use Copied Code。`-c` 强制用剪切板(静默创建或打开);`-f` 强制用路径(打开 Dashboard 并自动执行 Scan File) |
60
- | `// as:guard` / `// as:g` [关键词] | 按知识库 AI 审查当前文件,输出到终端 |
60
+ | `// as:guard` / `// as:g` [关键词或规模] | 按知识库 AI 审查;无后缀时仅检查当前文件;后缀 **file** / **target** / **project** 可扩大范围(target=当前 target 内所有源文件,project=项目内所有源文件);其他为检索关键词 |
61
61
  | `// as:search` / `// as:s` [关键词] | 从知识库检索并插入 Recipe/Snippet |
62
62
  | `// as:include` / `// as:import` | Snippet 内头文件/模块标记,保存时自动注入 |
63
63
 
@@ -76,11 +76,17 @@ asd ui # 启动 Dashboard(建议常驻)
76
76
  | `asd ais [Target]` | AI 扫描 Target → Candidates |
77
77
  | `asd search [keyword] --copy` | 搜索并复制第一条到剪贴板 |
78
78
  | `asd search [keyword] --pick` | 交互选择后复制/插入 |
79
- | `asd install:cursor-skill --mcp` | 安装 Skills 并配置 MCP(需 `asd ui` 运行) |
79
+ | `asd install:cursor-skill --mcp` | 安装 Skills、Cursor 规则(`.cursor/rules/*.mdc`)并配置 MCP(需 `asd ui` 运行) |
80
80
  | `asd install:full` | 全量安装;`--parser` 含 Swift 解析器;`--lancedb` 仅 LanceDB |
81
81
  | `asd embed` | 手动构建语义向量索引(`asd ui` 启动时也会自动执行) |
82
82
  | `asd spm-map` | 刷新 SPM 依赖映射(依赖关系图数据来源) |
83
83
 
84
+ ### 用 Cursor 做批量扫描
85
+
86
+ 除 `asd ais [Target]`(项目内 AI)外,可用 **Cursor 作为批量扫描工具**:在 Cursor 里让 Agent 通过 **MCP 工具**(`autosnippet_get_targets` → `autosnippet_get_target_files` → 按文件提取 → `autosnippet_submit_candidates`)扫描指定 Target,用 Cursor 模型提取候选并提交到 Dashboard,再到 **Candidates** 页审核入库。
87
+
88
+ 简单一句:「扫描 BDNetwork ,生产 Recipes 到候选」。AutoSnippet 将所有能力都通过语义交给 Cursor 了。
89
+
84
90
  ## 全量安装与可选依赖
85
91
 
86
92
  克隆或需完整能力时,**任意目录**执行:
@@ -100,7 +106,7 @@ asd install:full --lancedb # 仅安装 LanceDB(向量检索更快)
100
106
  | 脚本 | 作用 |
101
107
  |------|------|
102
108
  | `scripts/ensure-parse-package.js` | 仅当 `ASD_BUILD_SWIFT_PARSER=1` 时构建 Swift 解析器并打印「正在安装…」;否则打印跳过说明并退出。 |
103
- | `scripts/build-native-ui.js` | 仅在 macOS 上用本机 Swift 编译 `tools/native-ui/main.swift` → `bin/native-ui`;失败则静默跳过。 |
109
+ | `scripts/build-native-ui.js` | 仅在 macOS 上用本机 Swift 编译 `resources/native-ui/main.swift` → `resources/native-ui/native-ui`;失败则静默跳过。 |
104
110
 
105
111
  未安装或跳过不影响核心功能。详见 [npm lifecycle scripts](https://docs.npmjs.com/cli/v10/using-npm/scripts#life-cycle-scripts)。
106
112
 
@@ -108,15 +114,15 @@ asd install:full --lancedb # 仅安装 LanceDB(向量检索更快)
108
114
 
109
115
  - **AI**:项目根 `.env`,设置 `ASD_GOOGLE_API_KEY` 等(见 `.env.example`)。可选 `ASD_AI_PROVIDER`、代理等。
110
116
  - **LanceDB**:`asd install:full --lancedb`,在 boxspec 的 `context.storage.adapter` 中配置 `"lance"`。
111
- - **Native UI**(可选):macOS 上 `npm install` 会尝试构建 `bin/native-ui`(需本机 Swift);未构建时回退到 AppleScript/inquirer,功能正常。
117
+ - **Native UI**(可选):macOS 上 `npm install` 会尝试构建 `resources/native-ui/native-ui`(需本机 Swift);未构建时回退到 AppleScript/inquirer,功能正常。
112
118
 
113
119
  ## Recipe 格式
114
120
 
115
121
  完整 Recipe 为 Markdown 文件,需包含:
116
122
 
117
123
  - **Frontmatter**(`---` 包裹的 YAML):`title`、`trigger` 必填;可选 `category`、`language`、`headers` 等
118
- - **## Snippet / Code Reference**:下接代码块,供 Snippet 与检索使用
119
- - **## AI Context / Usage Guide**:使用说明,供 AI 与 Guard 检索
124
+ - **Snippet / Code Reference**:下接代码块,供 Snippet 与检索使用
125
+ - **AI Context / Usage Guide**:使用说明,供 AI 与 Guard 检索
120
126
 
121
127
  ## 术语
122
128
 
@@ -125,7 +131,6 @@ asd install:full --lancedb # 仅安装 LanceDB(向量检索更快)
125
131
  - **项目根**:含 `AutoSnippetRoot.boxspec.json` 的目录
126
132
 
127
133
  **详细介绍**:启动 `asd ui` 后访问 Dashboard → **使用说明** 页;或参阅 [使用文档](docs/使用文档.md)(含 Skills 一览、AI 配置、闭环详解等)。
128
- **发布流程**:CI 与 npm 发布见 [发布流程](docs/发布流程.md)(打 tag `v*` 触发自动发布)。
129
134
 
130
135
  ---
131
136
 
package/bin/asnip.js CHANGED
@@ -868,13 +868,24 @@ commander
868
868
 
869
869
  // --copy: 复制第一条到剪贴板
870
870
  if (options.copy) {
871
- const code = results[0].code || results[0].content || '';
871
+ const selected = results[0];
872
+ const code = selected.code || selected.content || '';
872
873
  if (nativeUi.writeClipboard(code)) {
873
- console.log(`✅ 已复制到剪贴板 (${results[0].title}),Cmd+V 粘贴`);
874
+ console.log(`✅ 已复制到剪贴板 (${selected.title}),Cmd+V 粘贴`);
874
875
  } else {
875
876
  console.log('--- 第一条结果 ---\n');
876
877
  console.log(code);
877
878
  }
879
+ try {
880
+ const recipeStats = require('../lib/recipe/recipeStats');
881
+ if (selected.type === 'recipe') {
882
+ recipeStats.recordRecipeUsage(projectRoot, {
883
+ trigger: selected.trigger,
884
+ recipeFilePath: selected.name,
885
+ source: 'human'
886
+ });
887
+ }
888
+ } catch (_) {}
878
889
  return;
879
890
  }
880
891
 
@@ -894,6 +905,16 @@ commander
894
905
  console.log('已取消');
895
906
  return;
896
907
  }
908
+ try {
909
+ const recipeStats = require('../lib/recipe/recipeStats');
910
+ if (selected.type === 'recipe') {
911
+ recipeStats.recordRecipeUsage(projectRoot, {
912
+ trigger: selected.trigger,
913
+ recipeFilePath: selected.name,
914
+ source: 'human'
915
+ });
916
+ }
917
+ } catch (_) {}
897
918
  if (options.insert) {
898
919
  const insertPath = path.isAbsolute(options.insert) ? options.insert : path.join(projectRoot, options.insert);
899
920
  try {
package/bin/ui.js CHANGED
@@ -128,7 +128,7 @@ function launch(projectRoot, port = 3000, options = {}) {
128
128
  }
129
129
  });
130
130
 
131
- // API: 上下文语义搜索(供 Agent/Skill 调用)
131
+ // API: 上下文语义搜索(供 Agent/Skill 调用),返回项合并 recipe-stats 供 AI 可见
132
132
  app.post('/api/context/search', async (req, res) => {
133
133
  try {
134
134
  const { query, limit = 5, filter } = req.body;
@@ -139,7 +139,37 @@ function launch(projectRoot, port = 3000, options = {}) {
139
139
  const ai = await AiFactory.getProvider(projectRoot);
140
140
  if (!ai) return res.status(400).json({ error: 'AI 未配置,无法进行语义检索' });
141
141
  const service = getInstance(projectRoot);
142
- const items = await service.search(query, { limit, filter });
142
+ let items = await service.search(query, { limit, filter });
143
+ try {
144
+ const recipeStats = require('../lib/recipe/recipeStats');
145
+ // MCP/Agent 引用:本次搜索返回的 Recipe 记一次 ai 使用
146
+ for (const it of items) {
147
+ const meta = it.metadata || {};
148
+ if (meta.type !== 'recipe') continue;
149
+ const sourcePath = meta.sourcePath || meta.source || it.id || '';
150
+ const fileKey = sourcePath ? path.basename(sourcePath) : null;
151
+ if (fileKey) recipeStats.recordRecipeUsage(projectRoot, { recipeFilePath: fileKey, source: 'ai' });
152
+ }
153
+ const stats = recipeStats.getRecipeStats(projectRoot);
154
+ const byFileEntries = Object.values(stats.byFile || {});
155
+ items = items.map((it) => {
156
+ const meta = it.metadata || {};
157
+ const fileKey = path.basename(meta.sourcePath || meta.source || it.id || '');
158
+ const entry = fileKey ? (stats.byFile || {})[fileKey] : null;
159
+ if (!entry) return it;
160
+ const score = recipeStats.getAuthorityScore(entry, byFileEntries, {});
161
+ return {
162
+ ...it,
163
+ stats: {
164
+ authority: entry.authority ?? 0,
165
+ guardUsageCount: entry.guardUsageCount ?? 0,
166
+ humanUsageCount: entry.humanUsageCount ?? 0,
167
+ aiUsageCount: entry.aiUsageCount ?? 0,
168
+ authorityScore: Math.round(score * 100) / 100
169
+ }
170
+ };
171
+ });
172
+ } catch (_) {}
143
173
  res.json({ items });
144
174
  } catch (err) {
145
175
  console.error(`[API Error]`, err);
@@ -399,10 +429,34 @@ function launch(projectRoot, port = 3000, options = {}) {
399
429
  };
400
430
 
401
431
  const allMdFiles = getAllFiles(recipesDir);
432
+ let statsMap = {};
433
+ try {
434
+ const recipeStats = require('../lib/recipe/recipeStats');
435
+ const stats = recipeStats.getRecipeStats(projectRoot);
436
+ statsMap = stats.byFile || {};
437
+ } catch (_) {}
438
+ const byFileEntries = Object.values(statsMap);
402
439
  recipes = allMdFiles.map(filePath => {
403
440
  const content = fs.readFileSync(filePath, 'utf8');
404
- const relativePath = path.relative(recipesDir, filePath);
405
- return { name: relativePath, content };
441
+ const relativePath = path.relative(recipesDir, filePath).replace(/\\/g, '/');
442
+ const fileKey = path.basename(relativePath);
443
+ const entry = statsMap[fileKey];
444
+ let stats = null;
445
+ if (entry) {
446
+ try {
447
+ const recipeStats = require('../lib/recipe/recipeStats');
448
+ const score = recipeStats.getAuthorityScore(entry, byFileEntries, {});
449
+ stats = {
450
+ authority: entry.authority ?? 0,
451
+ guardUsageCount: entry.guardUsageCount ?? 0,
452
+ humanUsageCount: entry.humanUsageCount ?? 0,
453
+ aiUsageCount: entry.aiUsageCount ?? 0,
454
+ lastUsedAt: entry.lastUsedAt || null,
455
+ authorityScore: Math.round(score * 100) / 100
456
+ };
457
+ } catch (_) {}
458
+ }
459
+ return { name: relativePath, content, stats };
406
460
  });
407
461
  }
408
462
 
@@ -732,6 +786,154 @@ function launch(projectRoot, port = 3000, options = {}) {
732
786
  }
733
787
  });
734
788
 
789
+ // API: 设置 Recipe 权威分(0~5)
790
+ app.post('/api/recipes/set-authority', async (req, res) => {
791
+ try {
792
+ const { name, authority } = req.body;
793
+ if (name == null || authority == null) {
794
+ return res.status(400).json({ error: 'name and authority (0-5) are required' });
795
+ }
796
+ const v = Math.max(0, Math.min(5, Number(authority)));
797
+ const recipeStats = require('../lib/recipe/recipeStats');
798
+ recipeStats.setAuthority(projectRoot, { recipeFilePath: name }, v);
799
+ res.json({ success: true, name, authority: v });
800
+ } catch (err) {
801
+ console.error(`[API Error]`, err);
802
+ res.status(500).json({ error: err.message });
803
+ }
804
+ });
805
+
806
+ // API: 记录 Recipe 使用(供 MCP「确认代码使用」等场景,记为 human 使用)
807
+ app.post('/api/recipes/record-usage', async (req, res) => {
808
+ try {
809
+ const { recipeFilePaths, source } = req.body;
810
+ const list = Array.isArray(recipeFilePaths) ? recipeFilePaths : (recipeFilePaths != null ? [String(recipeFilePaths)] : []);
811
+ const src = source === 'human' || source === 'guard' || source === 'ai' ? source : 'human';
812
+ if (list.length === 0) {
813
+ return res.status(400).json({ error: 'recipeFilePaths (array or single string) is required' });
814
+ }
815
+ const recipeStats = require('../lib/recipe/recipeStats');
816
+ for (const name of list) {
817
+ const fileKey = typeof name === 'string' && name.trim() ? path.basename(name.trim()) : null;
818
+ if (fileKey) recipeStats.recordRecipeUsage(projectRoot, { recipeFilePath: fileKey, source: src });
819
+ }
820
+ res.json({ success: true, count: list.length, source: src });
821
+ } catch (err) {
822
+ console.error(`[API Error]`, err);
823
+ res.status(500).json({ error: err.message });
824
+ }
825
+ });
826
+
827
+ // API: Guard 规则表
828
+ app.get('/api/guard/rules', (req, res) => {
829
+ try {
830
+ const guardRules = require('../lib/guard/guardRules');
831
+ const data = guardRules.getGuardRules(projectRoot);
832
+ res.json(data);
833
+ } catch (err) {
834
+ console.error(`[API Error]`, err);
835
+ res.status(500).json({ error: err.message });
836
+ }
837
+ });
838
+
839
+ // API: 新增或更新一条 Guard 规则(Dashboard / AI 写入规则)
840
+ app.post('/api/guard/rules', (req, res) => {
841
+ try {
842
+ const { ruleId, message, severity, pattern, languages, note, dimension } = req.body;
843
+ if (!ruleId || !message || !severity || !pattern || !languages) {
844
+ return res.status(400).json({ error: 'ruleId、message、severity、pattern、languages 为必填' });
845
+ }
846
+ const guardRules = require('../lib/guard/guardRules');
847
+ const result = guardRules.addOrUpdateRule(projectRoot, ruleId, {
848
+ message,
849
+ severity,
850
+ pattern,
851
+ languages: Array.isArray(languages) ? languages : [languages].filter(Boolean),
852
+ note,
853
+ ...(dimension === 'file' || dimension === 'target' || dimension === 'project' ? { dimension } : {})
854
+ });
855
+ res.json({ success: true, ...result });
856
+ } catch (err) {
857
+ console.error(`[API Error]`, err);
858
+ res.status(500).json({ error: err.message });
859
+ }
860
+ });
861
+
862
+ // API: 根据用户语义描述由 AI 生成一条 Guard 规则(返回表单用,用户可修改后确认写入)
863
+ app.post('/api/guard/rules/generate', async (req, res) => {
864
+ try {
865
+ const { description } = req.body;
866
+ if (!description || typeof description !== 'string' || !description.trim()) {
867
+ return res.status(400).json({ error: '请提供语义描述(description)' });
868
+ }
869
+ const ai = await AiFactory.getProvider(projectRoot);
870
+ if (!ai) {
871
+ return res.status(400).json({ error: 'AI 未配置,无法生成规则。请先在项目根配置 .env 或 boxspec.ai' });
872
+ }
873
+ const prompt = `用户希望添加一条 Guard 静态检查规则,语义描述如下:
874
+
875
+ 「${description.trim()}」
876
+
877
+ 请根据上述描述,生成一条规则。你只能回复一个合法的 JSON 对象,不要包含任何其他文字、markdown 或代码块标记。JSON 必须包含且仅包含以下字段:
878
+ - ruleId: 字符串,英文、短横线格式,如 no-main-thread-sync
879
+ - message: 字符串,违反时提示的说明(中文或英文)
880
+ - severity: 字符串,只能是 "error" 或 "warning"
881
+ - pattern: 字符串,用于对代码每一行匹配的正则表达式;在 JSON 中反斜杠需双写,如 "dispatch_sync\\\\s*\\\\("
882
+ - languages: 数组,元素为 "objc" 和/或 "swift",如 ["objc","swift"]
883
+ - note: 字符串,可选,备注说明
884
+ - dimension: 字符串,可选,审查规模。只能是 "file"、"target"、"project" 之一,或不写该字段(表示任意规模均运行)。file=仅同文件内审查,target=仅同 SPM target 内,project=仅整个项目内。根据规则语义选择合适规模。
885
+
886
+ 只输出这一份 JSON,不要解释。`;
887
+ const raw = await ai.chat(prompt);
888
+ let text = (raw && typeof raw === 'string' ? raw : String(raw)).trim();
889
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
890
+ if (jsonMatch) text = jsonMatch[0];
891
+ text = text.replace(/^```(?:json)?\s*|\s*```$/g, '').trim();
892
+ const rule = JSON.parse(text);
893
+ if (!rule.ruleId || !rule.message || !rule.pattern) {
894
+ return res.status(400).json({ error: 'AI 返回的规则缺少 ruleId、message 或 pattern' });
895
+ }
896
+ const out = {
897
+ ruleId: String(rule.ruleId).trim().replace(/\s+/g, '-'),
898
+ message: String(rule.message || '').trim(),
899
+ severity: rule.severity === 'error' ? 'error' : 'warning',
900
+ pattern: String(rule.pattern || '').trim(),
901
+ languages: Array.isArray(rule.languages) ? rule.languages.filter(l => l === 'objc' || l === 'swift') : ['objc', 'swift'],
902
+ note: rule.note != null ? String(rule.note).trim() : '',
903
+ dimension: rule.dimension === 'file' || rule.dimension === 'target' || rule.dimension === 'project' ? rule.dimension : ''
904
+ };
905
+ if (out.languages.length === 0) out.languages = ['objc', 'swift'];
906
+ res.json(out);
907
+ } catch (err) {
908
+ console.error(`[API Error]`, err);
909
+ res.status(500).json({ error: err.message || 'AI 生成规则失败' });
910
+ }
911
+ });
912
+
913
+ // API: Guard 违反记录
914
+ app.get('/api/guard/violations', (req, res) => {
915
+ try {
916
+ const guardViolations = require('../lib/guard/guardViolations');
917
+ const data = guardViolations.getGuardViolations(projectRoot);
918
+ res.json(data);
919
+ } catch (err) {
920
+ console.error(`[API Error]`, err);
921
+ res.status(500).json({ error: err.message });
922
+ }
923
+ });
924
+
925
+ // API: 清空 Guard 违反记录
926
+ app.post('/api/guard/violations/clear', (req, res) => {
927
+ try {
928
+ const guardViolations = require('../lib/guard/guardViolations');
929
+ guardViolations.clearRuns(projectRoot);
930
+ res.json({ success: true });
931
+ } catch (err) {
932
+ console.error(`[API Error]`, err);
933
+ res.status(500).json({ error: err.message });
934
+ }
935
+ });
936
+
735
937
  // API: 获取 SPM Targets
736
938
  app.get('/api/spm/targets', async (req, res) => {
737
939
  try {
@@ -787,10 +989,20 @@ function launch(projectRoot, port = 3000, options = {}) {
787
989
  }
788
990
  });
789
991
 
790
- // API: 获取 Target 将要扫描的文件列表(不调用 AI)
992
+ // API: 获取 Target 将要扫描的文件列表(不调用 AI)。支持 body.target 或 body.targetName(按名称查 target
791
993
  app.post('/api/spm/target-files', async (req, res) => {
792
994
  try {
793
- const { target } = req.body;
995
+ let target = req.body?.target;
996
+ if (!target && req.body?.targetName) {
997
+ const targets = await targetScanner.listAllTargets(projectRoot);
998
+ target = targets.find(t => t.name === req.body.targetName);
999
+ if (!target) {
1000
+ return res.status(404).json({ error: `未找到 Target: ${req.body.targetName}` });
1001
+ }
1002
+ }
1003
+ if (!target) {
1004
+ return res.status(400).json({ error: '需要 body.target 或 body.targetName' });
1005
+ }
794
1006
  const files = await targetScanner.getTargetFilesContent(target);
795
1007
  const scannedFiles = files.map(f => ({
796
1008
  name: f.name,
@@ -842,6 +1054,23 @@ function launch(projectRoot, port = 3000, options = {}) {
842
1054
  }
843
1055
  });
844
1056
 
1057
+ // API: 追加候选(供 Cursor/MCP 批量扫描:Cursor AI 提取后提交,无需项目内 AI)
1058
+ app.post('/api/candidates/append', async (req, res) => {
1059
+ try {
1060
+ const { targetName, items, source, expiresInHours } = req.body;
1061
+ if (!targetName || !Array.isArray(items) || items.length === 0) {
1062
+ return res.status(400).json({ error: '需要 targetName 与 items(数组,至少一条)' });
1063
+ }
1064
+ const safeSource = (source && typeof source === 'string') ? source : 'cursor-scan';
1065
+ const hours = typeof expiresInHours === 'number' ? expiresInHours : 24;
1066
+ await candidateService.appendCandidates(projectRoot, String(targetName), items, safeSource, hours);
1067
+ res.json({ ok: true, count: items.length, targetName: String(targetName) });
1068
+ } catch (err) {
1069
+ console.error(`[API Error]`, err);
1070
+ res.status(500).json({ error: err.message });
1071
+ }
1072
+ });
1073
+
845
1074
  // API: 删除候选内容
846
1075
  app.post('/api/candidates/delete', async (req, res) => {
847
1076
  try {
@@ -0,0 +1 @@
1
+ /*! tailwindcss v4.1.18 | MIT License | https://tailwindcss.com */@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-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking: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-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;--tw-duration:initial;--tw-ease: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-red-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-200:oklch(88.5% .062 18.334);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-red-800:oklch(44.4% .177 26.899);--color-orange-50:oklch(98% .016 73.684);--color-orange-100:oklch(95.4% .038 75.164);--color-orange-500:oklch(70.5% .213 47.604);--color-orange-600:oklch(64.6% .222 41.116);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-100:oklch(96.2% .059 95.617);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-amber-600:oklch(66.6% .179 58.318);--color-amber-700:oklch(55.5% .163 48.998);--color-emerald-50:oklch(97.9% .021 166.113);--color-emerald-100:oklch(95% .052 163.051);--color-emerald-600:oklch(59.6% .145 163.225);--color-cyan-50:oklch(98.4% .019 200.873);--color-cyan-100:oklch(95.6% .045 203.388);--color-cyan-600:oklch(60.9% .126 221.723);--color-blue-50:oklch(97% .014 254.604);--color-blue-100:oklch(93.2% .032 255.585);--color-blue-200:oklch(88.2% .059 254.128);--color-blue-300:oklch(80.9% .105 251.813);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-700:oklch(48.8% .243 264.376);--color-indigo-50:oklch(96.2% .018 272.314);--color-indigo-100:oklch(93% .034 272.788);--color-indigo-600:oklch(51.1% .262 276.966);--color-purple-50:oklch(97.7% .014 308.299);--color-purple-100:oklch(94.6% .033 307.174);--color-purple-600:oklch(55.8% .288 302.321);--color-pink-50:oklch(97.1% .014 343.198);--color-pink-100:oklch(94.8% .028 342.258);--color-pink-600:oklch(59.2% .249 .584);--color-slate-50:oklch(98.4% .003 247.858);--color-slate-100:oklch(96.8% .007 247.896);--color-slate-200:oklch(92.9% .013 255.508);--color-slate-300:oklch(86.9% .022 252.894);--color-slate-400:oklch(70.4% .04 256.788);--color-slate-500:oklch(55.4% .046 257.417);--color-slate-600:oklch(44.6% .043 257.281);--color-slate-700:oklch(37.2% .044 257.287);--color-slate-800:oklch(27.9% .041 260.031);--color-slate-900:oklch(20.8% .042 265.755);--color-white:#fff;--spacing:.25rem;--container-md:28rem;--container-lg:32rem;--container-2xl:42rem;--container-3xl:48rem;--container-6xl:72rem;--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: 1.5 ;--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;--tracking-wider:.05em;--tracking-widest:.1em;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--ease-out:cubic-bezier(0,0,.2,1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--blur-sm:8px;--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)}}@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%;-moz-tab-size:4;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;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]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance: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{.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.inset-0{inset:calc(var(--spacing)*0)}.top-0\.5{top:calc(var(--spacing)*.5)}.top-1\/2{top:50%}.top-4{top:calc(var(--spacing)*4)}.top-full{top:100%}.right-0{right:calc(var(--spacing)*0)}.right-0\.5{right:calc(var(--spacing)*.5)}.right-4{right:calc(var(--spacing)*4)}.left-0\.5{left:calc(var(--spacing)*.5)}.left-3{left:calc(var(--spacing)*3)}.z-10{z-index:10}.z-50{z-index:50}.m-auto{margin:auto}.mx-2{margin-inline:calc(var(--spacing)*2)}.mx-auto{margin-inline:auto}.my-3{margin-block:calc(var(--spacing)*3)}.mt-0\.5{margin-top:calc(var(--spacing)*.5)}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-1\.5{margin-top:calc(var(--spacing)*1.5)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-3{margin-top:calc(var(--spacing)*3)}.mt-4{margin-top:calc(var(--spacing)*4)}.mt-6{margin-top:calc(var(--spacing)*6)}.mr-4{margin-right:calc(var(--spacing)*4)}.mb-0\.5{margin-bottom:calc(var(--spacing)*.5)}.mb-1{margin-bottom:calc(var(--spacing)*1)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-6{margin-bottom:calc(var(--spacing)*6)}.mb-8{margin-bottom:calc(var(--spacing)*8)}.ml-1{margin-left:calc(var(--spacing)*1)}.ml-1\.5{margin-left:calc(var(--spacing)*1.5)}.ml-2{margin-left:calc(var(--spacing)*2)}.ml-4{margin-left:calc(var(--spacing)*4)}.ml-auto{margin-left:auto}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.line-clamp-6{-webkit-line-clamp:6;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.flex{display:flex}.grid{display:grid}.h-1\.5{height:calc(var(--spacing)*1.5)}.h-2\.5{height:calc(var(--spacing)*2.5)}.h-3{height:calc(var(--spacing)*3)}.h-4{height:calc(var(--spacing)*4)}.h-8{height:calc(var(--spacing)*8)}.h-10{height:calc(var(--spacing)*10)}.h-16{height:calc(var(--spacing)*16)}.h-64{height:calc(var(--spacing)*64)}.h-\[85vh\]{height:85vh}.h-full{height:100%}.h-screen{height:100vh}.max-h-32{max-height:calc(var(--spacing)*32)}.max-h-60{max-height:calc(var(--spacing)*60)}.max-h-\[85vh\]{max-height:85vh}.max-h-\[90vh\]{max-height:90vh}.max-h-\[280px\]{max-height:280px}.min-h-0{min-height:calc(var(--spacing)*0)}.min-h-\[80px\]{min-height:80px}.min-h-\[100px\]{min-height:100px}.min-h-\[320px\]{min-height:320px}.min-h-\[400px\]{min-height:400px}.min-h-\[480px\]{min-height:480px}.w-1\.5{width:calc(var(--spacing)*1.5)}.w-3{width:calc(var(--spacing)*3)}.w-8{width:calc(var(--spacing)*8)}.w-10{width:calc(var(--spacing)*10)}.w-16{width:calc(var(--spacing)*16)}.w-64{width:calc(var(--spacing)*64)}.w-72{width:calc(var(--spacing)*72)}.w-80{width:calc(var(--spacing)*80)}.w-96{width:calc(var(--spacing)*96)}.w-\[512px\]{width:512px}.w-fit{width:fit-content}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-6xl{max-width:var(--container-6xl)}.max-w-\[80\%\]{max-width:80%}.max-w-\[85\%\]{max-width:85%}.max-w-\[1400px\]{max-width:1400px}.max-w-lg{max-width:var(--container-lg)}.max-w-md{max-width:var(--container-md)}.max-w-none{max-width:none}.min-w-\[32px\]{min-width:32px}.min-w-\[200px\]{min-width:200px}.min-w-full{min-width:100%}.flex-1{flex:1}.shrink-0{flex-shrink:0}.-translate-y-1\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.scale-105{--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x)var(--tw-scale-y)}.rotate-180{rotate:180deg}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.resize-none{resize:none}.resize-y{resize:vertical}.list-inside{list-style-position:inside}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-0\.5{gap:calc(var(--spacing)*.5)}.gap-1{gap:calc(var(--spacing)*1)}.gap-1\.5{gap:calc(var(--spacing)*1.5)}.gap-2{gap:calc(var(--spacing)*2)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}.gap-6{gap:calc(var(--spacing)*6)}.gap-8{gap:calc(var(--spacing)*8)}:where(.space-y-0\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*.5)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*.5)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*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-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*4)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*6)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*6)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-8>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*8)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*8)*calc(1 - var(--tw-space-y-reverse)))}.gap-x-8{column-gap:calc(var(--spacing)*8)}.gap-y-4{row-gap:calc(var(--spacing)*4)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-4{border-style:var(--tw-border-style);border-width:4px}.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-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-l-4{border-left-style:var(--tw-border-style);border-left-width:4px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-none{--tw-border-style:none;border-style:none}.border-amber-100{border-color:var(--color-amber-100)}.border-amber-200{border-color:var(--color-amber-200)}.border-blue-100{border-color:var(--color-blue-100)}.border-blue-200{border-color:var(--color-blue-200)}.border-blue-600{border-color:var(--color-blue-600)}.border-cyan-100{border-color:var(--color-cyan-100)}.border-emerald-100{border-color:var(--color-emerald-100)}.border-indigo-100{border-color:var(--color-indigo-100)}.border-orange-100{border-color:var(--color-orange-100)}.border-pink-100{border-color:var(--color-pink-100)}.border-purple-100{border-color:var(--color-purple-100)}.border-red-200{border-color:var(--color-red-200)}.border-slate-100{border-color:var(--color-slate-100)}.border-slate-200{border-color:var(--color-slate-200)}.border-transparent{border-color:#0000}.border-t-blue-600{border-top-color:var(--color-blue-600)}.border-t-transparent{border-top-color:#0000}.bg-amber-50{background-color:var(--color-amber-50)}.bg-amber-100{background-color:var(--color-amber-100)}.bg-amber-600{background-color:var(--color-amber-600)}.bg-blue-50{background-color:var(--color-blue-50)}.bg-blue-100{background-color:var(--color-blue-100)}.bg-blue-600{background-color:var(--color-blue-600)}.bg-cyan-50{background-color:var(--color-cyan-50)}.bg-emerald-50{background-color:var(--color-emerald-50)}.bg-indigo-50{background-color:var(--color-indigo-50)}.bg-orange-50{background-color:var(--color-orange-50)}.bg-pink-50{background-color:var(--color-pink-50)}.bg-purple-50{background-color:var(--color-purple-50)}.bg-red-50{background-color:var(--color-red-50)}.bg-red-100{background-color:var(--color-red-100)}.bg-slate-50{background-color:var(--color-slate-50)}.bg-slate-50\/50{background-color:#f8fafc80}@supports (color:color-mix(in lab,red,red)){.bg-slate-50\/50{background-color:color-mix(in oklab,var(--color-slate-50)50%,transparent)}}.bg-slate-50\/80{background-color:#f8fafccc}@supports (color:color-mix(in lab,red,red)){.bg-slate-50\/80{background-color:color-mix(in oklab,var(--color-slate-50)80%,transparent)}}.bg-slate-100{background-color:var(--color-slate-100)}.bg-slate-200{background-color:var(--color-slate-200)}.bg-slate-300{background-color:var(--color-slate-300)}.bg-slate-600{background-color:var(--color-slate-600)}.bg-slate-900{background-color:var(--color-slate-900)}.bg-slate-900\/50{background-color:#0f172b80}@supports (color:color-mix(in lab,red,red)){.bg-slate-900\/50{background-color:color-mix(in oklab,var(--color-slate-900)50%,transparent)}}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.bg-white\/90{background-color:#ffffffe6}@supports (color:color-mix(in lab,red,red)){.bg-white\/90{background-color:color-mix(in oklab,var(--color-white)90%,transparent)}}.p-1{padding:calc(var(--spacing)*1)}.p-1\.5{padding:calc(var(--spacing)*1.5)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.p-5{padding:calc(var(--spacing)*5)}.p-6{padding:calc(var(--spacing)*6)}.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-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.px-5{padding-inline:calc(var(--spacing)*5)}.px-6{padding-inline:calc(var(--spacing)*6)}.px-8{padding-inline:calc(var(--spacing)*8)}.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-2\.5{padding-block:calc(var(--spacing)*2.5)}.py-3{padding-block:calc(var(--spacing)*3)}.py-4{padding-block:calc(var(--spacing)*4)}.py-8{padding-block:calc(var(--spacing)*8)}.py-12{padding-block:calc(var(--spacing)*12)}.pr-1{padding-right:calc(var(--spacing)*1)}.pr-2{padding-right:calc(var(--spacing)*2)}.pr-4{padding-right:calc(var(--spacing)*4)}.pr-12{padding-right:calc(var(--spacing)*12)}.pb-2{padding-bottom:calc(var(--spacing)*2)}.pb-3{padding-bottom:calc(var(--spacing)*3)}.pl-0{padding-left:calc(var(--spacing)*0)}.pl-3{padding-left:calc(var(--spacing)*3)}.pl-4{padding-left:calc(var(--spacing)*4)}.pl-6{padding-left:calc(var(--spacing)*6)}.pl-10{padding-left:calc(var(--spacing)*10)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.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-\[8px\]{font-size:8px}.text-\[9px\]{font-size:9px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.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)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-amber-500{color:var(--color-amber-500)}.text-amber-600{color:var(--color-amber-600)}.text-amber-700{color:var(--color-amber-700)}.text-blue-400{color:var(--color-blue-400)}.text-blue-500{color:var(--color-blue-500)}.text-blue-600{color:var(--color-blue-600)}.text-blue-700{color:var(--color-blue-700)}.text-cyan-600{color:var(--color-cyan-600)}.text-emerald-600{color:var(--color-emerald-600)}.text-indigo-600{color:var(--color-indigo-600)}.text-orange-500{color:var(--color-orange-500)}.text-orange-600{color:var(--color-orange-600)}.text-pink-600{color:var(--color-pink-600)}.text-purple-600{color:var(--color-purple-600)}.text-red-500{color:var(--color-red-500)}.text-red-600{color:var(--color-red-600)}.text-red-700{color:var(--color-red-700)}.text-red-800{color:var(--color-red-800)}.text-slate-100{color:var(--color-slate-100)}.text-slate-300{color:var(--color-slate-300)}.text-slate-400{color:var(--color-slate-400)}.text-slate-500{color:var(--color-slate-500)}.text-slate-600{color:var(--color-slate-600)}.text-slate-700{color:var(--color-slate-700)}.text-slate-800{color:var(--color-slate-800)}.text-slate-900{color:var(--color-slate-900)}.text-white{color:var(--color-white)}.uppercase{text-transform:uppercase}.italic{font-style:italic}.opacity-0{opacity:0}.opacity-20{opacity:.2}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-80{opacity:.8}.opacity-90{opacity:.9}.opacity-100{opacity:1}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px 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-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)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + 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)}.ring-blue-200{--tw-ring-color:var(--color-blue-200)}.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-\[2px\]{--tw-backdrop-blur:blur(2px);-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-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-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))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-shadow{transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-500{--tw-duration:.5s;transition-duration:.5s}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.outline-none{--tw-outline-style:none;outline-style:none}@media(hover:hover){.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.first\:mt-0:first-child{margin-top:calc(var(--spacing)*0)}.last\:mb-0:last-child{margin-bottom:calc(var(--spacing)*0)}.last\:border-0:last-child{border-style:var(--tw-border-style);border-width:0}.last\:pb-0:last-child{padding-bottom:calc(var(--spacing)*0)}@media(hover:hover){.hover\:border-blue-300:hover{border-color:var(--color-blue-300)}.hover\:border-slate-200:hover{border-color:var(--color-slate-200)}.hover\:bg-amber-700:hover{background-color:var(--color-amber-700)}.hover\:bg-blue-50:hover{background-color:var(--color-blue-50)}.hover\:bg-blue-50\/50:hover{background-color:#eff6ff80}@supports (color:color-mix(in lab,red,red)){.hover\:bg-blue-50\/50:hover{background-color:color-mix(in oklab,var(--color-blue-50)50%,transparent)}}.hover\:bg-blue-100:hover{background-color:var(--color-blue-100)}.hover\:bg-blue-700:hover{background-color:var(--color-blue-700)}.hover\:bg-orange-50:hover{background-color:var(--color-orange-50)}.hover\:bg-red-50:hover{background-color:var(--color-red-50)}.hover\:bg-red-200:hover{background-color:var(--color-red-200)}.hover\:bg-slate-50:hover{background-color:var(--color-slate-50)}.hover\:bg-slate-100:hover{background-color:var(--color-slate-100)}.hover\:bg-slate-200:hover{background-color:var(--color-slate-200)}.hover\:bg-slate-700:hover{background-color:var(--color-slate-700)}.hover\:bg-slate-800:hover{background-color:var(--color-slate-800)}.hover\:bg-white:hover{background-color:var(--color-white)}.hover\:text-amber-400:hover{color:var(--color-amber-400)}.hover\:text-blue-600:hover{color:var(--color-blue-600)}.hover\:text-blue-700:hover{color:var(--color-blue-700)}.hover\:text-orange-600:hover{color:var(--color-orange-600)}.hover\:text-red-500:hover{color:var(--color-red-500)}.hover\:text-red-600:hover{color:var(--color-red-600)}.hover\:text-red-700:hover{color:var(--color-red-700)}.hover\:text-slate-600:hover{color:var(--color-slate-600)}.hover\:text-slate-700:hover{color:var(--color-slate-700)}.hover\:text-slate-900:hover{color:var(--color-slate-900)}.hover\:underline:hover{text-decoration-line:underline}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px 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)}}.focus\:border-blue-500:focus{border-color:var(--color-blue-500)}.focus\:border-transparent:focus{border-color:#0000}.focus\:ring-2:focus{--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\:ring-blue-500:focus{--tw-ring-color:var(--color-blue-500)}.focus\:ring-blue-500\/10:focus{--tw-ring-color:#3080ff1a}@supports (color:color-mix(in lab,red,red)){.focus\:ring-blue-500\/10:focus{--tw-ring-color:color-mix(in oklab,var(--color-blue-500)10%,transparent)}}.focus\:ring-blue-500\/20:focus{--tw-ring-color:#3080ff33}@supports (color:color-mix(in lab,red,red)){.focus\:ring-blue-500\/20:focus{--tw-ring-color:color-mix(in oklab,var(--color-blue-500)20%,transparent)}}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.active\:scale-95:active{--tw-scale-x:95%;--tw-scale-y:95%;--tw-scale-z:95%;scale:var(--tw-scale-x)var(--tw-scale-y)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:bg-slate-300:disabled{background-color:var(--color-slate-300)}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-50:disabled{opacity:.5}@media(min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(min-width:64rem){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}}.no-scrollbar::-webkit-scrollbar{display:none}.no-scrollbar{-ms-overflow-style:none;scrollbar-width:none}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@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-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{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-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}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}