@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/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-BEwJNmPo.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/index-BnvciBdD.css">
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.12",
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.12"
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 handleDownload = useCallback(() => {
29
- const a = document.createElement("a");
30
- a.href = `/api/render/${job.id}/download`;
31
- a.download = job.filename;
32
- a.click();
33
- }, [job.id, job.filename]);
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
- className="px-3 py-2.5 border-b border-neutral-800/30 last:border-0"
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
- {/* Status indicator */}
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-2 h-2 rounded-full bg-[#3CE6AC] animate-pulse" />
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
- {job.status === "complete" && (
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={onDelete}
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
  >
@@ -128,6 +128,7 @@ export function useRenderQueue(projectId: string | null) {
128
128
  ? "failed"
129
129
  : j.status,
130
130
  durationMs: data.status === "complete" ? Date.now() - startTime : undefined,
131
+ error: data.error ?? j.error,
131
132
  }
132
133
  : j,
133
134
  ),
@@ -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-cover"
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=0.5`;
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-cover"
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 = 0.4,
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-cover"
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>
@@ -117,7 +117,6 @@ export const Player = forwardRef<HTMLIFrameElement, PlayerProps>(
117
117
  width: dims.w,
118
118
  height: dims.h,
119
119
  border: "none",
120
- outline: "1px solid black",
121
120
  transform: `scale(${scale})`,
122
121
  transformOrigin: "center center",
123
122
  flexShrink: 0,
@@ -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))}}