@alexberardi/jarvis-admin 0.1.65 → 0.1.67

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 (60) hide show
  1. package/package.json +1 -1
  2. package/public/assets/index-CAv1RM9y.css +1 -0
  3. package/public/assets/index-CPK3ZbaB.js +18 -0
  4. package/public/index.html +2 -2
  5. package/server/dist/app.d.ts.map +1 -1
  6. package/server/dist/app.js +24 -0
  7. package/server/dist/app.js.map +1 -1
  8. package/server/dist/data/image-digests.json +58 -0
  9. package/server/dist/data/service-registry.json +13 -6
  10. package/server/dist/middleware/auth.d.ts +14 -0
  11. package/server/dist/middleware/auth.d.ts.map +1 -1
  12. package/server/dist/middleware/auth.js +19 -0
  13. package/server/dist/middleware/auth.js.map +1 -1
  14. package/server/dist/routes/install.d.ts.map +1 -1
  15. package/server/dist/routes/install.js +35 -8
  16. package/server/dist/routes/install.js.map +1 -1
  17. package/server/dist/routes/nodes.d.ts.map +1 -1
  18. package/server/dist/routes/nodes.js +5 -1
  19. package/server/dist/routes/nodes.js.map +1 -1
  20. package/server/dist/routes/setup.d.ts.map +1 -1
  21. package/server/dist/routes/setup.js +2 -1
  22. package/server/dist/routes/setup.js.map +1 -1
  23. package/server/dist/routes/traces.d.ts.map +1 -1
  24. package/server/dist/routes/traces.js +9 -2
  25. package/server/dist/routes/traces.js.map +1 -1
  26. package/server/dist/services/commandCenter.d.ts +11 -0
  27. package/server/dist/services/commandCenter.d.ts.map +1 -0
  28. package/server/dist/services/commandCenter.js +20 -0
  29. package/server/dist/services/commandCenter.js.map +1 -0
  30. package/server/dist/services/generators/compose-generator.d.ts +11 -1
  31. package/server/dist/services/generators/compose-generator.d.ts.map +1 -1
  32. package/server/dist/services/generators/compose-generator.js +151 -39
  33. package/server/dist/services/generators/compose-generator.js.map +1 -1
  34. package/server/dist/services/generators/env-generator.d.ts.map +1 -1
  35. package/server/dist/services/generators/env-generator.js +17 -2
  36. package/server/dist/services/generators/env-generator.js.map +1 -1
  37. package/server/dist/services/generators/secret-generator.d.ts +1 -1
  38. package/server/dist/services/generators/secret-generator.d.ts.map +1 -1
  39. package/server/dist/services/generators/secret-generator.js +15 -0
  40. package/server/dist/services/generators/secret-generator.js.map +1 -1
  41. package/server/dist/services/upgrade/compose-upgrader.d.ts +47 -0
  42. package/server/dist/services/upgrade/compose-upgrader.d.ts.map +1 -1
  43. package/server/dist/services/upgrade/compose-upgrader.js +122 -33
  44. package/server/dist/services/upgrade/compose-upgrader.js.map +1 -1
  45. package/server/dist/services/upgrade/image-digest-resolver.d.ts +16 -0
  46. package/server/dist/services/upgrade/image-digest-resolver.d.ts.map +1 -0
  47. package/server/dist/services/upgrade/image-digest-resolver.js +80 -0
  48. package/server/dist/services/upgrade/image-digest-resolver.js.map +1 -0
  49. package/server/dist/services/upgrade/minisign.d.ts +53 -0
  50. package/server/dist/services/upgrade/minisign.d.ts.map +1 -0
  51. package/server/dist/services/upgrade/minisign.js +126 -0
  52. package/server/dist/services/upgrade/minisign.js.map +1 -0
  53. package/server/dist/services/upgrade/self-updater.d.ts.map +1 -1
  54. package/server/dist/services/upgrade/self-updater.js +61 -1
  55. package/server/dist/services/upgrade/self-updater.js.map +1 -1
  56. package/server/dist/types/wizard.d.ts +8 -0
  57. package/server/dist/types/wizard.d.ts.map +1 -1
  58. package/server/src/data/service-registry.json +13 -6
  59. package/public/assets/index-DeByHCh9.css +0 -1
  60. package/public/assets/index-DeCnqmbD.js +0 -18
@@ -0,0 +1,126 @@
1
+ import { createHash, createPublicKey, verify as cryptoVerify } from 'node:crypto';
2
+ // DER SubjectPublicKeyInfo prefix for an Ed25519 key — prepended to the raw
3
+ // 32-byte public key so Node's crypto can build a KeyObject from it.
4
+ const ED25519_SPKI_PREFIX = Buffer.from('302a300506032b6570032100', 'hex');
5
+ /**
6
+ * Raw 32-byte Ed25519 key from a minisign public key (the base64 second line of
7
+ * a `.pub`, or the bare base64). `null` if malformed.
8
+ *
9
+ * Layout: `[2-byte algo 'Ed'][8-byte key id][32-byte ed25519 pubkey]` = 42 bytes.
10
+ */
11
+ function parsePublicKey(publicKeyB64) {
12
+ let raw;
13
+ try {
14
+ raw = Buffer.from(publicKeyB64.trim(), 'base64');
15
+ }
16
+ catch {
17
+ return null;
18
+ }
19
+ if (raw.length !== 42 || raw[0] !== 0x45 || raw[1] !== 0x64)
20
+ return null; // 'Ed'
21
+ return raw.subarray(10, 42);
22
+ }
23
+ /**
24
+ * Parse a `.minisig` file's signature line → `{ prehashed, sig }`, or `null`.
25
+ *
26
+ * Layout of the (base64) 2nd line: `[2-byte algo][8-byte key id][64-byte sig]`.
27
+ * algo `ED` (0x45,0x44) = prehashed (Ed25519 over BLAKE2b-512 of the file);
28
+ * `Ed` (0x45,0x64) = legacy (Ed25519 over the file bytes).
29
+ */
30
+ function parseSignature(minisigContent) {
31
+ const lines = minisigContent.split('\n').map((l) => l.trim()).filter(Boolean);
32
+ if (lines.length < 2)
33
+ return null;
34
+ let raw;
35
+ try {
36
+ raw = Buffer.from(lines[1], 'base64');
37
+ }
38
+ catch {
39
+ return null;
40
+ }
41
+ if (raw.length !== 74 || raw[0] !== 0x45)
42
+ return null;
43
+ const mode = raw[1];
44
+ if (mode !== 0x44 && mode !== 0x64)
45
+ return null;
46
+ return { prehashed: mode === 0x44, sig: raw.subarray(10, 74) };
47
+ }
48
+ /**
49
+ * Verify a minisign signature over `artifact` against `publicKeyB64` (the base64
50
+ * line of a minisign public key). Returns true ONLY for a valid signature by
51
+ * that key. Supports minisign's prehashed (BLAKE2b-512) and legacy modes.
52
+ * In-language (Node crypto) — no `minisign` binary dependency.
53
+ */
54
+ export function verifyMinisign(publicKeyB64, minisigContent, artifact) {
55
+ const pub = parsePublicKey(publicKeyB64);
56
+ const parsed = parseSignature(minisigContent);
57
+ if (!pub || !parsed)
58
+ return false;
59
+ const message = parsed.prehashed
60
+ ? createHash('blake2b512').update(artifact).digest()
61
+ : artifact;
62
+ try {
63
+ const key = createPublicKey({
64
+ key: Buffer.concat([ED25519_SPKI_PREFIX, pub]),
65
+ format: 'der',
66
+ type: 'spki',
67
+ });
68
+ return cryptoVerify(null, message, key, parsed.sig);
69
+ }
70
+ catch {
71
+ return false;
72
+ }
73
+ }
74
+ /**
75
+ * Parse a `sha256sum`-format checksums file into filename → lowercase-hex-digest.
76
+ * Handles both text (`hash␠␠name`) and binary (`hash␠*name`) lines; ignores any
77
+ * malformed line. Only 64-hex digests are accepted, so a truncated/garbage line
78
+ * can never masquerade as a valid checksum.
79
+ */
80
+ export function parseChecksums(content) {
81
+ const map = new Map();
82
+ for (const line of content.split('\n')) {
83
+ const m = line.trim().match(/^([0-9a-fA-F]{64})\s+\*?(.+)$/);
84
+ if (m)
85
+ map.set(m[2].trim(), m[1].toLowerCase());
86
+ }
87
+ return map;
88
+ }
89
+ /**
90
+ * The self-update trust policy — pure, so it can be exhaustively tested without
91
+ * touching the network or the filesystem.
92
+ *
93
+ * - No signature material → UNSIGNED. Allowed only if the operator opted in
94
+ * via `allowUnsigned`; we never fabricate trust.
95
+ * - Signature present but invalid for our key → refuse. This is a tampering /
96
+ * wrong-signer signal and is **not** overridable by `allowUnsigned` (the
97
+ * escape hatch covers "no signature", never "bad signature").
98
+ * - Signature valid → every artifact must be listed in checksums.txt AND match
99
+ * its SHA-256. Any missing/mismatched artifact → refuse (also not
100
+ * overridable — a validly-signed manifest that doesn't match the bytes we
101
+ * downloaded means the download was tampered).
102
+ */
103
+ export function decideUpdateVerification(input) {
104
+ const { publicKeyB64, checksums, minisigContent, artifacts, allowUnsigned } = input;
105
+ if (!checksums || !minisigContent) {
106
+ return allowUnsigned
107
+ ? { allow: true, signed: false, note: 'release is unsigned; proceeding under JARVIS_ALLOW_UNSIGNED_UPDATE' }
108
+ : { allow: false, reason: 'release is not signed (checksums.txt.minisig missing)' };
109
+ }
110
+ if (!verifyMinisign(publicKeyB64, minisigContent, checksums)) {
111
+ return { allow: false, reason: 'checksums.txt signature is invalid for the trusted key' };
112
+ }
113
+ const hashes = parseChecksums(checksums.toString('utf-8'));
114
+ for (const { filename, data } of artifacts) {
115
+ const want = hashes.get(filename);
116
+ if (!want) {
117
+ return { allow: false, reason: `${filename} is not listed in the signed checksums.txt` };
118
+ }
119
+ const got = createHash('sha256').update(data).digest('hex');
120
+ if (got !== want) {
121
+ return { allow: false, reason: `${filename} does not match the signed checksums.txt (sha256 mismatch)` };
122
+ }
123
+ }
124
+ return { allow: true, signed: true };
125
+ }
126
+ //# sourceMappingURL=minisign.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"minisign.js","sourceRoot":"","sources":["../../../src/services/upgrade/minisign.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"self-updater.d.ts","sourceRoot":"","sources":["../../../src/services/upgrade/self-updater.ts"],"names":[],"mappings":"AASA,KAAK,IAAI,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;AAmBnD;;GAEG;AACH,wBAAsB,UAAU,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAuDjF;AAoDD,+DAA+D;AAC/D,wBAAgB,gBAAgB,IAAI;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAQ/F;AAED,6DAA6D;AAC7D,wBAAgB,kBAAkB,IAAI,IAAI,CAKzC"}
1
+ {"version":3,"file":"self-updater.d.ts","sourceRoot":"","sources":["../../../src/services/upgrade/self-updater.ts"],"names":[],"mappings":"AAgBA,KAAK,IAAI,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;AAyBnD;;GAEG;AACH,wBAAsB,UAAU,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAkEjF;AAoGD,+DAA+D;AAC/D,wBAAgB,gBAAgB,IAAI;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAQ/F;AAED,6DAA6D;AAC7D,wBAAgB,kBAAkB,IAAI,IAAI,CAKzC"}
@@ -4,7 +4,18 @@ import { homedir, platform, arch } from 'node:os';
4
4
  import { execSync, spawn } from 'node:child_process';
5
5
  import { pipeline } from 'node:stream/promises';
6
6
  import { Readable } from 'node:stream';
7
+ import { decideUpdateVerification } from './minisign.js';
7
8
  const REPO = 'alexberardi/jarvis-admin';
9
+ // Trusted minisign public key for release artifacts (key id 725ba202b54fa2c9).
10
+ // Releases sign checksums.txt -> checksums.txt.minisig in CI; the self-updater
11
+ // verifies that signature and the artifacts' SHA-256 against it before applying
12
+ // an update. Rotating the signing key means updating this constant.
13
+ const RELEASE_SIGNING_PUBKEY = 'RWRyW6ICtU+iyX4p4RnS24ju0gRsWpxvv6B8pI9G+ZS01q8t8oupAQ8L';
14
+ /** Operator opt-in to accept an UNSIGNED release (never overrides a bad signature). */
15
+ function allowUnsignedUpdate() {
16
+ const v = (process.env.JARVIS_ALLOW_UNSIGNED_UPDATE ?? '').trim().toLowerCase();
17
+ return v === '1' || v === 'true' || v === 'yes';
18
+ }
8
19
  function detectBinaryName() {
9
20
  const os = platform();
10
21
  const a = arch();
@@ -34,12 +45,21 @@ export async function selfUpdate(targetVersion, emit) {
34
45
  const binaryUrl = `https://github.com/${REPO}/releases/download/${tag}/${binaryName}`;
35
46
  const newBinaryPath = join(binDir, 'jarvis-admin.new');
36
47
  await downloadFile(binaryUrl, newBinaryPath);
37
- chmodSync(newBinaryPath, 0o755);
38
48
  // Download new frontend assets
39
49
  emit({ phase: 'download', message: 'Downloading frontend assets...' });
40
50
  const publicUrl = `https://github.com/${REPO}/releases/download/${tag}/public.tar.gz`;
41
51
  const tarPath = join(binDir, 'public.tar.gz');
42
52
  await downloadFile(publicUrl, tarPath);
53
+ // Verify the release signature BEFORE we make the binary executable, extract
54
+ // the (untrusted) tarball, or swap anything in. A tampered or unsigned update
55
+ // is refused here (unless the operator opted into JARVIS_ALLOW_UNSIGNED_UPDATE
56
+ // for an unsigned release — which never overrides a bad signature).
57
+ emit({ phase: 'verify', message: 'Verifying release signature...' });
58
+ await verifyReleaseArtifacts(tag, [
59
+ { path: newBinaryPath, filename: binaryName },
60
+ { path: tarPath, filename: 'public.tar.gz' },
61
+ ], emit);
62
+ chmodSync(newBinaryPath, 0o755);
43
63
  // Extract to public.new/
44
64
  const publicNewDir = join(binDir, 'public.new');
45
65
  mkdirSync(publicNewDir, { recursive: true });
@@ -121,6 +141,46 @@ async function downloadFile(url, destPath) {
121
141
  const fileStream = createWriteStream(destPath);
122
142
  await pipeline(Readable.fromWeb(res.body), fileStream);
123
143
  }
144
+ /** Fetch a small release asset into memory; null on any 404/error (asset absent). */
145
+ async function downloadOptional(url) {
146
+ try {
147
+ const res = await fetch(url, { redirect: 'follow', signal: AbortSignal.timeout(60_000) });
148
+ if (!res.ok)
149
+ return null;
150
+ return Buffer.from(await res.arrayBuffer());
151
+ }
152
+ catch {
153
+ return null;
154
+ }
155
+ }
156
+ /**
157
+ * Fetch checksums.txt + its minisign signature for `tag` and enforce the update
158
+ * trust policy against the already-downloaded artifacts. Throws (aborting the
159
+ * update, before any swap) if the release is unsigned without opt-in, the
160
+ * signature is invalid, or an artifact's hash doesn't match the signed manifest.
161
+ */
162
+ async function verifyReleaseArtifacts(tag, artifacts, emit) {
163
+ const base = `https://github.com/${REPO}/releases/download/${tag}`;
164
+ const checksums = await downloadOptional(`${base}/checksums.txt`);
165
+ const minisig = await downloadOptional(`${base}/checksums.txt.minisig`);
166
+ const result = decideUpdateVerification({
167
+ publicKeyB64: RELEASE_SIGNING_PUBKEY,
168
+ checksums,
169
+ minisigContent: minisig ? minisig.toString('utf-8') : null,
170
+ artifacts: artifacts.map((a) => ({ filename: a.filename, data: readFileSync(a.path) })),
171
+ allowUnsigned: allowUnsignedUpdate(),
172
+ });
173
+ if (!result.allow) {
174
+ throw new Error(`Update refused: ${result.reason}. Set JARVIS_ALLOW_UNSIGNED_UPDATE=1 to accept an ` +
175
+ `unsigned release at your own risk (this does not bypass a failed signature check).`);
176
+ }
177
+ if (result.signed) {
178
+ emit({ phase: 'verify', message: 'Release signature verified.' });
179
+ }
180
+ else {
181
+ emit({ phase: 'verify', level: 'warn', message: result.note ?? 'Proceeding with an UNSIGNED release.' });
182
+ }
183
+ }
124
184
  /** Check if an upgrade was interrupted and needs to resume. */
125
185
  export function getUpgradeMarker() {
126
186
  const path = getMarkerPath();
@@ -1 +1 @@
1
- {"version":3,"file":"self-updater.js","sourceRoot":"","sources":["../../../src/services/upgrade/self-updater.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"self-updater.js","sourceRoot":"","sources":["../../../src/services/upgrade/self-updater.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,10 @@
1
1
  export type GpuType = 'nvidia' | 'amd' | 'amd-rocm' | 'apple' | 'none';
2
+ /**
3
+ * Whisper GPU backend, chosen explicitly and independently of the LLM gpuType.
4
+ * Default "cpu" — Whisper's base.en is fast on CPU and leaves the GPU for the LLM.
5
+ * Maps to the whisper image suffix: cpu "" / cuda "-cuda" / vulkan "-vulkan" / rocm "-rocm".
6
+ */
7
+ export type WhisperBackend = 'cpu' | 'cuda' | 'vulkan' | 'rocm';
2
8
  export interface HardwareInfo {
3
9
  platform: 'darwin' | 'linux';
4
10
  arch: string;
@@ -19,6 +25,8 @@ export interface WizardState {
19
25
  dbUser: string;
20
26
  whisperModel: string;
21
27
  whisperModelPath: string;
28
+ /** Explicit Whisper GPU backend (default "cpu" when unset). Independent of hardware.gpuType. */
29
+ whisperBackend?: WhisperBackend;
22
30
  llmInterface: string;
23
31
  deploymentMode: 'local' | 'remote-llm';
24
32
  deploymentTarget: 'standard' | 'compose-export';
@@ -1 +1 @@
1
- {"version":3,"file":"wizard.d.ts","sourceRoot":"","sources":["../../src/types/wizard.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"wizard.d.ts","sourceRoot":"","sources":["../../src/types/wizard.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "3.0.0",
3
- "platformVersion": "0.1.0",
3
+ "platformVersion": "0.1.66",
4
4
  "services": [
5
5
  {
6
6
  "id": "jarvis-config-service",
@@ -81,7 +81,9 @@
81
81
  { "name": "ADMIN_API_KEY", "description": "Admin endpoint protection", "required": true, "secret": true, "secretRef": "ADMIN_API_KEY" },
82
82
  { "name": "JARVIS_AUTH_SECRET_KEY", "description": "JWT signing key (shared with jarvis-auth)", "required": true, "secret": true, "secretRef": "AUTH_SECRET_KEY" },
83
83
  { "name": "JARVIS_CONFIG_URL", "description": "Config service URL", "required": false, "default": "http://jarvis-config-service:7700" },
84
- { "name": "JARVIS_MQTT_BROKER_URL", "description": "MQTT broker URL for node communication", "required": false, "default": "mqtt://mosquitto:1884" }
84
+ { "name": "JARVIS_MQTT_BROKER_URL", "description": "MQTT broker URL for node communication", "required": false, "default": "mqtt://mosquitto:1884" },
85
+ { "name": "MQTT_USERNAME", "description": "Shared broker username", "required": false, "default": "jarvis" },
86
+ { "name": "MQTT_PASSWORD", "description": "Shared broker password (authenticates CC to mosquitto)", "required": true, "secret": true, "secretRef": "MQTT_PASSWORD" }
85
87
  ]
86
88
  },
87
89
  {
@@ -225,7 +227,8 @@
225
227
  "dependsOn": ["jarvis-auth", "jarvis-config-service"],
226
228
  "envVars": [
227
229
  { "name": "JARVIS_CONFIG_URL", "description": "Config service URL", "required": false, "default": "http://jarvis-config-service:7700" },
228
- { "name": "JARVIS_COMPOSE_PATH", "description": "Where the host's docker-compose.yml + .env live (mounted into the container)", "required": false, "default": "/host/compose" }
230
+ { "name": "JARVIS_COMPOSE_PATH", "description": "Where the host's docker-compose.yml + .env live (mounted into the container)", "required": false, "default": "/host/compose" },
231
+ { "name": "COMMAND_CENTER_ADMIN_KEY", "description": "Admin API key for command-center admin endpoints (traces, node detail); shares the ADMIN_API_KEY secret", "required": true, "secret": true, "secretRef": "ADMIN_API_KEY" }
229
232
  ],
230
233
  "volumes": [
231
234
  "/var/run/docker.sock:/var/run/docker.sock",
@@ -282,9 +285,13 @@
282
285
  "id": "mosquitto",
283
286
  "name": "Mosquitto",
284
287
  "description": "MQTT message broker — node-to-service communication for TTS, commands, and settings",
285
- "image": "eclipse-mosquitto:2",
288
+ "image": "eclipse-mosquitto:2.0",
286
289
  "port": 1884,
287
- "envVars": [],
290
+ "envVars": [
291
+ { "name": "MQTT_USERNAME", "description": "Shared broker username", "required": false, "default": "jarvis" },
292
+ { "name": "MQTT_PASSWORD", "description": "Shared broker password (hashed into the password_file at startup)", "required": true, "secret": true, "secretRef": "MQTT_PASSWORD" },
293
+ { "name": "MQTT_ALLOW_ANON", "description": "Accept anonymous clients (transition window). Set false to lock the broker.", "required": false, "default": "true" }
294
+ ],
288
295
  "volumes": ["jarvis-mosquitto-data:/mosquitto/data"]
289
296
  },
290
297
  {
@@ -294,7 +301,7 @@
294
301
  "image": "grafana/grafana:latest",
295
302
  "port": 3000,
296
303
  "envVars": [
297
- { "name": "GF_SECURITY_ADMIN_PASSWORD", "description": "Grafana admin password", "required": true, "default": "jarvis" }
304
+ { "name": "GF_SECURITY_ADMIN_PASSWORD", "description": "Grafana admin password", "required": true, "secret": true, "secretRef": "GRAFANA_ADMIN_PASSWORD" }
298
305
  ],
299
306
  "volumes": ["jarvis-grafana-data:/var/lib/grafana"]
300
307
  }
@@ -1 +0,0 @@
1
- @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-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-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}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-300:oklch(80.8% .114 19.571);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-red-900:oklch(39.6% .141 25.723);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-500:oklch(79.5% .184 86.047);--color-yellow-600:oklch(68.1% .162 75.834);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-green-600:oklch(62.7% .194 149.214);--color-emerald-400:oklch(76.5% .177 163.223);--color-emerald-500:oklch(69.6% .17 162.48);--color-emerald-600:oklch(59.6% .145 163.225);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-purple-400:oklch(71.4% .203 305.504);--color-purple-500:oklch(62.7% .265 303.9);--color-purple-600:oklch(55.8% .288 302.321);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-sm:24rem;--container-md:28rem;--container-2xl:42rem;--container-3xl:48rem;--container-4xl:56rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75/1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5/2.25);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wide:.025em;--tracking-wider:.05em;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--animate-bounce:bounce 1s infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.invisible{visibility:hidden}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.inset-0{inset:calc(var(--spacing)*0)}.top-0{top:calc(var(--spacing)*0)}.top-0\.5{top:calc(var(--spacing)*.5)}.top-1\/2{top:50%}.top-2{top:calc(var(--spacing)*2)}.right-1\.5{right:calc(var(--spacing)*1.5)}.right-2{right:calc(var(--spacing)*2)}.left-0{left:calc(var(--spacing)*0)}.left-0\.5{left:calc(var(--spacing)*.5)}.left-3{left:calc(var(--spacing)*3)}.z-10{z-index:10}.z-50{z-index:50}.col-span-2{grid-column:span 2/span 2}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.mx-0\.5{margin-inline:calc(var(--spacing)*.5)}.mx-1{margin-inline:calc(var(--spacing)*1)}.mx-3{margin-inline:calc(var(--spacing)*3)}.mx-auto{margin-inline:auto}.mt-0\.5{margin-top:calc(var(--spacing)*.5)}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-3{margin-top:calc(var(--spacing)*3)}.mt-4{margin-top:calc(var(--spacing)*4)}.mt-6{margin-top:calc(var(--spacing)*6)}.mb-1{margin-bottom:calc(var(--spacing)*1)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-6{margin-bottom:calc(var(--spacing)*6)}.mb-8{margin-bottom:calc(var(--spacing)*8)}.ml-1{margin-left:calc(var(--spacing)*1)}.ml-2{margin-left:calc(var(--spacing)*2)}.ml-4{margin-left:calc(var(--spacing)*4)}.ml-5{margin-left:calc(var(--spacing)*5)}.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}.h-2{height:calc(var(--spacing)*2)}.h-2\.5{height:calc(var(--spacing)*2.5)}.h-3{height:calc(var(--spacing)*3)}.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-14{height:calc(var(--spacing)*14)}.h-64{height:calc(var(--spacing)*64)}.h-\[18px\]{height:18px}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-32{max-height:calc(var(--spacing)*32)}.max-h-48{max-height:calc(var(--spacing)*48)}.max-h-64{max-height:calc(var(--spacing)*64)}.max-h-80{max-height:calc(var(--spacing)*80)}.min-h-\[50vh\]{min-height:50vh}.min-h-\[60vh\]{min-height:60vh}.min-h-screen{min-height:100vh}.w-1\.5{width:calc(var(--spacing)*1.5)}.w-2{width:calc(var(--spacing)*2)}.w-2\.5{width:calc(var(--spacing)*2.5)}.w-3{width:calc(var(--spacing)*3)}.w-4{width:calc(var(--spacing)*4)}.w-5{width:calc(var(--spacing)*5)}.w-6{width:calc(var(--spacing)*6)}.w-7{width:calc(var(--spacing)*7)}.w-8{width:calc(var(--spacing)*8)}.w-11{width:calc(var(--spacing)*11)}.w-14{width:calc(var(--spacing)*14)}.w-16{width:calc(var(--spacing)*16)}.w-20{width:calc(var(--spacing)*20)}.w-32{width:calc(var(--spacing)*32)}.w-36{width:calc(var(--spacing)*36)}.w-56{width:calc(var(--spacing)*56)}.w-\[18px\]{width:18px}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-4xl{max-width:var(--container-4xl)}.max-w-md{max-width:var(--container-md)}.max-w-sm{max-width:var(--container-sm)}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:calc(var(--spacing)*0)}.min-w-\[180px\]{min-width:180px}.flex-1{flex:1}.shrink-0{flex-shrink:0}.translate-x-0{--tw-translate-x:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-5{--tw-translate-x:calc(var(--spacing)*5);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)}.animate-bounce{animation:var(--animate-bounce)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.list-inside{list-style-position:inside}.list-decimal{list-style-type:decimal}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.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-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0\.5{gap:calc(var(--spacing)*.5)}.gap-1{gap:calc(var(--spacing)*1)}.gap-1\.5{gap:calc(var(--spacing)*1.5)}.gap-2{gap:calc(var(--spacing)*2)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}:where(.space-y-0\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*.5)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*.5)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*3)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*4)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*6)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*6)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-8>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*8)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*8)*calc(1 - var(--tw-space-y-reverse)))}.gap-x-4{column-gap:calc(var(--spacing)*4)}.gap-y-1{row-gap:calc(var(--spacing)*1)}: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-\[var\(--color-border\)\]>:not(:last-child)){border-color:var(--color-border)}.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)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-4{border-style:var(--tw-border-style);border-width:4px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-\[var\(--color-border\)\],.border-\[var\(--color-border\)\]\/50{border-color:var(--color-border)}@supports (color:color-mix(in lab,red,red)){.border-\[var\(--color-border\)\]\/50{border-color:color-mix(in oklab,var(--color-border)50%,transparent)}}.border-\[var\(--color-primary\)\],.border-\[var\(--color-primary\)\]\/30{border-color:var(--color-primary)}@supports (color:color-mix(in lab,red,red)){.border-\[var\(--color-primary\)\]\/30{border-color:color-mix(in oklab,var(--color-primary)30%,transparent)}}.border-amber-300{border-color:var(--color-amber-300)}.border-amber-500\/30{border-color:#f99c004d}@supports (color:color-mix(in lab,red,red)){.border-amber-500\/30{border-color:color-mix(in oklab,var(--color-amber-500)30%,transparent)}}.border-amber-500\/40{border-color:#f99c0066}@supports (color:color-mix(in lab,red,red)){.border-amber-500\/40{border-color:color-mix(in oklab,var(--color-amber-500)40%,transparent)}}.border-blue-500\/20{border-color:#3080ff33}@supports (color:color-mix(in lab,red,red)){.border-blue-500\/20{border-color:color-mix(in oklab,var(--color-blue-500)20%,transparent)}}.border-blue-500\/40{border-color:#3080ff66}@supports (color:color-mix(in lab,red,red)){.border-blue-500\/40{border-color:color-mix(in oklab,var(--color-blue-500)40%,transparent)}}.border-green-500\/20{border-color:#00c75833}@supports (color:color-mix(in lab,red,red)){.border-green-500\/20{border-color:color-mix(in oklab,var(--color-green-500)20%,transparent)}}.border-green-500\/30{border-color:#00c7584d}@supports (color:color-mix(in lab,red,red)){.border-green-500\/30{border-color:color-mix(in oklab,var(--color-green-500)30%,transparent)}}.border-green-500\/40{border-color:#00c75866}@supports (color:color-mix(in lab,red,red)){.border-green-500\/40{border-color:color-mix(in oklab,var(--color-green-500)40%,transparent)}}.border-purple-500\/40{border-color:#ac4bff66}@supports (color:color-mix(in lab,red,red)){.border-purple-500\/40{border-color:color-mix(in oklab,var(--color-purple-500)40%,transparent)}}.border-red-300{border-color:var(--color-red-300)}.border-red-500\/30{border-color:#fb2c364d}@supports (color:color-mix(in lab,red,red)){.border-red-500\/30{border-color:color-mix(in oklab,var(--color-red-500)30%,transparent)}}.border-yellow-500\/20{border-color:#edb20033}@supports (color:color-mix(in lab,red,red)){.border-yellow-500\/20{border-color:color-mix(in oklab,var(--color-yellow-500)20%,transparent)}}.border-t-\[var\(--color-primary\)\]{border-top-color:var(--color-primary)}.border-t-transparent{border-top-color:#0000}.bg-\[\#0d1117\]{background-color:#0d1117}.bg-\[var\(--color-background\)\]{background-color:var(--color-background)}.bg-\[var\(--color-bg\)\]{background-color:var(--color-bg)}.bg-\[var\(--color-border\)\]{background-color:var(--color-border)}.bg-\[var\(--color-primary\)\],.bg-\[var\(--color-primary\)\]\/5{background-color:var(--color-primary)}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--color-primary\)\]\/5{background-color:color-mix(in oklab,var(--color-primary)5%,transparent)}}.bg-\[var\(--color-primary\)\]\/10{background-color:var(--color-primary)}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--color-primary\)\]\/10{background-color:color-mix(in oklab,var(--color-primary)10%,transparent)}}.bg-\[var\(--color-primary\)\]\/20{background-color:var(--color-primary)}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--color-primary\)\]\/20{background-color:color-mix(in oklab,var(--color-primary)20%,transparent)}}.bg-\[var\(--color-primary\)\]\/30{background-color:var(--color-primary)}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--color-primary\)\]\/30{background-color:color-mix(in oklab,var(--color-primary)30%,transparent)}}.bg-\[var\(--color-surface\)\]{background-color:var(--color-surface)}.bg-\[var\(--color-surface-alt\)\],.bg-\[var\(--color-surface-alt\)\]\/40{background-color:var(--color-surface-alt)}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--color-surface-alt\)\]\/40{background-color:color-mix(in oklab,var(--color-surface-alt)40%,transparent)}}.bg-\[var\(--color-text-muted\)\]\/30{background-color:var(--color-text-muted)}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--color-text-muted\)\]\/30{background-color:color-mix(in oklab,var(--color-text-muted)30%,transparent)}}.bg-amber-500{background-color:var(--color-amber-500)}.bg-amber-500\/5{background-color:#f99c000d}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/5{background-color:color-mix(in oklab,var(--color-amber-500)5%,transparent)}}.bg-amber-500\/10{background-color:#f99c001a}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/10{background-color:color-mix(in oklab,var(--color-amber-500)10%,transparent)}}.bg-black\/40{background-color:#0006}@supports (color:color-mix(in lab,red,red)){.bg-black\/40{background-color:color-mix(in oklab,var(--color-black)40%,transparent)}}.bg-blue-500\/5{background-color:#3080ff0d}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/5{background-color:color-mix(in oklab,var(--color-blue-500)5%,transparent)}}.bg-blue-500\/10{background-color:#3080ff1a}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/10{background-color:color-mix(in oklab,var(--color-blue-500)10%,transparent)}}.bg-emerald-500{background-color:var(--color-emerald-500)}.bg-emerald-500\/10{background-color:#00bb7f1a}@supports (color:color-mix(in lab,red,red)){.bg-emerald-500\/10{background-color:color-mix(in oklab,var(--color-emerald-500)10%,transparent)}}.bg-gray-400{background-color:var(--color-gray-400)}.bg-gray-500\/10{background-color:#6a72821a}@supports (color:color-mix(in lab,red,red)){.bg-gray-500\/10{background-color:color-mix(in oklab,var(--color-gray-500)10%,transparent)}}.bg-green-500{background-color:var(--color-green-500)}.bg-green-500\/5{background-color:#00c7580d}@supports (color:color-mix(in lab,red,red)){.bg-green-500\/5{background-color:color-mix(in oklab,var(--color-green-500)5%,transparent)}}.bg-green-500\/10{background-color:#00c7581a}@supports (color:color-mix(in lab,red,red)){.bg-green-500\/10{background-color:color-mix(in oklab,var(--color-green-500)10%,transparent)}}.bg-green-600{background-color:var(--color-green-600)}.bg-purple-500\/10{background-color:#ac4bff1a}@supports (color:color-mix(in lab,red,red)){.bg-purple-500\/10{background-color:color-mix(in oklab,var(--color-purple-500)10%,transparent)}}.bg-red-50{background-color:var(--color-red-50)}.bg-red-500{background-color:var(--color-red-500)}.bg-red-500\/5{background-color:#fb2c360d}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/5{background-color:color-mix(in oklab,var(--color-red-500)5%,transparent)}}.bg-red-500\/10{background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/10{background-color:color-mix(in oklab,var(--color-red-500)10%,transparent)}}.bg-red-700{background-color:var(--color-red-700)}.bg-white{background-color:var(--color-white)}.bg-yellow-500\/5{background-color:#edb2000d}@supports (color:color-mix(in lab,red,red)){.bg-yellow-500\/5{background-color:color-mix(in oklab,var(--color-yellow-500)5%,transparent)}}.bg-yellow-500\/15{background-color:#edb20026}@supports (color:color-mix(in lab,red,red)){.bg-yellow-500\/15{background-color:color-mix(in oklab,var(--color-yellow-500)15%,transparent)}}.p-0\.5{padding:calc(var(--spacing)*.5)}.p-1{padding:calc(var(--spacing)*1)}.p-1\.5{padding:calc(var(--spacing)*1.5)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.p-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-4{padding-inline:calc(var(--spacing)*4)}.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-4{padding-block:calc(var(--spacing)*4)}.py-8{padding-block:calc(var(--spacing)*8)}.py-12{padding-block:calc(var(--spacing)*12)}.py-20{padding-block:calc(var(--spacing)*20)}.pt-1{padding-top:calc(var(--spacing)*1)}.pt-2{padding-top:calc(var(--spacing)*2)}.pt-3{padding-top:calc(var(--spacing)*3)}.pr-3{padding-right:calc(var(--spacing)*3)}.pr-8{padding-right:calc(var(--spacing)*8)}.pb-2{padding-bottom:calc(var(--spacing)*2)}.pl-9{padding-left:calc(var(--spacing)*9)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.leading-5{--tw-leading:calc(var(--spacing)*5);line-height:calc(var(--spacing)*5)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-\[var\(--color-primary\)\]{color:var(--color-primary)}.text-\[var\(--color-secondary\)\]{color:var(--color-secondary)}.text-\[var\(--color-tertiary\)\]{color:var(--color-tertiary)}.text-\[var\(--color-text\)\]{color:var(--color-text)}.text-\[var\(--color-text-muted\)\]{color:var(--color-text-muted)}.text-amber-400{color:var(--color-amber-400)}.text-amber-500{color:var(--color-amber-500)}.text-blue-500{color:var(--color-blue-500)}.text-blue-600{color:var(--color-blue-600)}.text-emerald-500{color:var(--color-emerald-500)}.text-emerald-600{color:var(--color-emerald-600)}.text-gray-300{color:var(--color-gray-300)}.text-gray-500{color:var(--color-gray-500)}.text-green-500{color:var(--color-green-500)}.text-green-600{color:var(--color-green-600)}.text-purple-500{color:var(--color-purple-500)}.text-purple-600{color:var(--color-purple-600)}.text-red-400{color:var(--color-red-400)}.text-red-500{color:var(--color-red-500)}.text-red-600{color:var(--color-red-600)}.text-red-700{color:var(--color-red-700)}.text-white{color:var(--color-white)}.text-yellow-400{color:var(--color-yellow-400)}.text-yellow-600{color:var(--color-yellow-600)}.capitalize{text-transform:capitalize}.uppercase{text-transform:uppercase}.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,)}.underline{text-decoration-line:underline}.accent-\[var\(--color-primary\)\]{accent-color:var(--color-primary)}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--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)}.ring-red-500{--tw-ring-color:var(--color-red-500)}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.outline-none{--tw-outline-style:none;outline-style:none}.placeholder\:text-\[var\(--color-text-muted\)\]::placeholder{color:var(--color-text-muted)}@media(hover:hover){.hover\:border-\[var\(--color-primary\)\]:hover,.hover\:border-\[var\(--color-primary\)\]\/30:hover{border-color:var(--color-primary)}@supports (color:color-mix(in lab,red,red)){.hover\:border-\[var\(--color-primary\)\]\/30:hover{border-color:color-mix(in oklab,var(--color-primary)30%,transparent)}}.hover\:border-\[var\(--color-primary\)\]\/50:hover{border-color:var(--color-primary)}@supports (color:color-mix(in lab,red,red)){.hover\:border-\[var\(--color-primary\)\]\/50:hover{border-color:color-mix(in oklab,var(--color-primary)50%,transparent)}}.hover\:bg-\[var\(--color-bg-secondary\)\]:hover{background-color:var(--color-bg-secondary)}.hover\:bg-\[var\(--color-primary\)\]\/10:hover{background-color:var(--color-primary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-\[var\(--color-primary\)\]\/10:hover{background-color:color-mix(in oklab,var(--color-primary)10%,transparent)}}.hover\:bg-\[var\(--color-surface\)\]:hover{background-color:var(--color-surface)}.hover\:bg-\[var\(--color-surface-alt\)\]:hover{background-color:var(--color-surface-alt)}.hover\:bg-red-500\/10:hover{background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-500\/10:hover{background-color:color-mix(in oklab,var(--color-red-500)10%,transparent)}}.hover\:bg-red-500\/20:hover{background-color:#fb2c3633}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-500\/20:hover{background-color:color-mix(in oklab,var(--color-red-500)20%,transparent)}}.hover\:text-\[var\(--color-primary\)\]:hover{color:var(--color-primary)}.hover\:text-\[var\(--color-text\)\]:hover{color:var(--color-text)}.hover\:text-red-500:hover{color:var(--color-red-500)}.hover\:no-underline:hover{text-decoration-line:none}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-80:hover{opacity:.8}.hover\:opacity-90:hover{opacity:.9}}.focus\:ring-1:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-\[var\(--color-primary\)\]:focus{--tw-ring-color:var(--color-primary)}.disabled\:invisible:disabled{visibility:hidden}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}@media(hover:hover){.disabled\:hover\:bg-transparent:disabled:hover{background-color:#0000}}@media(min-width:40rem){.sm\:inline{display:inline}.sm\:w-6{width:calc(var(--spacing)*6)}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(prefers-color-scheme:dark){.dark\:border-red-700{border-color:var(--color-red-700)}.dark\:bg-red-900\/20{background-color:#82181a33}@supports (color:color-mix(in lab,red,red)){.dark\:bg-red-900\/20{background-color:color-mix(in oklab,var(--color-red-900)20%,transparent)}}.dark\:text-blue-400{color:var(--color-blue-400)}.dark\:text-emerald-400{color:var(--color-emerald-400)}.dark\:text-green-400{color:var(--color-green-400)}.dark\:text-purple-400{color:var(--color-purple-400)}.dark\:text-red-400{color:var(--color-red-400)}.dark\:text-yellow-400{color:var(--color-yellow-400)}}}:root{--color-primary:#8a00c4;--color-secondary:#22c55e;--color-tertiary:#0891b2;--color-background:#f8fafc;--color-surface:#fff;--color-surface-alt:#f1f5f9;--color-border:#e2e8f0;--color-text:#0f172a;--color-text-muted:#64748b}.dark{--color-primary:#c084fc;--color-secondary:#4ade80;--color-tertiary:#38bdf8;--color-background:#0f172a;--color-surface:#0b1222;--color-surface-alt:#1e293b;--color-border:#334155;--color-text:#f8fafc;--color-text-muted:#94a3b8}body{-webkit-font-smoothing:antialiased;background-color:var(--color-background);color:var(--color-text);margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif}@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-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-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}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}@keyframes bounce{0%,to{animation-timing-function:cubic-bezier(.8,0,1,1);transform:translateY(-25%)}50%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:none}}