@akairos/typeset-studio 0.2.5 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +67 -17
  2. package/dist/assets/index-CVzTRIjV.js +317 -0
  3. package/dist/assets/index-JCv_QmUn.css +1 -0
  4. package/dist/index.html +2 -2
  5. package/dist/lib/adapter.js +1 -1
  6. package/dist/lib/{canvas-renderer-cxnar75n.js → canvas-renderer-wce6xgb2.js} +1 -1
  7. package/dist/lib/{index-zdhqg5f0.js → index-697fkv5y.js} +294 -57
  8. package/dist/lib/{index-h2580xma.js → index-dkwbnaj7.js} +3 -3
  9. package/dist/lib/{index-t9w3p2p9.js → index-qszn98j2.js} +73 -5
  10. package/dist/lib/index.js +3 -3
  11. package/dist/lib/{render-xxd348m1.js → render-064weavx.js} +2 -2
  12. package/package.json +3 -3
  13. package/src/adapter.test.ts +3 -1
  14. package/src/adapter.ts +1 -1
  15. package/src/core/canvas-draw.ts +53 -43
  16. package/src/core/canvas-presets.ts +37 -0
  17. package/src/core/canvas-renderer.test.ts +60 -1
  18. package/src/core/canvas-style.ts +149 -0
  19. package/src/core/engine.ts +2 -0
  20. package/src/core/geometry.ts +4 -3
  21. package/src/core/types.ts +30 -3
  22. package/src/index.ts +4 -0
  23. package/src/server/index.ts +1 -1
  24. package/src/server/routes/slots.test.ts +24 -0
  25. package/src/server/routes/templates.ts +17 -2
  26. package/src/web/App.tsx +27 -7
  27. package/src/web/components/CanvasStyleInspector.tsx +95 -0
  28. package/src/web/components/Header.tsx +25 -10
  29. package/src/web/components/ParamsInspector.tsx +6 -1
  30. package/src/web/components/SaveTemplateModal.tsx +106 -0
  31. package/src/web/components/TemplateDrawer.tsx +323 -0
  32. package/src/web/hooks/useTypeset.ts +3 -1
  33. package/dist/assets/index-3Ko1UCBU.css +0 -1
  34. package/dist/assets/index-De-aHsvp.js +0 -317
  35. package/src/web/components/TemplateModal.tsx +0 -393
@@ -0,0 +1,323 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { Check, Trash2, X, Sparkles, Star } from "lucide-react";
3
+ import { TemplateSlot, TypesetConfig } from "../../core/types";
4
+
5
+ interface TemplateDrawerProps {
6
+ isOpen: boolean;
7
+ onClose: () => void;
8
+ refreshKey: number;
9
+ onApplyTemplate: (tpl: TypesetConfig) => void;
10
+ adminKey: string;
11
+ }
12
+
13
+ interface SlotsResponse {
14
+ ok: boolean;
15
+ account: string;
16
+ message?: string;
17
+ error?: string;
18
+ slots: {
19
+ article_cover?: { id: string; template?: TypesetConfig };
20
+ newspic_cover?: { id: string; template?: TypesetConfig };
21
+ newspic_content?: { id: string; template?: TypesetConfig };
22
+ };
23
+ }
24
+
25
+ export const TemplateDrawer: React.FC<TemplateDrawerProps> = ({
26
+ isOpen,
27
+ onClose,
28
+ refreshKey,
29
+ onApplyTemplate,
30
+ adminKey,
31
+ }) => {
32
+ const [templates, setTemplates] = useState<TypesetConfig[]>([]);
33
+ const [slotsData, setSlotsData] = useState<SlotsResponse["slots"]>({});
34
+ const [account, setAccount] = useState<string>("default");
35
+ const [activeSlotFilter, setActiveSlotFilter] = useState<"all" | TemplateSlot>("all");
36
+ const [isLoading, setIsLoading] = useState(false);
37
+ const [error, setError] = useState<string | null>(null);
38
+
39
+ const fetchSlots = async (acc = account) => {
40
+ try {
41
+ const res = await fetch(`/api/templates/slots?account=${encodeURIComponent(acc)}`, { headers: { "x-admin-key": adminKey } });
42
+ const data: SlotsResponse = await res.json();
43
+ if (!res.ok || !data.ok) throw new Error(data.message || data.error || "获取槽位失败");
44
+ if (data.ok && data.slots) {
45
+ setSlotsData(data.slots);
46
+ }
47
+ } catch (err) {
48
+ setError(err instanceof Error ? err.message : "模板请求失败");
49
+ }
50
+ };
51
+
52
+ const handleSetSlotDefault = async (slot: TemplateSlot, templateId: string) => {
53
+ try {
54
+ const res = await fetch("/api/templates/slots", {
55
+ method: "POST",
56
+ headers: {
57
+ "Content-Type": "application/json",
58
+ ...(adminKey ? { "x-admin-key": adminKey } : {}),
59
+ },
60
+ body: JSON.stringify({ slot, templateId, account }),
61
+ });
62
+ const data = await res.json();
63
+ if (!res.ok || !data.ok) throw new Error(data.message || data.error || "设置默认模板失败");
64
+ if (data.ok) {
65
+ await fetchSlots(account);
66
+ }
67
+ } catch (err) {
68
+ setError(err instanceof Error ? err.message : "模板请求失败");
69
+ }
70
+ };
71
+
72
+ const fetchTemplates = async () => {
73
+ setIsLoading(true);
74
+ setError(null);
75
+ try {
76
+ const res = await fetch("/api/templates", {
77
+ headers: adminKey ? { "x-admin-key": adminKey } : {},
78
+ });
79
+ const data = await res.json();
80
+ if (res.ok && data.ok && Array.isArray(data.templates)) {
81
+ setTemplates(data.templates);
82
+ } else {
83
+ setError(data.message || "获取模板失败,请检查密钥");
84
+ }
85
+ } catch {
86
+ setError("连接后端服务失败");
87
+ } finally {
88
+ setIsLoading(false);
89
+ }
90
+ };
91
+
92
+ useEffect(() => {
93
+ if (isOpen) {
94
+ fetchTemplates();
95
+ fetchSlots(account);
96
+ }
97
+ }, [isOpen, account, adminKey, refreshKey]);
98
+
99
+ useEffect(() => {
100
+ if (!isOpen) return;
101
+ const handleEscape = (event: KeyboardEvent) => {
102
+ if (event.key === "Escape" && !document.querySelector("dialog[open]")) onClose();
103
+ };
104
+ window.addEventListener("keydown", handleEscape);
105
+ return () => window.removeEventListener("keydown", handleEscape);
106
+ }, [isOpen, onClose]);
107
+
108
+ const handleDelete = async (id: string) => {
109
+ try {
110
+ const res = await fetch(`/api/templates/${encodeURIComponent(id)}`, {
111
+ method: "DELETE",
112
+ headers: adminKey ? { "x-admin-key": adminKey } : {},
113
+ });
114
+ const data = await res.json();
115
+ if (!res.ok || !data.ok) throw new Error(data.message || data.error || "删除模板失败");
116
+ setTemplates((prev) => prev.filter((t) => t.id !== id));
117
+ await fetchSlots(account);
118
+ } catch (err) {
119
+ setError(err instanceof Error ? err.message : "模板请求失败");
120
+ }
121
+ };
122
+
123
+ if (!isOpen) return null;
124
+
125
+ const filteredTemplates = templates.filter((tpl) => {
126
+ if (activeSlotFilter === "all") return true;
127
+ return tpl.slot === activeSlotFilter;
128
+ });
129
+
130
+ return (
131
+ <aside aria-label="模板库" className="absolute inset-y-0 right-0 z-40 w-80 max-w-full bg-white dark:bg-[#0f0f0f] border-l border-[#eaeaea] dark:border-[#262626] flex flex-col shadow-xl overflow-hidden">
132
+ {/* Header */}
133
+ <div className="flex items-center justify-between px-5 py-3.5 border-b border-[#eaeaea] dark:border-[#222222]">
134
+ <div className="flex items-center gap-2 font-semibold text-sm text-[#171717] dark:text-white">
135
+ <Sparkles className="w-4 h-4 text-emerald-500 dark:text-emerald-400" />
136
+ <span>模板库</span>
137
+ </div>
138
+ <button
139
+ onClick={onClose}
140
+ aria-label="关闭模板库"
141
+ className="text-[#888888] hover:text-black dark:hover:text-white p-1 rounded-md hover:bg-[#f5f5f5] dark:hover:bg-[#1a1a1a]"
142
+ >
143
+ <X className="w-4 h-4" />
144
+ </button>
145
+ </div>
146
+
147
+ {/* Content */}
148
+ <div className="p-3 flex-1 min-h-0 flex flex-col gap-4 overflow-y-auto">
149
+ {error && (
150
+ <div role="alert" className="text-xs text-red-500 bg-red-500/10 p-2 rounded border border-red-500/20">{error}</div>
151
+ )}
152
+
153
+ {/* Account and Slot Filters */}
154
+ <div className="flex flex-col gap-2">
155
+ <div className="flex items-center justify-between text-xs">
156
+ <div className="flex items-center gap-1.5 font-medium text-[#666666] dark:text-[#888888]">
157
+ <span>槽位账号:</span>
158
+ <select
159
+ value={account}
160
+ onChange={(e) => setAccount(e.target.value)}
161
+ className="bg-white dark:bg-[#141414] border border-[#d4d4d4] dark:border-[#2a2a2a] rounded px-2 py-0.5 text-xs text-black dark:text-white focus:outline-none font-mono"
162
+ >
163
+ <option value="default">default (主账号)</option>
164
+ <option value="ancientone">ancientone</option>
165
+ </select>
166
+ </div>
167
+ </div>
168
+
169
+ {/* Slot Category Tabs */}
170
+ <div className="flex flex-wrap items-center gap-1 text-xs">
171
+ <button
172
+ onClick={() => setActiveSlotFilter("all")}
173
+ className={`px-2.5 py-1 rounded text-xs transition-colors font-medium ${
174
+ activeSlotFilter === "all"
175
+ ? "bg-black text-white dark:bg-white dark:text-black"
176
+ : "text-[#666666] dark:text-[#888888] hover:bg-[#f5f5f5] dark:hover:bg-[#1c1c1c]"
177
+ }`}
178
+ >
179
+ 全部
180
+ </button>
181
+ <button
182
+ onClick={() => setActiveSlotFilter("article_cover")}
183
+ className={`px-2.5 py-1 rounded text-xs transition-colors font-medium ${
184
+ activeSlotFilter === "article_cover"
185
+ ? "bg-black text-white dark:bg-white dark:text-black"
186
+ : "text-[#666666] dark:text-[#888888] hover:bg-[#f5f5f5] dark:hover:bg-[#1c1c1c]"
187
+ }`}
188
+ >
189
+ 文章封面
190
+ </button>
191
+ <button
192
+ onClick={() => setActiveSlotFilter("newspic_cover")}
193
+ className={`px-2.5 py-1 rounded text-xs transition-colors font-medium ${
194
+ activeSlotFilter === "newspic_cover"
195
+ ? "bg-black text-white dark:bg-white dark:text-black"
196
+ : "text-[#666666] dark:text-[#888888] hover:bg-[#f5f5f5] dark:hover:bg-[#1c1c1c]"
197
+ }`}
198
+ >
199
+ 贴图封面
200
+ </button>
201
+ <button
202
+ onClick={() => setActiveSlotFilter("newspic_content")}
203
+ className={`px-2.5 py-1 rounded text-xs transition-colors font-medium ${
204
+ activeSlotFilter === "newspic_content"
205
+ ? "bg-black text-white dark:bg-white dark:text-black"
206
+ : "text-[#666666] dark:text-[#888888] hover:bg-[#f5f5f5] dark:hover:bg-[#1c1c1c]"
207
+ }`}
208
+ >
209
+ 贴图内容
210
+ </button>
211
+ </div>
212
+ </div>
213
+
214
+ {/* Template List */}
215
+ <div className="flex flex-col gap-2">
216
+ {isLoading ? (
217
+ <div className="text-center py-8 text-xs text-[#888888] dark:text-[#555555]">正在加载模板...</div>
218
+ ) : filteredTemplates.length === 0 ? (
219
+ <div className="text-center py-8 text-xs text-[#888888] dark:text-[#555555]">暂无匹配模板</div>
220
+ ) : (
221
+ filteredTemplates.map((tpl) => {
222
+ const isArticleCoverDefault = slotsData.article_cover?.id === tpl.id;
223
+ const isNewspicCoverDefault = slotsData.newspic_cover?.id === tpl.id;
224
+ const isNewspicContentDefault = slotsData.newspic_content?.id === tpl.id;
225
+
226
+ return (
227
+ <div
228
+ key={tpl.id}
229
+ className="flex flex-col justify-between p-3 rounded-lg bg-[#fafafa] dark:bg-[#141414] border border-[#eaeaea] dark:border-[#222222] hover:border-[#cccccc] dark:hover:border-[#333333] transition-all gap-2 group"
230
+ >
231
+ <div className="flex items-center gap-3">
232
+ <div
233
+ className="w-5 h-5 rounded border border-[#d4d4d4] dark:border-[#333333] shrink-0"
234
+ style={{ backgroundColor: tpl.theme.bgColor }}
235
+ />
236
+ <div>
237
+ <div className="flex items-center gap-1.5 flex-wrap">
238
+ <span className="text-xs font-semibold text-[#171717] dark:text-white">{tpl.name}</span>
239
+ {tpl.slot && (
240
+ <span className="text-[10px] px-1.5 py-0.2 rounded bg-blue-500/10 text-blue-600 dark:text-blue-400 border border-blue-500/20 font-medium">
241
+ {tpl.slot === "article_cover"
242
+ ? "文章封面"
243
+ : tpl.slot === "newspic_cover"
244
+ ? "贴图封面"
245
+ : "贴图内容"}
246
+ </span>
247
+ )}
248
+ {isArticleCoverDefault && (
249
+ <span className="text-[10px] px-1.5 py-0.2 rounded bg-amber-500/10 text-amber-600 dark:text-amber-400 border border-amber-500/20 font-medium flex items-center gap-0.5">
250
+ <Star className="w-2.5 h-2.5 fill-amber-500" /> 文章封面默认
251
+ </span>
252
+ )}
253
+ {isNewspicCoverDefault && (
254
+ <span className="text-[10px] px-1.5 py-0.2 rounded bg-amber-500/10 text-amber-600 dark:text-amber-400 border border-amber-500/20 font-medium flex items-center gap-0.5">
255
+ <Star className="w-2.5 h-2.5 fill-amber-500" /> 贴图封面默认
256
+ </span>
257
+ )}
258
+ {isNewspicContentDefault && (
259
+ <span className="text-[10px] px-1.5 py-0.2 rounded bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border border-emerald-500/20 font-medium flex items-center gap-0.5">
260
+ <Star className="w-2.5 h-2.5 fill-emerald-500" /> 贴图内容默认
261
+ </span>
262
+ )}
263
+ </div>
264
+ <div className="text-[10px] text-[#888888] dark:text-[#666666] font-mono mt-0.5">
265
+ {tpl.aspectRatio} • 字号: {tpl.algorithm.baseFontSize}px
266
+ {tpl.images && tpl.images.length > 0 && ` • 配图: ${tpl.images.length}个`}
267
+ </div>
268
+ </div>
269
+ </div>
270
+
271
+ <div className="flex flex-wrap items-center gap-2">
272
+ {/* Set as slot default selector */}
273
+ {tpl.id && (
274
+ <select
275
+ value=""
276
+ onChange={(e) => {
277
+ if (e.target.value) {
278
+ handleSetSlotDefault(e.target.value as TemplateSlot, tpl.id!);
279
+ }
280
+ }}
281
+ className="text-[11px] bg-white dark:bg-[#1a1a1a] border border-[#d4d4d4] dark:border-[#333333] text-[#666666] dark:text-[#aaaaaa] rounded px-2 py-1 focus:outline-none"
282
+ title="为此账号设为某个槽位的默认模板"
283
+ >
284
+ <option value="">设为槽位默认...</option>
285
+ <option value="article_cover">设为「文章封面默认」</option>
286
+ <option value="newspic_cover">设为「贴图封面默认」</option>
287
+ <option value="newspic_content">设为「贴图内容默认」</option>
288
+ </select>
289
+ )}
290
+
291
+ <button
292
+ onClick={() => {
293
+ onApplyTemplate(tpl);
294
+ }}
295
+ className="flex items-center gap-1 px-2.5 py-1 bg-white dark:bg-[#1f1f1f] hover:bg-emerald-50 dark:hover:bg-emerald-500/20 text-[#333333] dark:text-[#cccccc] hover:text-emerald-700 dark:hover:text-emerald-400 border border-[#d4d4d4] dark:border-[#2a2a2a] hover:border-emerald-500/40 rounded text-xs transition-all font-medium"
296
+ >
297
+ <Check className="w-3 h-3" />
298
+ <span>套用</span>
299
+ </button>
300
+
301
+ {tpl.id &&
302
+ !tpl.id.startsWith("tpl_wechat_") &&
303
+ !tpl.id.startsWith("tpl_longform_") &&
304
+ !tpl.id.startsWith("tpl_square_") &&
305
+ !tpl.id.startsWith("tpl_wide_") && (
306
+ <button
307
+ onClick={() => handleDelete(tpl.id!)}
308
+ className="p-1 text-[#888888] dark:text-[#555555] hover:text-red-500 rounded hover:bg-[#f0f0f0] dark:hover:bg-[#1f1f1f]"
309
+ title="删除自定义模板"
310
+ >
311
+ <Trash2 className="w-3.5 h-3.5" />
312
+ </button>
313
+ )}
314
+ </div>
315
+ </div>
316
+ );
317
+ })
318
+ )}
319
+ </div>
320
+ </div>
321
+ </aside>
322
+ );
323
+ };
@@ -210,6 +210,7 @@ export function useTypeset(adminKey: string, isVerified: boolean | null) {
210
210
  setConfig((prev) => ({
211
211
  ...prev,
212
212
  theme: { ...prev.theme, ...partial },
213
+ ...(partial.bgColor ? { canvasStyle: { ...prev.canvasStyle, fill: { type: "solid" as const, color: partial.bgColor } } } : {}),
213
214
  }));
214
215
  };
215
216
 
@@ -358,7 +359,8 @@ export function useTypeset(adminKey: string, isVerified: boolean | null) {
358
359
  const loadTemplate = (tpl: TypesetConfig) => {
359
360
  try {
360
361
  validateTemplateAssets(tpl.images);
361
- setConfig(tpl);
362
+ // 套用后编辑的是副本,名称用于顶部状态栏和另存为模板。
363
+ setConfig({ ...tpl, name: `${tpl.name || "未命名模板"}-副本` });
362
364
  setBackgroundLayouts({});
363
365
  setImages((prev) => {
364
366
  const next = applyTemplateImages(prev, tpl.images);
@@ -1 +0,0 @@
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:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,PingFang SC,Hiragino Sans GB,Microsoft YaHei,sans-serif;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:GeistMono,JetBrains Mono,SFMono-Regular,Menlo,Monaco,Consolas,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{--bg-page: #fafafa;--bg-surface: #ffffff;--bg-surface-elevated: #f5f5f5;--border-subtle: #eaeaea;--border-strong: #d4d4d4;--text-primary: #171717;--text-secondary: #666666;--text-tertiary: #999999;--grid-line: rgba(0, 0, 0, .05);--scrollbar-thumb: #e0e0e0;--scrollbar-thumb-hover: #c0c0c0}.dark{--bg-page: #000000;--bg-surface: #0a0a0a;--bg-surface-elevated: #141414;--border-subtle: #222222;--border-strong: #333333;--text-primary: #ededed;--text-secondary: #888888;--text-tertiary: #555555;--grid-line: rgba(255, 255, 255, .03);--scrollbar-thumb: #262626;--scrollbar-thumb-hover: #3a3a3a}body{background-color:var(--bg-page);color:var(--text-primary);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,PingFang SC,Hiragino Sans GB,Microsoft YaHei,sans-serif}.visible{visibility:visible}.static{position:static}.\!fixed{position:fixed!important}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.left-0{left:0}.top-0{top:0}.z-20{z-index:20}.z-30{z-index:30}.z-50{z-index:50}.mx-0\.5{margin-left:.125rem;margin-right:.125rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-6{margin-bottom:1.5rem}.ml-1{margin-left:.25rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.block{display:block}.inline{display:inline}.flex{display:flex}.table{display:table}.grid{display:grid}.list-item{display:list-item}.hidden{display:none}.h-1\.5{height:.375rem}.h-12{height:3rem}.h-14{height:3.5rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-screen{height:100vh}.max-h-\[75vh\]{max-height:75vh}.min-h-0{min-height:0px}.w-1\.5{width:.375rem}.w-11{width:2.75rem}.w-12{width:3rem}.w-2\.5{width:.625rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-80{width:20rem}.w-\[1px\]{width:1px}.w-full{width:100%}.w-screen{width:100vw}.min-w-0{min-width:0px}.min-w-fit{min-width:-moz-fit-content;min-width:fit-content}.max-w-\[120px\]{max-width:120px}.max-w-full{max-width:100%}.max-w-md{max-width:28rem}.max-w-xl{max-width:36rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-none{resize:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-start{justify-content:flex-start}.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-2\.5{gap:.625rem}.gap-3{gap:.75rem}.gap-3\.5{gap:.875rem}.gap-4{gap:1rem}.gap-7{gap:1.75rem}.self-end{align-self:flex-end}.self-center{align-self:center}.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}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-sm{border-radius:.125rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-0{border-width:0px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-\[\#444\]{--tw-border-opacity: 1;border-color:rgb(68 68 68 / var(--tw-border-opacity, 1))}.border-\[\#bbf7d0\]{--tw-border-opacity: 1;border-color:rgb(187 247 208 / var(--tw-border-opacity, 1))}.border-\[\#ccc\]{--tw-border-opacity: 1;border-color:rgb(204 204 204 / var(--tw-border-opacity, 1))}.border-\[\#d4d4d4\]{--tw-border-opacity: 1;border-color:rgb(212 212 212 / var(--tw-border-opacity, 1))}.border-\[\#dfdfdf\]{--tw-border-opacity: 1;border-color:rgb(223 223 223 / var(--tw-border-opacity, 1))}.border-\[\#e0e0e0\]{--tw-border-opacity: 1;border-color:rgb(224 224 224 / var(--tw-border-opacity, 1))}.border-\[\#e5e5e5\]{--tw-border-opacity: 1;border-color:rgb(229 229 229 / var(--tw-border-opacity, 1))}.border-\[\#eaeaea\]{--tw-border-opacity: 1;border-color:rgb(234 234 234 / var(--tw-border-opacity, 1))}.border-\[\#eeeeee\]{--tw-border-opacity: 1;border-color:rgb(238 238 238 / var(--tw-border-opacity, 1))}.border-\[\#f0f0f0\]{--tw-border-opacity: 1;border-color:rgb(240 240 240 / var(--tw-border-opacity, 1))}.border-\[\#f5f5f5\]{--tw-border-opacity: 1;border-color:rgb(245 245 245 / var(--tw-border-opacity, 1))}.border-amber-200{--tw-border-opacity: 1;border-color:rgb(253 230 138 / var(--tw-border-opacity, 1))}.border-amber-500\/20{border-color:#f59e0b33}.border-amber-500\/50{border-color:#f59e0b80}.border-blue-500{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.border-blue-500\/20{border-color:#3b82f633}.border-emerald-200{--tw-border-opacity: 1;border-color:rgb(167 243 208 / var(--tw-border-opacity, 1))}.border-emerald-500{--tw-border-opacity: 1;border-color:rgb(16 185 129 / var(--tw-border-opacity, 1))}.border-emerald-500\/20{border-color:#10b98133}.border-transparent{border-color:transparent}.bg-\[\#09090b\]{--tw-bg-opacity: 1;background-color:rgb(9 9 11 / var(--tw-bg-opacity, 1))}.bg-\[\#0a0a0a\]{--tw-bg-opacity: 1;background-color:rgb(10 10 10 / var(--tw-bg-opacity, 1))}.bg-\[\#e0e0e0\]{--tw-bg-opacity: 1;background-color:rgb(224 224 224 / var(--tw-bg-opacity, 1))}.bg-\[\#e5e5e5\]{--tw-bg-opacity: 1;background-color:rgb(229 229 229 / var(--tw-bg-opacity, 1))}.bg-\[\#eaeaea\]{--tw-bg-opacity: 1;background-color:rgb(234 234 234 / var(--tw-bg-opacity, 1))}.bg-\[\#eeeeee\]{--tw-bg-opacity: 1;background-color:rgb(238 238 238 / var(--tw-bg-opacity, 1))}.bg-\[\#f0fdf4\]{--tw-bg-opacity: 1;background-color:rgb(240 253 244 / var(--tw-bg-opacity, 1))}.bg-\[\#f5f5f5\]{--tw-bg-opacity: 1;background-color:rgb(245 245 245 / var(--tw-bg-opacity, 1))}.bg-\[\#f9f9f9\]{--tw-bg-opacity: 1;background-color:rgb(249 249 249 / var(--tw-bg-opacity, 1))}.bg-\[\#f9fcfa\]{--tw-bg-opacity: 1;background-color:rgb(249 252 250 / var(--tw-bg-opacity, 1))}.bg-\[\#fafafa\]{--tw-bg-opacity: 1;background-color:rgb(250 250 250 / var(--tw-bg-opacity, 1))}.bg-\[\#ffffff\]{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-amber-50{--tw-bg-opacity: 1;background-color:rgb(255 251 235 / var(--tw-bg-opacity, 1))}.bg-amber-500\/10{background-color:#f59e0b1a}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.bg-black\/50{background-color:#00000080}.bg-blue-500\/10{background-color:#3b82f61a}.bg-emerald-50{--tw-bg-opacity: 1;background-color:rgb(236 253 245 / var(--tw-bg-opacity, 1))}.bg-emerald-500{--tw-bg-opacity: 1;background-color:rgb(16 185 129 / var(--tw-bg-opacity, 1))}.bg-emerald-500\/10{background-color:#10b9811a}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-white\/20{background-color:#fff3}.bg-white\/80{background-color:#fffc}.bg-white\/90{background-color:#ffffffe6}.fill-amber-500{fill:#f59e0b}.fill-emerald-500{fill:#10b981}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-3{padding:.75rem}.p-3\.5{padding:.875rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.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-3\.5{padding-left:.875rem;padding-right:.875rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.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\.5{padding-top:.875rem;padding-bottom:.875rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-1{padding-bottom:.25rem}.pb-32{padding-bottom:8rem}.pl-2{padding-left:.5rem}.pt-1{padding-top:.25rem}.pt-2{padding-top:.5rem}.text-center{text-align:center}.font-mono{font-family:GeistMono,JetBrains Mono,SFMono-Regular,Menlo,Monaco,Consolas,monospace}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-black{font-weight:900}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.italic{font-style:italic}.leading-relaxed{line-height:1.625}.tracking-tight{letter-spacing:-.025em}.text-\[\#171717\]{--tw-text-opacity: 1;color:rgb(23 23 23 / var(--tw-text-opacity, 1))}.text-\[\#333333\]{--tw-text-opacity: 1;color:rgb(51 51 51 / var(--tw-text-opacity, 1))}.text-\[\#444444\]{--tw-text-opacity: 1;color:rgb(68 68 68 / var(--tw-text-opacity, 1))}.text-\[\#555555\]{--tw-text-opacity: 1;color:rgb(85 85 85 / var(--tw-text-opacity, 1))}.text-\[\#666666\]{--tw-text-opacity: 1;color:rgb(102 102 102 / var(--tw-text-opacity, 1))}.text-\[\#777777\]{--tw-text-opacity: 1;color:rgb(119 119 119 / var(--tw-text-opacity, 1))}.text-\[\#888888\]{--tw-text-opacity: 1;color:rgb(136 136 136 / var(--tw-text-opacity, 1))}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.text-amber-500{--tw-text-opacity: 1;color:rgb(245 158 11 / var(--tw-text-opacity, 1))}.text-amber-600{--tw-text-opacity: 1;color:rgb(217 119 6 / var(--tw-text-opacity, 1))}.text-amber-700{--tw-text-opacity: 1;color:rgb(180 83 9 / var(--tw-text-opacity, 1))}.text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1))}.text-blue-500{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity, 1))}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.text-emerald-500{--tw-text-opacity: 1;color:rgb(16 185 129 / var(--tw-text-opacity, 1))}.text-emerald-600{--tw-text-opacity: 1;color:rgb(5 150 105 / var(--tw-text-opacity, 1))}.text-emerald-700{--tw-text-opacity: 1;color:rgb(4 120 87 / var(--tw-text-opacity, 1))}.text-indigo-500{--tw-text-opacity: 1;color:rgb(99 102 241 / var(--tw-text-opacity, 1))}.text-purple-600{--tw-text-opacity: 1;color:rgb(147 51 234 / var(--tw-text-opacity, 1))}.text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.underline{text-decoration-line:underline}.underline-offset-2{text-underline-offset:2px}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.placeholder-\[\#888888\]::-moz-placeholder{--tw-placeholder-opacity: 1;color:rgb(136 136 136 / var(--tw-placeholder-opacity, 1))}.placeholder-\[\#888888\]::placeholder{--tw-placeholder-opacity: 1;color:rgb(136 136 136 / var(--tw-placeholder-opacity, 1))}.placeholder-\[\#999999\]::-moz-placeholder{--tw-placeholder-opacity: 1;color:rgb(153 153 153 / var(--tw-placeholder-opacity, 1))}.placeholder-\[\#999999\]::placeholder{--tw-placeholder-opacity: 1;color:rgb(153 153 153 / var(--tw-placeholder-opacity, 1))}.accent-emerald-500{accent-color:#10b981}.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-inner{--tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / .05);--tw-shadow-colored: inset 0 2px 4px 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-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)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur{--tw-backdrop-blur: blur(8px);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-md{--tw-backdrop-blur: blur(12px);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(4px);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:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-shadow{transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-150{transition-duration:.15s}.duration-300{transition-duration:.3s}::-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)}.bg-grid{background-size:32px 32px;background-image:linear-gradient(to right,var(--grid-line) 1px,transparent 1px),linear-gradient(to bottom,var(--grid-line) 1px,transparent 1px)}input[type=range]{accent-color:currentColor}.btn-vercel{transition:all .15s ease}.btn-vercel:active{transform:scale(.98)}.selection\:bg-\[\#eaeaea\] *::-moz-selection{--tw-bg-opacity: 1;background-color:rgb(234 234 234 / var(--tw-bg-opacity, 1))}.selection\:bg-\[\#eaeaea\] *::selection{--tw-bg-opacity: 1;background-color:rgb(234 234 234 / var(--tw-bg-opacity, 1))}.selection\:text-\[\#000000\] *::-moz-selection{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1))}.selection\:text-\[\#000000\] *::selection{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1))}.selection\:bg-\[\#eaeaea\]::-moz-selection{--tw-bg-opacity: 1;background-color:rgb(234 234 234 / var(--tw-bg-opacity, 1))}.selection\:bg-\[\#eaeaea\]::selection{--tw-bg-opacity: 1;background-color:rgb(234 234 234 / var(--tw-bg-opacity, 1))}.selection\:text-\[\#000000\]::-moz-selection{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1))}.selection\:text-\[\#000000\]::selection{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1))}.hover\:scale-105:hover{--tw-scale-x: 1.05;--tw-scale-y: 1.05;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))}.hover\:border-\[\#cccccc\]:hover{--tw-border-opacity: 1;border-color:rgb(204 204 204 / var(--tw-border-opacity, 1))}.hover\:border-black:hover{--tw-border-opacity: 1;border-color:rgb(0 0 0 / var(--tw-border-opacity, 1))}.hover\:border-emerald-500\/40:hover{border-color:#10b98166}.hover\:bg-\[\#262626\]:hover{--tw-bg-opacity: 1;background-color:rgb(38 38 38 / var(--tw-bg-opacity, 1))}.hover\:bg-\[\#e8e8e8\]:hover{--tw-bg-opacity: 1;background-color:rgb(232 232 232 / var(--tw-bg-opacity, 1))}.hover\:bg-\[\#eaeaea\]:hover{--tw-bg-opacity: 1;background-color:rgb(234 234 234 / var(--tw-bg-opacity, 1))}.hover\:bg-\[\#eeeeee\]:hover{--tw-bg-opacity: 1;background-color:rgb(238 238 238 / var(--tw-bg-opacity, 1))}.hover\:bg-\[\#f0f0f0\]:hover{--tw-bg-opacity: 1;background-color:rgb(240 240 240 / var(--tw-bg-opacity, 1))}.hover\:bg-\[\#f5f5f5\]:hover{--tw-bg-opacity: 1;background-color:rgb(245 245 245 / var(--tw-bg-opacity, 1))}.hover\:bg-blue-500\/20:hover{background-color:#3b82f633}.hover\:bg-emerald-50:hover{--tw-bg-opacity: 1;background-color:rgb(236 253 245 / var(--tw-bg-opacity, 1))}.hover\:text-\[\#171717\]:hover{--tw-text-opacity: 1;color:rgb(23 23 23 / var(--tw-text-opacity, 1))}.hover\:text-black:hover{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1))}.hover\:text-blue-700:hover{--tw-text-opacity: 1;color:rgb(29 78 216 / var(--tw-text-opacity, 1))}.hover\:text-emerald-700:hover{--tw-text-opacity: 1;color:rgb(4 120 87 / var(--tw-text-opacity, 1))}.hover\:text-red-500:hover{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:shadow-2xl:hover{--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)}.focus\:border-black:focus{--tw-border-opacity: 1;border-color:rgb(0 0 0 / var(--tw-border-opacity, 1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.disabled\:opacity-20:disabled{opacity:.2}.disabled\:opacity-50:disabled{opacity:.5}.dark\:border-\[\#1a1a1a\]:is(.dark *){--tw-border-opacity: 1;border-color:rgb(26 26 26 / var(--tw-border-opacity, 1))}.dark\:border-\[\#1c1c1c\]:is(.dark *){--tw-border-opacity: 1;border-color:rgb(28 28 28 / var(--tw-border-opacity, 1))}.dark\:border-\[\#222222\]:is(.dark *){--tw-border-opacity: 1;border-color:rgb(34 34 34 / var(--tw-border-opacity, 1))}.dark\:border-\[\#262626\]:is(.dark *){--tw-border-opacity: 1;border-color:rgb(38 38 38 / var(--tw-border-opacity, 1))}.dark\:border-\[\#2a2a2a\]:is(.dark *){--tw-border-opacity: 1;border-color:rgb(42 42 42 / var(--tw-border-opacity, 1))}.dark\:border-\[\#333333\]:is(.dark *){--tw-border-opacity: 1;border-color:rgb(51 51 51 / var(--tw-border-opacity, 1))}.dark\:border-amber-500\/20:is(.dark *){border-color:#f59e0b33}.dark\:border-emerald-500\/20:is(.dark *){border-color:#10b98133}.dark\:bg-\[\#000000\]:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.dark\:bg-\[\#000000\]\/80:is(.dark *){background-color:#000c}.dark\:bg-\[\#00e676\]:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(0 230 118 / var(--tw-bg-opacity, 1))}.dark\:bg-\[\#050505\]:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(5 5 5 / var(--tw-bg-opacity, 1))}.dark\:bg-\[\#0a0a0a\]:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(10 10 10 / var(--tw-bg-opacity, 1))}.dark\:bg-\[\#0d0d0d\]:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(13 13 13 / var(--tw-bg-opacity, 1))}.dark\:bg-\[\#0f0f0f\]:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(15 15 15 / var(--tw-bg-opacity, 1))}.dark\:bg-\[\#111111\]:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(17 17 17 / var(--tw-bg-opacity, 1))}.dark\:bg-\[\#111111\]\/90:is(.dark *){background-color:#111111e6}.dark\:bg-\[\#141414\]:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(20 20 20 / var(--tw-bg-opacity, 1))}.dark\:bg-\[\#161616\]:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(22 22 22 / var(--tw-bg-opacity, 1))}.dark\:bg-\[\#181818\]:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(24 24 24 / var(--tw-bg-opacity, 1))}.dark\:bg-\[\#181818\]\/90:is(.dark *){background-color:#181818e6}.dark\:bg-\[\#1a1a1a\]:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(26 26 26 / var(--tw-bg-opacity, 1))}.dark\:bg-\[\#1f1f1f\]:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(31 31 31 / var(--tw-bg-opacity, 1))}.dark\:bg-\[\#222222\]:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(34 34 34 / var(--tw-bg-opacity, 1))}.dark\:bg-\[\#242424\]:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(36 36 36 / var(--tw-bg-opacity, 1))}.dark\:bg-\[\#262626\]:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(38 38 38 / var(--tw-bg-opacity, 1))}.dark\:bg-\[\#2b2b2b\]:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(43 43 43 / var(--tw-bg-opacity, 1))}.dark\:bg-\[\#333333\]:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(51 51 51 / var(--tw-bg-opacity, 1))}.dark\:bg-amber-500\/10:is(.dark *){background-color:#f59e0b1a}.dark\:bg-black:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.dark\:bg-black\/70:is(.dark *){background-color:#000000b3}.dark\:bg-emerald-500\/10:is(.dark *){background-color:#10b9811a}.dark\:bg-white:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.dark\:text-\[\#00e676\]:is(.dark *){--tw-text-opacity: 1;color:rgb(0 230 118 / var(--tw-text-opacity, 1))}.dark\:text-\[\#555555\]:is(.dark *){--tw-text-opacity: 1;color:rgb(85 85 85 / var(--tw-text-opacity, 1))}.dark\:text-\[\#666666\]:is(.dark *){--tw-text-opacity: 1;color:rgb(102 102 102 / var(--tw-text-opacity, 1))}.dark\:text-\[\#888888\]:is(.dark *){--tw-text-opacity: 1;color:rgb(136 136 136 / var(--tw-text-opacity, 1))}.dark\:text-\[\#aaaaaa\]:is(.dark *){--tw-text-opacity: 1;color:rgb(170 170 170 / var(--tw-text-opacity, 1))}.dark\:text-\[\#cccccc\]:is(.dark *){--tw-text-opacity: 1;color:rgb(204 204 204 / var(--tw-text-opacity, 1))}.dark\:text-\[\#ededed\]:is(.dark *){--tw-text-opacity: 1;color:rgb(237 237 237 / var(--tw-text-opacity, 1))}.dark\:text-amber-400:is(.dark *){--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.dark\:text-black:is(.dark *){--tw-text-opacity: 1;color:rgb(0 0 0 / 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-emerald-400:is(.dark *){--tw-text-opacity: 1;color:rgb(52 211 153 / var(--tw-text-opacity, 1))}.dark\:text-indigo-400:is(.dark *){--tw-text-opacity: 1;color:rgb(129 140 248 / 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-white:is(.dark *){--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.dark\:placeholder-\[\#444444\]:is(.dark *)::-moz-placeholder{--tw-placeholder-opacity: 1;color:rgb(68 68 68 / var(--tw-placeholder-opacity, 1))}.dark\:placeholder-\[\#444444\]:is(.dark *)::placeholder{--tw-placeholder-opacity: 1;color:rgb(68 68 68 / var(--tw-placeholder-opacity, 1))}.dark\:placeholder-\[\#555555\]:is(.dark *)::-moz-placeholder{--tw-placeholder-opacity: 1;color:rgb(85 85 85 / var(--tw-placeholder-opacity, 1))}.dark\:placeholder-\[\#555555\]:is(.dark *)::placeholder{--tw-placeholder-opacity: 1;color:rgb(85 85 85 / var(--tw-placeholder-opacity, 1))}.dark\:shadow-2xl:is(.dark *){--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)}.dark\:selection\:bg-\[\#333333\] *:is(.dark *)::-moz-selection{--tw-bg-opacity: 1;background-color:rgb(51 51 51 / var(--tw-bg-opacity, 1))}.dark\:selection\:bg-\[\#333333\] *:is(.dark *)::selection{--tw-bg-opacity: 1;background-color:rgb(51 51 51 / var(--tw-bg-opacity, 1))}.dark\:selection\:text-\[\#ffffff\] *:is(.dark *)::-moz-selection{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.dark\:selection\:text-\[\#ffffff\] *:is(.dark *)::selection{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.dark\:selection\:bg-\[\#333333\]:is(.dark *)::-moz-selection{--tw-bg-opacity: 1;background-color:rgb(51 51 51 / var(--tw-bg-opacity, 1))}.dark\:selection\:bg-\[\#333333\]:is(.dark *)::selection{--tw-bg-opacity: 1;background-color:rgb(51 51 51 / var(--tw-bg-opacity, 1))}.dark\:selection\:text-\[\#ffffff\]:is(.dark *)::-moz-selection{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.dark\:selection\:text-\[\#ffffff\]:is(.dark *)::selection{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.dark\:hover\:border-\[\#333333\]:hover:is(.dark *){--tw-border-opacity: 1;border-color:rgb(51 51 51 / var(--tw-border-opacity, 1))}.dark\:hover\:border-\[\#555555\]:hover:is(.dark *){--tw-border-opacity: 1;border-color:rgb(85 85 85 / var(--tw-border-opacity, 1))}.dark\:hover\:bg-\[\#141414\]:hover:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(20 20 20 / var(--tw-bg-opacity, 1))}.dark\:hover\:bg-\[\#161616\]:hover:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(22 22 22 / var(--tw-bg-opacity, 1))}.dark\:hover\:bg-\[\#1a1a1a\]:hover:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(26 26 26 / var(--tw-bg-opacity, 1))}.dark\:hover\:bg-\[\#1c1c1c\]:hover:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(28 28 28 / var(--tw-bg-opacity, 1))}.dark\:hover\:bg-\[\#1f1f1f\]:hover:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(31 31 31 / var(--tw-bg-opacity, 1))}.dark\:hover\:bg-\[\#202020\]:hover:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(32 32 32 / var(--tw-bg-opacity, 1))}.dark\:hover\:bg-\[\#222222\]:hover:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(34 34 34 / var(--tw-bg-opacity, 1))}.dark\:hover\:bg-\[\#333333\]:hover:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(51 51 51 / var(--tw-bg-opacity, 1))}.dark\:hover\:bg-\[\#eaeaea\]:hover:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(234 234 234 / var(--tw-bg-opacity, 1))}.dark\:hover\:bg-emerald-500\/20:hover:is(.dark *){background-color:#10b98133}.dark\:hover\:text-\[\#cccccc\]:hover:is(.dark *){--tw-text-opacity: 1;color:rgb(204 204 204 / var(--tw-text-opacity, 1))}.dark\:hover\:text-blue-300:hover:is(.dark *){--tw-text-opacity: 1;color:rgb(147 197 253 / var(--tw-text-opacity, 1))}.dark\:hover\:text-emerald-400:hover:is(.dark *){--tw-text-opacity: 1;color:rgb(52 211 153 / var(--tw-text-opacity, 1))}.dark\:hover\:text-white:hover:is(.dark *){--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.dark\:focus\:border-\[\#444444\]:focus:is(.dark *){--tw-border-opacity: 1;border-color:rgb(68 68 68 / var(--tw-border-opacity, 1))}@media(min-width:640px){.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:self-auto{align-self:auto}}@media(min-width:768px){.md\:inline{display:inline}}