@reddb-io/ui 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/_app/immutable/assets/0.DQDLkSdR.css +1 -0
- package/build/_app/immutable/assets/Workspace.CvP5De7i.css +1 -0
- package/build/_app/immutable/chunks/65XjEwaF.js +367 -0
- package/build/_app/immutable/chunks/BjFADNkA.js +992 -0
- package/build/_app/immutable/chunks/{ALqaDrNQ.js → C278awGv.js} +1 -1
- package/build/_app/immutable/chunks/{Ov8N-vxt.js → C9rXzYLu.js} +1 -1
- package/build/_app/immutable/chunks/CDKIdLnm.js +187 -0
- package/build/_app/immutable/chunks/{DXY_Rxa7.js → CXNPJgv1.js} +7 -7
- package/build/_app/immutable/chunks/{Bgso8cDH.js → DqvWKbSc.js} +1 -1
- package/build/_app/immutable/chunks/pDJnDxXM.js +1 -0
- package/build/_app/immutable/entry/app.DdaMXHAv.js +2 -0
- package/build/_app/immutable/entry/start.-MLBtQXB.js +1 -0
- package/build/_app/immutable/nodes/{0.CHPiFxDo.js → 0.Ysoz3Y3h.js} +1 -1
- package/build/_app/immutable/nodes/{1.BC_4YZ0h.js → 1.C2jbGTOJ.js} +1 -1
- package/build/_app/immutable/nodes/11.CAG5_cGP.js +1 -0
- package/build/_app/immutable/nodes/{10.B7DMKSNc.js → 12.DQG5zpEx.js} +5 -5
- package/build/_app/immutable/nodes/7.CAG5_cGP.js +1 -0
- package/build/_app/immutable/nodes/{7.BZX9Iird.js → 8.CrHI4F9p.js} +1 -1
- package/build/_app/version.json +1 -1
- package/build/index.html +12 -12
- package/dist/embed/{RedUiEmbedRoot-BGoLxJf1.js → RedUiEmbedRoot-BwFUA5X-.js} +12715 -10653
- package/dist/embed/RedUiEmbedRoot-BwFUA5X-.js.map +1 -0
- package/dist/embed/index-DGhgtnaK.js +577 -0
- package/dist/embed/{index-B6ZQhCsu.js.map → index-DGhgtnaK.js.map} +1 -1
- package/dist/embed/{index-fad77a13.esm-BqPj0saH.js → index-fad77a13.esm-BMULHdcE.js} +22 -20
- package/dist/embed/{index-fad77a13.esm-BqPj0saH.js.map → index-fad77a13.esm-BMULHdcE.js.map} +1 -1
- package/dist/embed/index.js +1 -1
- package/dist/embed/sigma-edge-curve.esm-CAT5Xrom.js +536 -0
- package/dist/embed/sigma-edge-curve.esm-CAT5Xrom.js.map +1 -0
- package/dist/embed/{sigma-node-border.esm-CGtlgqmo.js → sigma-node-border.esm-Bnk4BRbw.js} +32 -32
- package/dist/embed/{sigma-node-border.esm-CGtlgqmo.js.map → sigma-node-border.esm-Bnk4BRbw.js.map} +1 -1
- package/dist/embed/{sigma.esm-y0qgY-OC.js → sigma.esm-D5yoExsH.js} +3 -3
- package/dist/embed/{sigma.esm-y0qgY-OC.js.map → sigma.esm-D5yoExsH.js.map} +1 -1
- package/package.json +5 -3
- package/src/lib/Appearance.svelte +90 -0
- package/src/lib/CommandPalette.svelte +2 -0
- package/src/lib/SettingsView.svelte +376 -0
- package/src/lib/Topbar.svelte +15 -1
- package/src/lib/Workspace.svelte +14 -1
- package/src/lib/index.ts +2 -0
- package/src/lib/renderers/GraphRenderer.svelte +448 -44
- package/src/lib/renderers/graph-bundle.ts +401 -0
- package/src/lib/renderers/graph-communities.ts +193 -0
- package/src/lib/renderers/graph-render.ts +48 -5
- package/src/lib/renderers/graph-sigma.ts +274 -8
- package/src/lib/router.svelte.ts +84 -57
- package/src/lib/settings-sections.ts +267 -0
- package/src/lib/skins/skins.svelte.ts +64 -0
- package/src/lib/skins/skins.ts +178 -0
- package/build/_app/immutable/assets/0.C63jY5jX.css +0 -1
- package/build/_app/immutable/assets/Workspace.BCzG1DsH.css +0 -1
- package/build/_app/immutable/chunks/BMHKbvs8.js +0 -1
- package/build/_app/immutable/chunks/BQHbwf54.js +0 -916
- package/build/_app/immutable/chunks/Bmgx1mfv.js +0 -310
- package/build/_app/immutable/entry/app.D_IVtpdv.js +0 -2
- package/build/_app/immutable/entry/start.3oC0UQ_Q.js +0 -1
- package/dist/embed/RedUiEmbedRoot-BGoLxJf1.js.map +0 -1
- package/dist/embed/index-B6ZQhCsu.js +0 -577
- /package/build/_app/immutable/assets/{10.Dblyk0Kr.css → 12.Dblyk0Kr.css} +0 -0
- /package/build/_app/immutable/nodes/{8.CAG5_cGP.js → 10.CAG5_cGP.js} +0 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
// Data-driven settings registry + a pure config-key resolver.
|
|
2
|
+
//
|
|
3
|
+
// Settings are defined as DATA, not hand-built rows: `SECTIONS` lists the
|
|
4
|
+
// curated config keys per section, and three override maps (`LABELS`,
|
|
5
|
+
// `DESCRIPTIONS`, `ENUM_OPTIONS`) keyed by config path supply the human copy.
|
|
6
|
+
// `resolveControl(key, value)` maps a single reddb config/connection key to a
|
|
7
|
+
// rendered control descriptor (human label, description, input kind, enum
|
|
8
|
+
// options), degrading gracefully for keys nobody curated. Adding a curated key
|
|
9
|
+
// is a one-line edit to a section's `keys` array.
|
|
10
|
+
//
|
|
11
|
+
// Kept framework-free (no Svelte imports) so the registry and the resolver are
|
|
12
|
+
// trivially unit-testable. Icons are mapped to components in `SettingsView`,
|
|
13
|
+
// not here — a section carries only an icon *name*. Mirrors the registry idiom
|
|
14
|
+
// in `renderers/registry`: a tiny pure surface over curated data.
|
|
15
|
+
|
|
16
|
+
/** The input control a config value is rendered through. */
|
|
17
|
+
export type ControlKind = "text" | "number" | "boolean" | "enum";
|
|
18
|
+
|
|
19
|
+
/** Where a config key is sourced from — gates permission-aware rendering. */
|
|
20
|
+
export type SettingsSource = "stats" | "cluster" | "capabilities" | "session";
|
|
21
|
+
|
|
22
|
+
/** A resolved control for one config key. */
|
|
23
|
+
export interface ControlDescriptor {
|
|
24
|
+
/** The config path this descriptor was resolved from. */
|
|
25
|
+
key: string;
|
|
26
|
+
/** Human label — a curated override or a humanized key. */
|
|
27
|
+
label: string;
|
|
28
|
+
/** Optional one-line explanation. */
|
|
29
|
+
description?: string;
|
|
30
|
+
/** Input kind inferred from the curated enum map or the value's type. */
|
|
31
|
+
kind: ControlKind;
|
|
32
|
+
/** Allowed values when `kind === "enum"`. */
|
|
33
|
+
options?: readonly string[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface SettingsSection {
|
|
37
|
+
/** Stable identifier used as the active-section key (component state only). */
|
|
38
|
+
id: string;
|
|
39
|
+
/** Nav label. */
|
|
40
|
+
label: string;
|
|
41
|
+
/** One-line description shown under the section title. */
|
|
42
|
+
blurb: string;
|
|
43
|
+
/** Icon name resolved to a lucide component in `SettingsView`. */
|
|
44
|
+
icon: string;
|
|
45
|
+
/** Curated config keys, rendered top-to-bottom. One-line edit to extend. */
|
|
46
|
+
keys: string[];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The curated sections. Each `keys` entry is a flattened reddb config path
|
|
51
|
+
* (see `flattenConfig`) sourced from `/stats`, `/cluster/status`,
|
|
52
|
+
* `/capabilities`, or the session (`whoami`). Order is the render order.
|
|
53
|
+
*/
|
|
54
|
+
export const SECTIONS: SettingsSection[] = [
|
|
55
|
+
{
|
|
56
|
+
id: "overview",
|
|
57
|
+
label: "Overview",
|
|
58
|
+
blurb: "What this reddb instance is and where it runs.",
|
|
59
|
+
icon: "settings",
|
|
60
|
+
keys: [
|
|
61
|
+
"deployment.mode",
|
|
62
|
+
"read_only",
|
|
63
|
+
"system.os",
|
|
64
|
+
"system.arch",
|
|
65
|
+
"system.hostname",
|
|
66
|
+
"system.cpu_cores",
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: "storage",
|
|
71
|
+
label: "Storage",
|
|
72
|
+
blurb: "Live store footprint for the active connection.",
|
|
73
|
+
icon: "database",
|
|
74
|
+
keys: [
|
|
75
|
+
"store.collection_count",
|
|
76
|
+
"store.total_entities",
|
|
77
|
+
"store.cross_ref_count",
|
|
78
|
+
"store.total_memory_bytes",
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: "cluster",
|
|
83
|
+
label: "Cluster",
|
|
84
|
+
blurb: "Deployment and replication topology.",
|
|
85
|
+
icon: "network",
|
|
86
|
+
keys: [
|
|
87
|
+
"deployment.mode",
|
|
88
|
+
"deployment.file_path",
|
|
89
|
+
"replication.replica_count",
|
|
90
|
+
"replication.lag_ms",
|
|
91
|
+
],
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: "capabilities",
|
|
95
|
+
label: "Capabilities",
|
|
96
|
+
blurb: "Endpoints negotiated for this server version.",
|
|
97
|
+
icon: "plug",
|
|
98
|
+
keys: [
|
|
99
|
+
"capabilities.vcs",
|
|
100
|
+
"capabilities.clusterStatus",
|
|
101
|
+
"capabilities.collectionMetadata",
|
|
102
|
+
"capabilities.cdcStream",
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: "session",
|
|
107
|
+
label: "Session",
|
|
108
|
+
blurb: "Who red-ui is authenticated as on this connection.",
|
|
109
|
+
icon: "shield",
|
|
110
|
+
keys: ["session.username", "session.role", "session.authenticated"],
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
/** Curated label overrides, keyed by config path. */
|
|
115
|
+
export const LABELS: Record<string, string> = {
|
|
116
|
+
"deployment.mode": "Deployment mode",
|
|
117
|
+
"deployment.file_path": "Backing file",
|
|
118
|
+
read_only: "Read-only",
|
|
119
|
+
"system.os": "Operating system",
|
|
120
|
+
"system.arch": "Architecture",
|
|
121
|
+
"system.hostname": "Hostname",
|
|
122
|
+
"system.cpu_cores": "CPU cores",
|
|
123
|
+
"store.collection_count": "Collections",
|
|
124
|
+
"store.total_entities": "Entities",
|
|
125
|
+
"store.cross_ref_count": "Cross-references",
|
|
126
|
+
"store.total_memory_bytes": "Memory in use",
|
|
127
|
+
"replication.replica_count": "Replicas",
|
|
128
|
+
"replication.lag_ms": "Replication lag",
|
|
129
|
+
"capabilities.vcs": "Version control",
|
|
130
|
+
"capabilities.clusterStatus": "Cluster status",
|
|
131
|
+
"capabilities.collectionMetadata": "Collection metadata",
|
|
132
|
+
"capabilities.cdcStream": "Change stream",
|
|
133
|
+
"session.username": "Username",
|
|
134
|
+
"session.role": "Role",
|
|
135
|
+
"session.authenticated": "Authenticated",
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
/** Curated descriptions, keyed by config path. */
|
|
139
|
+
export const DESCRIPTIONS: Record<string, string> = {
|
|
140
|
+
"deployment.mode": "How this reddb instance is running.",
|
|
141
|
+
"deployment.file_path": "On-disk store path for an embedded or local server.",
|
|
142
|
+
read_only: "Whether the store rejects writes (a lock or a replica role).",
|
|
143
|
+
"store.total_memory_bytes": "Resident memory the store currently holds.",
|
|
144
|
+
"replication.lag_ms": "How far this replica trails the primary, in ms.",
|
|
145
|
+
"capabilities.vcs": "Commit and diff endpoints — gated when unsupported.",
|
|
146
|
+
"capabilities.cdcStream": "Server-sent change stream over /changes/stream.",
|
|
147
|
+
"session.role": "The role granted to this principal on the active target.",
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/** Curated enum options, keyed by config path. Presence ⇒ `kind: "enum"`. */
|
|
151
|
+
export const ENUM_OPTIONS: Record<string, readonly string[]> = {
|
|
152
|
+
"deployment.mode": ["embedded", "server", "docker", "replicated"],
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Turn a config path into a human label when nobody curated one:
|
|
157
|
+
* `store.total_memory_bytes` → "Total memory bytes". Uses the last path
|
|
158
|
+
* segment, swaps `_`/`-` for spaces, and sentence-cases the result.
|
|
159
|
+
*/
|
|
160
|
+
export function humanizeKey(key: string): string {
|
|
161
|
+
const last = key.split(".").pop() ?? key;
|
|
162
|
+
const words = last.replace(/[_-]+/g, " ").trim();
|
|
163
|
+
if (!words) return key;
|
|
164
|
+
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Resolve a config key (and its live value) to a rendered control descriptor.
|
|
169
|
+
*
|
|
170
|
+
* Resolution order, each degrading gracefully:
|
|
171
|
+
* - label: `LABELS[key]` → `humanizeKey(key)`
|
|
172
|
+
* - description: `DESCRIPTIONS[key]` → omitted
|
|
173
|
+
* - kind: `ENUM_OPTIONS[key]` ⇒ `"enum"`; else inferred from the value's
|
|
174
|
+
* runtime type (boolean / number) → `"text"` for everything else, including
|
|
175
|
+
* `undefined`. So an unknown key with no value still yields a valid `"text"`
|
|
176
|
+
* descriptor rather than throwing.
|
|
177
|
+
*/
|
|
178
|
+
export function resolveControl(
|
|
179
|
+
key: string,
|
|
180
|
+
value?: unknown
|
|
181
|
+
): ControlDescriptor {
|
|
182
|
+
const options = ENUM_OPTIONS[key];
|
|
183
|
+
const kind: ControlKind = options
|
|
184
|
+
? "enum"
|
|
185
|
+
: typeof value === "boolean"
|
|
186
|
+
? "boolean"
|
|
187
|
+
: typeof value === "number"
|
|
188
|
+
? "number"
|
|
189
|
+
: "text";
|
|
190
|
+
const descriptor: ControlDescriptor = {
|
|
191
|
+
key,
|
|
192
|
+
label: LABELS[key] ?? humanizeKey(key),
|
|
193
|
+
kind,
|
|
194
|
+
};
|
|
195
|
+
const description = DESCRIPTIONS[key];
|
|
196
|
+
if (description) descriptor.description = description;
|
|
197
|
+
if (options) descriptor.options = options;
|
|
198
|
+
return descriptor;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** Which live source a config key is read from — drives permission-awareness. */
|
|
202
|
+
export function sourceForKey(key: string): SettingsSource {
|
|
203
|
+
if (key.startsWith("capabilities.")) return "capabilities";
|
|
204
|
+
if (key.startsWith("session.")) return "session";
|
|
205
|
+
if (
|
|
206
|
+
key.startsWith("deployment.") ||
|
|
207
|
+
key.startsWith("storage.") ||
|
|
208
|
+
key.startsWith("wal.") ||
|
|
209
|
+
key.startsWith("throughput.") ||
|
|
210
|
+
key.startsWith("replication.")
|
|
211
|
+
) {
|
|
212
|
+
return "cluster";
|
|
213
|
+
}
|
|
214
|
+
return "stats";
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Flatten a nested config snapshot into dotted leaf paths. Nested plain objects
|
|
219
|
+
* recurse (`{ store: { collection_count: 3 } }` → `"store.collection_count"`);
|
|
220
|
+
* arrays and primitives are kept as leaf values. `undefined`/`null` branches
|
|
221
|
+
* are skipped so a server that omits a sub-tree simply yields no keys for it.
|
|
222
|
+
*/
|
|
223
|
+
export function flattenConfig(
|
|
224
|
+
input: Record<string, unknown>
|
|
225
|
+
): Record<string, unknown> {
|
|
226
|
+
const out: Record<string, unknown> = {};
|
|
227
|
+
const walk = (obj: Record<string, unknown>, prefix: string) => {
|
|
228
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
229
|
+
const key = prefix ? `${prefix}.${k}` : k;
|
|
230
|
+
if (v === undefined || v === null) continue;
|
|
231
|
+
if (typeof v === "object" && !Array.isArray(v)) {
|
|
232
|
+
walk(v as Record<string, unknown>, key);
|
|
233
|
+
} else {
|
|
234
|
+
out[key] = v;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
walk(input, "");
|
|
239
|
+
return out;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Resolve a section by id, falling back to the first section when the id is
|
|
244
|
+
* unknown or null. The settings view drives the active section purely from
|
|
245
|
+
* component state, so this never touches the URL.
|
|
246
|
+
*/
|
|
247
|
+
export function resolveSection(id: string | null): SettingsSection {
|
|
248
|
+
return SECTIONS.find((s) => s.id === id) ?? SECTIONS[0];
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Filter a section's curated keys by a free-text query, matching against both
|
|
253
|
+
* the config path AND the resolved human label (case-insensitive). An empty or
|
|
254
|
+
* whitespace-only query returns the keys unchanged. Pure + framework-free so
|
|
255
|
+
* the settings search is unit-testable without rendering. The settings view
|
|
256
|
+
* keeps one query string per section id, so switching sections preserves each
|
|
257
|
+
* section's typed query.
|
|
258
|
+
*/
|
|
259
|
+
export function filterKeys(keys: readonly string[], query: string): string[] {
|
|
260
|
+
const q = query.trim().toLowerCase();
|
|
261
|
+
if (!q) return [...keys];
|
|
262
|
+
return keys.filter(
|
|
263
|
+
(key) =>
|
|
264
|
+
key.toLowerCase().includes(q) ||
|
|
265
|
+
resolveControl(key).label.toLowerCase().includes(q)
|
|
266
|
+
);
|
|
267
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// Thin reactive shell over the pure skin core (`skins.ts`).
|
|
2
|
+
//
|
|
3
|
+
// Holds the selected skin in rune state, applies it to the document, and
|
|
4
|
+
// persists the choice so it survives reloads. All the colour math lives in
|
|
5
|
+
// the pure `skinToCssVars` mapping — this file only does DOM + localStorage.
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
DEFAULT_SKIN,
|
|
9
|
+
STORAGE_KEY,
|
|
10
|
+
applySkin,
|
|
11
|
+
skinByName,
|
|
12
|
+
type Skin,
|
|
13
|
+
} from "./skins";
|
|
14
|
+
|
|
15
|
+
function readPersisted(): string | null {
|
|
16
|
+
if (typeof localStorage === "undefined") return null;
|
|
17
|
+
return localStorage.getItem(STORAGE_KEY);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function persist(name: string) {
|
|
21
|
+
if (typeof localStorage === "undefined") return;
|
|
22
|
+
localStorage.setItem(STORAGE_KEY, name);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function rootEl(target?: HTMLElement | null): HTMLElement | null {
|
|
26
|
+
return (
|
|
27
|
+
target ??
|
|
28
|
+
(typeof document !== "undefined" ? document.documentElement : null)
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
class SkinStore {
|
|
33
|
+
/** The currently selected skin (reactive). */
|
|
34
|
+
current = $state<Skin>(DEFAULT_SKIN);
|
|
35
|
+
private target: HTMLElement | null = null;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Sync the store with the persisted selection and apply it. Call once on
|
|
39
|
+
* mount (after the theme inline script has run). With nothing persisted the
|
|
40
|
+
* store reports the default skin but leaves the inherited theme untouched.
|
|
41
|
+
*/
|
|
42
|
+
init(target?: HTMLElement | null) {
|
|
43
|
+
this.target = rootEl(target);
|
|
44
|
+
const persisted = readPersisted();
|
|
45
|
+
if (persisted) {
|
|
46
|
+
this.current = skinByName(persisted);
|
|
47
|
+
if (this.target) applySkin(this.current, this.target);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Select a skin: apply its vars to the DOM and persist the choice. */
|
|
52
|
+
select(name: string) {
|
|
53
|
+
const skin = skinByName(name);
|
|
54
|
+
this.current = skin;
|
|
55
|
+
const el = rootEl(this.target);
|
|
56
|
+
if (el) {
|
|
57
|
+
this.target = el;
|
|
58
|
+
applySkin(skin, el);
|
|
59
|
+
}
|
|
60
|
+
persist(skin.name);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const skins = new SkinStore();
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// Skin-as-data theming (#75).
|
|
2
|
+
//
|
|
3
|
+
// A skin is *plain data*: a name, human labels, a handful of colour seeds
|
|
4
|
+
// (dark), and a type pairing. Adding a skin is a single entry in `SKINS` —
|
|
5
|
+
// no render code changes. The pure `skinToCssVars` mapping turns those seeds
|
|
6
|
+
// into the full `--color-*` custom-property set the app already consumes,
|
|
7
|
+
// deriving the `--color-bg-0..bg-3` surface ramp (and the `fg`/`line` ramps)
|
|
8
|
+
// via CSS `color-mix` so the steps stay perceptually monotonic without
|
|
9
|
+
// hand-tuning every level. The DOM application + persistence below is a thin
|
|
10
|
+
// shell over that pure core.
|
|
11
|
+
//
|
|
12
|
+
// Dark-only by design: every seed is a dark surface, and there is no
|
|
13
|
+
// `light` / `system` branch. The default skin keeps reddb's signature
|
|
14
|
+
// `#ff2056` accent.
|
|
15
|
+
|
|
16
|
+
export interface SkinSeeds {
|
|
17
|
+
/** Darkest surface — becomes `--color-bg-0`; every other surface ramps up from it. */
|
|
18
|
+
bg: string;
|
|
19
|
+
/** Surgical accent — `--color-accent` and the primary node role. */
|
|
20
|
+
accent: string;
|
|
21
|
+
/** Primary text (`--color-fg-0`); the fg ramp darkens toward `bg`. */
|
|
22
|
+
fg: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface SkinTypography {
|
|
26
|
+
/** `--font-sans` stack. */
|
|
27
|
+
sans: string;
|
|
28
|
+
/** `--font-mono` stack. */
|
|
29
|
+
mono: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface Skin {
|
|
33
|
+
/** Stable id, persisted in localStorage and used for selection. */
|
|
34
|
+
name: string;
|
|
35
|
+
/** Human label shown on the preview card. */
|
|
36
|
+
label: string;
|
|
37
|
+
/** One-line description of the mood. */
|
|
38
|
+
description: string;
|
|
39
|
+
/** Dark colour seeds. */
|
|
40
|
+
colors: SkinSeeds;
|
|
41
|
+
/** Type pairing. */
|
|
42
|
+
typography: SkinTypography;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const FONT_SANS =
|
|
46
|
+
"'Inter Variable', 'Inter', system-ui, -apple-system, sans-serif";
|
|
47
|
+
const FONT_MONO =
|
|
48
|
+
"'JetBrains Mono Variable', 'JetBrains Mono', ui-monospace, 'SF Mono', 'Menlo', monospace";
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The registry. Two curated dark skins — the mechanism, not a buffet.
|
|
52
|
+
* The first entry is the default.
|
|
53
|
+
*/
|
|
54
|
+
export const SKINS: readonly Skin[] = [
|
|
55
|
+
{
|
|
56
|
+
name: "reddb",
|
|
57
|
+
label: "Reddb",
|
|
58
|
+
description: "The signature near-black with a surgical rose accent.",
|
|
59
|
+
colors: { bg: "#050607", accent: "#ff2056", fg: "#f7f8fa" },
|
|
60
|
+
typography: { sans: FONT_SANS, mono: FONT_MONO },
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: "nocturne",
|
|
64
|
+
label: "Nocturne",
|
|
65
|
+
description: "Indigo-tinted midnight with a violet accent.",
|
|
66
|
+
colors: { bg: "#0a0b14", accent: "#7c5cff", fg: "#f5f6fb" },
|
|
67
|
+
typography: { sans: FONT_SANS, mono: FONT_MONO },
|
|
68
|
+
},
|
|
69
|
+
] as const;
|
|
70
|
+
|
|
71
|
+
export const DEFAULT_SKIN = SKINS[0];
|
|
72
|
+
|
|
73
|
+
export const STORAGE_KEY = "red-ui-skin";
|
|
74
|
+
|
|
75
|
+
/** Look a skin up by name, falling back to the default. */
|
|
76
|
+
export function skinByName(name: string | null | undefined): Skin {
|
|
77
|
+
return SKINS.find((s) => s.name === name) ?? DEFAULT_SKIN;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ─── pure mapping (the unit-testable core) ──────────────────────────────────
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* `color-mix(in srgb, <lift> P%, <base>)`. In the sRGB space this is a linear
|
|
84
|
+
* interpolation, so a strictly increasing `pct` of a lighter `lift` yields a
|
|
85
|
+
* strictly lighter result — which is what keeps the surface ramp monotonic.
|
|
86
|
+
*/
|
|
87
|
+
function mix(lift: string, pct: number, base: string): string {
|
|
88
|
+
return `color-mix(in srgb, ${lift} ${pct}%, ${base})`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** `color-mix(in srgb, <color> P%, transparent)` — a translucent wash of `color`. */
|
|
92
|
+
function alpha(color: string, pct: number): string {
|
|
93
|
+
return `color-mix(in srgb, ${color} ${pct}%, transparent)`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Surface ramp `bg-0..bg-3`, lightened from the `bg` seed by an increasing
|
|
98
|
+
* fraction of white. `bg-0` is the pure seed (0% lift); the lift percentages
|
|
99
|
+
* are strictly increasing, so the ramp is monotonic by construction.
|
|
100
|
+
*/
|
|
101
|
+
const SURFACE_LIFT = [0, 4, 9, 15] as const;
|
|
102
|
+
/** Hairline ramp `line-1..3`, also lightened from `bg` with white. */
|
|
103
|
+
const LINE_LIFT = [6, 13, 22] as const;
|
|
104
|
+
/**
|
|
105
|
+
* Text ramp `fg-0..fg-3`, faded from the `fg` seed toward `bg` by a
|
|
106
|
+
* decreasing fraction of `fg`. `fg-0` is the pure seed (100%); the
|
|
107
|
+
* percentages decrease, so readability steps down monotonically.
|
|
108
|
+
*/
|
|
109
|
+
const TEXT_MIX = [100, 78, 58, 45] as const;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Pure seed → CSS-var mapping. Returns the complete custom-property set the
|
|
113
|
+
* app's tokens expect, with the `bg`/`fg`/`line` ramps derived via `color-mix`
|
|
114
|
+
* and the accent washes derived from the accent seed. No DOM, no I/O — this is
|
|
115
|
+
* what the unit tests exercise.
|
|
116
|
+
*/
|
|
117
|
+
export function skinToCssVars(skin: Skin): Record<string, string> {
|
|
118
|
+
const { bg, accent, fg } = skin.colors;
|
|
119
|
+
const white = "#ffffff";
|
|
120
|
+
|
|
121
|
+
const vars: Record<string, string> = {
|
|
122
|
+
// Surfaces — monotonic ramp from the bg seed.
|
|
123
|
+
"--color-bg-0":
|
|
124
|
+
SURFACE_LIFT[0] === 0 ? bg : mix(white, SURFACE_LIFT[0], bg),
|
|
125
|
+
"--color-bg-1": mix(white, SURFACE_LIFT[1], bg),
|
|
126
|
+
"--color-bg-2": mix(white, SURFACE_LIFT[2], bg),
|
|
127
|
+
"--color-bg-3": mix(white, SURFACE_LIFT[3], bg),
|
|
128
|
+
|
|
129
|
+
// Text — monotonic ramp fading toward the bg seed.
|
|
130
|
+
"--color-fg-0": fg,
|
|
131
|
+
"--color-fg-1": mix(fg, TEXT_MIX[1], bg),
|
|
132
|
+
"--color-fg-2": mix(fg, TEXT_MIX[2], bg),
|
|
133
|
+
"--color-fg-3": mix(fg, TEXT_MIX[3], bg),
|
|
134
|
+
|
|
135
|
+
// Hairlines.
|
|
136
|
+
"--color-line-1": mix(white, LINE_LIFT[0], bg),
|
|
137
|
+
"--color-line-2": mix(white, LINE_LIFT[1], bg),
|
|
138
|
+
"--color-line-3": mix(white, LINE_LIFT[2], bg),
|
|
139
|
+
|
|
140
|
+
// Surgical accent + washes derived from the accent seed.
|
|
141
|
+
"--color-accent": accent,
|
|
142
|
+
"--color-accent-soft": alpha(accent, 12),
|
|
143
|
+
"--color-accent-glow": alpha(accent, 45),
|
|
144
|
+
"--color-ambient-glow": alpha(accent, 6),
|
|
145
|
+
|
|
146
|
+
// Node roles — primary tracks the accent; replica/embedded stay legible
|
|
147
|
+
// semantic hues that read on any dark surface.
|
|
148
|
+
"--color-role-primary": accent,
|
|
149
|
+
"--color-role-replica": "#60a5fa",
|
|
150
|
+
"--color-role-embedded": "#4ade80",
|
|
151
|
+
|
|
152
|
+
// Semantic — dark-tuned, shared across skins.
|
|
153
|
+
"--color-ok": "#4ade80",
|
|
154
|
+
"--color-warn": "#fbbf24",
|
|
155
|
+
"--color-danger": "#ff5470",
|
|
156
|
+
"--color-info": "#60a5fa",
|
|
157
|
+
|
|
158
|
+
// Typography.
|
|
159
|
+
"--font-sans": skin.typography.sans,
|
|
160
|
+
"--font-mono": skin.typography.mono,
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
return vars;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Apply a skin's derived custom properties to a DOM element as inline styles.
|
|
168
|
+
* Inline styles win over the stylesheet's `:root` rules, so this overrides the
|
|
169
|
+
* active theme tokens regardless of `data-theme`. The thin shell
|
|
170
|
+
* (`skins.svelte.ts`) wires this to mount + persistence.
|
|
171
|
+
*/
|
|
172
|
+
export function applySkin(skin: Skin, target: HTMLElement) {
|
|
173
|
+
const vars = skinToCssVars(skin);
|
|
174
|
+
for (const [k, v] of Object.entries(vars)) target.style.setProperty(k, v);
|
|
175
|
+
// Skins are dark-only; pin data-theme so the shadcn semantic aliases that
|
|
176
|
+
// key off it stay consistent with the inline surface ramp.
|
|
177
|
+
target.dataset.theme = "dark";
|
|
178
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/*! tailwindcss v4.3.0 | 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-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:"Inter Variable", "Inter", system-ui, -apple-system, sans-serif;--font-serif:ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;--font-mono:"JetBrains Mono Variable", "JetBrains Mono", ui-monospace, "SF Mono", "Menlo", monospace;--color-yellow-200:oklch(94.5% .129 101.54);--color-lime-500:oklch(76.8% .233 130.85);--color-purple-700:oklch(49.6% .265 301.924);--color-zinc-300:oklch(87.1% .006 286.286);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-md:28rem;--container-2xl:42rem;--container-6xl:72rem;--text-xs:11px;--text-xs--line-height:1.45;--text-sm:12px;--text-sm--line-height:1.5;--text-lg:18px;--text-lg--line-height:1.3;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--tracking-tight:-.025em;--tracking-wide:.025em;--tracking-wider:.05em;--leading-relaxed:1.625;--radius-md:5px;--radius-lg:8px;--shadow-md:0 4px 12px #0f172a14, 0 0 0 1px var(--color-line-2);--shadow-lg:0 16px 48px #0f172a24, 0 0 0 1px var(--color-line-2);--ease-out:cubic-bezier(.16, 1, .3, 1);--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--blur-sm:8px;--blur-md:12px;--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);--color-bg-0:#fff;--color-bg-1:#fafafa;--color-bg-2:#f4f4f5;--color-bg-3:#e7e7ea;--color-fg-0:#0a0a0b;--color-fg-1:#3f3f46;--color-fg-2:#52525b;--color-fg-3:#71717a;--color-line-1:#ececef;--color-line-2:#d4d4d8;--color-line-3:#a1a1aa;--color-accent:#e11d48;--color-accent-soft:#e11d481a;--color-accent-glow:#e11d484d;--color-ok:#16a34a;--color-warn:#b45309;--color-danger:#dc2626;--color-info:#2563eb;--color-role-primary:#e11d48;--color-role-replica:#2563eb;--color-role-embedded:#16a34a;--color-ambient-glow:#e11d480a;--ease-snap:cubic-bezier(.25, 1, .5, 1)}}@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}:root{--background:var(--color-bg-0);--foreground:var(--color-fg-0);--card:var(--color-bg-1);--card-foreground:var(--color-fg-0);--popover:var(--color-bg-1);--popover-foreground:var(--color-fg-0);--primary:var(--color-accent);--primary-foreground:#fff;--secondary:var(--color-bg-2);--secondary-foreground:var(--color-fg-1);--muted:var(--color-bg-2);--muted-foreground:var(--color-fg-2);--accent:var(--color-bg-3);--accent-foreground:var(--color-fg-0);--destructive:var(--color-danger);--destructive-foreground:#fff;--border:var(--color-line-2);--input:var(--color-line-2);--ring:var(--color-accent);--radius:var(--radius-md)}*{box-sizing:border-box}html,body{background:var(--color-bg-0);color:var(--color-fg-0);font-family:var(--font-sans);font-feature-settings:"cv11","ss01","ss03";letter-spacing:-.005em;-webkit-font-smoothing:antialiased;text-rendering:optimizelegibility;margin:0;font-size:13px;line-height:1.5}.type-h1{letter-spacing:-.02em;color:var(--color-fg-0);font-size:22px;font-weight:600;line-height:1.2}.type-h2{letter-spacing:-.015em;color:var(--color-fg-0);font-size:16px;font-weight:600;line-height:1.3}.type-label{font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.1em;color:var(--color-fg-2);font-size:10px;font-weight:500}.type-caption{color:var(--color-fg-2);font-size:11px;line-height:1.45}.type-code{font-family:var(--font-mono);color:var(--color-fg-1);font-size:12px}body:before{content:"";background-image:radial-gradient(ellipse 80% 50% at 50% -10%,var(--color-ambient-glow),transparent),linear-gradient(var(--color-line-1) 1px,transparent 1px),linear-gradient(90deg,var(--color-line-1) 1px,transparent 1px);opacity:.35;pointer-events:none;z-index:0;background-size:100% 100%,96px 96px,96px 96px;position:fixed;top:0;right:0;bottom:0;left:0}::selection{background:var(--color-accent-soft);color:var(--color-fg-0)}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:var(--color-line-2);border:2px solid var(--color-bg-0);border-radius:8px}::-webkit-scrollbar-thumb:hover{background:var(--color-line-3)}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.invisible{visibility:hidden}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing) * 0)}.top-0{top:calc(var(--spacing) * 0)}.top-1\/2{top:50%}.top-4{top:calc(var(--spacing) * 4)}.top-\[calc\(100\%\+6px\)\]{top:calc(100% + 6px)}.top-full{top:100%}.right-0{right:calc(var(--spacing) * 0)}.right-1{right:calc(var(--spacing) * 1)}.right-4{right:calc(var(--spacing) * 4)}.bottom-0{bottom:calc(var(--spacing) * 0)}.left-0{left:calc(var(--spacing) * 0)}.left-1\/2{left:50%}.left-2\.5{left:calc(var(--spacing) * 2.5)}.left-4{left:calc(var(--spacing) * 4)}.z-10{z-index:10}.z-20{z-index:20}.z-40{z-index:40}.z-50{z-index:50}.z-\[1\]{z-index:1}.z-\[100\]{z-index:100}.z-\[200\]{z-index:200}.z-\[9999\]{z-index:9999}.col-start-1{grid-column-start:1}.col-start-2{grid-column-start:2}.row-start-1{grid-row-start:1}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.m-0{margin:calc(var(--spacing) * 0)}.mx-4{margin-inline:calc(var(--spacing) * 4)}.mx-auto{margin-inline:auto}.mx-px{margin-inline:1px}.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-5{margin-top:calc(var(--spacing) * 5)}.mr-0\.5{margin-right:calc(var(--spacing) * .5)}.-mb-px{margin-bottom:-1px}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.ml-1{margin-left:calc(var(--spacing) * 1)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-auto{margin-left:auto}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.size-2{width:calc(var(--spacing) * 2);height:calc(var(--spacing) * 2)}.size-3{width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.size-3\.5{width:calc(var(--spacing) * 3.5);height:calc(var(--spacing) * 3.5)}.size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.size-6{width:calc(var(--spacing) * 6);height:calc(var(--spacing) * 6)}.size-7{width:calc(var(--spacing) * 7);height:calc(var(--spacing) * 7)}.size-8{width:calc(var(--spacing) * 8);height:calc(var(--spacing) * 8)}.size-9{width:calc(var(--spacing) * 9);height:calc(var(--spacing) * 9)}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2{height:calc(var(--spacing) * 2)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-11{height:calc(var(--spacing) * 11)}.h-\[18px\]{height:18px}.h-\[82px\]{height:82px}.h-\[180px\]{height:180px}.h-\[320px\]{height:320px}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-\[50vh\]{max-height:50vh}.max-h-\[80vh\]{max-height:80vh}.max-h-\[180px\]{max-height:180px}.max-h-\[320px\]{max-height:320px}.max-h-\[calc\(100vh-96px\)\]{max-height:calc(100vh - 96px)}.min-h-0{min-height:calc(var(--spacing) * 0)}.min-h-\[36px\]{min-height:36px}.min-h-\[160px\]{min-height:160px}.min-h-\[240px\]{min-height:240px}.min-h-\[280px\]{min-height:280px}.min-h-\[720px\]{min-height:720px}.min-h-screen{min-height:100vh}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-2{width:calc(var(--spacing) * 2)}.w-4{width:calc(var(--spacing) * 4)}.w-7{width:calc(var(--spacing) * 7)}.w-10{width:calc(var(--spacing) * 10)}.w-12{width:calc(var(--spacing) * 12)}.w-14{width:calc(var(--spacing) * 14)}.w-16{width:calc(var(--spacing) * 16)}.w-64{width:calc(var(--spacing) * 64)}.w-\[110px\]{width:110px}.w-\[150px\]{width:150px}.w-\[160px\]{width:160px}.w-\[180px\]{width:180px}.w-\[190px\]{width:190px}.w-\[200px\]{width:200px}.w-\[220px\]{width:220px}.w-\[260px\]{width:260px}.w-\[320px\]{width:320px}.w-\[340px\]{width:340px}.w-\[360px\]{width:360px}.w-\[380px\]{width:380px}.w-\[420px\]{width:420px}.w-\[min\(34rem\,90vw\)\]{width:min(34rem,90vw)}.w-\[min\(320px\,48vw\)\]{width:min(320px,48vw)}.w-full{width:100%}.w-px{width:1px}.max-w-2xl{max-width:var(--container-2xl)}.max-w-6xl{max-width:var(--container-6xl)}.max-w-\[38vw\]{max-width:38vw}.max-w-\[180px\]{max-width:180px}.max-w-\[260px\]{max-width:260px}.max-w-\[280px\]{max-width:280px}.max-w-\[520px\]{max-width:520px}.max-w-\[640px\]{max-width:640px}.max-w-full{max-width:100%}.max-w-md{max-width:var(--container-md)}.min-w-0{min-width:calc(var(--spacing) * 0)}.min-w-12{min-width:calc(var(--spacing) * 12)}.min-w-\[78px\]{min-width:78px}.min-w-\[92px\]{min-width:92px}.min-w-\[94px\]{min-width:94px}.min-w-\[180px\]{min-width:180px}.min-w-\[220px\]{min-width:220px}.min-w-\[260px\]{min-width:260px}.flex-1{flex:1}.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.-translate-x-1\/2{--tw-translate-x: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-spin{animation:var(--animate-spin)}.cursor-pointer{cursor:pointer}.resize-none{resize:none}.list-none{list-style-type:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-\[1fr_1fr_auto_auto\]{grid-template-columns:1fr 1fr auto auto}.grid-cols-\[16px_1fr_auto\]{grid-template-columns:16px 1fr auto}.grid-cols-\[52px_1fr_90px\]{grid-template-columns:52px 1fr 90px}.grid-cols-\[60px_1fr\]{grid-template-columns:60px 1fr}.grid-cols-\[78px_1fr\]{grid-template-columns:78px 1fr}.grid-cols-\[82px_1fr\]{grid-template-columns:82px 1fr}.grid-cols-\[100px_1fr\]{grid-template-columns:100px 1fr}.grid-cols-\[110px_1fr\]{grid-template-columns:110px 1fr}.grid-cols-\[120px_1fr\]{grid-template-columns:120px 1fr}.grid-cols-\[160px_1fr\]{grid-template-columns:160px 1fr}.grid-cols-\[260px_1fr\]{grid-template-columns:260px 1fr}.grid-cols-\[280px_1fr\]{grid-template-columns:280px 1fr}.grid-cols-\[320px_1fr\]{grid-template-columns:320px 1fr}.grid-cols-\[auto_1fr\]{grid-template-columns:auto 1fr}.grid-cols-\[minmax\(0\,1fr\)_360px\]{grid-template-columns:minmax(0,1fr) 360px}.grid-cols-\[minmax\(160px\,0\.8fr\)_1\.2fr\]{grid-template-columns:minmax(160px,.8fr) 1.2fr}.grid-cols-\[minmax\(280px\,0\.75fr\)_1\.25fr\]{grid-template-columns:minmax(280px,.75fr) 1.25fr}.grid-cols-\[minmax\(320px\,0\.9fr\)_1\.1fr\]{grid-template-columns:minmax(320px,.9fr) 1.1fr}.grid-cols-\[minmax\(360px\,0\.9fr\)_1\.1fr\]{grid-template-columns:minmax(360px,.9fr) 1.1fr}.grid-cols-\[repeat\(auto-fill\,minmax\(180px\,1fr\)\)\]{grid-template-columns:repeat(auto-fill,minmax(180px,1fr))}.grid-cols-\[repeat\(auto-fill\,minmax\(220px\,1fr\)\)\]{grid-template-columns:repeat(auto-fill,minmax(220px,1fr))}.grid-rows-\[44px_32px_1fr\]{grid-template-rows:44px 32px 1fr}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.content-start{align-content:flex-start}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.items-stretch{align-items:stretch}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.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-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}: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)))}.gap-x-2{column-gap:calc(var(--spacing) * 2)}.gap-x-3{column-gap:calc(var(--spacing) * 3)}.gap-x-4{column-gap:calc(var(--spacing) * 4)}.gap-y-0\.5{row-gap:calc(var(--spacing) * .5)}.gap-y-1{row-gap:calc(var(--spacing) * 1)}.gap-y-1\.5{row-gap:calc(var(--spacing) * 1.5)}.gap-y-2{row-gap:calc(var(--spacing) * 2)}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-line-1>:not(:last-child)){border-color:var(--color-line-1)}.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-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-4{border-style:var(--tw-border-style);border-width:4px}.border-x{border-inline-style:var(--tw-border-style);border-inline-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-accent\/30{border-color:#e11d484d}@supports (color:color-mix(in lab,red,red)){.border-accent\/30{border-color:color-mix(in oklab,var(--color-accent) 30%,transparent)}}.border-accent\/35{border-color:#e11d4859}@supports (color:color-mix(in lab,red,red)){.border-accent\/35{border-color:color-mix(in oklab,var(--color-accent) 35%,transparent)}}.border-accent\/40{border-color:#e11d4866}@supports (color:color-mix(in lab,red,red)){.border-accent\/40{border-color:color-mix(in oklab,var(--color-accent) 40%,transparent)}}.border-accent\/60{border-color:#e11d4899}@supports (color:color-mix(in lab,red,red)){.border-accent\/60{border-color:color-mix(in oklab,var(--color-accent) 60%,transparent)}}.border-danger\/30{border-color:#dc26264d}@supports (color:color-mix(in lab,red,red)){.border-danger\/30{border-color:color-mix(in oklab,var(--color-danger) 30%,transparent)}}.border-danger\/40{border-color:#dc262666}@supports (color:color-mix(in lab,red,red)){.border-danger\/40{border-color:color-mix(in oklab,var(--color-danger) 40%,transparent)}}.border-lime-500{border-color:var(--color-lime-500)}.border-line-1{border-color:var(--color-line-1)}.border-line-1\/50{border-color:#ececef80}@supports (color:color-mix(in lab,red,red)){.border-line-1\/50{border-color:color-mix(in oklab,var(--color-line-1) 50%,transparent)}}.border-line-1\/60{border-color:#ececef99}@supports (color:color-mix(in lab,red,red)){.border-line-1\/60{border-color:color-mix(in oklab,var(--color-line-1) 60%,transparent)}}.border-line-2{border-color:var(--color-line-2)}.border-ok\/30{border-color:#16a34a4d}@supports (color:color-mix(in lab,red,red)){.border-ok\/30{border-color:color-mix(in oklab,var(--color-ok) 30%,transparent)}}.border-ok\/40{border-color:#16a34a66}@supports (color:color-mix(in lab,red,red)){.border-ok\/40{border-color:color-mix(in oklab,var(--color-ok) 40%,transparent)}}.border-warn\/30{border-color:#b453094d}@supports (color:color-mix(in lab,red,red)){.border-warn\/30{border-color:color-mix(in oklab,var(--color-warn) 30%,transparent)}}.border-warn\/40{border-color:#b4530966}@supports (color:color-mix(in lab,red,red)){.border-warn\/40{border-color:color-mix(in oklab,var(--color-warn) 40%,transparent)}}.border-zinc-300{border-color:var(--color-zinc-300)}.border-b-accent{border-bottom-color:var(--color-accent)}.bg-accent{background-color:var(--color-accent)}.bg-accent\/5{background-color:#e11d480d}@supports (color:color-mix(in lab,red,red)){.bg-accent\/5{background-color:color-mix(in oklab,var(--color-accent) 5%,transparent)}}.bg-accent\/10{background-color:#e11d481a}@supports (color:color-mix(in lab,red,red)){.bg-accent\/10{background-color:color-mix(in oklab,var(--color-accent) 10%,transparent)}}.bg-accent\/80{background-color:#e11d48cc}@supports (color:color-mix(in lab,red,red)){.bg-accent\/80{background-color:color-mix(in oklab,var(--color-accent) 80%,transparent)}}.bg-bg-0{background-color:var(--color-bg-0)}.bg-bg-0\/70{background-color:#ffffffb3}@supports (color:color-mix(in lab,red,red)){.bg-bg-0\/70{background-color:color-mix(in oklab,var(--color-bg-0) 70%,transparent)}}.bg-bg-1{background-color:var(--color-bg-1)}.bg-bg-1\/20{background-color:#fafafa33}@supports (color:color-mix(in lab,red,red)){.bg-bg-1\/20{background-color:color-mix(in oklab,var(--color-bg-1) 20%,transparent)}}.bg-bg-1\/30{background-color:#fafafa4d}@supports (color:color-mix(in lab,red,red)){.bg-bg-1\/30{background-color:color-mix(in oklab,var(--color-bg-1) 30%,transparent)}}.bg-bg-1\/35{background-color:#fafafa59}@supports (color:color-mix(in lab,red,red)){.bg-bg-1\/35{background-color:color-mix(in oklab,var(--color-bg-1) 35%,transparent)}}.bg-bg-1\/40{background-color:#fafafa66}@supports (color:color-mix(in lab,red,red)){.bg-bg-1\/40{background-color:color-mix(in oklab,var(--color-bg-1) 40%,transparent)}}.bg-bg-1\/60{background-color:#fafafa99}@supports (color:color-mix(in lab,red,red)){.bg-bg-1\/60{background-color:color-mix(in oklab,var(--color-bg-1) 60%,transparent)}}.bg-bg-1\/80{background-color:#fafafacc}@supports (color:color-mix(in lab,red,red)){.bg-bg-1\/80{background-color:color-mix(in oklab,var(--color-bg-1) 80%,transparent)}}.bg-bg-2{background-color:var(--color-bg-2)}.bg-bg-2\/30{background-color:#f4f4f54d}@supports (color:color-mix(in lab,red,red)){.bg-bg-2\/30{background-color:color-mix(in oklab,var(--color-bg-2) 30%,transparent)}}.bg-bg-3{background-color:var(--color-bg-3)}.bg-black\/45{background-color:#00000073}@supports (color:color-mix(in lab,red,red)){.bg-black\/45{background-color:color-mix(in oklab,var(--color-black) 45%,transparent)}}.bg-danger{background-color:var(--color-danger)}.bg-danger\/10{background-color:#dc26261a}@supports (color:color-mix(in lab,red,red)){.bg-danger\/10{background-color:color-mix(in oklab,var(--color-danger) 10%,transparent)}}.bg-fg-3{background-color:var(--color-fg-3)}.bg-line-1{background-color:var(--color-line-1)}.bg-line-2{background-color:var(--color-line-2)}.bg-ok{background-color:var(--color-ok)}.bg-ok\/10{background-color:#16a34a1a}@supports (color:color-mix(in lab,red,red)){.bg-ok\/10{background-color:color-mix(in oklab,var(--color-ok) 10%,transparent)}}.bg-role-embedded{background-color:var(--color-role-embedded)}.bg-role-primary{background-color:var(--color-role-primary)}.bg-role-replica{background-color:var(--color-role-replica)}.bg-transparent{background-color:#0000}.bg-warn\/5{background-color:#b453090d}@supports (color:color-mix(in lab,red,red)){.bg-warn\/5{background-color:color-mix(in oklab,var(--color-warn) 5%,transparent)}}.bg-warn\/10{background-color:#b453091a}@supports (color:color-mix(in lab,red,red)){.bg-warn\/10{background-color:color-mix(in oklab,var(--color-warn) 10%,transparent)}}.bg-warn\/15{background-color:#b4530926}@supports (color:color-mix(in lab,red,red)){.bg-warn\/15{background-color:color-mix(in oklab,var(--color-warn) 15%,transparent)}}.bg-white{background-color:var(--color-white)}.bg-yellow-200{background-color:var(--color-yellow-200)}.fill-bg-1{fill:var(--color-bg-1)}.fill-fg-0{fill:var(--color-fg-0)}.fill-fg-3{fill:var(--color-fg-3)}.p-0{padding:calc(var(--spacing) * 0)}.p-0\.5{padding:calc(var(--spacing) * .5)}.p-1{padding:calc(var(--spacing) * 1)}.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-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.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-6{padding-block:calc(var(--spacing) * 6)}.py-16{padding-block:calc(var(--spacing) * 16)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pr-1\.5{padding-right:calc(var(--spacing) * 1.5)}.pr-2{padding-right:calc(var(--spacing) * 2)}.pr-2\.5{padding-right:calc(var(--spacing) * 2.5)}.pr-5{padding-right:calc(var(--spacing) * 5)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.pl-2{padding-left:calc(var(--spacing) * 2)}.pl-2\.5{padding-left:calc(var(--spacing) * 2.5)}.pl-8{padding-left:calc(var(--spacing) * 8)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.align-top{vertical-align:top}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.font-serif{font-family:var(--font-serif)}.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-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[9px\]{font-size:9px}.text-\[10\.5px\]{font-size:10.5px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.text-\[15px\]{font-size:15px}.text-\[18px\]{font-size:18px}.text-\[20px\]{font-size:20px}.leading-5{--tw-leading:calc(var(--spacing) * 5);line-height:calc(var(--spacing) * 5)}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.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-\[0\.08em\]{--tw-tracking:.08em;letter-spacing:.08em}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre{white-space:pre}.whitespace-pre-wrap{white-space:pre-wrap}.text-accent{color:var(--color-accent)}.text-black{color:var(--color-black)}.text-danger{color:var(--color-danger)}.text-fg-0{color:var(--color-fg-0)}.text-fg-1{color:var(--color-fg-1)}.text-fg-2{color:var(--color-fg-2)}.text-fg-3{color:var(--color-fg-3)}.text-line-3{color:var(--color-line-3)}.text-ok{color:var(--color-ok)}.text-purple-700{color:var(--color-purple-700)}.text-warn{color:var(--color-warn)}.text-white{color:var(--color-white)}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.line-through{text-decoration-line:line-through}.no-underline{text-decoration-line:none}.accent-accent{accent-color:var(--color-accent)}.accent-current{accent-color:currentColor}.opacity-0{opacity:0}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-100{opacity:1}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-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-\[0_0_12px_var\(--color-accent-glow\)\]{--tw-shadow:0 0 12px var(--tw-shadow-color,var(--color-accent-glow));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 16px 48px var(--tw-shadow-color,#0f172a24), 0 0 0 1px var(--tw-shadow-color,var(--color-line-2));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.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-md{--tw-backdrop-blur:blur(var(--blur-md));-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,)}.backdrop-filter{-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.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))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}@media(hover:hover){.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.placeholder\:text-fg-3::placeholder{color:var(--color-fg-3)}.last\:border-0:last-child{border-style:var(--tw-border-style);border-width:0}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}@media(hover:hover){.hover\:border-accent:hover{border-color:var(--color-accent)}.hover\:border-fg-3:hover{border-color:var(--color-fg-3)}.hover\:border-line-2:hover{border-color:var(--color-line-2)}.hover\:border-line-3:hover{border-color:var(--color-line-3)}.hover\:bg-\[\#ff3868\]:hover{background-color:#ff3868}.hover\:bg-accent\/20:hover{background-color:#e11d4833}@supports (color:color-mix(in lab,red,red)){.hover\:bg-accent\/20:hover{background-color:color-mix(in oklab,var(--color-accent) 20%,transparent)}}.hover\:bg-accent\/90:hover{background-color:#e11d48e6}@supports (color:color-mix(in lab,red,red)){.hover\:bg-accent\/90:hover{background-color:color-mix(in oklab,var(--color-accent) 90%,transparent)}}.hover\:bg-bg-1:hover{background-color:var(--color-bg-1)}.hover\:bg-bg-1\/40:hover{background-color:#fafafa66}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bg-1\/40:hover{background-color:color-mix(in oklab,var(--color-bg-1) 40%,transparent)}}.hover\:bg-bg-1\/50:hover{background-color:#fafafa80}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bg-1\/50:hover{background-color:color-mix(in oklab,var(--color-bg-1) 50%,transparent)}}.hover\:bg-bg-1\/60:hover{background-color:#fafafa99}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bg-1\/60:hover{background-color:color-mix(in oklab,var(--color-bg-1) 60%,transparent)}}.hover\:bg-bg-2:hover{background-color:var(--color-bg-2)}.hover\:bg-bg-2\/40:hover{background-color:#f4f4f566}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bg-2\/40:hover{background-color:color-mix(in oklab,var(--color-bg-2) 40%,transparent)}}.hover\:bg-bg-2\/60:hover{background-color:#f4f4f599}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bg-2\/60:hover{background-color:color-mix(in oklab,var(--color-bg-2) 60%,transparent)}}.hover\:bg-bg-3:hover{background-color:var(--color-bg-3)}.hover\:bg-danger\/20:hover{background-color:#dc262633}@supports (color:color-mix(in lab,red,red)){.hover\:bg-danger\/20:hover{background-color:color-mix(in oklab,var(--color-danger) 20%,transparent)}}.hover\:text-accent:hover{color:var(--color-accent)}.hover\:text-danger:hover{color:var(--color-danger)}.hover\:text-fg-0:hover{color:var(--color-fg-0)}.hover\:text-fg-1:hover{color:var(--color-fg-1)}}.focus\:border-accent:focus{border-color:var(--color-accent)}.focus\:border-line-3:focus{border-color:var(--color-line-3)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-accent\/40:focus-visible{--tw-ring-color:#e11d4866}@supports (color:color-mix(in lab,red,red)){.focus-visible\:ring-accent\/40:focus-visible{--tw-ring-color:color-mix(in oklab, var(--color-accent) 40%, transparent)}}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:text-fg-3:disabled{color:var(--color-fg-3)}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}@media(prefers-reduced-motion:reduce){.motion-reduce\:backdrop-blur-none{--tw-backdrop-blur: ;-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,)}}@media(min-width:40rem){.sm\:inline{display:inline}}}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(./inter-cyrillic-ext-wght-normal.BOeWTOD4.woff2)format("woff2-variations");unicode-range:U+460-52F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(./inter-cyrillic-wght-normal.DqGufNeO.woff2)format("woff2-variations");unicode-range:U+301,U+400-45F,U+490-491,U+4B0-4B1,U+2116}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(./inter-greek-ext-wght-normal.DlzME5K_.woff2)format("woff2-variations");unicode-range:U+1F??}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(./inter-greek-wght-normal.CkhJZR-_.woff2)format("woff2-variations");unicode-range:U+370-377,U+37A-37F,U+384-38A,U+38C,U+38E-3A1,U+3A3-3FF}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(./inter-vietnamese-wght-normal.CBcvBZtf.woff2)format("woff2-variations");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(./inter-latin-ext-wght-normal.DO1Apj_S.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(./inter-latin-wght-normal.Dx4kXJAl.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(data:font/woff2;base64,d09GMgABAAAAAAfsABQAAAAAEAwAAAeCAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhwbHhwoP0hWQVJbBmA/U1RBVIFiJyYAdC9qEQgKhGSEAAsgADCGCAE2AiQDOgQgBYlMB4EUDAcbLQ4onoexrSC/2ZyLAa8p8VHB8/x3Vue+V0hVJalMJg2nx/TCrQXxBeqLjQG7FyM1WEa/X1tEXN7cFz9EJEMmMUz3RihWSSKeQCbcIou0izz/C8v+fq3VfajEa9gDD11CImXS7qL/RJFVzC1qiB6KmKeD6TZdQ6IRGv78dL6uSVVCfgni5mzu7kcgQBgAEAQTQRCoL++STTYybkJxNfQxAAIAGu8OdEB9teW2jh4BpgDqFjAeSEByW3zFP0CBBgNMsMCGEDjgggdhiEAUAeIIED7ABTDUEnkIE9Q9ahFgKttcVhApo4ACB4qobHaccgDfEjFO6aaWUhjMLt2SyIvHKoDqoA4CSUwEIYQCEjhAO9R1G6keDeDZGjNo+AhxOjCEGTr1WeIF3kYBiLAOKvkJSMiKX0VdAyQt3SDJClCkxJCHkCzfqyVTriJZLcolS32JZHUekq2TYNkYtCtjYHMQXSxGjXDz2t/yLWXzDzxz+o3zFwDEaN23F+13pyMdQAEaSKAR9vcGq4A4MTSKCElGW+M7UcY7xqkggITb28ZJhlqc9q2twYKTt0NjixBgYvO9BIihEBLYuOFXQzfIQ7dXGUEEEgFDooBfAzqiQbpJrhiWSuKJCRFKYbHCyJKI2G5GiZbNAvgAu5pc3vwx4G+g3aDkhklABiSz0BICXrYghtYhx/cdJ+44rY2oZ0aMNRFz3VZjb6W33F3gzltqtOCV8tTHSpOeXuItfvr5lCdfzFpqtEitvqdcdGGFd28ZqqC0tPbeChGXgrIlnhSWu/eUso4uKWFLugyDzQJhflY4659+WjQ++6x72WUMv9G8mw6QJl7BVxX5fe/kpUsOvnZwee9uQ0cGXYd0o89XB2748sDSnt8d2VphdOTTgceDVvOds0v9P/s7HPq15aGun/6Vllb56f1dl0t1LejqrNkpdRZsG8TOnM5vkBG5oiVyVGnS8LHps5cfNWJs6qKPfaNSxiQNBUm3cKNWROr0GSur7Za31k1vieq7LH11VF+jXdRIasRKflc7jkobm1Z9te1IyZA0pDkhLR98+H37Zf1c/8at+dB7x+7GfVyTfJMPiYztsnl59Y5l4j+0n1RXlpHnF3Tq7HecmNF/CJodEMAikruxiyJaGLvHOdAfoA+oDvpjBm2b91cHGRZMU9n25xEU0A8fgEEAdKI3Q1iDtc034sug5YVMkE2jsE+BIkwSoQ3gxXMqz9tELp48bd0cFKOKS7xYjEuXBnZP5ia7DyiO/X/YI+PQSbt2uSdqAkWL9nQbV1XB94/+uPfdZz8dnXYFBYrcTl2SIR/ybxJNJPz/Gupb0JaZeens2ekC7EKr8t+Ls/P5VJPYJdHKyqfg2nqU6bhlidzcddQV/7MmecTzJ5VPcKXkNKSEogHjYFx6QZ7rQ+FSe8njaiNuOnXS8H2ScQ619c2mC3VTtauL0rRbXd/CkSOP37FY9Zkjz8+GibYUMOEWF+RdrFS8Ecv1SHOpPUPZGEIpjPvFyU5cXKjd6OXqorTqy9GwRd++HVufPGnVsW+aO3vggKZ18jR9sXaTC1PWTEsVUaK0FkNySbTQDqlm2PfDjZcu4aalnSLKjnOoYQ0nUlqqXcGpPu/4VgV/xU2pAqW4BW3qzhQ8/hFKhV2qE3+BKAtDqBXjfgnVdH4y0wg5tbVNRenNdTWOrenWLcupQdmsbq5b+18piTe/xRdp1xbILxNPJGInm2z6hoB21Lal0i+ePTtd7B45+3XhFJ329evskXm7qurUVREotqSluSo/L29d3qDhI4YOQqWhI4YNvBNfsMHeXKemXrxQfKeuPOGRVayA3JtkJKEgbPp+dXUDluddutRYLFoXGXWX6N3WFaGLbQtRSitVYNacTNSdy7AaG/HSaUEANcBoGXNdcZvZsOqQ1icBDv21/gzAoYPHH/WDW0qNR3QTYKEAEHig6o13NXbND06CQPlRtYjGNnSktRc09k1mAMDvAlDKfQjgy6fssInlfzmNAjKkDxoxHOBLdVRAIVt9j4qo+hA1w9T1aNBNTUOTTNUHLbqokE+UAfJXCIGw/IxCSL5GRUJeR40rL/UxTm4Q08H6MbCs70ObuNyIIXrINHQYInF06UUlevTjbQzTh5upiDMzMMogUtEnjPs/Y7jAHCJeB0GBHh04tC6FiB6ZFB1oArUSIoFoqhzCeAN6lHwm0T4C3VVPWvjpSMXReuWesMEcoqrmgtNBGd2noWeV0hNAz9rFeShNJxHGsPa3HXeKTk8b55hahySYHaYKKFFLpCfN8rsoaJn01CR04Gkc+5k7KVTCmClX8Q10HCrUEkVlSX+XO33oQR9609tJ516H497WSobWs5Up6TLaS10/dessIskgJSLiDlWvHVUywpkQ7hdPZqGyiEF0uVQerVcPamT1A3eKXdyI1vG9OoflrSXihZ1qqGE3nhmAgiIbRCQgPLEPtOM3UQwTLYaYYomNlpA44opnjV6jkD6id80OOrzf6BzmMD6eEa1zKyeYG1fzfEf16V6jw9XYOaar1/b2kP/IYX8oR2mcFvv2GtBV3JXgd437AQAA)format("woff2-variations");unicode-range:U+460-52F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(./jetbrains-mono-cyrillic-wght-normal.D73BlboJ.woff2)format("woff2-variations");unicode-range:U+301,U+400-45F,U+490-491,U+4B0-4B1,U+2116}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(./jetbrains-mono-greek-wght-normal.Bw9x6K1M.woff2)format("woff2-variations");unicode-range:U+370-377,U+37A-37F,U+384-38A,U+38C,U+38E-3A1,U+3A3-3FF}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(./jetbrains-mono-vietnamese-wght-normal.Bt-aOZkq.woff2)format("woff2-variations");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(./jetbrains-mono-latin-ext-wght-normal.DBQx-q_a.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(./jetbrains-mono-latin-wght-normal.B9CIFXIH.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}:root[data-theme=dark]{--color-bg-0:#050607;--color-bg-1:#0d0f13;--color-bg-2:#181b21;--color-bg-3:#232730;--color-fg-0:#f7f8fa;--color-fg-1:#c8ccd4;--color-fg-2:#9ca1a9;--color-fg-3:#7a7f87;--color-line-1:#181b21;--color-line-2:#2a2e36;--color-line-3:#3d424b;--color-accent:#ff2056;--color-accent-soft:#ff20561f;--color-accent-glow:#ff205673;--color-ok:#4ade80;--color-warn:#fbbf24;--color-danger:#ff5470;--color-info:#60a5fa;--color-role-primary:#ff2056;--color-role-replica:#60a5fa;--color-role-embedded:#4ade80;--color-ambient-glow:#ff20560f;--shadow-md:0 4px 12px #0006, 0 0 0 1px var(--color-line-2);--shadow-lg:0 16px 48px #0009, 0 0 0 1px var(--color-line-2)}@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-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-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-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}
|