summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--js/.gitignore34
-rw-r--r--js/Pepe.tsx369
-rw-r--r--js/README.md15
-rw-r--r--js/bun.lock189
-rw-r--r--js/index.ts1
-rw-r--r--js/output.css901
-rw-r--r--js/package.json16
-rw-r--r--js/styles.css182
-rw-r--r--js/tailwind.config.js5
-rw-r--r--js/tsconfig.json29
10 files changed, 1741 insertions, 0 deletions
diff --git a/js/.gitignore b/js/.gitignore
new file mode 100644
index 0000000..a14702c
--- /dev/null
+++ b/js/.gitignore
@@ -0,0 +1,34 @@
+# dependencies (bun install)
+node_modules
+
+# output
+out
+dist
+*.tgz
+
+# code coverage
+coverage
+*.lcov
+
+# logs
+logs
+_.log
+report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
+
+# dotenv environment variable files
+.env
+.env.development.local
+.env.test.local
+.env.production.local
+.env.local
+
+# caches
+.eslintcache
+.cache
+*.tsbuildinfo
+
+# IntelliJ based IDEs
+.idea
+
+# Finder (MacOS) folder config
+.DS_Store
diff --git a/js/Pepe.tsx b/js/Pepe.tsx
new file mode 100644
index 0000000..c829058
--- /dev/null
+++ b/js/Pepe.tsx
@@ -0,0 +1,369 @@
+"use client";
+
+import type React from "react";
+
+import { useState } from "react";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import { Alert, AlertDescription } from "@/components/ui/alert";
+import {
+ Loader2,
+ Server,
+ MapPin,
+ CheckCircle,
+ AlertCircle,
+ Key,
+} from "lucide-react";
+
+// Mock data for locations
+const mockLocations = [
+ {
+ id: "nyc",
+ city: "New York",
+ country: "United States",
+ flag: "πŸ‡ΊπŸ‡Έ",
+ region: "North America",
+ },
+ {
+ id: "lon",
+ city: "London",
+ country: "United Kingdom",
+ flag: "πŸ‡¬πŸ‡§",
+ region: "Europe",
+ },
+ {
+ id: "fra",
+ city: "Frankfurt",
+ country: "Germany",
+ flag: "πŸ‡©πŸ‡ͺ",
+ region: "Europe",
+ },
+ {
+ id: "sgp",
+ city: "Singapore",
+ country: "Singapore",
+ flag: "πŸ‡ΈπŸ‡¬",
+ region: "Asia Pacific",
+ },
+ {
+ id: "tok",
+ city: "Tokyo",
+ country: "Japan",
+ flag: "πŸ‡―πŸ‡΅",
+ region: "Asia Pacific",
+ },
+ {
+ id: "syd",
+ city: "Sydney",
+ country: "Australia",
+ flag: "πŸ‡¦πŸ‡Ί",
+ region: "Asia Pacific",
+ },
+ {
+ id: "tor",
+ city: "Toronto",
+ country: "Canada",
+ flag: "πŸ‡¨πŸ‡¦",
+ region: "North America",
+ },
+ {
+ id: "ams",
+ city: "Amsterdam",
+ country: "Netherlands",
+ flag: "πŸ‡³πŸ‡±",
+ region: "Europe",
+ },
+];
+
+type Step = "api-key" | "location-select" | "creating" | "success" | "error";
+
+interface Location {
+ id: string;
+ city: string;
+ country: string;
+ flag: string;
+ region: string;
+}
+
+export default function VPSProvider() {
+ const [step, setStep] = useState<Step>("api-key");
+ const [apiKey, setApiKey] = useState("");
+ const [selectedLocation, setSelectedLocation] = useState<Location | null>(
+ null,
+ );
+ const [error, setError] = useState("");
+ const [isValidating, setIsValidating] = useState(false);
+ const [isCreating, setIsCreating] = useState(false);
+
+ // Mock API key validation
+ const validateApiKey = async (key: string) => {
+ setIsValidating(true);
+ setError("");
+
+ // Simulate API call
+ await new Promise((resolve) => setTimeout(resolve, 1500));
+
+ // Mock validation - accept keys that are at least 20 characters
+ if (key.length >= 20) {
+ setStep("location-select");
+ } else {
+ setError("Invalid API key. Please check your credentials and try again.");
+ }
+
+ setIsValidating(false);
+ };
+
+ // Mock VPS creation
+ const createVPS = async (location: Location) => {
+ setIsCreating(true);
+ setError("");
+ setStep("creating");
+
+ // Simulate VPS creation
+ await new Promise((resolve) => setTimeout(resolve, 3000));
+
+ // Mock success/failure (90% success rate)
+ if (Math.random() > 0.1) {
+ setStep("success");
+ } else {
+ setError("Failed to create VPS. Please try again or contact support.");
+ setStep("error");
+ }
+
+ setIsCreating(false);
+ };
+
+ const handleApiKeySubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ if (apiKey.trim()) {
+ validateApiKey(apiKey.trim());
+ }
+ };
+
+ const handleLocationSelect = (location: Location) => {
+ setSelectedLocation(location);
+ createVPS(location);
+ };
+
+ const handleRetry = () => {
+ setError("");
+ setStep("location-select");
+ setSelectedLocation(null);
+ };
+
+ const handleNext = () => {
+ // This is where the user's flow continues
+ console.log("Proceeding to next step...");
+ };
+
+ const renderApiKeyStep = () => (
+ <Card className="w-full max-w-md mx-auto">
+ <CardHeader className="text-center">
+ <div className="mx-auto w-12 h-12 bg-blue-100 rounded-full flex items-center justify-center mb-4">
+ <Key className="w-6 h-4 text-blue-600" />
+ </div>
+ <CardTitle>Connect Your Account</CardTitle>
+ <CardDescription>
+ Enter your API key to get started with VPS deployment
+ </CardDescription>
+ </CardHeader>
+ <CardContent>
+ <form onSubmit={handleApiKeySubmit} className="space-y-4">
+ <div className="space-y-2">
+ <Label htmlFor="apiKey">API Key</Label>
+ <Input
+ id="apiKey"
+ type="password"
+ placeholder="Enter your API key..."
+ value={apiKey}
+ onChange={(e) => setApiKey(e.target.value)}
+ disabled={isValidating}
+ />
+ </div>
+
+ {error && (
+ <Alert variant="destructive">
+ <AlertCircle className="h-4 w-4" />
+ <AlertDescription>{error}</AlertDescription>
+ </Alert>
+ )}
+
+ <Button
+ type="submit"
+ className="w-full"
+ disabled={!apiKey.trim() || isValidating}
+ >
+ {isValidating ? (
+ <>
+ <Loader2 className="mr-2 h-4 w-4 animate-spin" />
+ Validating...
+ </>
+ ) : (
+ "Validate API Key"
+ )}
+ </Button>
+ </form>
+ </CardContent>
+ </Card>
+ );
+
+ const renderLocationSelect = () => (
+ <Card className="w-full max-w-4xl mx-auto">
+ <CardHeader className="text-center">
+ <div className="mx-auto w-12 h-12 bg-green-100 rounded-full flex items-center justify-center mb-4">
+ <MapPin className="w-6 h-6 text-green-600" />
+ </div>
+ <CardTitle>Choose Server Location</CardTitle>
+ <CardDescription>
+ Select the location where you want to deploy your VPS
+ </CardDescription>
+ </CardHeader>
+ <CardContent>
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
+ {mockLocations.map((location) => (
+ <Card
+ key={location.id}
+ className="cursor-pointer hover:shadow-md transition-shadow border-2 hover:border-blue-200"
+ onClick={() => handleLocationSelect(location)}
+ >
+ <CardContent className="p-4">
+ <div className="flex items-center space-x-3">
+ <span className="text-2xl">{location.flag}</span>
+ <div className="flex-1">
+ <h3 className="font-semibold">{location.city}</h3>
+ <p className="text-sm text-muted-foreground">
+ {location.country}
+ </p>
+ <p className="text-xs text-muted-foreground">
+ {location.region}
+ </p>
+ </div>
+ </div>
+ </CardContent>
+ </Card>
+ ))}
+ </div>
+ </CardContent>
+ </Card>
+ );
+
+ const renderCreating = () => (
+ <Card className="w-full max-w-md mx-auto">
+ <CardHeader className="text-center">
+ <div className="mx-auto w-12 h-12 bg-blue-100 rounded-full flex items-center justify-center mb-4">
+ <Loader2 className="w-6 h-6 text-blue-600 animate-spin" />
+ </div>
+ <CardTitle>Creating Your VPS</CardTitle>
+ <CardDescription>
+ Setting up your server in {selectedLocation?.city},{" "}
+ {selectedLocation?.country}
+ </CardDescription>
+ </CardHeader>
+ <CardContent className="text-center">
+ <div className="flex items-center justify-center space-x-2 mb-4">
+ <span className="text-2xl">{selectedLocation?.flag}</span>
+ <span className="font-medium">{selectedLocation?.city}</span>
+ </div>
+ <p className="text-sm text-muted-foreground">
+ This may take a few minutes. Please don't close this window.
+ </p>
+ </CardContent>
+ </Card>
+ );
+
+ const renderSuccess = () => (
+ <Card className="w-full max-w-md mx-auto">
+ <CardHeader className="text-center">
+ <div className="mx-auto w-12 h-12 bg-green-100 rounded-full flex items-center justify-center mb-4">
+ <CheckCircle className="w-6 h-6 text-green-600" />
+ </div>
+ <CardTitle>VPS Created Successfully!</CardTitle>
+ <CardDescription>
+ Your server is ready in {selectedLocation?.city},{" "}
+ {selectedLocation?.country}
+ </CardDescription>
+ </CardHeader>
+ <CardContent className="text-center space-y-4">
+ <div className="flex items-center justify-center space-x-2 mb-4">
+ <span className="text-2xl">{selectedLocation?.flag}</span>
+ <span className="font-medium">{selectedLocation?.city}</span>
+ </div>
+
+ <Alert>
+ <Server className="h-4 w-4" />
+ <AlertDescription>
+ Your VPS is now online and ready to use!
+ </AlertDescription>
+ </Alert>
+
+ <Button onClick={handleNext} className="w-full">
+ Next
+ </Button>
+ </CardContent>
+ </Card>
+ );
+
+ const renderError = () => (
+ <Card className="w-full max-w-md mx-auto">
+ <CardHeader className="text-center">
+ <div className="mx-auto w-12 h-12 bg-red-100 rounded-full flex items-center justify-center mb-4">
+ <AlertCircle className="w-6 h-6 text-red-600" />
+ </div>
+ <CardTitle>Creation Failed</CardTitle>
+ <CardDescription>
+ We couldn't create your VPS at this time
+ </CardDescription>
+ </CardHeader>
+ <CardContent className="text-center space-y-4">
+ {error && (
+ <Alert variant="destructive">
+ <AlertCircle className="h-4 w-4" />
+ <AlertDescription>{error}</AlertDescription>
+ </Alert>
+ )}
+
+ <div className="flex space-x-2">
+ <Button variant="outline" onClick={handleRetry} className="flex-1">
+ Try Again
+ </Button>
+ <Button
+ variant="outline"
+ onClick={() => setStep("api-key")}
+ className="flex-1"
+ >
+ Start Over
+ </Button>
+ </div>
+ </CardContent>
+ </Card>
+ );
+
+ return (
+ <div className="min-h-screen bg-gray-50 py-12 px-4">
+ <div className="max-w-6xl mx-auto">
+ <div className="text-center mb-8">
+ <h1 className="text-3xl font-bold text-gray-900 mb-2">
+ Cloud VPS Deployment
+ </h1>
+ <p className="text-gray-600">
+ Deploy your virtual private server in minutes
+ </p>
+ </div>
+
+ {step === "api-key" && renderApiKeyStep()}
+ {step === "location-select" && renderLocationSelect()}
+ {step === "creating" && renderCreating()}
+ {step === "success" && renderSuccess()}
+ {step === "error" && renderError()}
+ </div>
+ </div>
+ );
+}
diff --git a/js/README.md b/js/README.md
new file mode 100644
index 0000000..874ec57
--- /dev/null
+++ b/js/README.md
@@ -0,0 +1,15 @@
+# js
+
+To install dependencies:
+
+```bash
+bun install
+```
+
+To run:
+
+```bash
+bun run index.ts
+```
+
+This project was created using `bun init` in bun v1.2.16. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
diff --git a/js/bun.lock b/js/bun.lock
new file mode 100644
index 0000000..432f5f9
--- /dev/null
+++ b/js/bun.lock
@@ -0,0 +1,189 @@
+{
+ "lockfileVersion": 1,
+ "workspaces": {
+ "": {
+ "name": "js",
+ "dependencies": {
+ "@tailwindcss/cli": "^4.1.11",
+ "tailwindcss": "^4.1.11",
+ },
+ "devDependencies": {
+ "@types/bun": "latest",
+ },
+ "peerDependencies": {
+ "typescript": "^5",
+ },
+ },
+ },
+ "packages": {
+ "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
+
+ "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="],
+
+ "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="],
+
+ "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
+
+ "@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="],
+
+ "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="],
+
+ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
+
+ "@parcel/watcher": ["@parcel/watcher@2.5.1", "", { "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", "micromatch": "^4.0.5", "node-addon-api": "^7.0.0" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.1", "@parcel/watcher-darwin-arm64": "2.5.1", "@parcel/watcher-darwin-x64": "2.5.1", "@parcel/watcher-freebsd-x64": "2.5.1", "@parcel/watcher-linux-arm-glibc": "2.5.1", "@parcel/watcher-linux-arm-musl": "2.5.1", "@parcel/watcher-linux-arm64-glibc": "2.5.1", "@parcel/watcher-linux-arm64-musl": "2.5.1", "@parcel/watcher-linux-x64-glibc": "2.5.1", "@parcel/watcher-linux-x64-musl": "2.5.1", "@parcel/watcher-win32-arm64": "2.5.1", "@parcel/watcher-win32-ia32": "2.5.1", "@parcel/watcher-win32-x64": "2.5.1" } }, "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg=="],
+
+ "@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.1", "", { "os": "android", "cpu": "arm64" }, "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA=="],
+
+ "@parcel/watcher-darwin-arm64": ["@parcel/watcher-darwin-arm64@2.5.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw=="],
+
+ "@parcel/watcher-darwin-x64": ["@parcel/watcher-darwin-x64@2.5.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg=="],
+
+ "@parcel/watcher-freebsd-x64": ["@parcel/watcher-freebsd-x64@2.5.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ=="],
+
+ "@parcel/watcher-linux-arm-glibc": ["@parcel/watcher-linux-arm-glibc@2.5.1", "", { "os": "linux", "cpu": "arm" }, "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA=="],
+
+ "@parcel/watcher-linux-arm-musl": ["@parcel/watcher-linux-arm-musl@2.5.1", "", { "os": "linux", "cpu": "arm" }, "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q=="],
+
+ "@parcel/watcher-linux-arm64-glibc": ["@parcel/watcher-linux-arm64-glibc@2.5.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w=="],
+
+ "@parcel/watcher-linux-arm64-musl": ["@parcel/watcher-linux-arm64-musl@2.5.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg=="],
+
+ "@parcel/watcher-linux-x64-glibc": ["@parcel/watcher-linux-x64-glibc@2.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A=="],
+
+ "@parcel/watcher-linux-x64-musl": ["@parcel/watcher-linux-x64-musl@2.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg=="],
+
+ "@parcel/watcher-win32-arm64": ["@parcel/watcher-win32-arm64@2.5.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw=="],
+
+ "@parcel/watcher-win32-ia32": ["@parcel/watcher-win32-ia32@2.5.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ=="],
+
+ "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="],
+
+ "@tailwindcss/cli": ["@tailwindcss/cli@4.1.11", "", { "dependencies": { "@parcel/watcher": "^2.5.1", "@tailwindcss/node": "4.1.11", "@tailwindcss/oxide": "4.1.11", "enhanced-resolve": "^5.18.1", "mri": "^1.2.0", "picocolors": "^1.1.1", "tailwindcss": "4.1.11" }, "bin": { "tailwindcss": "dist/index.mjs" } }, "sha512-7RAFOrVaXCFz5ooEG36Kbh+sMJiI2j4+Ozp71smgjnLfBRu7DTfoq8DsTvzse2/6nDeo2M3vS/FGaxfDgr3rtQ=="],
+
+ "@tailwindcss/node": ["@tailwindcss/node@4.1.11", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", "tailwindcss": "4.1.11" } }, "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q=="],
+
+ "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.11", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.4.3" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.11", "@tailwindcss/oxide-darwin-arm64": "4.1.11", "@tailwindcss/oxide-darwin-x64": "4.1.11", "@tailwindcss/oxide-freebsd-x64": "4.1.11", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", "@tailwindcss/oxide-linux-x64-musl": "4.1.11", "@tailwindcss/oxide-wasm32-wasi": "4.1.11", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" } }, "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg=="],
+
+ "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.11", "", { "os": "android", "cpu": "arm64" }, "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg=="],
+
+ "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ=="],
+
+ "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw=="],
+
+ "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.11", "", { "os": "freebsd", "cpu": "x64" }, "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA=="],
+
+ "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11", "", { "os": "linux", "cpu": "arm" }, "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg=="],
+
+ "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ=="],
+
+ "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ=="],
+
+ "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.11", "", { "os": "linux", "cpu": "x64" }, "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg=="],
+
+ "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.11", "", { "os": "linux", "cpu": "x64" }, "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q=="],
+
+ "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.11", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@emnapi/wasi-threads": "^1.0.2", "@napi-rs/wasm-runtime": "^0.2.11", "@tybys/wasm-util": "^0.9.0", "tslib": "^2.8.0" }, "cpu": "none" }, "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g=="],
+
+ "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w=="],
+
+ "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.11", "", { "os": "win32", "cpu": "x64" }, "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg=="],
+
+ "@types/bun": ["@types/bun@1.2.17", "", { "dependencies": { "bun-types": "1.2.17" } }, "sha512-l/BYs/JYt+cXA/0+wUhulYJB6a6p//GTPiJ7nV+QHa8iiId4HZmnu/3J/SowP5g0rTiERY2kfGKXEK5Ehltx4Q=="],
+
+ "@types/node": ["@types/node@24.0.5", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-CXEG9E7GCTOZIre0WdDznmnhvF7xi7AmnP/zF496trmLiqlfdtxp9nPRgLVqfmJ8jgtcKcs0EcvOu2yDZSuvTg=="],
+
+ "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
+
+ "bun-types": ["bun-types@1.2.17", "", { "dependencies": { "@types/node": "*" } }, "sha512-ElC7ItwT3SCQwYZDYoAH+q6KT4Fxjl8DtZ6qDulUFBmXA8YB4xo+l54J9ZJN+k2pphfn9vk7kfubeSd5QfTVJQ=="],
+
+ "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
+
+ "detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="],
+
+ "enhanced-resolve": ["enhanced-resolve@5.18.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ=="],
+
+ "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
+
+ "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
+
+ "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
+
+ "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
+
+ "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
+
+ "jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="],
+
+ "lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="],
+
+ "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="],
+
+ "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="],
+
+ "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig=="],
+
+ "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.1", "", { "os": "linux", "cpu": "arm" }, "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q=="],
+
+ "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw=="],
+
+ "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ=="],
+
+ "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw=="],
+
+ "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ=="],
+
+ "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA=="],
+
+ "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="],
+
+ "magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
+
+ "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
+
+ "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
+
+ "minizlib": ["minizlib@3.0.2", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA=="],
+
+ "mkdirp": ["mkdirp@3.0.1", "", { "bin": { "mkdirp": "dist/cjs/src/bin.js" } }, "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="],
+
+ "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="],
+
+ "node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="],
+
+ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
+
+ "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
+
+ "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
+
+ "tailwindcss": ["tailwindcss@4.1.11", "", {}, "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA=="],
+
+ "tapable": ["tapable@2.2.2", "", {}, "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg=="],
+
+ "tar": ["tar@7.4.3", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" } }, "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw=="],
+
+ "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
+
+ "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
+
+ "undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="],
+
+ "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
+
+ "@tailwindcss/oxide/detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.11", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.9.0" }, "bundled": true }, "sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
+
+ "lightningcss/detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="],
+ }
+}
diff --git a/js/index.ts b/js/index.ts
new file mode 100644
index 0000000..f67b2c6
--- /dev/null
+++ b/js/index.ts
@@ -0,0 +1 @@
+console.log("Hello via Bun!"); \ No newline at end of file
diff --git a/js/output.css b/js/output.css
new file mode 100644
index 0000000..ef300d9
--- /dev/null
+++ b/js/output.css
@@ -0,0 +1,901 @@
+/*! tailwindcss v4.1.11 | MIT License | https://tailwindcss.com */
+@layer properties;
+@layer theme, base, components, utilities;
+
+@layer theme {
+
+ :root,
+ :host {
+ --font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",
+ "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+ --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
+ "Courier New", monospace;
+ --color-red-100: oklch(93.6% 0.032 17.717);
+ --color-red-600: oklch(57.7% 0.245 27.325);
+ --color-green-100: oklch(96.2% 0.044 156.743);
+ --color-green-600: oklch(62.7% 0.194 149.214);
+ --color-blue-100: oklch(93.2% 0.032 255.585);
+ --color-blue-200: oklch(88.2% 0.059 254.128);
+ --color-blue-600: oklch(54.6% 0.245 262.881);
+ --color-gray-50: oklch(98.5% 0.002 247.839);
+ --color-gray-600: oklch(44.6% 0.03 256.802);
+ --color-gray-900: oklch(21% 0.034 264.665);
+ --spacing: 0.25rem;
+ --container-md: 28rem;
+ --container-4xl: 56rem;
+ --container-6xl: 72rem;
+ --text-xs: 0.75rem;
+ --text-xs--line-height: calc(1 / 0.75);
+ --text-sm: 0.875rem;
+ --text-sm--line-height: calc(1.25 / 0.875);
+ --text-2xl: 1.5rem;
+ --text-2xl--line-height: calc(2 / 1.5);
+ --text-3xl: 1.875rem;
+ --text-3xl--line-height: calc(2.25 / 1.875);
+ --font-weight-medium: 500;
+ --font-weight-semibold: 600;
+ --font-weight-bold: 700;
+ --animate-spin: spin 1s linear infinite;
+ --default-transition-duration: 150ms;
+ --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+ --default-font-family: var(--font-sans);
+ --default-mono-font-family: var(--font-mono);
+ }
+}
+
+@layer base {
+
+ *,
+ ::after,
+ ::before,
+ ::backdrop,
+ ::file-selector-button {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+ border: 0 solid;
+ }
+
+ html,
+ :host {
+ line-height: 1.5;
+ -webkit-text-size-adjust: 100%;
+ tab-size: 4;
+ font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");
+ font-feature-settings: var(--default-font-feature-settings, normal);
+ font-variation-settings: var(--default-font-variation-settings, normal);
+ -webkit-tap-highlight-color: transparent;
+ }
+
+ hr {
+ height: 0;
+ color: inherit;
+ border-top-width: 1px;
+ }
+
+ abbr:where([title]) {
+ -webkit-text-decoration: underline dotted;
+ text-decoration: underline dotted;
+ }
+
+ h1,
+ h2,
+ h3,
+ h4,
+ h5,
+ h6 {
+ font-size: inherit;
+ font-weight: inherit;
+ }
+
+ a {
+ color: inherit;
+ -webkit-text-decoration: inherit;
+ text-decoration: inherit;
+ }
+
+ b,
+ strong {
+ font-weight: bolder;
+ }
+
+ code,
+ kbd,
+ samp,
+ pre {
+ font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);
+ font-feature-settings: var(--default-mono-font-feature-settings, normal);
+ font-variation-settings: var(--default-mono-font-variation-settings, normal);
+ font-size: 1em;
+ }
+
+ small {
+ font-size: 80%;
+ }
+
+ sub,
+ sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+ }
+
+ sub {
+ bottom: -0.25em;
+ }
+
+ sup {
+ top: -0.5em;
+ }
+
+ table {
+ text-indent: 0;
+ border-color: inherit;
+ border-collapse: collapse;
+ }
+
+ :-moz-focusring {
+ outline: auto;
+ }
+
+ progress {
+ vertical-align: baseline;
+ }
+
+ summary {
+ display: list-item;
+ }
+
+ ol,
+ ul,
+ menu {
+ list-style: none;
+ }
+
+ img,
+ svg,
+ video,
+ canvas,
+ audio,
+ iframe,
+ embed,
+ object {
+ display: block;
+ vertical-align: middle;
+ }
+
+ img,
+ video {
+ max-width: 100%;
+ height: auto;
+ }
+
+ button,
+ input,
+ select,
+ optgroup,
+ textarea,
+ ::file-selector-button {
+ font: inherit;
+ font-feature-settings: inherit;
+ font-variation-settings: inherit;
+ letter-spacing: inherit;
+ color: inherit;
+ border-radius: 0;
+ background-color: transparent;
+ opacity: 1;
+ }
+
+ :where(select:is([multiple], [size])) optgroup {
+ font-weight: bolder;
+ }
+
+ :where(select:is([multiple], [size])) optgroup option {
+ padding-inline-start: 20px;
+ }
+
+ ::file-selector-button {
+ margin-inline-end: 4px;
+ }
+
+ ::placeholder {
+ opacity: 1;
+ }
+
+ @supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) {
+ ::placeholder {
+ color: currentcolor;
+
+ @supports (color: color-mix(in lab, red, red)) {
+ color: color-mix(in oklab, currentcolor 50%, transparent);
+ }
+ }
+ }
+
+ textarea {
+ resize: vertical;
+ }
+
+ ::-webkit-search-decoration {
+ -webkit-appearance: none;
+ }
+
+ ::-webkit-date-and-time-value {
+ min-height: 1lh;
+ text-align: inherit;
+ }
+
+ ::-webkit-datetime-edit {
+ display: inline-flex;
+ }
+
+ ::-webkit-datetime-edit-fields-wrapper {
+ padding: 0;
+ }
+
+ ::-webkit-datetime-edit,
+ ::-webkit-datetime-edit-year-field,
+ ::-webkit-datetime-edit-month-field,
+ ::-webkit-datetime-edit-day-field,
+ ::-webkit-datetime-edit-hour-field,
+ ::-webkit-datetime-edit-minute-field,
+ ::-webkit-datetime-edit-second-field,
+ ::-webkit-datetime-edit-millisecond-field,
+ ::-webkit-datetime-edit-meridiem-field {
+ padding-block: 0;
+ }
+
+ :-moz-ui-invalid {
+ box-shadow: none;
+ }
+
+ button,
+ input:where([type="button"], [type="reset"], [type="submit"]),
+ ::file-selector-button {
+ appearance: button;
+ }
+
+ ::-webkit-inner-spin-button,
+ ::-webkit-outer-spin-button {
+ height: auto;
+ }
+
+ [hidden]:where(:not([hidden="until-found"])) {
+ display: none !important;
+ }
+}
+
+@layer utilities {
+ .mx-auto {
+ margin-inline: auto;
+ }
+
+ .mr-2 {
+ margin-right: calc(var(--spacing) * 2);
+ }
+
+ .mb-2 {
+ margin-bottom: calc(var(--spacing) * 2);
+ }
+
+ .mb-4 {
+ margin-bottom: calc(var(--spacing) * 4);
+ }
+
+ .mb-8 {
+ margin-bottom: calc(var(--spacing) * 8);
+ }
+
+ .flex {
+ display: flex;
+ }
+
+ .grid {
+ display: grid;
+ }
+
+ .table {
+ display: table;
+ }
+
+ .h-4 {
+ height: calc(var(--spacing) * 4);
+ }
+
+ .h-6 {
+ height: calc(var(--spacing) * 6);
+ }
+
+ .h-8 {
+ height: calc(var(--spacing) * 8);
+ }
+
+ .h-12 {
+ height: calc(var(--spacing) * 12);
+ }
+
+ .min-h-screen {
+ min-height: 100vh;
+ }
+
+ .w-4 {
+ width: calc(var(--spacing) * 4);
+ }
+
+ .w-6 {
+ width: calc(var(--spacing) * 6);
+ }
+
+ .w-12 {
+ width: calc(var(--spacing) * 12);
+ }
+
+ .w-full {
+ width: 100%;
+ }
+
+ .max-w-4xl {
+ max-width: var(--container-4xl);
+ }
+
+ .max-w-6xl {
+ max-width: var(--container-6xl);
+ }
+
+ .max-w-md {
+ max-width: var(--container-md);
+ }
+
+ .flex-1 {
+ flex: 1;
+ }
+
+ .border-collapse {
+ border-collapse: collapse;
+ }
+
+ .transform {
+ transform: var(--tw-rotate-x, ) var(--tw-rotate-y, ) var(--tw-rotate-z, ) var(--tw-skew-x, ) var(--tw-skew-y, );
+ }
+
+ .animate-spin {
+ animation: var(--animate-spin);
+ }
+
+ .cursor-pointer {
+ cursor: pointer;
+ }
+
+ .resize {
+ resize: both;
+ }
+
+ .grid-cols-1 {
+ grid-template-columns: repeat(1, minmax(0, 1fr));
+ }
+
+ .items-center {
+ align-items: center;
+ }
+
+ .justify-center {
+ justify-content: center;
+ }
+
+ .gap-4 {
+ gap: calc(var(--spacing) * 4);
+ }
+
+ .space-y-2 {
+ :where(& > :not(:last-child)) {
+ --tw-space-y-reverse: 0;
+ margin-block-start: calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));
+ margin-block-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)));
+ }
+ }
+
+ .space-y-4 {
+ :where(& > :not(:last-child)) {
+ --tw-space-y-reverse: 0;
+ margin-block-start: calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));
+ margin-block-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)));
+ }
+ }
+
+ .space-x-2 {
+ :where(& > :not(:last-child)) {
+ --tw-space-x-reverse: 0;
+ margin-inline-start: calc(calc(var(--spacing) * 2) * var(--tw-space-x-reverse));
+ margin-inline-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)));
+ }
+ }
+
+ .space-x-3 {
+ :where(& > :not(:last-child)) {
+ --tw-space-x-reverse: 0;
+ margin-inline-start: calc(calc(var(--spacing) * 3) * var(--tw-space-x-reverse));
+ margin-inline-end: calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-x-reverse)));
+ }
+ }
+
+ .rounded-full {
+ border-radius: calc(infinity * 1px);
+ }
+
+ .border {
+ border-style: var(--tw-border-style);
+ border-width: 1px;
+ }
+
+ .border-2 {
+ border-style: var(--tw-border-style);
+ border-width: 2px;
+ }
+
+ .bg-blue-100 {
+ background-color: var(--color-blue-100);
+ }
+
+ .bg-gray-50 {
+ background-color: var(--color-gray-50);
+ }
+
+ .bg-green-100 {
+ background-color: var(--color-green-100);
+ }
+
+ .bg-red-100 {
+ background-color: var(--color-red-100);
+ }
+
+ .p-4 {
+ padding: calc(var(--spacing) * 4);
+ }
+
+ .px-4 {
+ padding-inline: calc(var(--spacing) * 4);
+ }
+
+ .py-12 {
+ padding-block: calc(var(--spacing) * 12);
+ }
+
+ .text-center {
+ text-align: center;
+ }
+
+ .text-2xl {
+ font-size: var(--text-2xl);
+ line-height: var(--tw-leading, var(--text-2xl--line-height));
+ }
+
+ .text-3xl {
+ font-size: var(--text-3xl);
+ line-height: var(--tw-leading, var(--text-3xl--line-height));
+ }
+
+ .text-sm {
+ font-size: var(--text-sm);
+ line-height: var(--tw-leading, var(--text-sm--line-height));
+ }
+
+ .text-xs {
+ font-size: var(--text-xs);
+ line-height: var(--tw-leading, var(--text-xs--line-height));
+ }
+
+ .font-bold {
+ --tw-font-weight: var(--font-weight-bold);
+ font-weight: var(--font-weight-bold);
+ }
+
+ .font-medium {
+ --tw-font-weight: var(--font-weight-medium);
+ font-weight: var(--font-weight-medium);
+ }
+
+ .font-semibold {
+ --tw-font-weight: var(--font-weight-semibold);
+ font-weight: var(--font-weight-semibold);
+ }
+
+ .text-blue-600 {
+ color: var(--color-blue-600);
+ }
+
+ .text-gray-600 {
+ color: var(--color-gray-600);
+ }
+
+ .text-gray-900 {
+ color: var(--color-gray-900);
+ }
+
+ .text-green-600 {
+ color: var(--color-green-600);
+ }
+
+ .text-red-600 {
+ color: var(--color-red-600);
+ }
+
+ .underline {
+ text-decoration-line: underline;
+ }
+
+ .outline {
+ outline-style: var(--tw-outline-style);
+ outline-width: 1px;
+ }
+
+ .transition {
+ transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, visibility, content-visibility, overlay, pointer-events;
+ transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
+ transition-duration: var(--tw-duration, var(--default-transition-duration));
+ }
+
+ .transition-shadow {
+ transition-property: box-shadow;
+ transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
+ transition-duration: var(--tw-duration, var(--default-transition-duration));
+ }
+
+ .hover\:border-blue-200 {
+ &:hover {
+ @media (hover: hover) {
+ border-color: var(--color-blue-200);
+ }
+ }
+ }
+
+ .hover\:shadow-md {
+ &:hover {
+ @media (hover: hover) {
+ --tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
+ }
+ }
+ }
+
+ .md\:grid-cols-2 {
+ @media (width >=48rem) {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+ }
+
+ .lg\:grid-cols-3 {
+ @media (width >=64rem) {
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ }
+ }
+}
+
+:root {
+ --red05: rgba(255, 65, 54, 0.05);
+ --red100: rgba(255, 65, 54, 1);
+ --blue05: rgba(33, 157, 255, 0.05);
+ --blue30: rgba(33, 157, 255, 0.3);
+ --blue100: rgba(33, 157, 255, 1);
+ --black05: rgba(0, 0, 0, 0.05);
+ --black20: rgba(0, 0, 0, 0.2);
+ --black60: rgba(0, 0, 0, 0.6);
+ --white: rgba(255, 255, 255, 1);
+ --text: rgba(0, 0, 0, 1);
+ --heihong: rgb(141, 15, 15);
+ --hong: rgb(183, 14, 14);
+ --huang: rgb(230, 180, 60);
+ --lan: rgb(30, 60, 80);
+}
+
+[data-theme=dark] {
+ --background-color: black;
+ --text-color: rgb(200, 200, 200);
+}
+
+[data-theme=light] {
+ --background-color: white;
+ --text-color: black;
+}
+
+@font-face {
+ font-family: Crimson Text;
+ src: url(https://s3.spandrell.ch/assets/fonts/CrimsonText-Regular.ttf) format('truetype');
+ font-weight: 400;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: Crimson Text;
+ src: url(https://s3.spandrell.ch/assets/fonts/CrimsonText-Italic.ttf) format('truetype');
+ font-weight: 400;
+ font-style: italic;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: Crimson Text;
+ src: url(https://s3.spandrell.ch/assets/fonts/CrimsonText-SemiBold.ttf) format('truetype');
+ font-weight: 600;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: Crimson Text;
+ src: url(https://s3.spandrell.ch/assets/fonts/CrimsonText-SemiBoldItalic.ttf) format('truetype');
+ font-weight: 600;
+ font-style: italic;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: Crimson Text;
+ src: url(https://s3.spandrell.ch/assets/fonts/CrimsonText-Bold.ttf) format('truetype');
+ font-weight: 700;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: Crimson Text;
+ src: url(https://s3.spandrell.ch/assets/fonts/CrimsonText-BoldItalic.ttf) format('truetype');
+ font-weight: 700;
+ font-style: italic;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: "Inter";
+ src: url(https://s3.spandrell.ch/assets/fonts/InterVariableFont.otf) format('truetype-variations');
+ font-weight: 100 900;
+ font-style: normal;
+ font-display: swap;
+}
+
+html {
+ height: 100%;
+ color: var(--text-color);
+ -webkit-font-smoothing: antialiased;
+}
+
+body {
+ margin: 0;
+ width: 100%;
+ height: 100%;
+ font-size: 16px;
+ font-weight: 400;
+ line-height: 1.5;
+ font-size: 1rem;
+ line-height: 1.618;
+ text-align: left;
+ color: black !important;
+ text-rendering: optimizeLegibility !important;
+ -webkit-font-smoothing: antialiased;
+ word-break: break-word;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+p {
+ font-family: Crimson Text;
+}
+
+a,
+button,
+input {
+ transition: all .4s ease;
+}
+
+input[type="submit"],
+button,
+.button {
+ border-color: #181818;
+ background-color: #181818;
+ padding: 0.5rem 1.25rem;
+ color: #fff;
+ font-size: 0.875rem;
+ line-height: 1.5;
+ border-radius: 0.2rem;
+ display: inline-block;
+ font-weight: 400;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: middle;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ cursor: pointer;
+ -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;
+ transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;
+ -o-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+ transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+ transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;
+ font-family: 'Montserrat', sans-serif;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ font-size: 13px;
+ -webkit-transition: all .4s ease;
+ -o-transition: all .4s ease;
+ transition: all .4s ease;
+}
+
+@property --tw-rotate-x {
+ syntax: "*";
+ inherits: false;
+}
+
+@property --tw-rotate-y {
+ syntax: "*";
+ inherits: false;
+}
+
+@property --tw-rotate-z {
+ syntax: "*";
+ inherits: false;
+}
+
+@property --tw-skew-x {
+ syntax: "*";
+ inherits: false;
+}
+
+@property --tw-skew-y {
+ syntax: "*";
+ inherits: false;
+}
+
+@property --tw-space-y-reverse {
+ syntax: "*";
+ inherits: false;
+ initial-value: 0;
+}
+
+@property --tw-space-x-reverse {
+ syntax: "*";
+ inherits: false;
+ initial-value: 0;
+}
+
+@property --tw-border-style {
+ syntax: "*";
+ inherits: false;
+ initial-value: solid;
+}
+
+@property --tw-font-weight {
+ syntax: "*";
+ inherits: false;
+}
+
+@property --tw-outline-style {
+ syntax: "*";
+ inherits: false;
+ initial-value: solid;
+}
+
+@property --tw-shadow {
+ syntax: "*";
+ inherits: false;
+ initial-value: 0 0 #0000;
+}
+
+@property --tw-shadow-color {
+ syntax: "*";
+ inherits: false;
+}
+
+@property --tw-shadow-alpha {
+ syntax: "<percentage>";
+ inherits: false;
+ initial-value: 100%;
+}
+
+@property --tw-inset-shadow {
+ syntax: "*";
+ inherits: false;
+ initial-value: 0 0 #0000;
+}
+
+@property --tw-inset-shadow-color {
+ syntax: "*";
+ inherits: false;
+}
+
+@property --tw-inset-shadow-alpha {
+ syntax: "<percentage>";
+ inherits: false;
+ initial-value: 100%;
+}
+
+@property --tw-ring-color {
+ syntax: "*";
+ inherits: false;
+}
+
+@property --tw-ring-shadow {
+ syntax: "*";
+ inherits: false;
+ initial-value: 0 0 #0000;
+}
+
+@property --tw-inset-ring-color {
+ syntax: "*";
+ inherits: false;
+}
+
+@property --tw-inset-ring-shadow {
+ syntax: "*";
+ inherits: false;
+ initial-value: 0 0 #0000;
+}
+
+@property --tw-ring-inset {
+ syntax: "*";
+ inherits: false;
+}
+
+@property --tw-ring-offset-width {
+ syntax: "<length>";
+ inherits: false;
+ initial-value: 0px;
+}
+
+@property --tw-ring-offset-color {
+ syntax: "*";
+ inherits: false;
+ initial-value: #fff;
+}
+
+@property --tw-ring-offset-shadow {
+ syntax: "*";
+ inherits: false;
+ initial-value: 0 0 #0000;
+}
+
+@keyframes spin {
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+@layer properties {
+ @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
+
+ *,
+ ::before,
+ ::after,
+ ::backdrop {
+ --tw-rotate-x: initial;
+ --tw-rotate-y: initial;
+ --tw-rotate-z: initial;
+ --tw-skew-x: initial;
+ --tw-skew-y: initial;
+ --tw-space-y-reverse: 0;
+ --tw-space-x-reverse: 0;
+ --tw-border-style: solid;
+ --tw-font-weight: initial;
+ --tw-outline-style: solid;
+ --tw-shadow: 0 0 #0000;
+ --tw-shadow-color: initial;
+ --tw-shadow-alpha: 100%;
+ --tw-inset-shadow: 0 0 #0000;
+ --tw-inset-shadow-color: initial;
+ --tw-inset-shadow-alpha: 100%;
+ --tw-ring-color: initial;
+ --tw-ring-shadow: 0 0 #0000;
+ --tw-inset-ring-color: initial;
+ --tw-inset-ring-shadow: 0 0 #0000;
+ --tw-ring-inset: initial;
+ --tw-ring-offset-width: 0px;
+ --tw-ring-offset-color: #fff;
+ --tw-ring-offset-shadow: 0 0 #0000;
+ }
+ }
+} \ No newline at end of file
diff --git a/js/package.json b/js/package.json
new file mode 100644
index 0000000..0c965de
--- /dev/null
+++ b/js/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "js",
+ "module": "index.ts",
+ "type": "module",
+ "private": true,
+ "devDependencies": {
+ "@types/bun": "latest"
+ },
+ "peerDependencies": {
+ "typescript": "^5"
+ },
+ "dependencies": {
+ "@tailwindcss/cli": "^4.1.11",
+ "tailwindcss": "^4.1.11"
+ }
+}
diff --git a/js/styles.css b/js/styles.css
new file mode 100644
index 0000000..4b288be
--- /dev/null
+++ b/js/styles.css
@@ -0,0 +1,182 @@
+@import "tailwindcss";
+/* @config "./tailwind.config.js"; */
+
+/* Since we use dynamic classNames on Theme.re, we need to inline the colors here. This can cause tailwind to not generate some classes if there's a missing variant here or we add new colors. */
+/* @source inline("{hover:,}{text,bg,border}-[#FFC53D]"); */
+/* @source inline("{hover:,}{text,bg,border}-[#080808]"); */
+/* @source inline("{hover:,}{text,bg,border}-[#0F0F0F]"); */
+/* @source inline("{hover:,}{text,bg,border}-[#151515]"); */
+/* @source inline("{hover:,}{text,bg,border}-[#191919]"); */
+/* @source inline("{hover:,}{text,bg,border}-[#1E1E1E]"); */
+/* @source inline("{hover:,}{text,bg,border}-[#252525]"); */
+/* @source inline("{hover:,}{text,bg,border}-[#2A2A2A]"); */
+/* @source inline("{hover:,}{text,bg,border}-[#313131]"); */
+/* @source inline("{hover:,}{text,bg,border}-[#3A3A3A]"); */
+/* @source inline("{hover:,}{text,bg,border}-[#484848]"); */
+/* @source inline("{hover:,}{text,bg,border}-[#6E6E6E]"); */
+/* @source inline("{hover:,}{text,bg,border}-[#B4B4B4]"); */
+/* @source inline("{hover:,}{text,bg,border}-[#EEEEEE]"); */
+/* @source inline("{hover:,}{text,bg,border}-[#F5F5F5]"); */
+/* @source inline("{hover:,}{text,bg,border}-[#FFFFFF]"); */
+/* @source inline("{hover:,focus:,active:,disabled:,}{text,bg,border}-primary"); */
+
+
+
+:root {
+ --red05: rgba(255, 65, 54, 0.05);
+ --red100: rgba(255, 65, 54, 1);
+ --blue05: rgba(33, 157, 255, 0.05);
+ --blue30: rgba(33, 157, 255, 0.3);
+ --blue100: rgba(33, 157, 255, 1);
+ --black05: rgba(0, 0, 0, 0.05);
+ --black20: rgba(0, 0, 0, 0.2);
+ --black60: rgba(0, 0, 0, 0.6);
+ --white: rgba(255, 255, 255, 1);
+
+ --text: rgba(0, 0, 0, 1);
+ --heihong: rgb(141, 15, 15);
+ --hong: rgb(183, 14, 14);
+ --huang: rgb(230, 180, 60);
+ --lan: rgb(30, 60, 80);
+}
+
+[data-theme=dark] {
+ --background-color: black;
+ --text-color: rgb(200, 200, 200);
+}
+
+[data-theme=light] {
+ /* --background-color: #EFF1EC; */
+ --background-color: white;
+ --text-color: black;
+}
+
+@font-face {
+ font-family: Crimson Text;
+ src: url(https://s3.spandrell.ch/assets/fonts/CrimsonText-Regular.ttf) format('truetype');
+ font-weight: 400;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: Crimson Text;
+ src: url(https://s3.spandrell.ch/assets/fonts/CrimsonText-Italic.ttf) format('truetype');
+ font-weight: 400;
+ font-style: italic;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: Crimson Text;
+ src: url(https://s3.spandrell.ch/assets/fonts/CrimsonText-SemiBold.ttf) format('truetype');
+ font-weight: 600;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: Crimson Text;
+ src: url(https://s3.spandrell.ch/assets/fonts/CrimsonText-SemiBoldItalic.ttf) format('truetype');
+ font-weight: 600;
+ font-style: italic;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: Crimson Text;
+ src: url(https://s3.spandrell.ch/assets/fonts/CrimsonText-Bold.ttf) format('truetype');
+ font-weight: 700;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: Crimson Text;
+ src: url(https://s3.spandrell.ch/assets/fonts/CrimsonText-BoldItalic.ttf) format('truetype');
+ font-weight: 700;
+ font-style: italic;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: "Inter";
+ src: url(https://s3.spandrell.ch/assets/fonts/InterVariableFont.otf) format('truetype-variations');
+ font-weight: 100 900;
+ font-style: normal;
+ font-display: swap;
+}
+
+html {
+ height: 100%;
+ color: var(--text-color);
+ -webkit-font-smoothing: antialiased;
+}
+
+body {
+ margin: 0;
+ width: 100%;
+ height: 100%;
+ font-size: 16px;
+ font-weight: 400;
+ line-height: 1.5;
+ font-size: 1rem;
+ line-height: 1.618;
+ text-align: left;
+ color: black !important;
+ text-rendering: optimizeLegibility !important;
+ -webkit-font-smoothing: antialiased;
+ word-break: break-word;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+p {
+ font-family: Crimson Text;
+}
+
+
+a,
+button,
+input {
+ transition: all .4s ease;
+}
+
+
+input[type="submit"],
+button,
+.button {
+ border-color: #181818;
+ background-color: #181818;
+ padding: 0.5rem 1.25rem;
+ color: #fff;
+ font-size: 0.875rem;
+ line-height: 1.5;
+ border-radius: 0.2rem;
+ display: inline-block;
+ font-weight: 400;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: middle;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ cursor: pointer;
+ -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;
+ transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;
+ -o-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+ transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+ transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;
+ font-family: 'Montserrat', sans-serif;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ font-size: 13px;
+ -webkit-transition: all .4s ease;
+ -o-transition: all .4s ease;
+ transition: all .4s ease;
+}
diff --git a/js/tailwind.config.js b/js/tailwind.config.js
new file mode 100644
index 0000000..e97787d
--- /dev/null
+++ b/js/tailwind.config.js
@@ -0,0 +1,5 @@
+export default {
+ // files: ["../client/**/*.mlx","../server/**/*.mlx", "../universal/**/*.mlx"],
+ content:{files: ["*.mlx", "../**/*.{js, ts, jsx, tsx}","../**/*.mlx"]},
+ plugins: {},
+}
diff --git a/js/tsconfig.json b/js/tsconfig.json
new file mode 100644
index 0000000..bfa0fea
--- /dev/null
+++ b/js/tsconfig.json
@@ -0,0 +1,29 @@
+{
+ "compilerOptions": {
+ // Environment setup & latest features
+ "lib": ["ESNext"],
+ "target": "ESNext",
+ "module": "Preserve",
+ "moduleDetection": "force",
+ "jsx": "react-jsx",
+ "allowJs": true,
+
+ // Bundler mode
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "noEmit": true,
+
+ // Best practices
+ "strict": true,
+ "skipLibCheck": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedIndexedAccess": true,
+ "noImplicitOverride": true,
+
+ // Some stricter flags (disabled by default)
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
+ "noPropertyAccessFromIndexSignature": false
+ }
+}