@hyperframes/studio 0.6.106 → 0.6.108

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 (49) hide show
  1. package/dist/assets/{hyperframes-player-DgsMQSvV.js → hyperframes-player-67pq7USK.js} +2 -2
  2. package/dist/assets/index-BVqybwMG.css +1 -0
  3. package/dist/assets/index-BgbVhDYd.js +296 -0
  4. package/dist/assets/{index-CBS_7p5W.js → index-Btg-jqxS.js} +1 -1
  5. package/dist/assets/{index-CRCE5Dz_.js → index-tn9M43-U.js} +1 -1
  6. package/dist/index.html +2 -2
  7. package/package.json +5 -5
  8. package/src/App.tsx +1 -0
  9. package/src/components/StudioRightPanel.tsx +161 -75
  10. package/src/components/editor/PropertyPanel.tsx +26 -1
  11. package/src/components/editor/domEditOverlayGeometry.ts +2 -11
  12. package/src/components/editor/domEditingDom.ts +21 -0
  13. package/src/components/editor/domEditingElement.ts +2 -11
  14. package/src/components/editor/manualEditingAvailability.test.ts +12 -0
  15. package/src/components/editor/manualEditingAvailability.ts +6 -0
  16. package/src/components/editor/propertyPanelColorGradingControls.tsx +475 -0
  17. package/src/components/editor/propertyPanelColorGradingSection.tsx +355 -0
  18. package/src/components/editor/propertyPanelHelpers.ts +2 -1
  19. package/src/components/editor/propertyPanelPrimitives.tsx +32 -37
  20. package/src/contexts/DomEditContext.tsx +4 -0
  21. package/src/contexts/PanelLayoutContext.tsx +6 -0
  22. package/src/hooks/gsapScriptCommitTypes.ts +11 -0
  23. package/src/hooks/serializeByKey.test.ts +83 -0
  24. package/src/hooks/serializeByKey.ts +33 -0
  25. package/src/hooks/useDomEditCommits.ts +7 -0
  26. package/src/hooks/useDomEditSession.ts +2 -2
  27. package/src/hooks/useDomEditTextCommits.ts +82 -26
  28. package/src/hooks/useDomEditWiring.ts +1 -0
  29. package/src/hooks/useDomSelection.ts +2 -10
  30. package/src/hooks/useGsapAnimationOps.ts +5 -1
  31. package/src/hooks/useGsapKeyframeOps.ts +21 -1
  32. package/src/hooks/useGsapScriptCommits.ts +33 -5
  33. package/src/hooks/usePanelLayout.ts +26 -1
  34. package/src/hooks/useStudioContextValue.ts +8 -3
  35. package/src/icons/SystemIcons.tsx +4 -0
  36. package/src/player/lib/timelineDOM.test.ts +36 -0
  37. package/src/player/lib/timelineDOM.ts +2 -0
  38. package/src/player/lib/timelineElementHelpers.ts +18 -1
  39. package/src/styles/studio.css +81 -0
  40. package/src/utils/mediaTypes.ts +1 -0
  41. package/src/utils/sdkShadow.test.ts +187 -1
  42. package/src/utils/sdkShadow.ts +67 -10
  43. package/src/utils/sdkShadowGsapFidelity.ts +46 -18
  44. package/src/utils/sdkShadowGsapKeyframe.test.ts +265 -0
  45. package/src/utils/sdkShadowGsapKeyframe.ts +257 -0
  46. package/src/utils/sdkShadowNumeric.ts +11 -0
  47. package/src/utils/studioHelpers.ts +6 -0
  48. package/dist/assets/index-BrhJl2JY.css +0 -1
  49. package/dist/assets/index-koqvg-_0.js +0 -296
@@ -0,0 +1,265 @@
1
+ import { describe, expect, it, vi, beforeEach } from "vitest";
2
+ import { openComposition } from "@hyperframes/sdk";
3
+ import {
4
+ resolveKeyframeIndexByPercentage,
5
+ keyframeOpToEditOp,
6
+ gsapKeyframeFidelityMismatches,
7
+ runShadowGsapKeyframeFidelity,
8
+ type ShadowKeyframeOp,
9
+ } from "./sdkShadowGsapKeyframe";
10
+ import { runShadowDispatch } from "./sdkShadow";
11
+ import type { PatchOperation } from "./sourcePatcher";
12
+
13
+ // Capture sdk_shadow_dispatch telemetry.
14
+ const trackedEvents: Array<{ event: string; props: Record<string, unknown> }> = [];
15
+ vi.mock("./studioTelemetry", () => ({
16
+ trackStudioEvent: (event: string, props: Record<string, unknown>) =>
17
+ trackedEvents.push({ event, props }),
18
+ }));
19
+ // STUDIO_SDK_SHADOW_ENABLED defaults true (no env override in test), so the
20
+ // runners are active here without mocking the availability module.
21
+
22
+ beforeEach(() => {
23
+ trackedEvents.length = 0;
24
+ });
25
+ const lastShadow = () =>
26
+ trackedEvents.filter((e) => e.event === "sdk_shadow_dispatch").at(-1)?.props;
27
+
28
+ const ANIM_ID = "#hero-to-0-position";
29
+
30
+ function gsapHtml(scriptBody: string): string {
31
+ return /* html */ `<!DOCTYPE html><html><body>
32
+ <div data-hf-id="hf-hero" id="hero" class="clip">x</div>
33
+ <script>
34
+ ${scriptBody}
35
+ window.__timelines = [tl];
36
+ </script>
37
+ </body></html>`;
38
+ }
39
+
40
+ const KF_SCRIPT = `
41
+ const tl = gsap.timeline({ paused: true });
42
+ tl.to("#hero", { keyframes: { "0%": { x: 0 }, "50%": { x: 100 }, "100%": { x: 200 } }, duration: 5 }, 0);`;
43
+
44
+ // A script body string (not full HTML) for the index-resolution helpers.
45
+ const KF_SCRIPT_BODY = KF_SCRIPT;
46
+ const DUP_SCRIPT_BODY = `
47
+ const tl = gsap.timeline({ paused: true });
48
+ tl.to("#hero", { keyframes: { "0%": { x: 0 }, "50%": { x: 100 }, "50%": { x: 150 }, "100%": { x: 200 } }, duration: 5 }, 0);`;
49
+
50
+ describe("resolveKeyframeIndexByPercentage", () => {
51
+ it("resolves a unique percentage to its 0-based index", () => {
52
+ expect(resolveKeyframeIndexByPercentage(KF_SCRIPT_BODY, ANIM_ID, 50)).toEqual({
53
+ keyframeIndex: 1,
54
+ });
55
+ expect(resolveKeyframeIndexByPercentage(KF_SCRIPT_BODY, ANIM_ID, 100)).toEqual({
56
+ keyframeIndex: 2,
57
+ });
58
+ });
59
+
60
+ it("matches within ~0.001 tolerance", () => {
61
+ expect(resolveKeyframeIndexByPercentage(KF_SCRIPT_BODY, ANIM_ID, 50.0005).keyframeIndex).toBe(
62
+ 1,
63
+ );
64
+ });
65
+
66
+ it("returns null with not_found when no percentage matches", () => {
67
+ expect(resolveKeyframeIndexByPercentage(KF_SCRIPT_BODY, ANIM_ID, 33)).toEqual({
68
+ keyframeIndex: null,
69
+ reason: "not_found",
70
+ });
71
+ });
72
+
73
+ it("returns null with no_keyframes for an unknown animation", () => {
74
+ expect(resolveKeyframeIndexByPercentage(KF_SCRIPT_BODY, "#nope-to-0", 50)).toEqual({
75
+ keyframeIndex: null,
76
+ reason: "no_keyframes",
77
+ });
78
+ });
79
+
80
+ it("returns null with no_keyframes when script is empty", () => {
81
+ expect(resolveKeyframeIndexByPercentage(null, ANIM_ID, 50).reason).toBe("no_keyframes");
82
+ });
83
+
84
+ it("no-ops on ambiguity (duplicate-percentage keyframes — PR #1498 landmine)", () => {
85
+ expect(resolveKeyframeIndexByPercentage(DUP_SCRIPT_BODY, ANIM_ID, 50)).toEqual({
86
+ keyframeIndex: null,
87
+ reason: "ambiguous",
88
+ });
89
+ });
90
+
91
+ // Regression: a from/fromTo tween's id may normalize to "-to-" on write, so a
92
+ // "-from-"/"-fromTo-" animationId must fall back to the converted id (matching
93
+ // the writer's locateAnimationWithFallback) — else the keyframe diff goes blind.
94
+ it("falls back from a -from- id to the -to- tween", () => {
95
+ const fromId = ANIM_ID.replace("-to-", "-from-");
96
+ expect(resolveKeyframeIndexByPercentage(KF_SCRIPT_BODY, fromId, 50)).toEqual({
97
+ keyframeIndex: 1,
98
+ });
99
+ });
100
+ });
101
+
102
+ describe("keyframeOpToEditOp", () => {
103
+ it("maps add → addGsapKeyframe with position = percentage", () => {
104
+ const op: ShadowKeyframeOp = {
105
+ kind: "add",
106
+ animationId: ANIM_ID,
107
+ percentage: 25,
108
+ properties: { x: 50 },
109
+ };
110
+ expect(keyframeOpToEditOp(op, KF_SCRIPT_BODY)).toEqual({
111
+ op: { type: "addGsapKeyframe", animationId: ANIM_ID, position: 25, value: { x: 50 } },
112
+ });
113
+ });
114
+
115
+ it("maps remove → removeGsapKeyframe with resolved index", () => {
116
+ const op: ShadowKeyframeOp = { kind: "remove", animationId: ANIM_ID, percentage: 50 };
117
+ expect(keyframeOpToEditOp(op, KF_SCRIPT_BODY)).toEqual({
118
+ op: { type: "removeGsapKeyframe", animationId: ANIM_ID, keyframeIndex: 1 },
119
+ });
120
+ });
121
+
122
+ it("returns null op + reason when remove percentage is ambiguous", () => {
123
+ const op: ShadowKeyframeOp = { kind: "remove", animationId: ANIM_ID, percentage: 50 };
124
+ expect(keyframeOpToEditOp(op, DUP_SCRIPT_BODY)).toEqual({ op: null, reason: "ambiguous" });
125
+ });
126
+ });
127
+
128
+ describe("gsapKeyframeFidelityMismatches", () => {
129
+ it("reports no mismatches when keyframe arrays match", () => {
130
+ expect(gsapKeyframeFidelityMismatches(KF_SCRIPT_BODY, KF_SCRIPT_BODY, ANIM_ID)).toEqual([]);
131
+ });
132
+
133
+ it("reports a keyframes mismatch when arrays diverge", () => {
134
+ const other = `
135
+ const tl = gsap.timeline({ paused: true });
136
+ tl.to("#hero", { keyframes: { "0%": { x: 0 }, "50%": { x: 999 }, "100%": { x: 200 } }, duration: 5 }, 0);`;
137
+ const mismatches = gsapKeyframeFidelityMismatches(KF_SCRIPT_BODY, other, ANIM_ID);
138
+ expect(mismatches.some((m) => m.property === "keyframes")).toBe(true);
139
+ });
140
+ });
141
+
142
+ describe("runShadowGsapKeyframeFidelity (add)", () => {
143
+ it("emits gsap_keyframe with a keyframes mismatch when SDK adds but server didn't", async () => {
144
+ const beforeHtml = gsapHtml(KF_SCRIPT);
145
+ // server script unchanged (server "failed" to add the 25% keyframe) → drift
146
+ const session = await openComposition(beforeHtml);
147
+ const serverScript = session
148
+ .serialize()
149
+ .match(/<script\b[^>]*>([\s\S]*?)<\/script[^>]*>/i)?.[1];
150
+ expect(serverScript).toBeTruthy();
151
+ const op: ShadowKeyframeOp = {
152
+ kind: "add",
153
+ animationId: ANIM_ID,
154
+ percentage: 25,
155
+ properties: { x: 50 },
156
+ };
157
+ await runShadowGsapKeyframeFidelity(beforeHtml, op, serverScript);
158
+ const props = lastShadow();
159
+ expect(props?.op).toBe("gsap_keyframe");
160
+ expect(props?.dispatched).toBe(true);
161
+ expect(props?.mismatchCount).toBe(1);
162
+ });
163
+
164
+ it("emits dispatched:true mismatchCount:0 when SDK and server agree", async () => {
165
+ const beforeHtml = gsapHtml(KF_SCRIPT);
166
+ // Build the server's resulting script by applying the same op via the SDK.
167
+ const serverSession = await openComposition(beforeHtml);
168
+ serverSession.batch(() =>
169
+ serverSession.dispatch({
170
+ type: "addGsapKeyframe",
171
+ animationId: ANIM_ID,
172
+ position: 25,
173
+ value: { x: 50 },
174
+ }),
175
+ );
176
+ const serverScript = serverSession
177
+ .serialize()
178
+ .match(/<script\b[^>]*>([\s\S]*?)<\/script[^>]*>/i)?.[1];
179
+ const op: ShadowKeyframeOp = {
180
+ kind: "add",
181
+ animationId: ANIM_ID,
182
+ percentage: 25,
183
+ properties: { x: 50 },
184
+ };
185
+ await runShadowGsapKeyframeFidelity(beforeHtml, op, serverScript);
186
+ const props = lastShadow();
187
+ expect(props?.op).toBe("gsap_keyframe");
188
+ expect(props?.dispatched).toBe(true);
189
+ expect(props?.mismatchCount).toBe(0);
190
+ });
191
+ });
192
+
193
+ describe("runShadowGsapKeyframeFidelity (remove)", () => {
194
+ it("no-ops with reason when remove percentage is ambiguous", async () => {
195
+ const beforeHtml = gsapHtml(DUP_SCRIPT_BODY);
196
+ const op: ShadowKeyframeOp = { kind: "remove", animationId: ANIM_ID, percentage: 50 };
197
+ await runShadowGsapKeyframeFidelity(beforeHtml, op, "non-empty-server-script gsap");
198
+ const props = lastShadow();
199
+ expect(props?.op).toBe("gsap_keyframe");
200
+ expect(props?.dispatched).toBe(false);
201
+ expect(props?.reason).toBe("ambiguous");
202
+ });
203
+
204
+ it("dispatches a resolved remove and diffs", async () => {
205
+ const beforeHtml = gsapHtml(KF_SCRIPT);
206
+ const serverSession = await openComposition(beforeHtml);
207
+ serverSession.batch(() =>
208
+ serverSession.dispatch({
209
+ type: "removeGsapKeyframe",
210
+ animationId: ANIM_ID,
211
+ keyframeIndex: 1,
212
+ }),
213
+ );
214
+ const serverScript = serverSession
215
+ .serialize()
216
+ .match(/<script\b[^>]*>([\s\S]*?)<\/script[^>]*>/i)?.[1];
217
+ const op: ShadowKeyframeOp = { kind: "remove", animationId: ANIM_ID, percentage: 50 };
218
+ await runShadowGsapKeyframeFidelity(beforeHtml, op, serverScript);
219
+ const props = lastShadow();
220
+ expect(props?.op).toBe("gsap_keyframe");
221
+ expect(props?.dispatched).toBe(true);
222
+ expect(props?.mismatchCount).toBe(0);
223
+ });
224
+ });
225
+
226
+ describe("runShadowGsapKeyframeFidelity (guards)", () => {
227
+ it("skips when there is no server script", async () => {
228
+ const op: ShadowKeyframeOp = {
229
+ kind: "add",
230
+ animationId: ANIM_ID,
231
+ percentage: 25,
232
+ properties: { x: 50 },
233
+ };
234
+ await runShadowGsapKeyframeFidelity(gsapHtml(KF_SCRIPT), op, null);
235
+ expect(lastShadow()).toBeUndefined();
236
+ });
237
+ });
238
+
239
+ describe("runShadowDispatch unmapped-type guard", () => {
240
+ const ELEMENT_HTML = /* html */ `<!DOCTYPE html><html><body>
241
+ <div data-hf-id="hf-box" style="color: red;">Hi</div>
242
+ </body></html>`;
243
+
244
+ it("emits unmapped_type when a PatchOperation type isn't mapped", async () => {
245
+ const session = await openComposition(ELEMENT_HTML);
246
+ // PatchOperation.type is a closed union today; cast to exercise the defensive
247
+ // guard for a future unmapped type.
248
+ const ops = [{ type: "future-op", property: "x", value: "1" } as unknown as PatchOperation];
249
+ runShadowDispatch(session, { hfId: "hf-box" } as never, ops);
250
+ const props = lastShadow();
251
+ expect(props?.op).toBe("property");
252
+ expect(props?.dispatched).toBe(false);
253
+ expect(props?.reason).toBe("unmapped_type");
254
+ expect(props?.type).toBe("future-op");
255
+ });
256
+
257
+ it("dispatches normally for known PatchOperation types", async () => {
258
+ const session = await openComposition(ELEMENT_HTML);
259
+ const ops: PatchOperation[] = [{ type: "inline-style", property: "color", value: "#00f" }];
260
+ runShadowDispatch(session, { hfId: "hf-box" } as never, ops);
261
+ const props = lastShadow();
262
+ expect(props?.dispatched).toBe(true);
263
+ expect(props?.reason).toBeUndefined();
264
+ });
265
+ });
@@ -0,0 +1,257 @@
1
+ /**
2
+ * GSAP keyframe-op shadow (serialize round-trip diff). New module for the Stage 7
3
+ * shadow-parity push — kept out of sdkShadow.ts / sdkShadowGsapFidelity.ts so the
4
+ * shared files stay untouched (only additive imports) and the studio 600-line cap
5
+ * holds.
6
+ *
7
+ * Unlike tweens, the SDK exposes NO keyframe reader on ElementSnapshot, so there
8
+ * is no existence-parity path here. Instead we compare the two writers' output:
9
+ * open a fresh SDK doc from the server's pre-op file, dispatch the equivalent
10
+ * keyframe op, serialize, and diff the SDK's GSAP script against the server's
11
+ * resulting script.
12
+ *
13
+ * gsapFidelityMismatches (reused) matches tweens by resolved target element +
14
+ * method + position and diffs tween-level fields — but it does NOT look inside a
15
+ * tween's `keyframes` array. Keyframe drift therefore needs a dedicated diff,
16
+ * layered on top of the reused tween-level diff, matched by the GSAP animation id.
17
+ *
18
+ * SDK mapping (main, pre PR #1498 percentage-variant):
19
+ * add → addGsapKeyframe{animationId, position: percentage, value: properties}
20
+ * remove → removeGsapKeyframe{animationId, keyframeIndex} — the studio op is
21
+ * percentage-based, so we resolve percentage → index against the pre-op
22
+ * script (KF_PERCENT_TOLERANCE, aligned with the writer ~0.001) and
23
+ * no-op on ambiguity (duplicate-percentage keyframes can't be told
24
+ * apart by percentage — landmine from PR #1498).
25
+ */
26
+
27
+ import { openComposition } from "@hyperframes/sdk";
28
+ import type { EditOp } from "@hyperframes/sdk";
29
+ import { parseGsapScriptAcorn } from "@hyperframes/core/gsap-parser-acorn";
30
+ import type { GsapPercentageKeyframe } from "@hyperframes/core/gsap-parser";
31
+ import { STUDIO_SDK_SHADOW_ENABLED } from "../components/editor/manualEditingAvailability";
32
+ import { trackStudioEvent } from "./studioTelemetry";
33
+ import type { SdkShadowMismatch } from "./sdkShadow";
34
+ import {
35
+ extractGsapScript,
36
+ gsapFidelityMismatches,
37
+ makeSelectorResolver,
38
+ } from "./sdkShadowGsapFidelity";
39
+
40
+ // Match the GSAP writer's percentage equality tolerance so a remove resolves to
41
+ // the same keyframe the server would pick (writer rounds to ~3 decimals).
42
+ const KF_PERCENT_TOLERANCE = 0.001;
43
+
44
+ export type ShadowKeyframeOp =
45
+ | {
46
+ kind: "add";
47
+ animationId: string;
48
+ percentage: number;
49
+ properties: Record<string, number | string>;
50
+ }
51
+ | { kind: "remove"; animationId: string; percentage: number };
52
+
53
+ // ─── percentage → SDK op mapping ──────────────────────────────────────────────
54
+
55
+ function findAnimationKeyframes(
56
+ script: string,
57
+ animationId: string,
58
+ ): GsapPercentageKeyframe[] | null {
59
+ const parsed = parseGsapScriptAcorn(script);
60
+ // Match the writer's locateAnimationWithFallback (gsapParser.ts): a from/fromTo
61
+ // tween's derived id may be normalized to "-to-" on write, so fall back to the
62
+ // converted id when the exact one isn't found — otherwise the keyframe diff
63
+ // goes blind (both scripts resolve null → falsely "clean") on converted tweens.
64
+ const convertedId = animationId.replace(/-from-|-fromTo-/, "-to-");
65
+ const anim =
66
+ parsed.animations.find((a) => a.id === animationId) ??
67
+ parsed.animations.find((a) => a.id === convertedId);
68
+ return anim?.keyframes?.keyframes ?? null;
69
+ }
70
+
71
+ export interface KeyframeRemoveResolution {
72
+ /** Resolved 0-based index, or null when it can't be safely resolved. */
73
+ keyframeIndex: number | null;
74
+ /** Why no index — for telemetry when keyframeIndex is null. */
75
+ reason?: "no_keyframes" | "not_found" | "ambiguous";
76
+ }
77
+
78
+ /**
79
+ * Resolve a percentage-based remove to a keyframe index against the pre-op
80
+ * script. Returns null index (with a reason) when there are no keyframes, the
81
+ * percentage matches none, or — per the PR #1498 landmine — more than one
82
+ * keyframe shares the percentage (can't be disambiguated by percentage alone).
83
+ * Pure + exported so the mapping is unit-testable without an SDK session.
84
+ */
85
+ export function resolveKeyframeIndexByPercentage(
86
+ script: string | null | undefined,
87
+ animationId: string,
88
+ percentage: number,
89
+ ): KeyframeRemoveResolution {
90
+ if (!script) return { keyframeIndex: null, reason: "no_keyframes" };
91
+ const kfs = findAnimationKeyframes(script, animationId);
92
+ if (!kfs || kfs.length === 0) return { keyframeIndex: null, reason: "no_keyframes" };
93
+ const matches: number[] = [];
94
+ for (let i = 0; i < kfs.length; i++) {
95
+ if (Math.abs(kfs[i]?.percentage - percentage) <= KF_PERCENT_TOLERANCE) matches.push(i);
96
+ }
97
+ if (matches.length === 0) return { keyframeIndex: null, reason: "not_found" };
98
+ if (matches.length > 1) return { keyframeIndex: null, reason: "ambiguous" };
99
+ return { keyframeIndex: matches[0] };
100
+ }
101
+
102
+ /**
103
+ * Map a studio keyframe op to the SDK EditOp. For a remove this needs the pre-op
104
+ * script to resolve percentage → index; returns null (with a reason) when the
105
+ * index can't be safely resolved so the caller can emit a no-op-with-reason
106
+ * event instead of dispatching the wrong keyframe.
107
+ */
108
+ export function keyframeOpToEditOp(
109
+ op: ShadowKeyframeOp,
110
+ beforeScript: string | null | undefined,
111
+ ): { op: EditOp } | { op: null; reason: string } {
112
+ if (op.kind === "add") {
113
+ return {
114
+ op: {
115
+ type: "addGsapKeyframe",
116
+ animationId: op.animationId,
117
+ position: op.percentage,
118
+ value: op.properties,
119
+ },
120
+ };
121
+ }
122
+ const resolved = resolveKeyframeIndexByPercentage(beforeScript, op.animationId, op.percentage);
123
+ if (resolved.keyframeIndex === null) {
124
+ return { op: null, reason: resolved.reason ?? "unresolved" };
125
+ }
126
+ return {
127
+ op: {
128
+ type: "removeGsapKeyframe",
129
+ animationId: op.animationId,
130
+ keyframeIndex: resolved.keyframeIndex,
131
+ },
132
+ };
133
+ }
134
+
135
+ // ─── Keyframe-aware fidelity diff ─────────────────────────────────────────────
136
+
137
+ function canonicalKeyframe(kf: GsapPercentageKeyframe): string {
138
+ const props: Record<string, unknown> = {};
139
+ for (const key of Object.keys(kf.properties).sort()) {
140
+ const v = kf.properties[key];
141
+ props[key] =
142
+ typeof v === "string" && v.trim() !== "" && !Number.isNaN(Number(v)) ? Number(v) : v;
143
+ }
144
+ return JSON.stringify({ pct: Math.round(kf.percentage * 1000) / 1000, ease: kf.ease, props });
145
+ }
146
+
147
+ function canonicalKeyframes(kfs: GsapPercentageKeyframe[] | null): string {
148
+ if (!kfs) return "[]";
149
+ return JSON.stringify(
150
+ [...kfs].sort((a, b) => a.percentage - b.percentage).map(canonicalKeyframe),
151
+ );
152
+ }
153
+
154
+ /**
155
+ * Diff two GSAP scripts for a keyframe op: the reused tween-level diff PLUS a
156
+ * keyframe-array comparison for the targeted animation (which the tween-level
157
+ * diff doesn't inspect). Reports a `keyframes` value_mismatch when the SDK and
158
+ * server keyframe arrays diverge canonically.
159
+ */
160
+ export function gsapKeyframeFidelityMismatches(
161
+ sdkScript: string,
162
+ serverScript: string,
163
+ animationId: string,
164
+ resolveSelector?: (sel: string) => string,
165
+ ): SdkShadowMismatch[] {
166
+ const mismatches = gsapFidelityMismatches(sdkScript, serverScript, resolveSelector);
167
+ const sdkKfs = findAnimationKeyframes(sdkScript, animationId);
168
+ const serverKfs = findAnimationKeyframes(serverScript, animationId);
169
+ const sdkCanon = canonicalKeyframes(sdkKfs);
170
+ const serverCanon = canonicalKeyframes(serverKfs);
171
+ if (sdkCanon !== serverCanon) {
172
+ mismatches.push({
173
+ kind: "value_mismatch",
174
+ hfId: animationId,
175
+ property: "keyframes",
176
+ expected: serverCanon,
177
+ actual: sdkCanon,
178
+ });
179
+ }
180
+ return mismatches;
181
+ }
182
+
183
+ // ─── Telemetry runner ─────────────────────────────────────────────────────────
184
+
185
+ /**
186
+ * Shadow a GSAP keyframe op: open a fresh SDK doc from the server's pre-op file,
187
+ * apply the equivalent keyframe op, serialize, and diff against the server's
188
+ * resulting script. Emits sdk_shadow_dispatch op: "gsap_keyframe". Async,
189
+ * fire-and-forget; server stays authoritative. No-op when shadow is disabled.
190
+ */
191
+ export async function runShadowGsapKeyframeFidelity(
192
+ beforeHtml: string | null | undefined,
193
+ op: ShadowKeyframeOp,
194
+ serverScript: string | null | undefined,
195
+ ): Promise<void> {
196
+ if (!STUDIO_SDK_SHADOW_ENABLED) return;
197
+ // No server script to diff against → skip the (costly) openComposition.
198
+ if (!serverScript || !beforeHtml) return;
199
+ const beforeScript = extractGsapScript(beforeHtml);
200
+ const mapped = keyframeOpToEditOp(op, beforeScript);
201
+ if (mapped.op === null) {
202
+ // Ambiguous / not-found percentage: don't dispatch the wrong keyframe.
203
+ trackStudioEvent("sdk_shadow_dispatch", {
204
+ op: "gsap_keyframe",
205
+ dispatched: false,
206
+ reason: mapped.reason,
207
+ mismatchCount: 0,
208
+ });
209
+ return;
210
+ }
211
+ const editOp = mapped.op;
212
+ try {
213
+ const session = await openComposition(beforeHtml);
214
+ const verdict = session.can(editOp);
215
+ if (!verdict.ok) {
216
+ trackStudioEvent("sdk_shadow_dispatch", {
217
+ op: "gsap_keyframe",
218
+ dispatched: false,
219
+ reason: "cannot_dispatch",
220
+ code: verdict.code,
221
+ mismatchCount: 0,
222
+ });
223
+ return;
224
+ }
225
+ session.batch(() => session.dispatch(editOp));
226
+ const sdkScript = extractGsapScript(session.serialize());
227
+ if (sdkScript == null) {
228
+ trackStudioEvent("sdk_shadow_dispatch", {
229
+ op: "gsap_keyframe",
230
+ dispatched: false,
231
+ reason: "no_sdk_script",
232
+ mismatchCount: 0,
233
+ });
234
+ return;
235
+ }
236
+ const mismatches = gsapKeyframeFidelityMismatches(
237
+ sdkScript,
238
+ serverScript,
239
+ op.animationId,
240
+ makeSelectorResolver(beforeHtml),
241
+ );
242
+ trackStudioEvent("sdk_shadow_dispatch", {
243
+ op: "gsap_keyframe",
244
+ dispatched: true,
245
+ mismatchCount: mismatches.length,
246
+ mismatches: JSON.stringify(mismatches),
247
+ });
248
+ } catch (err) {
249
+ trackStudioEvent("sdk_shadow_dispatch", {
250
+ op: "gsap_keyframe",
251
+ dispatched: false,
252
+ reason: "fidelity_error",
253
+ error: String(err),
254
+ mismatchCount: 0,
255
+ });
256
+ }
257
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Relative-epsilon numeric equality shared by the shadow diffs (timing parity +
3
+ * GSAP value fidelity). Both writers round-trip durations/positions through JS
4
+ * number formatting, so a value like 3.1 can read back as 3.0999999999999996.
5
+ * Treat values within 1e-6 * max(1, |a|, |b|) as equal — tight enough that a
6
+ * real 2 vs 1 (or 0.5 vs 0.49) still flags, loose enough to absorb float noise.
7
+ */
8
+ export function relEqual(a: number, b: number): boolean {
9
+ if (a === b) return true;
10
+ return Math.abs(a - b) <= 1e-6 * Math.max(1, Math.abs(a), Math.abs(b));
11
+ }
@@ -14,6 +14,12 @@ export interface AppToast {
14
14
  }
15
15
 
16
16
  export type RightPanelTab = "layers" | "design" | "renders" | "block-params";
17
+ export type RightInspectorPane = "layers" | "design";
18
+
19
+ export interface RightInspectorPanes {
20
+ layers: boolean;
21
+ design: boolean;
22
+ }
17
23
 
18
24
  export interface AgentModalAnchorPoint {
19
25
  x: number;
@@ -1 +0,0 @@
1
- *,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.\!container{width:100%!important}.container{width:100%}@media(min-width:640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media(min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media(min-width:1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media(min-width:1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media(min-width:1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.\!visible{visibility:visible!important}.visible{visibility:visible}.invisible{visibility:hidden}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.inset-2{top:.5rem;right:.5rem;bottom:.5rem;left:.5rem}.-bottom-1\.5{bottom:-.375rem}.-right-1\.5{right:-.375rem}.bottom-0{bottom:0}.bottom-1{bottom:.25rem}.bottom-2{bottom:.5rem}.bottom-3{bottom:.75rem}.bottom-6{bottom:1.5rem}.bottom-full{bottom:100%}.left-0{left:0}.left-1{left:.25rem}.left-1\/2{left:50%}.left-2{left:.5rem}.left-3{left:.75rem}.right-0{right:0}.right-1{right:.25rem}.right-2{right:.5rem}.right-3{right:.75rem}.right-6{right:1.5rem}.top-0{top:0}.top-1{top:.25rem}.top-1\/2{top:50%}.top-14{top:3.5rem}.top-2{top:.5rem}.top-3{top:.75rem}.top-\[2px\]{top:2px}.top-\[calc\(100\%\+6px\)\]{top:calc(100% + 6px)}.top-full{top:100%}.isolate{isolation:isolate}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.z-\[100\]{z-index:100}.z-\[1\]{z-index:1}.z-\[200\]{z-index:200}.z-\[3\]{z-index:3}.z-\[4\]{z-index:4}.z-\[90\]{z-index:90}.z-\[91\]{z-index:91}.z-\[92\]{z-index:92}.z-\[9999\]{z-index:9999}.mx-1{margin-left:.25rem;margin-right:.25rem}.my-0\.5{margin-top:.125rem;margin-bottom:.125rem}.my-1{margin-top:.25rem;margin-bottom:.25rem}.-mt-\[72px\]{margin-top:-72px}.mb-0\.5{margin-bottom:.125rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.ml-1{margin-left:.25rem}.ml-1\.5{margin-left:.375rem}.ml-3{margin-left:.75rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-\[22px\]{margin-top:22px}.\!block{display:block!important}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.contents{display:contents}.hidden{display:none}.aspect-video{aspect-ratio:16 / 9}.h-0\.5{height:.125rem}.h-1{height:.25rem}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-11{height:2.75rem}.h-2{height:.5rem}.h-24{height:6rem}.h-28{height:7rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-32{height:8rem}.h-36{height:9rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\[3px\]{height:3px}.h-\[45px\]{height:45px}.h-\[52px\]{height:52px}.h-\[5px\]{height:5px}.h-full{height:100%}.h-px{height:1px}.max-h-24{max-height:6rem}.max-h-40{max-height:10rem}.max-h-64{max-height:16rem}.max-h-\[70\%\]{max-height:70%}.max-h-\[80vh\]{max-height:80vh}.max-h-full{max-height:100%}.min-h-0{min-height:0px}.min-h-7{min-height:1.75rem}.min-h-8{min-height:2rem}.min-h-9{min-height:2.25rem}.min-h-\[240px\]{min-height:240px}.w-1\.5{width:.375rem}.w-10{width:2.5rem}.w-14{width:3.5rem}.w-16{width:4rem}.w-2{width:.5rem}.w-20{width:5rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-52{width:13rem}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-80{width:20rem}.w-9{width:2.25rem}.w-\[118px\]{width:118px}.w-\[160px\]{width:160px}.w-\[292px\]{width:292px}.w-\[480px\]{width:480px}.w-\[560px\]{width:560px}.w-full{width:100%}.w-px{width:1px}.min-w-0{min-width:0px}.min-w-7{min-width:1.75rem}.min-w-8{min-width:2rem}.min-w-9{min-width:2.25rem}.min-w-\[140px\]{min-width:140px}.min-w-\[160px\]{min-width:160px}.min-w-\[16px\]{min-width:16px}.min-w-\[180px\]{min-width:180px}.min-w-\[20px\]{min-width:20px}.min-w-\[220px\]{min-width:220px}.min-w-\[36px\]{min-width:36px}.min-w-\[44px\]{min-width:44px}.min-w-\[56px\]{min-width:56px}.min-w-\[58px\]{min-width:58px}.min-w-\[96px\]{min-width:96px}.max-w-\[260px\]{max-width:260px}.max-w-\[280px\]{max-width:280px}.max-w-\[calc\(100vw-32px\)\]{max-width:calc(100vw - 32px)}.max-w-full{max-width:100%}.max-w-xl{max-width:36rem}.flex-1{flex:1 1 0%}.flex-shrink{flex-shrink:1}.flex-shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.flex-grow,.grow{flex-grow:1}.-translate-x-1\/2{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-0{--tw-translate-y: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-2{--tw-translate-y: .5rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-rotate-90{--tw-rotate: -90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-90{--tw-rotate: 90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.\!transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))!important}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-col-resize{cursor:col-resize}.cursor-crosshair{cursor:crosshair}.cursor-default{cursor:default}.cursor-ew-resize{cursor:ew-resize}.cursor-grab{cursor:grab}.cursor-grabbing{cursor:grabbing}.cursor-help{cursor:help}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.cursor-row-resize{cursor:row-resize}.touch-none{touch-action:none}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-none{resize:none}.resize-y{resize:vertical}.\!resize{resize:both!important}.resize{resize:both}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.grid-cols-\[minmax\(0\,1fr\)_68px_28px\]{grid-template-columns:minmax(0,1fr) 68px 28px}.grid-cols-\[minmax\(0\,1fr\)_auto\]{grid-template-columns:minmax(0,1fr) auto}.grid-cols-\[minmax\(0\,1fr\)_auto_auto\]{grid-template-columns:minmax(0,1fr) auto auto}.grid-cols-\[repeat\(auto-fit\,minmax\(118px\,1fr\)\)\]{grid-template-columns:repeat(auto-fit,minmax(118px,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-2\.5{gap:.625rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-\[2px\]{gap:2px}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.gap-y-1{row-gap:.25rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-\[14px\]{border-radius:14px}.rounded-\[18px\]{border-radius:18px}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-sm{border-radius:.125rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-none{border-style:none}.border-green-500\/30{border-color:#22c55e4d}.border-neutral-600{--tw-border-opacity: 1;border-color:rgb(82 82 82 / var(--tw-border-opacity, 1))}.border-neutral-700{--tw-border-opacity: 1;border-color:rgb(64 64 64 / var(--tw-border-opacity, 1))}.border-neutral-700\/40{border-color:#40404066}.border-neutral-700\/50{border-color:#40404080}.border-neutral-700\/60{border-color:#40404099}.border-neutral-800{--tw-border-opacity: 1;border-color:rgb(38 38 38 / var(--tw-border-opacity, 1))}.border-neutral-800\/30{border-color:#2626264d}.border-neutral-800\/40{border-color:#26262666}.border-neutral-800\/50{border-color:#26262680}.border-neutral-800\/60{border-color:#26262699}.border-panel-border{--tw-border-opacity: 1;border-color:rgb(30 30 30 / var(--tw-border-opacity, 1))}.border-purple-500\/20{border-color:#a855f733}.border-red-300\/20{border-color:#fca5a533}.border-red-400\/60{border-color:#f8717199}.border-red-500{--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity, 1))}.border-red-500\/30{border-color:#ef44444d}.border-studio-accent{--tw-border-opacity: 1;border-color:rgb(60 230 172 / var(--tw-border-opacity, 1))}.border-studio-accent\/25{border-color:#3ce6ac40}.border-studio-accent\/30{border-color:#3ce6ac4d}.border-studio-accent\/50{border-color:#3ce6ac80}.border-studio-accent\/60{border-color:#3ce6ac99}.border-studio-accent\/70{border-color:#3ce6acb3}.border-studio-accent\/80{border-color:#3ce6accc}.border-transparent{border-color:transparent}.border-white{--tw-border-opacity: 1;border-color:rgb(255 255 255 / var(--tw-border-opacity, 1))}.border-white\/20{border-color:#fff3}.border-white\/90{border-color:#ffffffe6}.border-t-neutral-500{--tw-border-opacity: 1;border-top-color:rgb(115 115 115 / var(--tw-border-opacity, 1))}.bg-\[\#0a0a0b\]{--tw-bg-opacity: 1;background-color:rgb(10 10 11 / var(--tw-bg-opacity, 1))}.bg-\[\#3CE6AC\]\/10{background-color:#3ce6ac1a}.bg-\[\#3CE6AC\]\/5{background-color:#3ce6ac0d}.bg-amber-400{--tw-bg-opacity: 1;background-color:rgb(251 191 36 / var(--tw-bg-opacity, 1))}.bg-amber-500\/10{background-color:#f59e0b1a}.bg-amber-500\/15{background-color:#f59e0b26}.bg-amber-500\/20{background-color:#f59e0b33}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.bg-black\/40{background-color:#0006}.bg-black\/50{background-color:#00000080}.bg-black\/60{background-color:#0009}.bg-blue-400{--tw-bg-opacity: 1;background-color:rgb(96 165 250 / var(--tw-bg-opacity, 1))}.bg-blue-500\/15{background-color:#3b82f626}.bg-cyan-400{--tw-bg-opacity: 1;background-color:rgb(34 211 238 / var(--tw-bg-opacity, 1))}.bg-cyan-500\/15{background-color:#06b6d426}.bg-emerald-400{--tw-bg-opacity: 1;background-color:rgb(52 211 153 / var(--tw-bg-opacity, 1))}.bg-emerald-500{--tw-bg-opacity: 1;background-color:rgb(16 185 129 / var(--tw-bg-opacity, 1))}.bg-emerald-500\/15{background-color:#10b98126}.bg-green-400{--tw-bg-opacity: 1;background-color:rgb(74 222 128 / var(--tw-bg-opacity, 1))}.bg-green-500\/15{background-color:#22c55e26}.bg-green-500\/20{background-color:#22c55e33}.bg-green-600{--tw-bg-opacity: 1;background-color:rgb(22 163 74 / var(--tw-bg-opacity, 1))}.bg-neutral-600{--tw-bg-opacity: 1;background-color:rgb(82 82 82 / var(--tw-bg-opacity, 1))}.bg-neutral-700{--tw-bg-opacity: 1;background-color:rgb(64 64 64 / var(--tw-bg-opacity, 1))}.bg-neutral-700\/40{background-color:#40404066}.bg-neutral-800{--tw-bg-opacity: 1;background-color:rgb(38 38 38 / var(--tw-bg-opacity, 1))}.bg-neutral-800\/50{background-color:#26262680}.bg-neutral-800\/60{background-color:#26262699}.bg-neutral-800\/70{background-color:#262626b3}.bg-neutral-900{--tw-bg-opacity: 1;background-color:rgb(23 23 23 / var(--tw-bg-opacity, 1))}.bg-neutral-900\/50{background-color:#17171780}.bg-neutral-900\/60{background-color:#17171799}.bg-neutral-900\/80{background-color:#171717cc}.bg-neutral-950{--tw-bg-opacity: 1;background-color:rgb(10 10 10 / var(--tw-bg-opacity, 1))}.bg-neutral-950\/80{background-color:#0a0a0acc}.bg-panel-accent{--tw-bg-opacity: 1;background-color:rgb(60 230 172 / var(--tw-bg-opacity, 1))}.bg-panel-accent\/10{background-color:#3ce6ac1a}.bg-panel-accent\/40{background-color:#3ce6ac66}.bg-panel-bg{--tw-bg-opacity: 1;background-color:rgb(12 12 14 / var(--tw-bg-opacity, 1))}.bg-panel-border{--tw-bg-opacity: 1;background-color:rgb(30 30 30 / var(--tw-bg-opacity, 1))}.bg-panel-hover{--tw-bg-opacity: 1;background-color:rgb(39 39 42 / var(--tw-bg-opacity, 1))}.bg-panel-input{--tw-bg-opacity: 1;background-color:rgb(22 22 24 / var(--tw-bg-opacity, 1))}.bg-pink-400{--tw-bg-opacity: 1;background-color:rgb(244 114 182 / var(--tw-bg-opacity, 1))}.bg-pink-500\/15{background-color:#ec489926}.bg-purple-400{--tw-bg-opacity: 1;background-color:rgb(192 132 252 / var(--tw-bg-opacity, 1))}.bg-purple-500\/10{background-color:#a855f71a}.bg-purple-500\/15{background-color:#a855f726}.bg-purple-900\/70{background-color:#581c87b3}.bg-red-400{--tw-bg-opacity: 1;background-color:rgb(248 113 113 / var(--tw-bg-opacity, 1))}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-red-500\/10{background-color:#ef44441a}.bg-red-500\/15{background-color:#ef444426}.bg-red-600{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity, 1))}.bg-red-900\/60{background-color:#7f1d1d99}.bg-red-950\/30{background-color:#450a0a4d}.bg-red-950\/85{background-color:#450a0ad9}.bg-rose-400{--tw-bg-opacity: 1;background-color:rgb(251 113 133 / var(--tw-bg-opacity, 1))}.bg-rose-500\/15{background-color:#f43f5e26}.bg-studio-accent{--tw-bg-opacity: 1;background-color:rgb(60 230 172 / var(--tw-bg-opacity, 1))}.bg-studio-accent\/10{background-color:#3ce6ac1a}.bg-studio-accent\/15{background-color:#3ce6ac26}.bg-studio-accent\/20{background-color:#3ce6ac33}.bg-studio-accent\/5{background-color:#3ce6ac0d}.bg-studio-accent\/60{background-color:#3ce6ac99}.bg-studio-accent\/90{background-color:#3ce6ace6}.bg-studio-accent\/\[0\.03\]{background-color:#3ce6ac08}.bg-studio-accent\/\[0\.04\]{background-color:#3ce6ac0a}.bg-studio-accent\/\[0\.05\]{background-color:#3ce6ac0d}.bg-studio-accent\/\[0\.06\]{background-color:#3ce6ac0f}.bg-transparent{background-color:transparent}.bg-violet-400{--tw-bg-opacity: 1;background-color:rgb(167 139 250 / var(--tw-bg-opacity, 1))}.bg-violet-500\/15{background-color:#8b5cf626}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-white\/10{background-color:#ffffff1a}.bg-white\/15{background-color:#ffffff26}.bg-white\/5{background-color:#ffffff0d}.bg-white\/70{background-color:#ffffffb3}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}.from-black{--tw-gradient-from: #000 var(--tw-gradient-from-position);--tw-gradient-to: rgb(0 0 0 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-white{--tw-gradient-from: #fff var(--tw-gradient-from-position);--tw-gradient-to: rgb(255 255 255 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.to-transparent{--tw-gradient-to: transparent var(--tw-gradient-to-position)}.fill-neutral-600{fill:#525252}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0{padding:0}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-\[2px\]{padding:2px}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.py-\[5px\]{padding-top:5px;padding-bottom:5px}.py-\[7px\]{padding-top:7px;padding-bottom:7px}.py-px{padding-top:1px;padding-bottom:1px}.pb-0\.5{padding-bottom:.125rem}.pb-1{padding-bottom:.25rem}.pb-2{padding-bottom:.5rem}.pb-2\.5{padding-bottom:.625rem}.pb-3{padding-bottom:.75rem}.pl-1{padding-left:.25rem}.pl-4{padding-left:1rem}.pl-7{padding-left:1.75rem}.pr-2{padding-right:.5rem}.pt-0\.5{padding-top:.125rem}.pt-1{padding-top:.25rem}.pt-1\.5{padding-top:.375rem}.pt-2{padding-top:.5rem}.pt-2\.5{padding-top:.625rem}.pt-3{padding-top:.75rem}.pt-\[72px\]{padding-top:72px}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-middle{vertical-align:middle}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.text-\[7px\]{font-size:7px}.text-\[8px\]{font-size:8px}.text-\[9px\]{font-size:9px}.text-base{font-size:1rem;line-height:1.5rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.capitalize{text-transform:capitalize}.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)}.leading-4{line-height:1rem}.leading-5{line-height:1.25rem}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.leading-tight{line-height:1.25}.tracking-\[0\.14em\]{letter-spacing:.14em}.tracking-\[0\.16em\]{letter-spacing:.16em}.tracking-wider{letter-spacing:.05em}.text-\[\#09090B\]{--tw-text-opacity: 1;color:rgb(9 9 11 / var(--tw-text-opacity, 1))}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-cyan-400{--tw-text-opacity: 1;color:rgb(34 211 238 / var(--tw-text-opacity, 1))}.text-emerald-400{--tw-text-opacity: 1;color:rgb(52 211 153 / var(--tw-text-opacity, 1))}.text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.text-neutral-100{--tw-text-opacity: 1;color:rgb(245 245 245 / var(--tw-text-opacity, 1))}.text-neutral-200{--tw-text-opacity: 1;color:rgb(229 229 229 / var(--tw-text-opacity, 1))}.text-neutral-300{--tw-text-opacity: 1;color:rgb(212 212 212 / var(--tw-text-opacity, 1))}.text-neutral-400{--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity, 1))}.text-neutral-50{--tw-text-opacity: 1;color:rgb(250 250 250 / var(--tw-text-opacity, 1))}.text-neutral-500{--tw-text-opacity: 1;color:rgb(115 115 115 / var(--tw-text-opacity, 1))}.text-neutral-600{--tw-text-opacity: 1;color:rgb(82 82 82 / var(--tw-text-opacity, 1))}.text-neutral-700{--tw-text-opacity: 1;color:rgb(64 64 64 / var(--tw-text-opacity, 1))}.text-neutral-950{--tw-text-opacity: 1;color:rgb(10 10 10 / var(--tw-text-opacity, 1))}.text-orange-400\/70{color:#fb923cb3}.text-panel-accent{--tw-text-opacity: 1;color:rgb(60 230 172 / var(--tw-text-opacity, 1))}.text-panel-accent\/70{color:#3ce6acb3}.text-panel-text-1{--tw-text-opacity: 1;color:rgb(228 228 231 / var(--tw-text-opacity, 1))}.text-panel-text-2{--tw-text-opacity: 1;color:rgb(161 161 170 / var(--tw-text-opacity, 1))}.text-panel-text-3{--tw-text-opacity: 1;color:rgb(113 113 122 / var(--tw-text-opacity, 1))}.text-panel-text-4{--tw-text-opacity: 1;color:rgb(82 82 91 / var(--tw-text-opacity, 1))}.text-panel-text-5{--tw-text-opacity: 1;color:rgb(63 63 70 / var(--tw-text-opacity, 1))}.text-panel-text-5\/30{color:#3f3f464d}.text-pink-400{--tw-text-opacity: 1;color:rgb(244 114 182 / var(--tw-text-opacity, 1))}.text-purple-200{--tw-text-opacity: 1;color:rgb(233 213 255 / var(--tw-text-opacity, 1))}.text-purple-300{--tw-text-opacity: 1;color:rgb(216 180 254 / var(--tw-text-opacity, 1))}.text-purple-400{--tw-text-opacity: 1;color:rgb(192 132 252 / var(--tw-text-opacity, 1))}.text-red-100{--tw-text-opacity: 1;color:rgb(254 226 226 / var(--tw-text-opacity, 1))}.text-red-200{--tw-text-opacity: 1;color:rgb(254 202 202 / var(--tw-text-opacity, 1))}.text-red-300{--tw-text-opacity: 1;color:rgb(252 165 165 / var(--tw-text-opacity, 1))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-rose-400{--tw-text-opacity: 1;color:rgb(251 113 133 / var(--tw-text-opacity, 1))}.text-studio-accent{--tw-text-opacity: 1;color:rgb(60 230 172 / var(--tw-text-opacity, 1))}.text-studio-accent\/50{color:#3ce6ac80}.text-studio-accent\/80{color:#3ce6accc}.text-violet-400{--tw-text-opacity: 1;color:rgb(167 139 250 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-white\/60{color:#fff9}.text-white\/80{color:#fffc}.text-white\/90{color:#ffffffe6}.underline{text-decoration-line:underline}.line-through{text-decoration-line:line-through}.placeholder-neutral-600::-moz-placeholder{--tw-placeholder-opacity: 1;color:rgb(82 82 82 / var(--tw-placeholder-opacity, 1))}.placeholder-neutral-600::placeholder{--tw-placeholder-opacity: 1;color:rgb(82 82 82 / var(--tw-placeholder-opacity, 1))}.accent-studio-accent{accent-color:#3CE6AC}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-25{opacity:.25}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.mix-blend-difference{mix-blend-mode:difference}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_0_0_1px_rgba\(0\,0\,0\,0\.35\)\]{--tw-shadow: 0 0 0 1px rgba(0,0,0,.35);--tw-shadow-colored: 0 0 0 1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_0_0_1px_rgba\(0\,0\,0\,0\.45\)\]{--tw-shadow: 0 0 0 1px rgba(0,0,0,.45);--tw-shadow-colored: 0 0 0 1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_0_0_1px_rgba\(0\,0\,0\,0\.85\)\,0_6px_14px_rgba\(0\,0\,0\,0\.5\)\]{--tw-shadow: 0 0 0 1px rgba(0,0,0,.85),0 6px 14px rgba(0,0,0,.5);--tw-shadow-colored: 0 0 0 1px var(--tw-shadow-color), 0 6px 14px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_0_0_1px_rgba\(0\,0\,0\,0\.85\)\,0_8px_18px_rgba\(0\,0\,0\,0\.45\)\]{--tw-shadow: 0 0 0 1px rgba(0,0,0,.85),0 8px 18px rgba(0,0,0,.45);--tw-shadow-colored: 0 0 0 1px var(--tw-shadow-color), 0 8px 18px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_0_0_1px_rgba\(60\,230\,172\,0\.25\)\]{--tw-shadow: 0 0 0 1px rgba(60,230,172,.25);--tw-shadow-colored: 0 0 0 1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_0_0_1px_rgba\(60\,230\,172\,0\.3\)\]{--tw-shadow: 0 0 0 1px rgba(60,230,172,.3);--tw-shadow-colored: 0 0 0 1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_0_0_2px_rgba\(60\,230\,172\,0\.18\)\]{--tw-shadow: 0 0 0 2px rgba(60,230,172,.18);--tw-shadow-colored: 0 0 0 2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[inset_0_0_0_1px_rgba\(255\,255\,255\,0\.06\)\]{--tw-shadow: inset 0 0 0 1px rgba(255,255,255,.06);--tw-shadow-colored: inset 0 0 0 1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[inset_0_0_0_2px_rgba\(60\,230\,172\,0\.6\)\]{--tw-shadow: inset 0 0 0 2px rgba(60,230,172,.6);--tw-shadow-colored: inset 0 0 0 2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[inset_0_1px_0_rgba\(255\,255\,255\,0\.03\)\]{--tw-shadow: inset 0 1px 0 rgba(255,255,255,.03);--tw-shadow-colored: inset 0 1px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[inset_0_1px_0_rgba\(255\,255\,255\,0\.04\)\]{--tw-shadow: inset 0 1px 0 rgba(255,255,255,.04);--tw-shadow-colored: inset 0 1px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[inset_0_1px_0_rgba\(255\,255\,255\,0\.08\)\]{--tw-shadow: inset 0 1px 0 rgba(255,255,255,.08);--tw-shadow-colored: inset 0 1px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[inset_0_1px_2px_rgba\(0\,0\,0\,0\.55\)\]{--tw-shadow: inset 0 1px 2px rgba(0,0,0,.55);--tw-shadow-colored: inset 0 1px 2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-black\/30{--tw-shadow-color: rgb(0 0 0 / .3);--tw-shadow: var(--tw-shadow-colored)}.shadow-black\/40{--tw-shadow-color: rgb(0 0 0 / .4);--tw-shadow: var(--tw-shadow-colored)}.shadow-black\/50{--tw-shadow-color: rgb(0 0 0 / .5);--tw-shadow: var(--tw-shadow-colored)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.outline-1{outline-width:1px}.-outline-offset-1{outline-offset:-1px}.outline-\[\#3CE6AC\]\/30{outline-color:#3ce6ac4d}.outline-\[\#3CE6AC\]\/40{outline-color:#3ce6ac66}.ring{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-2{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-panel-accent\/30{--tw-ring-color: rgb(60 230 172 / .3)}.ring-studio-accent{--tw-ring-opacity: 1;--tw-ring-color: rgb(60 230 172 / var(--tw-ring-opacity, 1))}.ring-white\/50{--tw-ring-color: rgb(255 255 255 / .5)}.blur{--tw-blur: blur(8px);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)}.drop-shadow{--tw-drop-shadow: drop-shadow(0 1px 2px rgb(0 0 0 / .1)) drop-shadow(0 1px 1px rgb(0 0 0 / .06));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)}.grayscale{--tw-grayscale: grayscale(100%);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)}.invert{--tw-invert: invert(100%);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)}.sepia{--tw-sepia: sepia(100%);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)}.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-sm{--tw-backdrop-blur: blur(4px);-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,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-150{transition-duration:.15s}.duration-300{transition-duration:.3s}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.\[drag\:3\]{drag:3}.\[drag\:4\]{drag:4}.\[drag\:5\]{drag:5}.\[drag\:7\]{drag:7}.\[kf\:runtime\]{kf:runtime}.\[kf\:static\]{kf:static}:root{color-scheme:dark}body{margin:0;padding:0;background:#0a0a0a;color:#e5e5e5;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;overflow:hidden}#root{width:100vw;height:100vh;height:100dvh}.cm-editor{height:100%;font-size:13px}.cm-editor .cm-scroller{font-family:JetBrains Mono,Fira Code,SF Mono,monospace}.cm-editor.cm-focused{outline:none}.hf-loader{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.75rem;width:min(34rem,100%);padding:1.5rem;box-sizing:border-box;text-align:center;cursor:default;-moz-user-select:none;user-select:none;-webkit-user-select:none;-webkit-user-drag:none}.hf-frame{display:grid;place-items:center;width:100%;height:100%;min-height:12rem;border:1px solid rgba(255,255,255,.08);background:#00000085}.hf-loader-mark-frame{display:grid;place-items:center;overflow:visible;transform-origin:50% 50%;-moz-user-select:none;user-select:none;-webkit-user-select:none;-webkit-user-drag:none}.hf-loader-mark{display:block;overflow:visible;filter:drop-shadow(0 0 7px rgba(79,219,94,.2));-moz-user-select:none;user-select:none;-webkit-user-select:none;-webkit-user-drag:none}.hf-loader-title{font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;font-size:1rem;font-weight:600;letter-spacing:0;color:var(--hf-heading, #f4f4f5);max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.hf-loader-detail{max-width:32rem;min-height:2.5rem;overflow:hidden;color:var(--hf-text-secondary, rgba(244, 244, 245, .68));font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;font-size:.82rem;line-height:1.6}.hf-loader-mono{width:min(36rem,100%);min-height:1.5rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--hf-text-tertiary, rgba(244, 244, 245, .46));font-family:IBM Plex Mono,SF Mono,Fira Code,monospace;font-size:.75rem;letter-spacing:0;font-variant-numeric:tabular-nums}.hf-loader-progress{width:min(18rem,72vw);height:.375rem;overflow:hidden;border-radius:999px;background:#ffffff1a}@keyframes hf-thumb-fade{0%{opacity:0}to{opacity:1}}.hf-loader-progress__fill{width:100%;height:100%;transform:scaleX(0);transform-origin:left center;border-radius:inherit;background:linear-gradient(90deg,#06e3fa,#4fdb5e);transition:transform .16s ease}.placeholder\:text-neutral-600::-moz-placeholder{--tw-text-opacity: 1;color:rgb(82 82 82 / var(--tw-text-opacity, 1))}.placeholder\:text-neutral-600::placeholder{--tw-text-opacity: 1;color:rgb(82 82 82 / var(--tw-text-opacity, 1))}.last\:border-0:last-child{border-width:0px}.focus-within\:ring-1:focus-within{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-within\:ring-panel-accent\/30:focus-within{--tw-ring-color: rgb(60 230 172 / .3)}.hover\:border-neutral-500:hover{--tw-border-opacity: 1;border-color:rgb(115 115 115 / var(--tw-border-opacity, 1))}.hover\:border-neutral-600:hover{--tw-border-opacity: 1;border-color:rgb(82 82 82 / var(--tw-border-opacity, 1))}.hover\:border-neutral-700:hover{--tw-border-opacity: 1;border-color:rgb(64 64 64 / var(--tw-border-opacity, 1))}.hover\:border-neutral-800:hover{--tw-border-opacity: 1;border-color:rgb(38 38 38 / var(--tw-border-opacity, 1))}.hover\:border-studio-accent\/50:hover{border-color:#3ce6ac80}.hover\:bg-black\/60:hover{background-color:#0009}.hover\:bg-black\/70:hover{background-color:#000000b3}.hover\:bg-neutral-200:hover{--tw-bg-opacity: 1;background-color:rgb(229 229 229 / var(--tw-bg-opacity, 1))}.hover\:bg-neutral-600:hover{--tw-bg-opacity: 1;background-color:rgb(82 82 82 / var(--tw-bg-opacity, 1))}.hover\:bg-neutral-700\/50:hover{background-color:#40404080}.hover\:bg-neutral-800:hover{--tw-bg-opacity: 1;background-color:rgb(38 38 38 / var(--tw-bg-opacity, 1))}.hover\:bg-neutral-800\/30:hover{background-color:#2626264d}.hover\:bg-neutral-800\/50:hover{background-color:#26262680}.hover\:bg-neutral-800\/70:hover{background-color:#262626b3}.hover\:bg-neutral-900:hover{--tw-bg-opacity: 1;background-color:rgb(23 23 23 / var(--tw-bg-opacity, 1))}.hover\:bg-panel-accent\/10:hover{background-color:#3ce6ac1a}.hover\:bg-panel-hover:hover{--tw-bg-opacity: 1;background-color:rgb(39 39 42 / var(--tw-bg-opacity, 1))}.hover\:bg-panel-hover\/30:hover{background-color:#27272a4d}.hover\:bg-panel-hover\/40:hover{background-color:#27272a66}.hover\:bg-red-400\/10:hover{background-color:#f871711a}.hover\:bg-red-500:hover{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.hover\:bg-red-600:hover{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity, 1))}.hover\:bg-red-800\/60:hover{background-color:#991b1b99}.hover\:bg-red-900\/30:hover{background-color:#7f1d1d4d}.hover\:bg-studio-accent:hover{--tw-bg-opacity: 1;background-color:rgb(60 230 172 / var(--tw-bg-opacity, 1))}.hover\:bg-studio-accent\/25:hover{background-color:#3ce6ac40}.hover\:bg-studio-accent\/80:hover{background-color:#3ce6accc}.hover\:bg-white\/10:hover{background-color:#ffffff1a}.hover\:bg-white\/25:hover{background-color:#ffffff40}.hover\:text-\[\#22c55e\]:hover{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.hover\:text-amber-300:hover{--tw-text-opacity: 1;color:rgb(252 211 77 / var(--tw-text-opacity, 1))}.hover\:text-neutral-100:hover{--tw-text-opacity: 1;color:rgb(245 245 245 / var(--tw-text-opacity, 1))}.hover\:text-neutral-200:hover{--tw-text-opacity: 1;color:rgb(229 229 229 / var(--tw-text-opacity, 1))}.hover\:text-neutral-300:hover{--tw-text-opacity: 1;color:rgb(212 212 212 / var(--tw-text-opacity, 1))}.hover\:text-neutral-400:hover{--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity, 1))}.hover\:text-orange-300:hover{--tw-text-opacity: 1;color:rgb(253 186 116 / var(--tw-text-opacity, 1))}.hover\:text-panel-accent:hover{--tw-text-opacity: 1;color:rgb(60 230 172 / var(--tw-text-opacity, 1))}.hover\:text-panel-text-1:hover{--tw-text-opacity: 1;color:rgb(228 228 231 / var(--tw-text-opacity, 1))}.hover\:text-panel-text-2:hover{--tw-text-opacity: 1;color:rgb(161 161 170 / var(--tw-text-opacity, 1))}.hover\:text-panel-text-3:hover{--tw-text-opacity: 1;color:rgb(113 113 122 / var(--tw-text-opacity, 1))}.hover\:text-red-300:hover{--tw-text-opacity: 1;color:rgb(252 165 165 / var(--tw-text-opacity, 1))}.hover\:text-red-400:hover{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.hover\:text-studio-accent:hover{--tw-text-opacity: 1;color:rgb(60 230 172 / var(--tw-text-opacity, 1))}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.hover\:text-white\/80:hover{color:#fffc}.hover\:opacity-100:hover{opacity:1}.hover\:opacity-80:hover{opacity:.8}.hover\:ring-1:hover{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.hover\:ring-white\/30:hover{--tw-ring-color: rgb(255 255 255 / .3)}.hover\:brightness-110:hover{--tw-brightness: brightness(1.1);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.focus\:border-\[\#3CE6AC\]:focus{--tw-border-opacity: 1;border-color:rgb(60 230 172 / var(--tw-border-opacity, 1))}.focus\:border-neutral-600:focus{--tw-border-opacity: 1;border-color:rgb(82 82 82 / var(--tw-border-opacity, 1))}.focus\:border-neutral-700:focus{--tw-border-opacity: 1;border-color:rgb(64 64 64 / var(--tw-border-opacity, 1))}.focus\:border-panel-accent:focus{--tw-border-opacity: 1;border-color:rgb(60 230 172 / var(--tw-border-opacity, 1))}.focus\:border-studio-accent:focus{--tw-border-opacity: 1;border-color:rgb(60 230 172 / var(--tw-border-opacity, 1))}.focus\:border-studio-accent\/40:focus{border-color:#3ce6ac66}.focus\:border-studio-accent\/60:focus{border-color:#3ce6ac99}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-panel-accent:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(60 230 172 / var(--tw-ring-opacity, 1))}.focus\:ring-panel-accent\/40:focus{--tw-ring-color: rgb(60 230 172 / .4)}.focus\:ring-studio-accent\/30:focus{--tw-ring-color: rgb(60 230 172 / .3)}.focus\:ring-studio-accent\/40:focus{--tw-ring-color: rgb(60 230 172 / .4)}.focus-visible\:rounded:focus-visible{border-radius:.25rem}.focus-visible\:ring-1:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-white\/30:focus-visible{--tw-ring-color: rgb(255 255 255 / .3)}.active\:scale-\[0\.97\]:active{--tw-scale-x: .97;--tw-scale-y: .97;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.active\:scale-\[0\.98\]:active{--tw-scale-x: .98;--tw-scale-y: .98;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.active\:cursor-grabbing:active{cursor:grabbing}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-default:disabled{cursor:default}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:text-neutral-600:disabled{--tw-text-opacity: 1;color:rgb(82 82 82 / var(--tw-text-opacity, 1))}.disabled\:text-neutral-700:disabled{--tw-text-opacity: 1;color:rgb(64 64 64 / var(--tw-text-opacity, 1))}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:scale-125{--tw-scale-x: 1.25;--tw-scale-y: 1.25;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/card:hover .group-hover\/card\:opacity-100{opacity:1}@media(min-width:640px){.sm\:inline{display:inline}}.\[\&\:\:-webkit-slider-runnable-track\]\:h-\[2px\]::-webkit-slider-runnable-track{height:2px}.\[\&\:\:-webkit-slider-runnable-track\]\:rounded-full::-webkit-slider-runnable-track{border-radius:9999px}.\[\&\:\:-webkit-slider-runnable-track\]\:bg-panel-border::-webkit-slider-runnable-track{--tw-bg-opacity: 1;background-color:rgb(30 30 30 / var(--tw-bg-opacity, 1))}.\[\&\:\:-webkit-slider-thumb\:active\]\:cursor-grabbing::-webkit-slider-thumb:active{cursor:grabbing}.\[\&\:\:-webkit-slider-thumb\]\:-mt-1::-webkit-slider-thumb{margin-top:-.25rem}.\[\&\:\:-webkit-slider-thumb\]\:h-\[10px\]::-webkit-slider-thumb{height:10px}.\[\&\:\:-webkit-slider-thumb\]\:w-\[10px\]::-webkit-slider-thumb{width:10px}.\[\&\:\:-webkit-slider-thumb\]\:cursor-grab::-webkit-slider-thumb{cursor:grab}.\[\&\:\:-webkit-slider-thumb\]\:appearance-none::-webkit-slider-thumb{-webkit-appearance:none;-moz-appearance:none;appearance:none}.\[\&\:\:-webkit-slider-thumb\]\:rounded-full::-webkit-slider-thumb{border-radius:9999px}.\[\&\:\:-webkit-slider-thumb\]\:bg-white::-webkit-slider-thumb{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.\[\&\:\:-webkit-slider-thumb\]\:shadow-\[0_0_0_2px_\#0C0C0E\,0_1px_3px_rgba\(0\,0\,0\,0\.5\)\]::-webkit-slider-thumb{--tw-shadow: 0 0 0 2px #0C0C0E,0 1px 3px rgba(0,0,0,.5);--tw-shadow-colored: 0 0 0 2px var(--tw-shadow-color), 0 1px 3px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}