@hyperframes/studio 0.1.12 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/index-2uBPlHR_.css +1 -0
- package/dist/assets/{index-BEwJNmPo.js → index-uQ8cgxb3.js} +22 -22
- package/dist/index.html +2 -2
- package/package.json +2 -2
- package/src/components/renders/RenderQueueItem.tsx +70 -17
- package/src/components/renders/useRenderQueue.ts +1 -0
- package/src/components/sidebar/AssetsTab.tsx +25 -27
- package/src/components/sidebar/CompositionsTab.tsx +2 -2
- package/src/components/ui/ExpandedVideoPreview.tsx +37 -0
- package/src/components/ui/VideoFrameThumbnail.tsx +54 -0
- package/src/player/components/CompositionThumbnail.tsx +2 -2
- package/src/player/components/Player.tsx +0 -1
- package/src/player/components/Timeline.tsx +1 -1
- package/dist/assets/index-BnvciBdD.css +0 -1
package/dist/index.html
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>HyperFrames Studio</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-uQ8cgxb3.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-2uBPlHR_.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/studio",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"@phosphor-icons/react": "^2.1.10",
|
|
33
33
|
"codemirror": "^6.0.1",
|
|
34
34
|
"motion": "^12.38.0",
|
|
35
|
-
"@hyperframes/core": "0.1.
|
|
35
|
+
"@hyperframes/core": "0.1.13"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/react": "^19.0.0",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { memo, useCallback, useState } from "react";
|
|
2
|
+
import { VideoFrameThumbnail } from "../ui/VideoFrameThumbnail";
|
|
2
3
|
import type { RenderJob } from "./useRenderQueue";
|
|
3
4
|
|
|
4
5
|
interface RenderQueueItemProps {
|
|
@@ -19,34 +20,84 @@ function formatTimeAgo(timestamp: number): string {
|
|
|
19
20
|
return `${Math.floor(diff / 3600000)}h ago`;
|
|
20
21
|
}
|
|
21
22
|
|
|
23
|
+
/** Static frame extracted once via hidden video + canvas. */
|
|
24
|
+
|
|
22
25
|
export const RenderQueueItem = memo(function RenderQueueItem({
|
|
23
26
|
job,
|
|
24
27
|
onDelete,
|
|
25
28
|
}: RenderQueueItemProps) {
|
|
26
29
|
const [hovered, setHovered] = useState(false);
|
|
27
30
|
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
const handleOpen = useCallback(() => {
|
|
32
|
+
window.open(`/api/render/${job.id}/view`, "_blank");
|
|
33
|
+
}, [job.id]);
|
|
34
|
+
|
|
35
|
+
const handleDownload = useCallback(
|
|
36
|
+
(e: React.MouseEvent) => {
|
|
37
|
+
e.stopPropagation();
|
|
38
|
+
const a = document.createElement("a");
|
|
39
|
+
a.href = `/api/render/${job.id}/download`;
|
|
40
|
+
a.download = job.filename;
|
|
41
|
+
a.click();
|
|
42
|
+
},
|
|
43
|
+
[job.id, job.filename],
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const viewSrc = `/api/render/${job.id}/view`;
|
|
47
|
+
const isComplete = job.status === "complete";
|
|
34
48
|
|
|
35
49
|
return (
|
|
36
50
|
<div
|
|
37
51
|
onPointerEnter={() => setHovered(true)}
|
|
38
52
|
onPointerLeave={() => setHovered(false)}
|
|
39
|
-
|
|
53
|
+
onClick={isComplete ? handleOpen : undefined}
|
|
54
|
+
className={[
|
|
55
|
+
"px-3 py-2.5 border-b border-neutral-800/30 last:border-0 transition-colors duration-150",
|
|
56
|
+
isComplete ? "cursor-pointer hover:bg-neutral-800/30" : "",
|
|
57
|
+
]
|
|
58
|
+
.filter(Boolean)
|
|
59
|
+
.join(" ")}
|
|
40
60
|
>
|
|
41
|
-
<div className="flex items-center gap-2">
|
|
42
|
-
{/*
|
|
43
|
-
<div className="flex-shrink-0">
|
|
61
|
+
<div className="flex items-center gap-2.5">
|
|
62
|
+
{/* Thumbnail — static frame; swaps to live video on hover */}
|
|
63
|
+
<div className="w-20 h-[45px] rounded overflow-hidden bg-neutral-900 flex-shrink-0 relative">
|
|
64
|
+
{isComplete && (
|
|
65
|
+
<>
|
|
66
|
+
{/* Live video — visible on hover */}
|
|
67
|
+
{hovered && (
|
|
68
|
+
<video
|
|
69
|
+
src={viewSrc}
|
|
70
|
+
autoPlay
|
|
71
|
+
muted
|
|
72
|
+
loop
|
|
73
|
+
playsInline
|
|
74
|
+
className="absolute inset-0 w-full h-full object-contain"
|
|
75
|
+
/>
|
|
76
|
+
)}
|
|
77
|
+
{/* Static frame — visible when not hovering */}
|
|
78
|
+
<div
|
|
79
|
+
className="absolute inset-0 transition-opacity duration-150"
|
|
80
|
+
style={{ opacity: hovered ? 0 : 1 }}
|
|
81
|
+
>
|
|
82
|
+
<VideoFrameThumbnail src={viewSrc} />
|
|
83
|
+
</div>
|
|
84
|
+
</>
|
|
85
|
+
)}
|
|
44
86
|
{job.status === "rendering" && (
|
|
45
|
-
<div className="w-
|
|
87
|
+
<div className="w-full h-full flex items-center justify-center">
|
|
88
|
+
<div className="w-2 h-2 rounded-full bg-[#3CE6AC] animate-pulse" />
|
|
89
|
+
</div>
|
|
90
|
+
)}
|
|
91
|
+
{job.status === "failed" && (
|
|
92
|
+
<div className="w-full h-full flex items-center justify-center">
|
|
93
|
+
<div className="w-2 h-2 rounded-full bg-red-400" />
|
|
94
|
+
</div>
|
|
95
|
+
)}
|
|
96
|
+
{job.status === "cancelled" && (
|
|
97
|
+
<div className="w-full h-full flex items-center justify-center">
|
|
98
|
+
<div className="w-2 h-2 rounded-full bg-neutral-600" />
|
|
99
|
+
</div>
|
|
46
100
|
)}
|
|
47
|
-
{job.status === "complete" && <div className="w-2 h-2 rounded-full bg-green-400" />}
|
|
48
|
-
{job.status === "failed" && <div className="w-2 h-2 rounded-full bg-red-400" />}
|
|
49
|
-
{job.status === "cancelled" && <div className="w-2 h-2 rounded-full bg-neutral-600" />}
|
|
50
101
|
</div>
|
|
51
102
|
|
|
52
103
|
{/* Info */}
|
|
@@ -62,7 +113,6 @@ export const RenderQueueItem = memo(function RenderQueueItem({
|
|
|
62
113
|
)}
|
|
63
114
|
</div>
|
|
64
115
|
|
|
65
|
-
{/* Progress bar + percentage */}
|
|
66
116
|
{job.status === "rendering" && (
|
|
67
117
|
<div className="mt-1">
|
|
68
118
|
<div className="flex items-center justify-between mb-0.5">
|
|
@@ -90,7 +140,7 @@ export const RenderQueueItem = memo(function RenderQueueItem({
|
|
|
90
140
|
{/* Actions */}
|
|
91
141
|
{hovered && (
|
|
92
142
|
<div className="flex items-center gap-1 flex-shrink-0">
|
|
93
|
-
{
|
|
143
|
+
{isComplete && (
|
|
94
144
|
<button
|
|
95
145
|
onClick={handleDownload}
|
|
96
146
|
className="p-1 rounded text-neutral-500 hover:text-green-400 transition-colors"
|
|
@@ -113,7 +163,10 @@ export const RenderQueueItem = memo(function RenderQueueItem({
|
|
|
113
163
|
</button>
|
|
114
164
|
)}
|
|
115
165
|
<button
|
|
116
|
-
onClick={
|
|
166
|
+
onClick={(e) => {
|
|
167
|
+
e.stopPropagation();
|
|
168
|
+
onDelete();
|
|
169
|
+
}}
|
|
117
170
|
className="p-1 rounded text-neutral-500 hover:text-red-400 transition-colors"
|
|
118
171
|
title="Remove"
|
|
119
172
|
>
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { memo, useState, useCallback, useRef } from "react";
|
|
2
2
|
import { ExpandOnHover } from "../ui/ExpandOnHover";
|
|
3
|
+
import { ExpandedVideoPreview } from "../ui/ExpandedVideoPreview";
|
|
4
|
+
import { VideoFrameThumbnail } from "../ui/VideoFrameThumbnail";
|
|
3
5
|
|
|
4
6
|
interface AssetsTabProps {
|
|
5
7
|
projectId: string;
|
|
@@ -32,28 +34,13 @@ function AssetThumbnail({
|
|
|
32
34
|
src={serveUrl}
|
|
33
35
|
alt={name}
|
|
34
36
|
loading="lazy"
|
|
35
|
-
className="w-full h-full object-
|
|
37
|
+
className="w-full h-full object-contain"
|
|
36
38
|
onError={(e) => {
|
|
37
39
|
(e.target as HTMLImageElement).style.display = "none";
|
|
38
40
|
}}
|
|
39
41
|
/>
|
|
40
42
|
)}
|
|
41
|
-
{isVideo &&
|
|
42
|
-
<>
|
|
43
|
-
<video
|
|
44
|
-
src={serveUrl}
|
|
45
|
-
muted
|
|
46
|
-
playsInline
|
|
47
|
-
preload="metadata"
|
|
48
|
-
className="w-full h-full object-cover"
|
|
49
|
-
/>
|
|
50
|
-
<div className="absolute inset-0 flex items-center justify-center bg-black/30">
|
|
51
|
-
<svg width="14" height="14" viewBox="0 0 24 24" fill="white" className="opacity-80">
|
|
52
|
-
<polygon points="6,3 20,12 6,21" />
|
|
53
|
-
</svg>
|
|
54
|
-
</div>
|
|
55
|
-
</>
|
|
56
|
-
)}
|
|
43
|
+
{isVideo && <VideoFrameThumbnail src={serveUrl} />}
|
|
57
44
|
{isAudio && (
|
|
58
45
|
<div className="w-full h-full flex items-center justify-center bg-neutral-900">
|
|
59
46
|
<svg
|
|
@@ -112,22 +99,33 @@ function ExpandedAssetPreview({
|
|
|
112
99
|
isAudio: boolean;
|
|
113
100
|
onCopy: () => void;
|
|
114
101
|
}) {
|
|
102
|
+
if (isVideo) {
|
|
103
|
+
return (
|
|
104
|
+
<ExpandedVideoPreview
|
|
105
|
+
src={serveUrl}
|
|
106
|
+
name={name}
|
|
107
|
+
subtitle={asset}
|
|
108
|
+
action={
|
|
109
|
+
<button
|
|
110
|
+
onClick={(e) => {
|
|
111
|
+
e.stopPropagation();
|
|
112
|
+
onCopy();
|
|
113
|
+
}}
|
|
114
|
+
className="px-4 py-1.5 text-xs font-semibold text-[#09090B] bg-[#3CE6AC] rounded-lg hover:brightness-110 transition-colors flex-shrink-0"
|
|
115
|
+
>
|
|
116
|
+
Copy Path
|
|
117
|
+
</button>
|
|
118
|
+
}
|
|
119
|
+
/>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
115
123
|
return (
|
|
116
124
|
<div className="w-full h-full bg-neutral-950 rounded-[16px] overflow-hidden flex flex-col">
|
|
117
125
|
<div className="flex-1 min-h-0 flex items-center justify-center bg-black p-4">
|
|
118
126
|
{isImage && (
|
|
119
127
|
<img src={serveUrl} alt={name} className="max-w-full max-h-full object-contain rounded" />
|
|
120
128
|
)}
|
|
121
|
-
{isVideo && (
|
|
122
|
-
<video
|
|
123
|
-
src={serveUrl}
|
|
124
|
-
autoPlay
|
|
125
|
-
muted
|
|
126
|
-
loop
|
|
127
|
-
playsInline
|
|
128
|
-
className="max-w-full max-h-full object-contain rounded"
|
|
129
|
-
/>
|
|
130
|
-
)}
|
|
131
129
|
{isAudio && (
|
|
132
130
|
<div className="flex flex-col items-center gap-4">
|
|
133
131
|
<svg
|
|
@@ -146,7 +146,7 @@ function CompCard({
|
|
|
146
146
|
onSelect: () => void;
|
|
147
147
|
}) {
|
|
148
148
|
const name = comp.replace(/^compositions\//, "").replace(/\.html$/, "");
|
|
149
|
-
const thumbnailUrl = `/api/projects/${projectId}/thumbnail/${comp}?t=
|
|
149
|
+
const thumbnailUrl = `/api/projects/${projectId}/thumbnail/${comp}?t=2`;
|
|
150
150
|
const previewUrl = `/api/projects/${projectId}/preview/comp/${comp}`;
|
|
151
151
|
|
|
152
152
|
const card = (
|
|
@@ -162,7 +162,7 @@ function CompCard({
|
|
|
162
162
|
src={thumbnailUrl}
|
|
163
163
|
alt={name}
|
|
164
164
|
loading="lazy"
|
|
165
|
-
className="w-full h-full object-
|
|
165
|
+
className="w-full h-full object-contain"
|
|
166
166
|
onError={(e) => {
|
|
167
167
|
(e.target as HTMLImageElement).style.display = "none";
|
|
168
168
|
}}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
interface ExpandedVideoPreviewProps {
|
|
4
|
+
src: string;
|
|
5
|
+
name: string;
|
|
6
|
+
subtitle: string;
|
|
7
|
+
action: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Shared expanded video preview used by AssetsTab (video assets) and
|
|
12
|
+
* the Renders panel. Autoplays the video muted+looped inside a full-bleed
|
|
13
|
+
* card. Caller provides the footer action slot (Copy Path, Open, etc.).
|
|
14
|
+
*/
|
|
15
|
+
export function ExpandedVideoPreview({ src, name, subtitle, action }: ExpandedVideoPreviewProps) {
|
|
16
|
+
return (
|
|
17
|
+
<div className="w-full h-full bg-neutral-950 rounded-[16px] overflow-hidden flex flex-col">
|
|
18
|
+
<div className="flex-1 min-h-0 flex items-center justify-center bg-black p-4">
|
|
19
|
+
<video
|
|
20
|
+
src={src}
|
|
21
|
+
autoPlay
|
|
22
|
+
muted
|
|
23
|
+
loop
|
|
24
|
+
playsInline
|
|
25
|
+
className="max-w-full max-h-full object-contain rounded"
|
|
26
|
+
/>
|
|
27
|
+
</div>
|
|
28
|
+
<div className="px-5 py-3 bg-neutral-900 border-t border-neutral-800/50 flex items-center justify-between flex-shrink-0">
|
|
29
|
+
<div className="min-w-0 flex-1 mr-4">
|
|
30
|
+
<div className="text-sm font-medium text-neutral-200 truncate">{name}</div>
|
|
31
|
+
<div className="text-[10px] text-neutral-600 font-mono mt-0.5 truncate">{subtitle}</div>
|
|
32
|
+
</div>
|
|
33
|
+
{action}
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useState, useEffect, useRef } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extracts a representative JPEG frame from a video URL using a hidden
|
|
5
|
+
* video + canvas. Seeks to ~10% of duration to avoid black opening frames.
|
|
6
|
+
* Used by AssetThumbnail (assets tab) and RenderQueueItem (renders tab).
|
|
7
|
+
*/
|
|
8
|
+
export function VideoFrameThumbnail({ src }: { src: string }) {
|
|
9
|
+
const [frame, setFrame] = useState<string | null>(null);
|
|
10
|
+
const didExtract = useRef(false);
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (didExtract.current) return;
|
|
14
|
+
didExtract.current = true;
|
|
15
|
+
|
|
16
|
+
const video = document.createElement("video");
|
|
17
|
+
video.crossOrigin = "anonymous";
|
|
18
|
+
video.muted = true;
|
|
19
|
+
video.preload = "metadata";
|
|
20
|
+
|
|
21
|
+
const canvas = document.createElement("canvas");
|
|
22
|
+
const ctx = canvas.getContext("2d");
|
|
23
|
+
|
|
24
|
+
const cleanup = () => {
|
|
25
|
+
video.src = "";
|
|
26
|
+
video.load();
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
video.addEventListener("loadedmetadata", () => {
|
|
30
|
+
video.currentTime = Math.min(2, video.duration * 0.1 || 2);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
video.addEventListener("seeked", () => {
|
|
34
|
+
if (!ctx) return;
|
|
35
|
+
canvas.width = video.videoWidth;
|
|
36
|
+
canvas.height = video.videoHeight;
|
|
37
|
+
ctx.drawImage(video, 0, 0);
|
|
38
|
+
setFrame(canvas.toDataURL("image/jpeg", 0.7));
|
|
39
|
+
cleanup();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
video.addEventListener("error", cleanup);
|
|
43
|
+
video.src = src;
|
|
44
|
+
video.load();
|
|
45
|
+
|
|
46
|
+
return cleanup;
|
|
47
|
+
}, [src]);
|
|
48
|
+
|
|
49
|
+
if (!frame) {
|
|
50
|
+
return <div className="w-full h-full bg-neutral-800 animate-pulse" />;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return <img src={frame} alt="" draggable={false} className="w-full h-full object-contain" />;
|
|
54
|
+
}
|
|
@@ -28,7 +28,7 @@ export const CompositionThumbnail = memo(function CompositionThumbnail({
|
|
|
28
28
|
previewUrl,
|
|
29
29
|
label,
|
|
30
30
|
labelColor,
|
|
31
|
-
seekTime =
|
|
31
|
+
seekTime = 2,
|
|
32
32
|
duration = 5,
|
|
33
33
|
width = 1920,
|
|
34
34
|
height = 1080,
|
|
@@ -112,7 +112,7 @@ export const CompositionThumbnail = memo(function CompositionThumbnail({
|
|
|
112
112
|
onLoad={(e) => {
|
|
113
113
|
(e.target as HTMLImageElement).style.opacity = "1";
|
|
114
114
|
}}
|
|
115
|
-
className="absolute inset-0 w-full h-full object-
|
|
115
|
+
className="absolute inset-0 w-full h-full object-contain"
|
|
116
116
|
style={{ opacity: 0, transition: "opacity 200ms ease-out" }}
|
|
117
117
|
/>
|
|
118
118
|
</div>
|
|
@@ -749,7 +749,7 @@ export const Timeline = memo(function Timeline({
|
|
|
749
749
|
|
|
750
750
|
{/* Keyboard shortcut hint — always visible */}
|
|
751
751
|
{!showPopover && !rangeSelection && (
|
|
752
|
-
<div className="absolute bottom-2 right-3 pointer-events-none">
|
|
752
|
+
<div className="absolute bottom-2 right-3 pointer-events-none z-20">
|
|
753
753
|
<div className="flex items-center gap-1.5 px-2 py-1 rounded-md bg-neutral-800/50 border border-neutral-700/20">
|
|
754
754
|
<kbd className="text-[9px] font-mono text-neutral-500 bg-neutral-700/40 px-1 py-0.5 rounded">
|
|
755
755
|
Shift
|
|
@@ -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}.invisible{visibility:hidden}.collapse{visibility:collapse}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.bottom-0{bottom:0}.bottom-2{bottom:.5rem}.bottom-full{bottom:100%}.left-0{left:0}.right-0{right:0}.right-3{right:.75rem}.top-0{top:0}.top-1\/2{top:50%}.z-10{z-index:10}.z-40{z-index:40}.z-50{z-index:50}.z-\[100\]{z-index:100}.z-\[1\]{z-index:1}.z-\[2\]{z-index:2}.mx-auto{margin-left:auto;margin-right:auto}.mb-0\.5{margin-bottom:.125rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.ml-1\.5{margin-left:.375rem}.ml-11{margin-left:2.75rem}.ml-auto{margin-left:auto}.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{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.aspect-video{aspect-ratio:16 / 9}.h-1{height:.25rem}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.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-\[5px\]{height:5px}.h-full{height:100%}.h-screen{height:100vh}.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{width:.25rem}.w-1\.5{width:.375rem}.w-16{width:4rem}.w-2{width:.5rem}.w-20{width:5rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-5{width:1.25rem}.w-64{width:16rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-80{width:20rem}.w-\[140px\]{width:140px}.w-full{width:100%}.w-px{width:1px}.w-screen{width:100vw}.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-\[56px\]{min-width:56px}.min-w-\[72px\]{min-width:72px}.max-w-4xl{max-width:56rem}.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}.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-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}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.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-5{gap:1.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))}.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-\[16px\]{border-radius:16px}.rounded-\[4px\]{border-radius:4px}.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-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-\[\#3CE6AC\]{--tw-border-opacity: 1;border-color:rgb(60 230 172 / var(--tw-border-opacity, 1))}.border-blue-400\/60{border-color:#60a5fa99}.border-blue-500{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.border-blue-500\/25{border-color:#3b82f640}.border-blue-500\/30{border-color:#3b82f64d}.border-blue-500\/50{border-color:#3b82f680}.border-green-500\/30{border-color:#22c55e4d}.border-neutral-700{--tw-border-opacity: 1;border-color:rgb(64 64 64 / var(--tw-border-opacity, 1))}.border-neutral-700\/20{border-color:#40404033}.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-transparent{border-color:transparent}.bg-\[\#0a0a0b\]{--tw-bg-opacity: 1;background-color:rgb(10 10 11 / var(--tw-bg-opacity, 1))}.bg-\[\#3CE6AC\]{--tw-bg-opacity: 1;background-color:rgb(60 230 172 / var(--tw-bg-opacity, 1))}.bg-\[\#3CE6AC\]\/10{background-color:#3ce6ac1a}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.bg-black\/30{background-color:#0000004d}.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{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.bg-blue-500\/10{background-color:#3b82f61a}.bg-blue-500\/15{background-color:#3b82f626}.bg-blue-500\/20{background-color:#3b82f633}.bg-blue-500\/\[0\.03\]{background-color:#3b82f608}.bg-blue-500\/\[0\.06\]{background-color:#3b82f60f}.bg-blue-950\/20{background-color:#17255433}.bg-green-400{--tw-bg-opacity: 1;background-color:rgb(74 222 128 / var(--tw-bg-opacity, 1))}.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-600\/60{background-color:#52525299}.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-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-transparent{background-color:transparent}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.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-3\.5{padding-left:.875rem;padding-right:.875rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.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-24{padding-top:6rem;padding-bottom:6rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-0\.5{padding-bottom:.125rem}.pb-16{padding-bottom:4rem}.pb-3{padding-bottom:.75rem}.pb-8{padding-bottom:2rem}.pr-1\.5{padding-right:.375rem}.pt-16{padding-top:4rem}.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-2xl{font-size:1.5rem;line-height:2rem}.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-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.text-\[\#09090B\]{--tw-text-opacity: 1;color:rgb(9 9 11 / var(--tw-text-opacity, 1))}.text-\[\#3CE6AC\]{--tw-text-opacity: 1;color:rgb(60 230 172 / var(--tw-text-opacity, 1))}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-blue-400\/50{color:#60a5fa80}.text-blue-400\/60{color:#60a5fa99}.text-blue-400\/80{color:#60a5facc}.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-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.opacity-20{opacity:.2}.opacity-25{opacity:.25}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.opacity-80{opacity:.8}.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-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}.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)}.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-\[width\]{transition-property:width;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-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-150{transition-duration:.15s}.duration-200{transition-duration:.2s}.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}.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-\[\#3CE6AC\]\/30:hover{border-color:#3ce6ac4d}.hover\:border-neutral-600:hover{--tw-border-opacity: 1;border-color:rgb(82 82 82 / var(--tw-border-opacity, 1))}.hover\:bg-\[\#3CE6AC\]\/80:hover{background-color:#3ce6accc}.hover\:bg-blue-500:hover{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.hover\:bg-blue-500\/25:hover{background-color:#3b82f640}.hover\:bg-neutral-200:hover{--tw-bg-opacity: 1;background-color:rgb(229 229 229 / 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-600:hover{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity, 1))}.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-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-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.hover\:shadow-lg:hover{--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)}.hover\:shadow-\[\#3CE6AC\]\/5:hover{--tw-shadow-color: rgb(60 230 172 / .05);--tw-shadow: var(--tw-shadow-colored)}.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-blue-500\/40:focus{border-color:#3b82f666}.focus\:border-neutral-600:focus{--tw-border-opacity: 1;border-color:rgb(82 82 82 / var(--tw-border-opacity, 1))}.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))}.active\:bg-blue-400:active{--tw-bg-opacity: 1;background-color:rgb(96 165 250 / var(--tw-bg-opacity, 1))}.disabled\:pointer-events-none:disabled{pointer-events:none}.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: 640px){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width: 1024px){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}
|