@hyperframes/studio 0.4.16 → 0.4.18

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.
@@ -1,5 +1,6 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
  import {
3
+ buildClipRangeSelection,
3
4
  buildPromptCopyText,
4
5
  buildTimelineElementAgentPrompt,
5
6
  buildTimelineAgentPrompt,
@@ -7,6 +8,7 @@ import {
7
8
  canOffsetTrimClipStart,
8
9
  getTimelineEditCapabilities,
9
10
  hasPatchableTimelineTarget,
11
+ resolveBlockedTimelineEditIntent,
10
12
  resolveTimelineAutoScroll,
11
13
  resolveTimelineMove,
12
14
  resolveTimelineResize,
@@ -199,6 +201,14 @@ describe("canOffsetTrimClipStart", () => {
199
201
  ).toBe(true);
200
202
  });
201
203
 
204
+ it("allows front trim for plain audio clips even before media-start exists", () => {
205
+ expect(
206
+ canOffsetTrimClipStart({
207
+ tag: "audio",
208
+ }),
209
+ ).toBe(true);
210
+ });
211
+
202
212
  it("blocks front trim for generic motion clips", () => {
203
213
  expect(
204
214
  canOffsetTrimClipStart({
@@ -223,6 +233,21 @@ describe("hasPatchableTimelineTarget", () => {
223
233
  });
224
234
 
225
235
  describe("getTimelineEditCapabilities", () => {
236
+ it("does not disable editable audio just because it spans multiple scenes", () => {
237
+ expect(
238
+ getTimelineEditCapabilities({
239
+ tag: "audio",
240
+ duration: 8,
241
+ selector: "#voiceover",
242
+ sourceDuration: 8,
243
+ }),
244
+ ).toEqual({
245
+ canMove: true,
246
+ canTrimStart: true,
247
+ canTrimEnd: true,
248
+ });
249
+ });
250
+
226
251
  it("disables move and trims for generic motion clips even when patchable", () => {
227
252
  expect(
228
253
  getTimelineEditCapabilities({
@@ -299,6 +324,111 @@ describe("getTimelineEditCapabilities", () => {
299
324
  });
300
325
  });
301
326
 
327
+ describe("resolveBlockedTimelineEditIntent", () => {
328
+ it("returns move when the clip body is blocked", () => {
329
+ expect(
330
+ resolveBlockedTimelineEditIntent({
331
+ width: 160,
332
+ offsetX: 80,
333
+ handleWidth: 18,
334
+ capabilities: {
335
+ canMove: false,
336
+ canTrimStart: false,
337
+ canTrimEnd: false,
338
+ },
339
+ }),
340
+ ).toBe("move");
341
+ });
342
+
343
+ it("returns resize-start when the left edge is blocked", () => {
344
+ expect(
345
+ resolveBlockedTimelineEditIntent({
346
+ width: 160,
347
+ offsetX: 8,
348
+ handleWidth: 18,
349
+ capabilities: {
350
+ canMove: false,
351
+ canTrimStart: false,
352
+ canTrimEnd: true,
353
+ },
354
+ }),
355
+ ).toBe("resize-start");
356
+ });
357
+
358
+ it("returns resize-end when the right edge is blocked", () => {
359
+ expect(
360
+ resolveBlockedTimelineEditIntent({
361
+ width: 160,
362
+ offsetX: 154,
363
+ handleWidth: 18,
364
+ capabilities: {
365
+ canMove: false,
366
+ canTrimStart: true,
367
+ canTrimEnd: false,
368
+ },
369
+ }),
370
+ ).toBe("resize-end");
371
+ });
372
+
373
+ it("does not block the left edge when the clip can still be moved", () => {
374
+ expect(
375
+ resolveBlockedTimelineEditIntent({
376
+ width: 160,
377
+ offsetX: 8,
378
+ handleWidth: 18,
379
+ capabilities: {
380
+ canMove: true,
381
+ canTrimStart: false,
382
+ canTrimEnd: true,
383
+ },
384
+ }),
385
+ ).toBe(null);
386
+ });
387
+
388
+ it("does not swallow the full surface of a narrow movable clip", () => {
389
+ expect(
390
+ resolveBlockedTimelineEditIntent({
391
+ width: 12,
392
+ offsetX: 6,
393
+ handleWidth: 18,
394
+ capabilities: {
395
+ canMove: true,
396
+ canTrimStart: false,
397
+ canTrimEnd: false,
398
+ },
399
+ }),
400
+ ).toBe(null);
401
+ });
402
+
403
+ it("returns null when the relevant edit is supported", () => {
404
+ expect(
405
+ resolveBlockedTimelineEditIntent({
406
+ width: 160,
407
+ offsetX: 8,
408
+ handleWidth: 18,
409
+ capabilities: {
410
+ canMove: true,
411
+ canTrimStart: true,
412
+ canTrimEnd: true,
413
+ },
414
+ }),
415
+ ).toBe(null);
416
+ });
417
+ });
418
+
419
+ describe("buildClipRangeSelection", () => {
420
+ it("anchors the full clip range at the click position", () => {
421
+ expect(
422
+ buildClipRangeSelection({ start: 1.25, duration: 3.5 }, { anchorX: 320, anchorY: 180 }),
423
+ ).toEqual({
424
+ start: 1.25,
425
+ end: 4.75,
426
+ anchorX: 320,
427
+ anchorY: 180,
428
+ });
429
+ });
430
+ });
431
+
302
432
  describe("resolveTimelineAutoScroll", () => {
303
433
  it("does not scroll when the pointer stays away from the edges", () => {
304
434
  expect(
@@ -420,6 +550,25 @@ describe("resolveTimelineResize", () => {
420
550
  ).toEqual({ start: 1.5, duration: 2.5, playbackStart: 1 });
421
551
  });
422
552
 
553
+ it("can seed front trim from an implicit zero playback start", () => {
554
+ expect(
555
+ resolveTimelineResize(
556
+ {
557
+ start: 0,
558
+ duration: 8,
559
+ originClientX: 100,
560
+ pixelsPerSecond: 100,
561
+ minStart: 0,
562
+ maxEnd: 8,
563
+ playbackStart: 0,
564
+ playbackRate: 1,
565
+ },
566
+ "start",
567
+ 200,
568
+ ),
569
+ ).toEqual({ start: 1, duration: 7, playbackStart: 1 });
570
+ });
571
+
423
572
  it("prevents extending media left past available source before media-start", () => {
424
573
  expect(
425
574
  resolveTimelineResize(
@@ -175,6 +175,15 @@ export interface TimelineEditCapabilities {
175
175
  canTrimEnd: boolean;
176
176
  }
177
177
 
178
+ export type BlockedTimelineEditIntent = "move" | "resize-start" | "resize-end";
179
+
180
+ export interface TimelineRangeSelection {
181
+ start: number;
182
+ end: number;
183
+ anchorX: number;
184
+ anchorY: number;
185
+ }
186
+
178
187
  function isDeterministicTimelineWindow(input: {
179
188
  tag: string;
180
189
  compositionSrc?: string;
@@ -207,12 +216,7 @@ export function canOffsetTrimClipStart(input: {
207
216
  if (input.playbackStartAttr != null) return true;
208
217
  if (input.playbackStart != null) return true;
209
218
  const normalizedTag = input.tag.toLowerCase();
210
- if (!["video", "audio"].includes(normalizedTag)) return false;
211
- return (
212
- input.sourceDuration != null &&
213
- Number.isFinite(input.sourceDuration) &&
214
- input.sourceDuration > 0
215
- );
219
+ return ["video", "audio"].includes(normalizedTag);
216
220
  }
217
221
 
218
222
  export function getTimelineEditCapabilities(input: {
@@ -235,6 +239,41 @@ export function getTimelineEditCapabilities(input: {
235
239
  };
236
240
  }
237
241
 
242
+ export function resolveBlockedTimelineEditIntent(input: {
243
+ width: number;
244
+ offsetX: number;
245
+ handleWidth: number;
246
+ capabilities: TimelineEditCapabilities;
247
+ }): BlockedTimelineEditIntent | null {
248
+ if (input.capabilities.canMove) {
249
+ return null;
250
+ }
251
+
252
+ const safeWidth = Math.max(0, input.width);
253
+ const safeOffsetX = clamp(input.offsetX, 0, safeWidth);
254
+ const safeHandleWidth = Math.max(0, input.handleWidth);
255
+
256
+ if (safeOffsetX <= safeHandleWidth && !input.capabilities.canTrimStart) {
257
+ return "resize-start";
258
+ }
259
+ if (safeOffsetX >= Math.max(0, safeWidth - safeHandleWidth) && !input.capabilities.canTrimEnd) {
260
+ return "resize-end";
261
+ }
262
+ return "move";
263
+ }
264
+
265
+ export function buildClipRangeSelection(
266
+ clip: { start: number; duration: number },
267
+ anchor: { anchorX: number; anchorY: number },
268
+ ): TimelineRangeSelection {
269
+ return {
270
+ start: clip.start,
271
+ end: clip.start + clip.duration,
272
+ anchorX: anchor.anchorX,
273
+ anchorY: anchor.anchorY,
274
+ };
275
+ }
276
+
238
277
  export function buildTimelineAgentPrompt({
239
278
  rangeStart,
240
279
  rangeEnd,
@@ -559,7 +559,11 @@ export function useTimelinePlayer() {
559
559
 
560
560
  // Convert a runtime timeline message (from iframe postMessage) into TimelineElements
561
561
  const processTimelineMessage = useCallback(
562
- (data: { clips: ClipManifestClip[]; durationInFrames: number }) => {
562
+ (data: {
563
+ clips: ClipManifestClip[];
564
+ durationInFrames: number;
565
+ scenes?: Array<{ id: string; label: string; start: number; duration: number }>;
566
+ }) => {
563
567
  if (!data.clips || data.clips.length === 0) {
564
568
  return;
565
569
  }
@@ -0,0 +1,80 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ buildTimelineFileDropPlacements,
4
+ buildTimelineAssetInsertHtml,
5
+ getTimelineAssetKind,
6
+ insertTimelineAssetIntoSource,
7
+ resolveTimelineAssetSrc,
8
+ } from "./timelineAssetDrop";
9
+
10
+ describe("getTimelineAssetKind", () => {
11
+ it("detects image, video, and audio assets", () => {
12
+ expect(getTimelineAssetKind("assets/photo.png")).toBe("image");
13
+ expect(getTimelineAssetKind("assets/clip.mp4")).toBe("video");
14
+ expect(getTimelineAssetKind("assets/music.wav")).toBe("audio");
15
+ });
16
+ });
17
+
18
+ describe("buildTimelineAssetInsertHtml", () => {
19
+ it("builds an image clip with explicit timing and track", () => {
20
+ expect(
21
+ buildTimelineAssetInsertHtml({
22
+ id: "photo_asset",
23
+ assetPath: "assets/photo.png",
24
+ kind: "image",
25
+ start: 1.25,
26
+ duration: 3,
27
+ track: 2,
28
+ zIndex: 4,
29
+ }),
30
+ ).toContain('img id="photo_asset"');
31
+ });
32
+
33
+ it("builds an audio clip without visual layout styles", () => {
34
+ const html = buildTimelineAssetInsertHtml({
35
+ id: "music_asset",
36
+ assetPath: "assets/music.wav",
37
+ kind: "audio",
38
+ start: 0.5,
39
+ duration: 5,
40
+ track: 0,
41
+ zIndex: 1,
42
+ });
43
+ expect(html).toContain("<audio");
44
+ expect(html).not.toContain("object-fit");
45
+ });
46
+ });
47
+
48
+ describe("resolveTimelineAssetSrc", () => {
49
+ it("keeps project-root asset paths for index.html", () => {
50
+ expect(resolveTimelineAssetSrc("index.html", "assets/photo.png")).toBe("assets/photo.png");
51
+ });
52
+
53
+ it("rewrites asset paths relative to sub-compositions", () => {
54
+ expect(resolveTimelineAssetSrc("compositions/scene-a.html", "assets/photo.png")).toBe(
55
+ "../assets/photo.png",
56
+ );
57
+ });
58
+ });
59
+
60
+ describe("buildTimelineFileDropPlacements", () => {
61
+ it("uses the dropped start and stacks multiple files onto successive tracks", () => {
62
+ expect(buildTimelineFileDropPlacements({ start: 1.5, track: 2 }, 3)).toEqual([
63
+ { start: 1.5, track: 2 },
64
+ { start: 1.5, track: 3 },
65
+ { start: 1.5, track: 4 },
66
+ ]);
67
+ });
68
+ });
69
+
70
+ describe("insertTimelineAssetIntoSource", () => {
71
+ it("appends the new asset inside the root composition", () => {
72
+ const source = `<!doctype html><html><body><div id="root" data-composition-id="main"></div></body></html>`;
73
+ const html = insertTimelineAssetIntoSource(
74
+ source,
75
+ '<img id="photo_asset" data-start="0" data-duration="3" />',
76
+ );
77
+
78
+ expect(html).toContain('<div id="root" data-composition-id="main"><img id="photo_asset"');
79
+ });
80
+ });
@@ -0,0 +1,87 @@
1
+ import { AUDIO_EXT, IMAGE_EXT, VIDEO_EXT } from "./mediaTypes";
2
+
3
+ export const TIMELINE_ASSET_MIME = "application/x-hyperframes-asset";
4
+
5
+ export type TimelineAssetKind = "image" | "video" | "audio";
6
+
7
+ export function getTimelineAssetKind(assetPath: string): TimelineAssetKind | null {
8
+ if (IMAGE_EXT.test(assetPath)) return "image";
9
+ if (VIDEO_EXT.test(assetPath)) return "video";
10
+ if (AUDIO_EXT.test(assetPath)) return "audio";
11
+ return null;
12
+ }
13
+
14
+ export function buildTimelineAssetId(assetPath: string, existingIds: Iterable<string>): string {
15
+ const baseName = assetPath.split("/").pop() ?? "asset";
16
+ const normalized = baseName
17
+ .replace(/\.[^.]+$/, "")
18
+ .replace(/[^a-zA-Z0-9_-]+/g, "_")
19
+ .replace(/^_+|_+$/g, "")
20
+ .toLowerCase();
21
+ const baseId = normalized || "asset";
22
+ const ids = new Set(existingIds);
23
+ if (!ids.has(baseId)) return baseId;
24
+ let suffix = 2;
25
+ while (ids.has(`${baseId}_${suffix}`)) suffix += 1;
26
+ return `${baseId}_${suffix}`;
27
+ }
28
+
29
+ export function resolveTimelineAssetSrc(targetPath: string, assetPath: string): string {
30
+ const targetDir = targetPath.includes("/")
31
+ ? targetPath.slice(0, targetPath.lastIndexOf("/"))
32
+ : "";
33
+ if (!targetDir) return assetPath;
34
+
35
+ const fromParts = targetDir.split("/").filter(Boolean);
36
+ const toParts = assetPath.split("/").filter(Boolean);
37
+ while (fromParts.length > 0 && toParts.length > 0 && fromParts[0] === toParts[0]) {
38
+ fromParts.shift();
39
+ toParts.shift();
40
+ }
41
+
42
+ const up = fromParts.map(() => "..");
43
+ const relative = [...up, ...toParts].join("/");
44
+ return relative || assetPath.split("/").pop() || assetPath;
45
+ }
46
+
47
+ export function buildTimelineFileDropPlacements(
48
+ placement: { start: number; track: number },
49
+ count: number,
50
+ ): Array<{ start: number; track: number }> {
51
+ return Array.from({ length: Math.max(0, count) }, (_, index) => ({
52
+ start: placement.start,
53
+ track: placement.track + index,
54
+ }));
55
+ }
56
+
57
+ export function buildTimelineAssetInsertHtml(input: {
58
+ id: string;
59
+ assetPath: string;
60
+ kind: TimelineAssetKind;
61
+ start: number;
62
+ duration: number;
63
+ track: number;
64
+ zIndex: number;
65
+ }): string {
66
+ const sharedAttrs = `id="${input.id}" class="clip" src="${input.assetPath}" data-start="${input.start}" data-duration="${input.duration}" data-track-index="${input.track}"`;
67
+
68
+ if (input.kind === "image") {
69
+ return `<img ${sharedAttrs} style="position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; z-index: ${input.zIndex}" />`;
70
+ }
71
+
72
+ if (input.kind === "video") {
73
+ return `<video ${sharedAttrs} muted playsinline style="position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; z-index: ${input.zIndex}"></video>`;
74
+ }
75
+
76
+ return `<audio ${sharedAttrs} style="z-index: ${input.zIndex}"></audio>`;
77
+ }
78
+
79
+ export function insertTimelineAssetIntoSource(source: string, assetHtml: string): string {
80
+ const rootOpenTag = /<[^>]*data-composition-id="[^"]+"[^>]*>/i;
81
+ const match = rootOpenTag.exec(source);
82
+ if (!match || match.index == null) {
83
+ throw new Error("No composition root found in target source");
84
+ }
85
+ const insertAt = match.index + match[0].length;
86
+ return `${source.slice(0, insertAt)}${assetHtml}${source.slice(insertAt)}`;
87
+ }
@@ -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}.\!visible{visibility:visible!important}.visible{visibility:visible}.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}.bottom-0{bottom:0}.bottom-1{bottom:.25rem}.bottom-2{bottom:.5rem}.bottom-6{bottom:1.5rem}.bottom-full{bottom:100%}.left-0{left:0}.left-1\/2{left:50%}.left-2{left:.5rem}.right-0{right:0}.right-3{right:.75rem}.top-0{top:0}.top-1{top:.25rem}.top-1\/2{top:50%}.top-2{top:.5rem}.top-full{top:100%}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.z-\[100\]{z-index:100}.z-\[1\]{z-index:1}.z-\[200\]{z-index:200}.z-\[2\]{z-index:2}.z-\[90\]{z-index:90}.z-\[91\]{z-index:91}.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}.mb-0\.5{margin-bottom:.125rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.ml-1\.5{margin-left:.375rem}.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}.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}.h-1{height:.25rem}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-2{height:.5rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.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-\[1080px\]{height:1080px}.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-\[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}.w-1\.5{width:.375rem}.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-7{width:1.75rem}.w-8{width:2rem}.w-80{width:20rem}.w-\[160px\]{width:160px}.w-\[1920px\]{width:1920px}.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-\[56px\]{min-width:56px}.min-w-\[58px\]{min-width:58px}.min-w-\[72px\]{min-width:72px}.max-w-\[280px\]{max-width:280px}.max-w-full{max-width:100%}.max-w-xl{max-width:36rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.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))}.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-help{cursor:help}.cursor-pointer{cursor:pointer}.cursor-row-resize{cursor:row-resize}.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}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,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}.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))}.overflow-hidden{overflow:hidden}.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}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.rounded-l{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.rounded-r{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.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-red-500{--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity, 1))}.border-red-700\/50{border-color:#b91c1c80}.border-studio-accent{--tw-border-opacity: 1;border-color:rgb(60 230 172 / var(--tw-border-opacity, 1))}.border-studio-accent\/20{border-color:#3ce6ac33}.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-transparent{border-color:transparent}.border-white\/20{border-color:#fff3}.border-t-white{--tw-border-opacity: 1;border-top-color:rgb(255 255 255 / 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-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.bg-black\/20{background-color:#0003}.bg-black\/50{background-color:#00000080}.bg-black\/60{background-color:#0009}.bg-black\/80{background-color:#000c}.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\/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-950{--tw-bg-opacity: 1;background-color:rgb(10 10 10 / var(--tw-bg-opacity, 1))}.bg-red-400{--tw-bg-opacity: 1;background-color:rgb(248 113 113 / var(--tw-bg-opacity, 1))}.bg-red-500\/10{background-color:#ef44441a}.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-900\/90{background-color:#7f1d1de6}.bg-red-950\/30{background-color:#450a0a4d}.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\/\[0\.03\]{background-color:#3ce6ac08}.bg-studio-accent\/\[0\.05\]{background-color:#3ce6ac0d}.bg-studio-accent\/\[0\.06\]{background-color:#3ce6ac0f}.bg-studio-accent\/\[0\.07\]{background-color:#3ce6ac12}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-white\/10{background-color:#ffffff1a}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.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}.pb-0\.5{padding-bottom:.125rem}.pb-3{padding-bottom:.75rem}.pt-1\.5{padding-top:.375rem}.pt-3{padding-top:.75rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.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-\[13px\]{font-size:13px}.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-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-5{line-height:1.25rem}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.leading-tight{line-height:1.25}.tracking-\[0\.08em\]{letter-spacing:.08em}.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-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-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-purple-400{--tw-text-opacity: 1;color:rgb(192 132 252 / 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-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-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-white\/60{color:#fff9}.underline{text-decoration-line:underline}.line-through{text-decoration-line:line-through}.accent-studio-accent{accent-color:#3CE6AC}.opacity-25{opacity:.25}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.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-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\/40{--tw-shadow-color: rgb(0 0 0 / .4);--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-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)}.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)}.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)}.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)}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}.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}.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-studio-accent\/50:hover{border-color:#3ce6ac80}.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-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-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\/25:hover{background-color:#3ce6ac40}.hover\:bg-studio-accent\/80:hover{background-color:#3ce6accc}.hover\:text-amber-300:hover{--tw-text-opacity: 1;color:rgb(252 211 77 / var(--tw-text-opacity, 1))}.hover\:text-green-400:hover{--tw-text-opacity: 1;color:rgb(74 222 128 / 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-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\: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-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\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.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))}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.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))}@media(min-width:768px){.md\:inline{display:inline}}