glintkit 0.1.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/index.ts","../src/cli/commands/init.ts","../src/utils/logger.ts","../src/utils/config.ts","../src/cli/commands/add.ts","../src/registry/components.ts","../src/utils/detect-pm.ts","../src/utils/installer.ts","../src/utils/file-writer.ts","../src/registry/__generated__/templates.ts","../src/utils/css-injector.ts","../src/registry/css-presets.ts","../src/cli/commands/list.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { initCommand } from \"./commands/init.js\";\nimport { addCommand } from \"./commands/add.js\";\nimport { listCommand } from \"./commands/list.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"glintkit\")\n .description(\"Add beautiful 3D, glass, and motion components to your project\")\n .version(\"0.1.0\");\n\nprogram.addCommand(initCommand);\nprogram.addCommand(addCommand);\nprogram.addCommand(listCommand);\n\nprogram.parse();\n","import { Command } from \"commander\";\nimport prompts from \"prompts\";\nimport { logger } from \"../../utils/logger.js\";\nimport { writeConfig, configExists, detectAliasFromTsconfig } from \"../../utils/config.js\";\nimport type { GlintUIConfig } from \"../../utils/config.js\";\n\nexport const initCommand = new Command(\"init\")\n .description(\"Initialize glintkit configuration\")\n .option(\"-y, --yes\", \"Skip prompts and use defaults\")\n .action(async (options) => {\n const cwd = process.cwd();\n\n if (configExists(cwd)) {\n logger.warn(\"glintkit.json already exists. Overwriting...\");\n }\n\n logger.title(\"\\n glintkit init\\n\");\n\n // Auto-detect alias prefix from tsconfig\n const detectedAlias = detectAliasFromTsconfig(cwd);\n const aliasPrefix = detectedAlias || \"@\";\n\n let config: GlintUIConfig;\n\n if (options.yes) {\n config = {\n aliases: {\n components: `${aliasPrefix}/components/ui`,\n hooks: `${aliasPrefix}/hooks`,\n utils: `${aliasPrefix}/lib`,\n },\n tailwind: {\n css: \"src/app/globals.css\",\n },\n };\n } else {\n const response = await prompts([\n {\n type: \"text\",\n name: \"components\",\n message: \"Components directory alias:\",\n initial: `${aliasPrefix}/components/ui`,\n },\n {\n type: \"text\",\n name: \"hooks\",\n message: \"Hooks directory alias:\",\n initial: `${aliasPrefix}/hooks`,\n },\n {\n type: \"text\",\n name: \"utils\",\n message: \"Utils directory alias:\",\n initial: `${aliasPrefix}/lib`,\n },\n {\n type: \"text\",\n name: \"css\",\n message: \"Tailwind CSS file path:\",\n initial: \"src/app/globals.css\",\n },\n ]);\n\n if (!response.components) {\n logger.error(\"Init cancelled.\");\n process.exit(1);\n }\n\n config = {\n aliases: {\n components: response.components,\n hooks: response.hooks,\n utils: response.utils,\n },\n tailwind: {\n css: response.css,\n },\n };\n }\n\n writeConfig(config, cwd);\n logger.success(\"Created glintkit.json\");\n logger.break();\n logger.info(\"Now you can add components:\");\n logger.info(\" glintkit add 3d-card\");\n logger.info(\" glintkit add --category 3d\");\n logger.info(\" glintkit add --all\");\n logger.break();\n });\n","import pc from \"picocolors\";\n\nexport const logger = {\n info: (msg: string) => console.log(pc.cyan(\"ℹ\"), msg),\n success: (msg: string) => console.log(pc.green(\"✔\"), msg),\n warn: (msg: string) => console.log(pc.yellow(\"⚠\"), msg),\n error: (msg: string) => console.log(pc.red(\"✖\"), msg),\n break: () => console.log(\"\"),\n title: (msg: string) => console.log(pc.bold(pc.cyan(msg))),\n};\n","import fs from \"fs-extra\";\nimport path from \"path\";\n\nexport interface GlintUIConfig {\n aliases: {\n components: string;\n hooks: string;\n utils: string;\n };\n tailwind: {\n css: string;\n };\n}\n\nconst CONFIG_FILE = \"glintkit.json\";\n\nexport function getConfigPath(cwd: string = process.cwd()): string {\n return path.join(cwd, CONFIG_FILE);\n}\n\nexport function configExists(cwd: string = process.cwd()): boolean {\n return fs.existsSync(getConfigPath(cwd));\n}\n\nexport function readConfig(cwd: string = process.cwd()): GlintUIConfig {\n const configPath = getConfigPath(cwd);\n if (!fs.existsSync(configPath)) {\n throw new Error(\"glintkit.json not found. Run `glintkit init` first.\");\n }\n return fs.readJSONSync(configPath) as GlintUIConfig;\n}\n\nexport function writeConfig(config: GlintUIConfig, cwd: string = process.cwd()): void {\n fs.writeJSONSync(getConfigPath(cwd), config, { spaces: 2 });\n}\n\nexport function detectAliasFromTsconfig(cwd: string = process.cwd()): string | null {\n const tsconfigPath = path.join(cwd, \"tsconfig.json\");\n if (!fs.existsSync(tsconfigPath)) return null;\n\n try {\n const raw = fs.readFileSync(tsconfigPath, \"utf-8\");\n // Remove comments for JSON parsing\n const cleaned = raw.replace(/\\/\\/.*$/gm, \"\").replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\");\n const tsconfig = JSON.parse(cleaned);\n const paths = tsconfig?.compilerOptions?.paths;\n if (!paths) return null;\n\n // Look for @/* pattern\n for (const [alias, targets] of Object.entries(paths)) {\n if (alias.endsWith(\"/*\") && Array.isArray(targets) && targets.length > 0) {\n const prefix = alias.slice(0, -2); // Remove /*\n return prefix;\n }\n }\n } catch {\n // ignore parse errors\n }\n return null;\n}\n","import { Command } from \"commander\";\nimport prompts from \"prompts\";\nimport path from \"path\";\nimport ora from \"ora\";\nimport pc from \"picocolors\";\nimport { logger } from \"../../utils/logger.js\";\nimport { readConfig, configExists } from \"../../utils/config.js\";\nimport type { GlintUIConfig } from \"../../utils/config.js\";\nimport { REGISTRY, getComponent, getComponentsByCategory, getAllCategories } from \"../../registry/components.js\";\nimport type { RegistryComponent } from \"../../registry/index.js\";\nimport { detectPackageManager } from \"../../utils/detect-pm.js\";\nimport { installDependencies } from \"../../utils/installer.js\";\nimport { writeTemplateFile } from \"../../utils/file-writer.js\";\nimport { injectCSSPreset } from \"../../utils/css-injector.js\";\nimport { CSS_PRESETS } from \"../../registry/css-presets.js\";\n\nfunction resolveAllDependencies(names: string[]): RegistryComponent[] {\n const resolved = new Map<string, RegistryComponent>();\n const queue = [...names];\n\n while (queue.length > 0) {\n const name = queue.shift()!;\n if (resolved.has(name)) continue;\n\n const comp = getComponent(name);\n if (!comp) {\n logger.warn(`Component \"${name}\" not found in registry. Skipping.`);\n continue;\n }\n\n resolved.set(name, comp);\n\n for (const dep of comp.registryDependencies) {\n if (!resolved.has(dep)) {\n queue.push(dep);\n }\n }\n }\n\n return Array.from(resolved.values());\n}\n\nfunction resolveTargetDir(config: GlintUIConfig, fileType: string, cwd: string): string {\n // Map file types to config aliases, then resolve to filesystem paths\n const aliasMap: Record<string, string> = {\n component: config.aliases.components,\n hook: config.aliases.hooks,\n util: config.aliases.utils,\n };\n\n const alias = aliasMap[fileType];\n if (!alias) return path.join(cwd, \"src\", \"components\", \"ui\");\n\n // Convert alias like @/components/ui to actual path\n // Remove alias prefix (e.g., @/) and prepend src/\n const match = alias.match(/^@\\/(.*)/);\n if (match) {\n return path.join(cwd, \"src\", match[1]);\n }\n\n // Handle ~/... or other patterns\n const match2 = alias.match(/^~\\/(.*)/);\n if (match2) {\n return path.join(cwd, match2[1]);\n }\n\n // Fallback: treat as relative path\n return path.join(cwd, alias);\n}\n\nexport const addCommand = new Command(\"add\")\n .description(\"Add components to your project\")\n .argument(\"[components...]\", \"Component names to add\")\n .option(\"-c, --category <category>\", \"Add all components in a category\")\n .option(\"-a, --all\", \"Add all components\")\n .option(\"-y, --yes\", \"Skip confirmation prompt\")\n .option(\"--overwrite\", \"Overwrite existing files\")\n .action(async (componentNames: string[], options) => {\n const cwd = process.cwd();\n\n // Ensure config exists\n if (!configExists(cwd)) {\n logger.warn(\"glintkit.json not found. Running init first...\");\n const { execSync } = await import(\"child_process\");\n execSync(\"npx glintkit init -y\", { cwd, stdio: \"inherit\" });\n }\n\n const config = readConfig(cwd);\n\n // Determine which components to add\n let requestedNames: string[] = [];\n\n if (options.all) {\n requestedNames = REGISTRY.map((c) => c.name);\n } else if (options.category) {\n const cats = options.category.split(\",\");\n for (const cat of cats) {\n const comps = getComponentsByCategory(cat.trim() as any);\n requestedNames.push(...comps.map((c) => c.name));\n }\n if (requestedNames.length === 0) {\n logger.error(`No components found in category \"${options.category}\"`);\n logger.info(`Available categories: ${getAllCategories().join(\", \")}`);\n process.exit(1);\n }\n } else if (componentNames.length > 0) {\n requestedNames = componentNames;\n } else {\n // Interactive selection\n const response = await prompts({\n type: \"multiselect\",\n name: \"components\",\n message: \"Select components to add:\",\n choices: REGISTRY.map((c) => ({\n title: `${c.name} ${pc.dim(`(${c.category})`)}`,\n value: c.name,\n description: c.description,\n })),\n });\n\n if (!response.components || response.components.length === 0) {\n logger.error(\"No components selected.\");\n process.exit(1);\n }\n requestedNames = response.components;\n }\n\n // Validate requested names\n for (const name of requestedNames) {\n if (!getComponent(name)) {\n logger.error(`Component \"${name}\" not found.`);\n logger.info(\"Run `glintkit list` to see available components.\");\n process.exit(1);\n }\n }\n\n // Resolve all dependencies\n const allComponents = resolveAllDependencies(requestedNames);\n\n // Collect npm dependencies (deduplicated)\n const npmDeps = [...new Set(allComponents.flatMap((c) => c.npmDependencies))];\n\n // Collect CSS presets (deduplicated)\n const cssPresets = [...new Set(allComponents.flatMap((c) => c.cssPresets))];\n\n // Show summary\n logger.title(\"\\n Components to install:\\n\");\n for (const comp of allComponents) {\n const isRequested = requestedNames.includes(comp.name);\n const marker = isRequested ? pc.green(\"●\") : pc.dim(\"○\");\n console.log(` ${marker} ${comp.name} ${!isRequested ? pc.dim(\"(dependency)\") : \"\"}`);\n }\n\n if (npmDeps.length > 0) {\n logger.break();\n logger.info(`npm dependencies: ${npmDeps.join(\", \")}`);\n }\n if (cssPresets.length > 0) {\n logger.info(`CSS presets: ${cssPresets.join(\", \")}`);\n }\n\n // Confirmation\n if (!options.yes) {\n logger.break();\n const confirm = await prompts({\n type: \"confirm\",\n name: \"proceed\",\n message: \"Proceed with installation?\",\n initial: true,\n });\n\n if (!confirm.proceed) {\n logger.error(\"Cancelled.\");\n process.exit(0);\n }\n }\n\n // Install components\n const spinner = ora(\"Installing components...\").start();\n\n let filesWritten = 0;\n let filesSkipped = 0;\n\n for (const comp of allComponents) {\n for (const file of comp.files) {\n const targetDir = resolveTargetDir(config, file.type, cwd);\n const result = writeTemplateFile({\n templateKey: file.templateKey,\n targetDir,\n fileName: file.fileName,\n config,\n overwrite: options.overwrite ?? false,\n });\n\n if (result.written) {\n filesWritten++;\n } else {\n filesSkipped++;\n }\n }\n }\n\n spinner.succeed(`${filesWritten} file(s) written${filesSkipped > 0 ? `, ${filesSkipped} skipped (already exist)` : \"\"}`);\n\n // Install npm dependencies\n if (npmDeps.length > 0) {\n const pmSpinner = ora(\"Installing npm dependencies...\").start();\n try {\n const pm = detectPackageManager(cwd);\n installDependencies(npmDeps, pm, cwd);\n pmSpinner.succeed(`Installed: ${npmDeps.join(\", \")}`);\n } catch (error) {\n pmSpinner.fail(`Failed to install dependencies: ${(error as Error).message}`);\n logger.info(`You can manually install: ${npmDeps.join(\" \")}`);\n }\n }\n\n // Inject CSS presets\n if (cssPresets.length > 0) {\n const cssSpinner = ora(\"Injecting CSS presets...\").start();\n const cssPath = path.join(cwd, config.tailwind.css);\n\n for (const presetName of cssPresets) {\n const presetContent = CSS_PRESETS[presetName];\n if (presetContent) {\n injectCSSPreset(cssPath, presetName, presetContent);\n } else {\n logger.warn(`CSS preset \"${presetName}\" not found.`);\n }\n }\n\n cssSpinner.succeed(`Injected ${cssPresets.length} CSS preset(s)`);\n }\n\n logger.break();\n logger.success(\"Done! Components are ready to use.\");\n if (filesSkipped > 0) {\n logger.info(`Use --overwrite to replace existing files.`);\n }\n logger.break();\n });\n","import type { ComponentCategory, RegistryComponent } from \"./index\";\n\nexport const REGISTRY: RegistryComponent[] = [\n // Category: 3d\n {\n name: \"3d-card\",\n category: \"3d\",\n description: \"3D tilt card with mouse/touch tracking and depth layers\",\n files: [\n {\n templateKey: \"components/3d/3d-card\",\n fileName: \"3d-card.tsx\",\n type: \"component\",\n },\n ],\n npmDependencies: [],\n registryDependencies: [\"cn\"],\n cssPresets: [],\n },\n {\n name: \"prismatic-burst\",\n category: \"3d\",\n description:\n \"WebGL prismatic light effect with customizable colors and animations\",\n files: [\n {\n templateKey: \"components/3d/prismatic-burst\",\n fileName: \"prismatic-burst.tsx\",\n type: \"component\",\n },\n ],\n npmDependencies: [\"ogl\"],\n registryDependencies: [],\n cssPresets: [],\n },\n {\n name: \"glass-surface\",\n category: \"3d\",\n description: \"SVG-based glass refraction and distortion surface\",\n files: [\n {\n templateKey: \"components/3d/glass-surface\",\n fileName: \"glass-surface.tsx\",\n type: \"component\",\n },\n ],\n npmDependencies: [],\n registryDependencies: [\"cn\"],\n cssPresets: [],\n },\n {\n name: \"dome-gallery\",\n category: \"3d\",\n description: \"3D dome/sphere photo gallery with drag and inertia\",\n files: [\n {\n templateKey: \"components/3d/dome-gallery\",\n fileName: \"dome-gallery.tsx\",\n type: \"component\",\n },\n ],\n npmDependencies: [\"@use-gesture/react\"],\n registryDependencies: [],\n cssPresets: [],\n },\n {\n name: \"holo-card\",\n category: \"3d\",\n description: \"Holographic card with rainbow shine and flip animation\",\n files: [\n {\n templateKey: \"components/3d/holo-card\",\n fileName: \"holo-card.tsx\",\n type: \"component\",\n },\n ],\n npmDependencies: [],\n registryDependencies: [\"cn\"],\n cssPresets: [\"holo-card\"],\n },\n {\n name: \"flip-card\",\n category: \"3d\",\n description: \"3D flip card with tilt effect and customizable content\",\n files: [\n {\n templateKey: \"components/3d/flip-card\",\n fileName: \"flip-card.tsx\",\n type: \"component\",\n },\n ],\n npmDependencies: [],\n registryDependencies: [\"cn\", \"3d-card\"],\n cssPresets: [],\n },\n\n // Category: motion\n {\n name: \"counter\",\n category: \"motion\",\n description:\n \"Cyberpunk scramble counter that reveals numbers with matrix-style animation\",\n files: [\n {\n templateKey: \"components/motion/counter\",\n fileName: \"counter.tsx\",\n type: \"component\",\n },\n ],\n npmDependencies: [],\n registryDependencies: [\"cn\", \"use-scroll-animation\"],\n cssPresets: [],\n },\n {\n name: \"countdown-timer\",\n category: \"motion\",\n description:\n \"Countdown timer with multiple visual states (normal, approaching, live)\",\n files: [\n {\n templateKey: \"components/motion/countdown-timer\",\n fileName: \"countdown-timer.tsx\",\n type: \"component\",\n },\n ],\n npmDependencies: [],\n registryDependencies: [\"cn\", \"use-countdown\"],\n cssPresets: [\"glass\", \"gradient-text\", \"animations\"],\n },\n {\n name: \"shiny-text\",\n category: \"motion\",\n description: \"Animated shiny/glossy text with customizable sweep direction\",\n files: [\n {\n templateKey: \"components/motion/shiny-text\",\n fileName: \"shiny-text.tsx\",\n type: \"component\",\n },\n ],\n npmDependencies: [\"motion\"],\n registryDependencies: [],\n cssPresets: [],\n },\n {\n name: \"light-rays\",\n category: \"motion\",\n description: \"WebGL volumetric light rays from configurable origin\",\n files: [\n {\n templateKey: \"components/motion/light-rays\",\n fileName: \"light-rays.tsx\",\n type: \"component\",\n },\n ],\n npmDependencies: [\"ogl\"],\n registryDependencies: [],\n cssPresets: [],\n },\n\n // Category: glass\n {\n name: \"button\",\n category: \"glass\",\n description:\n \"Multi-variant button with glow border, gradient, and glass styles\",\n files: [\n {\n templateKey: \"components/glass/button\",\n fileName: \"button.tsx\",\n type: \"component\",\n },\n ],\n npmDependencies: [\"clsx\", \"tailwind-merge\"],\n registryDependencies: [\"cn\"],\n cssPresets: [\"glow-border\"],\n },\n {\n name: \"card\",\n category: \"glass\",\n description: \"Glass card with default, strong, gradient, and outline variants\",\n files: [\n {\n templateKey: \"components/glass/card\",\n fileName: \"card.tsx\",\n type: \"component\",\n },\n ],\n npmDependencies: [\"clsx\", \"tailwind-merge\"],\n registryDependencies: [\"cn\"],\n cssPresets: [\"glass\"],\n },\n {\n name: \"modal\",\n category: \"glass\",\n description:\n \"Modal dialog with glass backdrop, mobile handle bar, and portal rendering\",\n files: [\n {\n templateKey: \"components/glass/modal\",\n fileName: \"modal.tsx\",\n type: \"component\",\n },\n ],\n npmDependencies: [],\n registryDependencies: [\"cn\"],\n cssPresets: [\"glass\", \"animations\"],\n },\n {\n name: \"music-player\",\n category: \"glass\",\n description: \"Audio player with soundwave visualization and glass surface\",\n files: [\n {\n templateKey: \"components/glass/music-player\",\n fileName: \"music-player.tsx\",\n type: \"component\",\n },\n ],\n npmDependencies: [],\n registryDependencies: [\"glass-surface\"],\n cssPresets: [\"animations\"],\n },\n\n // Category: hooks\n {\n name: \"use-countdown\",\n category: \"hooks\",\n description: \"Countdown timer hook that calculates days, hours, minutes, seconds\",\n files: [\n {\n templateKey: \"hooks/use-countdown\",\n fileName: \"use-countdown.ts\",\n type: \"hook\",\n },\n ],\n npmDependencies: [],\n registryDependencies: [],\n cssPresets: [],\n },\n {\n name: \"use-scroll-animation\",\n category: \"hooks\",\n description: \"IntersectionObserver-based scroll trigger hook\",\n files: [\n {\n templateKey: \"hooks/use-scroll-animation\",\n fileName: \"use-scroll-animation.ts\",\n type: \"hook\",\n },\n ],\n npmDependencies: [],\n registryDependencies: [],\n cssPresets: [],\n },\n {\n name: \"use-media-query\",\n category: \"hooks\",\n description:\n \"Responsive breakpoint detection with predefined mobile/tablet/desktop helpers\",\n files: [\n {\n templateKey: \"hooks/use-media-query\",\n fileName: \"use-media-query.ts\",\n type: \"hook\",\n },\n ],\n npmDependencies: [],\n registryDependencies: [],\n cssPresets: [],\n },\n\n // Category: utils\n {\n name: \"cn\",\n category: \"utils\",\n description: \"Tailwind CSS class merge utility (clsx + tailwind-merge)\",\n files: [\n {\n templateKey: \"utils/cn\",\n fileName: \"cn.ts\",\n type: \"util\",\n },\n ],\n npmDependencies: [\"clsx\", \"tailwind-merge\"],\n registryDependencies: [],\n cssPresets: [],\n },\n];\n\nexport function getComponent(name: string): RegistryComponent | undefined {\n return REGISTRY.find((c) => c.name === name);\n}\n\nexport function getComponentsByCategory(\n category: ComponentCategory\n): RegistryComponent[] {\n return REGISTRY.filter((c) => c.category === category);\n}\n\nexport function getAllCategories(): ComponentCategory[] {\n return [...new Set(REGISTRY.map((c) => c.category))];\n}\n","import fs from \"fs-extra\";\nimport path from \"path\";\n\nexport type PackageManager = \"npm\" | \"pnpm\" | \"yarn\" | \"bun\";\n\nexport function detectPackageManager(cwd: string = process.cwd()): PackageManager {\n if (fs.existsSync(path.join(cwd, \"bun.lockb\")) || fs.existsSync(path.join(cwd, \"bun.lock\"))) return \"bun\";\n if (fs.existsSync(path.join(cwd, \"pnpm-lock.yaml\"))) return \"pnpm\";\n if (fs.existsSync(path.join(cwd, \"yarn.lock\"))) return \"yarn\";\n return \"npm\";\n}\n","import { execSync } from \"child_process\";\nimport type { PackageManager } from \"./detect-pm.js\";\nimport { logger } from \"./logger.js\";\n\nexport function installDependencies(\n deps: string[],\n pm: PackageManager,\n cwd: string = process.cwd()\n): void {\n if (deps.length === 0) return;\n\n const commands: Record<PackageManager, string> = {\n npm: `npm install ${deps.join(\" \")}`,\n pnpm: `pnpm add ${deps.join(\" \")}`,\n yarn: `yarn add ${deps.join(\" \")}`,\n bun: `bun add ${deps.join(\" \")}`,\n };\n\n const cmd = commands[pm];\n logger.info(`Installing dependencies with ${pm}...`);\n\n try {\n execSync(cmd, { cwd, stdio: \"pipe\" });\n } catch (error) {\n throw new Error(`Failed to install dependencies: ${(error as Error).message}`);\n }\n}\n","import fs from \"fs-extra\";\nimport path from \"path\";\nimport type { GlintUIConfig } from \"./config.js\";\nimport { TEMPLATES } from \"../registry/__generated__/templates.js\";\n\ntype AliasType = \"components\" | \"hooks\" | \"utils\";\n\nconst ALIAS_MAP: Record<string, AliasType> = {\n __COMPONENTS_ALIAS__: \"components\",\n __HOOKS_ALIAS__: \"hooks\",\n __UTILS_ALIAS__: \"utils\",\n};\n\nexport function replaceAliases(content: string, config: GlintUIConfig): string {\n let result = content;\n for (const [placeholder, key] of Object.entries(ALIAS_MAP)) {\n result = result.replaceAll(placeholder, config.aliases[key]);\n }\n return result;\n}\n\nexport interface WriteFileOptions {\n templateKey: string;\n targetDir: string;\n fileName: string;\n config: GlintUIConfig;\n overwrite?: boolean;\n}\n\nexport function writeTemplateFile(options: WriteFileOptions): { written: boolean; path: string } {\n const { templateKey, targetDir, fileName, config, overwrite = false } = options;\n const template = TEMPLATES[templateKey];\n if (!template) {\n throw new Error(`Template not found: ${templateKey}`);\n }\n\n const targetPath = path.join(targetDir, fileName);\n\n if (fs.existsSync(targetPath) && !overwrite) {\n return { written: false, path: targetPath };\n }\n\n fs.ensureDirSync(targetDir);\n const content = replaceAliases(template, config);\n fs.writeFileSync(targetPath, content, \"utf-8\");\n return { written: true, path: targetPath };\n}\n","// AUTO-GENERATED - DO NOT EDIT\n// Generated by scripts/bundle-templates.ts\n\nexport const TEMPLATES: Record<string, string> = {\n \"components/3d/3d-card\": `\"use client\";\n\nimport { cn } from \"__UTILS_ALIAS__/cn\";\nimport React, {\n createContext,\n useState,\n useContext,\n useRef,\n useEffect,\n} from \"react\";\n\nconst MouseEnterContext = createContext<\n [boolean, React.Dispatch<React.SetStateAction<boolean>>] | undefined\n>(undefined);\n\nexport const CardContainer = ({\n children,\n className,\n containerClassName,\n}: {\n children?: React.ReactNode;\n className?: string;\n containerClassName?: string;\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const [isMouseEntered, setIsMouseEntered] = useState(false);\n\n const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {\n if (!containerRef.current) return;\n const { left, top, width, height } =\n containerRef.current.getBoundingClientRect();\n const x = (e.clientX - left - width / 2) / 25;\n const y = (e.clientY - top - height / 2) / 25;\n containerRef.current.style.transform = \\`rotateY(\\${x}deg) rotateX(\\${y}deg)\\`;\n };\n\n const handleMouseEnter = () => {\n setIsMouseEntered(true);\n };\n\n const handleMouseLeave = () => {\n if (!containerRef.current) return;\n setIsMouseEntered(false);\n containerRef.current.style.transform = \\`rotateY(0deg) rotateX(0deg)\\`;\n };\n\n // Touch event handlers for mobile\n const handleTouchStart = (e: React.TouchEvent<HTMLDivElement>) => {\n // Don't preventDefault here - it blocks touch events on some mobile browsers\n // Instead, use CSS touch-action: none on the element\n setIsMouseEntered(true);\n\n // Initial tilt based on touch position\n if (!containerRef.current || !e.touches[0]) return;\n const { left, top, width, height } =\n containerRef.current.getBoundingClientRect();\n const touch = e.touches[0];\n const x = (touch.clientX - left - width / 2) / 15;\n const y = (touch.clientY - top - height / 2) / 15;\n containerRef.current.style.transform = \\`rotateY(\\${x}deg) rotateX(\\${y}deg)\\`;\n };\n\n const handleTouchMove = (e: React.TouchEvent<HTMLDivElement>) => {\n // Only prevent default if we're actively interacting with the card\n // This allows the initial touch to register properly\n if (isMouseEntered && e.cancelable) {\n e.preventDefault();\n }\n if (!containerRef.current || !e.touches[0]) return;\n const { left, top, width, height } =\n containerRef.current.getBoundingClientRect();\n const touch = e.touches[0];\n const x = (touch.clientX - left - width / 2) / 15; // Increased sensitivity for touch\n const y = (touch.clientY - top - height / 2) / 15;\n containerRef.current.style.transform = \\`rotateY(\\${x}deg) rotateX(\\${y}deg)\\`;\n };\n\n const handleTouchEnd = () => {\n if (!containerRef.current) return;\n setIsMouseEntered(false);\n containerRef.current.style.transform = \\`rotateY(0deg) rotateX(0deg)\\`;\n };\n\n return (\n <MouseEnterContext.Provider value={[isMouseEntered, setIsMouseEntered]}>\n <div\n className={cn(\n \"py-20 flex items-center justify-center\",\n containerClassName\n )}\n style={{\n perspective: \"1000px\",\n }}\n >\n <div\n ref={containerRef}\n onMouseEnter={handleMouseEnter}\n onMouseMove={handleMouseMove}\n onMouseLeave={handleMouseLeave}\n onTouchStart={handleTouchStart}\n onTouchMove={handleTouchMove}\n onTouchEnd={handleTouchEnd}\n className={cn(\n \"flex items-center justify-center relative transition-all duration-200 ease-linear\",\n className\n )}\n style={{\n transformStyle: \"preserve-3d\",\n touchAction: \"none\", // Prevent browser touch handling for reliable tilt\n }}\n >\n {children}\n </div>\n </div>\n </MouseEnterContext.Provider>\n );\n};\n\nexport const CardBody = ({\n children,\n className,\n}: {\n children: React.ReactNode;\n className?: string;\n}) => {\n // Clone children to add transform-style: preserve-3d\n const childrenWithPreserve3d = React.Children.map(children, (child) => {\n if (React.isValidElement(child)) {\n return React.cloneElement(child as React.ReactElement<{ style?: React.CSSProperties }>, {\n style: {\n ...(child.props as { style?: React.CSSProperties }).style,\n transformStyle: \"preserve-3d\",\n },\n });\n }\n return child;\n });\n\n return (\n <div\n className={cn(\"h-96 w-96\", className)}\n style={{\n transformStyle: \"preserve-3d\",\n }}\n >\n {childrenWithPreserve3d}\n </div>\n );\n};\n\nexport const CardItem = ({\n as: Tag = \"div\",\n children,\n className,\n translateX = 0,\n translateY = 0,\n translateZ = 0,\n rotateX = 0,\n rotateY = 0,\n rotateZ = 0,\n ...rest\n}: {\n as?: React.ElementType;\n children: React.ReactNode;\n className?: string;\n translateX?: number | string;\n translateY?: number | string;\n translateZ?: number | string;\n rotateX?: number | string;\n rotateY?: number | string;\n rotateZ?: number | string;\n [key: string]: unknown;\n}) => {\n const ref = useRef<HTMLElement>(null);\n const [isMouseEntered] = useMouseEnter();\n\n useEffect(() => {\n if (!ref.current) return;\n\n if (isMouseEntered) {\n ref.current.style.transform = \\`translateX(\\${translateX}px) translateY(\\${translateY}px) translateZ(\\${translateZ}px) rotateX(\\${rotateX}deg) rotateY(\\${rotateY}deg) rotateZ(\\${rotateZ}deg)\\`;\n } else {\n ref.current.style.transform = \\`translateX(0px) translateY(0px) translateZ(0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg)\\`;\n }\n }, [isMouseEntered, translateX, translateY, translateZ, rotateX, rotateY, rotateZ]);\n\n return (\n <Tag\n ref={ref as React.RefObject<never>}\n className={cn(\"w-fit transition duration-200 ease-linear\", className)}\n style={{ transformStyle: \"preserve-3d\" }}\n {...rest}\n >\n {children}\n </Tag>\n );\n};\n\nexport const useMouseEnter = () => {\n const context = useContext(MouseEnterContext);\n if (context === undefined) {\n throw new Error(\"useMouseEnter must be used within a MouseEnterProvider\");\n }\n return context;\n};\n`,\n \"components/3d/dome-gallery\": `\"use client\";\n\nimport { useEffect, useMemo, useRef, useCallback } from 'react';\nimport { useGesture } from '@use-gesture/react';\n\ntype ImageItem = string | { src: string; alt?: string };\n\ntype DomeGalleryProps = {\n images?: ImageItem[];\n fit?: number;\n fitBasis?: 'auto' | 'min' | 'max' | 'width' | 'height';\n minRadius?: number;\n maxRadius?: number;\n padFactor?: number;\n overlayBlurColor?: string;\n maxVerticalRotationDeg?: number;\n dragSensitivity?: number;\n enlargeTransitionMs?: number;\n segments?: number;\n dragDampening?: number;\n openedImageWidth?: string;\n openedImageHeight?: string;\n imageBorderRadius?: string;\n openedImageBorderRadius?: string;\n grayscale?: boolean;\n autoRotate?: boolean;\n autoRotateSpeed?: number;\n disableInteraction?: boolean;\n};\n\ntype ItemDef = {\n src: string;\n alt: string;\n x: number;\n y: number;\n sizeX: number;\n sizeY: number;\n};\n\nconst DEFAULTS = {\n maxVerticalRotationDeg: 5,\n dragSensitivity: 20,\n enlargeTransitionMs: 300,\n segments: 35\n};\n\nconst clamp = (v: number, min: number, max: number) => Math.min(Math.max(v, min), max);\nconst normalizeAngle = (d: number) => ((d % 360) + 360) % 360;\nconst wrapAngleSigned = (deg: number) => {\n const a = (((deg + 180) % 360) + 360) % 360;\n return a - 180;\n};\nconst getDataNumber = (el: HTMLElement, name: string, fallback: number) => {\n const attr = el.dataset[name] ?? el.getAttribute(\\`data-\\${name}\\`);\n const n = attr == null ? NaN : parseFloat(attr);\n return Number.isFinite(n) ? n : fallback;\n};\n\nfunction buildItems(pool: ImageItem[], seg: number): ItemDef[] {\n const xCols = Array.from({ length: seg }, (_, i) => -37 + i * 2);\n const evenYs = [-4, -2, 0, 2, 4];\n const oddYs = [-3, -1, 1, 3, 5];\n\n const coords = xCols.flatMap((x, c) => {\n const ys = c % 2 === 0 ? evenYs : oddYs;\n return ys.map(y => ({ x, y, sizeX: 2, sizeY: 2 }));\n });\n\n const totalSlots = coords.length;\n if (pool.length === 0) {\n return coords.map(c => ({ ...c, src: '', alt: '' }));\n }\n if (pool.length > totalSlots) {\n console.warn(\n \\`[DomeGallery] Provided image count (\\${pool.length}) exceeds available tiles (\\${totalSlots}). Some images will not be shown.\\`\n );\n }\n\n const normalizedImages = pool.map(image => {\n if (typeof image === 'string') {\n return { src: image, alt: '' };\n }\n return { src: image.src || '', alt: image.alt || '' };\n });\n\n const usedImages = Array.from({ length: totalSlots }, (_, i) => normalizedImages[i % normalizedImages.length]);\n\n for (let i = 1; i < usedImages.length; i++) {\n if (usedImages[i].src === usedImages[i - 1].src) {\n for (let j = i + 1; j < usedImages.length; j++) {\n if (usedImages[j].src !== usedImages[i].src) {\n const tmp = usedImages[i];\n usedImages[i] = usedImages[j];\n usedImages[j] = tmp;\n break;\n }\n }\n }\n }\n\n return coords.map((c, i) => ({\n ...c,\n src: usedImages[i].src,\n alt: usedImages[i].alt\n }));\n}\n\nfunction computeItemBaseRotation(offsetX: number, offsetY: number, sizeX: number, sizeY: number, segments: number) {\n const unit = 360 / segments / 2;\n const rotateY = unit * (offsetX + (sizeX - 1) / 2);\n const rotateX = unit * (offsetY - (sizeY - 1) / 2);\n return { rotateX, rotateY };\n}\n\nexport default function DomeGallery({\n images = [],\n fit = 0.8,\n fitBasis = 'auto',\n minRadius = 600,\n maxRadius = Infinity,\n padFactor = 0.25,\n overlayBlurColor = '#060010',\n maxVerticalRotationDeg = DEFAULTS.maxVerticalRotationDeg,\n dragSensitivity = DEFAULTS.dragSensitivity,\n enlargeTransitionMs = DEFAULTS.enlargeTransitionMs,\n segments = DEFAULTS.segments,\n dragDampening = 2,\n openedImageWidth = '400px',\n openedImageHeight = '400px',\n imageBorderRadius = '30px',\n openedImageBorderRadius = '30px',\n grayscale = false,\n autoRotate = false,\n autoRotateSpeed = 0.3,\n disableInteraction = false\n}: DomeGalleryProps) {\n const rootRef = useRef<HTMLDivElement>(null);\n const mainRef = useRef<HTMLDivElement>(null);\n const sphereRef = useRef<HTMLDivElement>(null);\n const frameRef = useRef<HTMLDivElement>(null);\n const viewerRef = useRef<HTMLDivElement>(null);\n const scrimRef = useRef<HTMLDivElement>(null);\n const focusedElRef = useRef<HTMLElement | null>(null);\n const originalTilePositionRef = useRef<{\n left: number;\n top: number;\n width: number;\n height: number;\n } | null>(null);\n\n const rotationRef = useRef({ x: 0, y: 0 });\n const startRotRef = useRef({ x: 0, y: 0 });\n const startPosRef = useRef<{ x: number; y: number } | null>(null);\n const draggingRef = useRef(false);\n const cancelTapRef = useRef(false);\n const movedRef = useRef(false);\n const inertiaRAF = useRef<number | null>(null);\n const pointerTypeRef = useRef<'mouse' | 'pen' | 'touch'>('mouse');\n const tapTargetRef = useRef<HTMLElement | null>(null);\n const openingRef = useRef(false);\n const openStartedAtRef = useRef(0);\n const lastDragEndAt = useRef(0);\n\n const scrollLockedRef = useRef(false);\n const lockScroll = useCallback(() => {\n if (scrollLockedRef.current) return;\n scrollLockedRef.current = true;\n document.body.classList.add('dg-scroll-lock');\n }, []);\n const unlockScroll = useCallback(() => {\n if (!scrollLockedRef.current) return;\n if (rootRef.current?.getAttribute('data-enlarging') === 'true') return;\n scrollLockedRef.current = false;\n document.body.classList.remove('dg-scroll-lock');\n }, []);\n\n const items = useMemo(() => buildItems(images, segments), [images, segments]);\n\n const applyTransform = (xDeg: number, yDeg: number) => {\n const el = sphereRef.current;\n if (el) {\n el.style.transform = \\`translateZ(calc(var(--radius) * -1)) rotateX(\\${xDeg}deg) rotateY(\\${yDeg}deg)\\`;\n }\n };\n\n const lockedRadiusRef = useRef<number | null>(null);\n\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n const ro = new ResizeObserver(entries => {\n const cr = entries[0].contentRect;\n const w = Math.max(1, cr.width),\n h = Math.max(1, cr.height);\n const minDim = Math.min(w, h),\n maxDim = Math.max(w, h),\n aspect = w / h;\n let basis: number;\n switch (fitBasis) {\n case 'min':\n basis = minDim;\n break;\n case 'max':\n basis = maxDim;\n break;\n case 'width':\n basis = w;\n break;\n case 'height':\n basis = h;\n break;\n default:\n basis = aspect >= 1.3 ? w : minDim;\n }\n let radius = basis * fit;\n const heightGuard = h * 1.35;\n radius = Math.min(radius, heightGuard);\n radius = clamp(radius, minRadius, maxRadius);\n lockedRadiusRef.current = Math.round(radius);\n\n const viewerPad = Math.max(8, Math.round(minDim * padFactor));\n root.style.setProperty('--radius', \\`\\${lockedRadiusRef.current}px\\`);\n root.style.setProperty('--viewer-pad', \\`\\${viewerPad}px\\`);\n root.style.setProperty('--overlay-blur-color', overlayBlurColor);\n root.style.setProperty('--tile-radius', imageBorderRadius);\n root.style.setProperty('--enlarge-radius', openedImageBorderRadius);\n root.style.setProperty('--image-filter', grayscale ? 'grayscale(1)' : 'none');\n applyTransform(rotationRef.current.x, rotationRef.current.y);\n\n const enlargedOverlay = viewerRef.current?.querySelector('.enlarge') as HTMLElement;\n if (enlargedOverlay && frameRef.current && mainRef.current) {\n const frameR = frameRef.current.getBoundingClientRect();\n const mainR = mainRef.current.getBoundingClientRect();\n\n const hasCustomSize = openedImageWidth && openedImageHeight;\n if (hasCustomSize) {\n const tempDiv = document.createElement('div');\n tempDiv.style.cssText = \\`position: absolute; width: \\${openedImageWidth}; height: \\${openedImageHeight}; visibility: hidden;\\`;\n document.body.appendChild(tempDiv);\n const tempRect = tempDiv.getBoundingClientRect();\n document.body.removeChild(tempDiv);\n\n const centeredLeft = frameR.left - mainR.left + (frameR.width - tempRect.width) / 2;\n const centeredTop = frameR.top - mainR.top + (frameR.height - tempRect.height) / 2;\n\n enlargedOverlay.style.left = \\`\\${centeredLeft}px\\`;\n enlargedOverlay.style.top = \\`\\${centeredTop}px\\`;\n } else {\n enlargedOverlay.style.left = \\`\\${frameR.left - mainR.left}px\\`;\n enlargedOverlay.style.top = \\`\\${frameR.top - mainR.top}px\\`;\n enlargedOverlay.style.width = \\`\\${frameR.width}px\\`;\n enlargedOverlay.style.height = \\`\\${frameR.height}px\\`;\n }\n }\n });\n ro.observe(root);\n return () => ro.disconnect();\n }, [\n fit,\n fitBasis,\n minRadius,\n maxRadius,\n padFactor,\n overlayBlurColor,\n grayscale,\n imageBorderRadius,\n openedImageBorderRadius,\n openedImageWidth,\n openedImageHeight\n ]);\n\n useEffect(() => {\n applyTransform(rotationRef.current.x, rotationRef.current.y);\n }, []);\n\n const stopInertia = useCallback(() => {\n if (inertiaRAF.current) {\n cancelAnimationFrame(inertiaRAF.current);\n inertiaRAF.current = null;\n }\n }, []);\n\n const startInertia = useCallback(\n (vx: number, vy: number) => {\n const MAX_V = 1.4;\n let vX = clamp(vx, -MAX_V, MAX_V) * 80;\n let vY = clamp(vy, -MAX_V, MAX_V) * 80;\n let frames = 0;\n const d = clamp(dragDampening ?? 0.6, 0, 1);\n const frictionMul = 0.94 + 0.055 * d;\n const stopThreshold = 0.015 - 0.01 * d;\n const maxFrames = Math.round(90 + 270 * d);\n const step = () => {\n vX *= frictionMul;\n vY *= frictionMul;\n if (Math.abs(vX) < stopThreshold && Math.abs(vY) < stopThreshold) {\n inertiaRAF.current = null;\n return;\n }\n if (++frames > maxFrames) {\n inertiaRAF.current = null;\n return;\n }\n const nextX = clamp(rotationRef.current.x - vY / 200, -maxVerticalRotationDeg, maxVerticalRotationDeg);\n const nextY = wrapAngleSigned(rotationRef.current.y + vX / 200);\n rotationRef.current = { x: nextX, y: nextY };\n applyTransform(nextX, nextY);\n inertiaRAF.current = requestAnimationFrame(step);\n };\n stopInertia();\n inertiaRAF.current = requestAnimationFrame(step);\n },\n [dragDampening, maxVerticalRotationDeg, stopInertia]\n );\n\n // Auto-rotation effect with visibility optimization\n const autoRotateRAF = useRef<number | null>(null);\n const isVisibleRef = useRef(false);\n const isScrollingRef = useRef(false);\n\n // IntersectionObserver to pause animation when not visible\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n\n const observer = new IntersectionObserver(\n (entries) => {\n isVisibleRef.current = entries[0].isIntersecting;\n },\n { threshold: 0.1 }\n );\n\n observer.observe(root);\n return () => observer.disconnect();\n }, []);\n\n // Pause animation during scroll for better performance\n useEffect(() => {\n let scrollTimeout: ReturnType<typeof setTimeout> | null = null;\n\n const handleScroll = () => {\n isScrollingRef.current = true;\n if (scrollTimeout) clearTimeout(scrollTimeout);\n scrollTimeout = setTimeout(() => {\n isScrollingRef.current = false;\n }, 150);\n };\n\n window.addEventListener('scroll', handleScroll, { passive: true });\n return () => {\n window.removeEventListener('scroll', handleScroll);\n if (scrollTimeout) clearTimeout(scrollTimeout);\n };\n }, []);\n\n useEffect(() => {\n if (!autoRotate) {\n if (autoRotateRAF.current) {\n cancelAnimationFrame(autoRotateRAF.current);\n autoRotateRAF.current = null;\n }\n return;\n }\n\n const animate = () => {\n // Don't rotate while dragging, scrolling, when an image is focused, or when not visible\n if (draggingRef.current || focusedElRef.current || inertiaRAF.current || !isVisibleRef.current || isScrollingRef.current) {\n autoRotateRAF.current = requestAnimationFrame(animate);\n return;\n }\n\n const nextY = wrapAngleSigned(rotationRef.current.y + autoRotateSpeed);\n rotationRef.current = { x: rotationRef.current.x, y: nextY };\n applyTransform(rotationRef.current.x, nextY);\n autoRotateRAF.current = requestAnimationFrame(animate);\n };\n\n autoRotateRAF.current = requestAnimationFrame(animate);\n\n return () => {\n if (autoRotateRAF.current) {\n cancelAnimationFrame(autoRotateRAF.current);\n autoRotateRAF.current = null;\n }\n };\n }, [autoRotate, autoRotateSpeed]);\n\n useGesture(\n {\n onDragStart: ({ event }) => {\n if (disableInteraction) return;\n if (focusedElRef.current) return;\n stopInertia();\n\n const evt = event as PointerEvent;\n pointerTypeRef.current = (evt.pointerType as 'mouse' | 'pen' | 'touch') || 'mouse';\n if (pointerTypeRef.current === 'touch') evt.preventDefault();\n if (pointerTypeRef.current === 'touch') lockScroll();\n draggingRef.current = true;\n cancelTapRef.current = false;\n movedRef.current = false;\n startRotRef.current = { ...rotationRef.current };\n startPosRef.current = { x: evt.clientX, y: evt.clientY };\n const potential = (evt.target as Element).closest?.('.item__image') as HTMLElement | null;\n tapTargetRef.current = potential || null;\n },\n onDrag: ({ event, last, velocity: velArr = [0, 0], direction: dirArr = [0, 0], movement }) => {\n if (focusedElRef.current || !draggingRef.current || !startPosRef.current) return;\n\n const evt = event as PointerEvent;\n if (pointerTypeRef.current === 'touch') evt.preventDefault();\n\n const dxTotal = evt.clientX - startPosRef.current.x;\n const dyTotal = evt.clientY - startPosRef.current.y;\n\n if (!movedRef.current) {\n const dist2 = dxTotal * dxTotal + dyTotal * dyTotal;\n if (dist2 > 16) movedRef.current = true;\n }\n\n const nextX = clamp(\n startRotRef.current.x - dyTotal / dragSensitivity,\n -maxVerticalRotationDeg,\n maxVerticalRotationDeg\n );\n const nextY = startRotRef.current.y + dxTotal / dragSensitivity;\n\n const cur = rotationRef.current;\n if (cur.x !== nextX || cur.y !== nextY) {\n rotationRef.current = { x: nextX, y: nextY };\n applyTransform(nextX, nextY);\n }\n\n if (last) {\n draggingRef.current = false;\n let isTap = false;\n\n if (startPosRef.current) {\n const dx = evt.clientX - startPosRef.current.x;\n const dy = evt.clientY - startPosRef.current.y;\n const dist2 = dx * dx + dy * dy;\n const TAP_THRESH_PX = pointerTypeRef.current === 'touch' ? 10 : 6;\n if (dist2 <= TAP_THRESH_PX * TAP_THRESH_PX) {\n isTap = true;\n }\n }\n\n let [vMagX, vMagY] = velArr;\n const [dirX, dirY] = dirArr;\n let vx = vMagX * dirX;\n let vy = vMagY * dirY;\n\n if (!isTap && Math.abs(vx) < 0.001 && Math.abs(vy) < 0.001 && Array.isArray(movement)) {\n const [mx, my] = movement;\n vx = (mx / dragSensitivity) * 0.02;\n vy = (my / dragSensitivity) * 0.02;\n }\n\n if (!isTap && (Math.abs(vx) > 0.005 || Math.abs(vy) > 0.005)) {\n startInertia(vx, vy);\n }\n startPosRef.current = null;\n cancelTapRef.current = !isTap;\n\n if (isTap && tapTargetRef.current && !focusedElRef.current) {\n openItemFromElement(tapTargetRef.current);\n }\n tapTargetRef.current = null;\n\n if (cancelTapRef.current) setTimeout(() => (cancelTapRef.current = false), 120);\n if (pointerTypeRef.current === 'touch') unlockScroll();\n if (movedRef.current) lastDragEndAt.current = performance.now();\n movedRef.current = false;\n }\n }\n },\n { target: mainRef, eventOptions: { passive: false } }\n );\n\n useEffect(() => {\n const scrim = scrimRef.current;\n if (!scrim) return;\n\n const close = () => {\n if (performance.now() - openStartedAtRef.current < 250) return;\n const el = focusedElRef.current;\n if (!el) return;\n const parent = el.parentElement as HTMLElement;\n const overlay = viewerRef.current?.querySelector('.enlarge') as HTMLElement | null;\n if (!overlay) return;\n\n const refDiv = parent.querySelector('.item__image--reference') as HTMLElement | null;\n\n const originalPos = originalTilePositionRef.current;\n if (!originalPos) {\n overlay.remove();\n if (refDiv) refDiv.remove();\n parent.style.setProperty('--rot-y-delta', \\`0deg\\`);\n parent.style.setProperty('--rot-x-delta', \\`0deg\\`);\n el.style.visibility = '';\n el.style.zIndex = '0';\n focusedElRef.current = null;\n rootRef.current?.removeAttribute('data-enlarging');\n openingRef.current = false;\n return;\n }\n\n const currentRect = overlay.getBoundingClientRect();\n const rootRect = rootRef.current!.getBoundingClientRect();\n\n const originalPosRelativeToRoot = {\n left: originalPos.left - rootRect.left,\n top: originalPos.top - rootRect.top,\n width: originalPos.width,\n height: originalPos.height\n };\n\n const overlayRelativeToRoot = {\n left: currentRect.left - rootRect.left,\n top: currentRect.top - rootRect.top,\n width: currentRect.width,\n height: currentRect.height\n };\n\n const animatingOverlay = document.createElement('div');\n animatingOverlay.className = 'enlarge-closing';\n animatingOverlay.style.cssText = \\`\n position: absolute;\n left: \\${overlayRelativeToRoot.left}px;\n top: \\${overlayRelativeToRoot.top}px;\n width: \\${overlayRelativeToRoot.width}px;\n height: \\${overlayRelativeToRoot.height}px;\n z-index: 9999;\n border-radius: \\${openedImageBorderRadius};\n overflow: hidden;\n box-shadow: 0 10px 30px rgba(0,0,0,.35);\n transition: all \\${enlargeTransitionMs}ms ease-out;\n pointer-events: none;\n margin: 0;\n transform: none;\n filter: \\${grayscale ? 'grayscale(1)' : 'none'};\n \\`;\n\n const originalImg = overlay.querySelector('img');\n if (originalImg) {\n const img = originalImg.cloneNode() as HTMLImageElement;\n img.style.cssText = 'width: 100%; height: 100%; object-fit: cover;';\n animatingOverlay.appendChild(img);\n }\n\n overlay.remove();\n rootRef.current!.appendChild(animatingOverlay);\n\n void animatingOverlay.getBoundingClientRect();\n\n requestAnimationFrame(() => {\n animatingOverlay.style.left = originalPosRelativeToRoot.left + 'px';\n animatingOverlay.style.top = originalPosRelativeToRoot.top + 'px';\n animatingOverlay.style.width = originalPosRelativeToRoot.width + 'px';\n animatingOverlay.style.height = originalPosRelativeToRoot.height + 'px';\n animatingOverlay.style.opacity = '0';\n });\n\n const cleanup = () => {\n animatingOverlay.remove();\n originalTilePositionRef.current = null;\n\n if (refDiv) refDiv.remove();\n parent.style.transition = 'none';\n el.style.transition = 'none';\n\n parent.style.setProperty('--rot-y-delta', \\`0deg\\`);\n parent.style.setProperty('--rot-x-delta', \\`0deg\\`);\n\n requestAnimationFrame(() => {\n el.style.visibility = '';\n el.style.opacity = '0';\n el.style.zIndex = '0';\n focusedElRef.current = null;\n rootRef.current?.removeAttribute('data-enlarging');\n\n requestAnimationFrame(() => {\n parent.style.transition = '';\n el.style.transition = 'opacity 300ms ease-out';\n\n requestAnimationFrame(() => {\n el.style.opacity = '1';\n setTimeout(() => {\n el.style.transition = '';\n el.style.opacity = '';\n openingRef.current = false;\n if (!draggingRef.current && rootRef.current?.getAttribute('data-enlarging') !== 'true') {\n document.body.classList.remove('dg-scroll-lock');\n }\n }, 300);\n });\n });\n });\n };\n\n animatingOverlay.addEventListener('transitionend', cleanup, {\n once: true\n });\n };\n\n scrim.addEventListener('click', close);\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') close();\n };\n window.addEventListener('keydown', onKey);\n\n return () => {\n scrim.removeEventListener('click', close);\n window.removeEventListener('keydown', onKey);\n };\n }, [enlargeTransitionMs, openedImageBorderRadius, grayscale]);\n\n const openItemFromElement = (el: HTMLElement) => {\n if (openingRef.current) return;\n openingRef.current = true;\n openStartedAtRef.current = performance.now();\n lockScroll();\n const parent = el.parentElement as HTMLElement;\n focusedElRef.current = el;\n el.setAttribute('data-focused', 'true');\n const offsetX = getDataNumber(parent, 'offsetX', 0);\n const offsetY = getDataNumber(parent, 'offsetY', 0);\n const sizeX = getDataNumber(parent, 'sizeX', 2);\n const sizeY = getDataNumber(parent, 'sizeY', 2);\n const parentRot = computeItemBaseRotation(offsetX, offsetY, sizeX, sizeY, segments);\n const parentY = normalizeAngle(parentRot.rotateY);\n const globalY = normalizeAngle(rotationRef.current.y);\n let rotY = -(parentY + globalY) % 360;\n if (rotY < -180) rotY += 360;\n const rotX = -parentRot.rotateX - rotationRef.current.x;\n parent.style.setProperty('--rot-y-delta', \\`\\${rotY}deg\\`);\n parent.style.setProperty('--rot-x-delta', \\`\\${rotX}deg\\`);\n const refDiv = document.createElement('div');\n refDiv.className = 'item__image item__image--reference opacity-0';\n refDiv.style.transform = \\`rotateX(\\${-parentRot.rotateX}deg) rotateY(\\${-parentRot.rotateY}deg)\\`;\n parent.appendChild(refDiv);\n\n void refDiv.offsetHeight;\n\n const tileR = refDiv.getBoundingClientRect();\n const mainR = mainRef.current?.getBoundingClientRect();\n const frameR = frameRef.current?.getBoundingClientRect();\n\n if (!mainR || !frameR || tileR.width <= 0 || tileR.height <= 0) {\n openingRef.current = false;\n focusedElRef.current = null;\n parent.removeChild(refDiv);\n unlockScroll();\n return;\n }\n\n originalTilePositionRef.current = {\n left: tileR.left,\n top: tileR.top,\n width: tileR.width,\n height: tileR.height\n };\n el.style.visibility = 'hidden';\n el.style.zIndex = '0';\n const overlay = document.createElement('div');\n overlay.className = 'enlarge';\n overlay.style.cssText = \\`position:absolute; left:\\${frameR.left - mainR.left}px; top:\\${frameR.top - mainR.top}px; width:\\${frameR.width}px; height:\\${frameR.height}px; opacity:0; z-index:30; will-change:transform,opacity; transform-origin:top left; transition:transform \\${enlargeTransitionMs}ms ease, opacity \\${enlargeTransitionMs}ms ease; border-radius:\\${openedImageBorderRadius}; overflow:hidden; box-shadow:0 10px 30px rgba(0,0,0,.35);\\`;\n const rawSrc = parent.dataset.src || (el.querySelector('img') as HTMLImageElement)?.src || '';\n const rawAlt = parent.dataset.alt || (el.querySelector('img') as HTMLImageElement)?.alt || '';\n const img = document.createElement('img');\n img.src = rawSrc;\n img.alt = rawAlt;\n img.style.cssText = \\`width:100%; height:100%; object-fit:cover; filter:\\${grayscale ? 'grayscale(1)' : 'none'};\\`;\n overlay.appendChild(img);\n viewerRef.current!.appendChild(overlay);\n const tx0 = tileR.left - frameR.left;\n const ty0 = tileR.top - frameR.top;\n const sx0 = tileR.width / frameR.width;\n const sy0 = tileR.height / frameR.height;\n\n const validSx0 = isFinite(sx0) && sx0 > 0 ? sx0 : 1;\n const validSy0 = isFinite(sy0) && sy0 > 0 ? sy0 : 1;\n\n overlay.style.transform = \\`translate(\\${tx0}px, \\${ty0}px) scale(\\${validSx0}, \\${validSy0})\\`;\n setTimeout(() => {\n if (!overlay.parentElement) return;\n overlay.style.opacity = '1';\n overlay.style.transform = 'translate(0px, 0px) scale(1, 1)';\n rootRef.current?.setAttribute('data-enlarging', 'true');\n }, 16);\n const wantsResize = openedImageWidth || openedImageHeight;\n if (wantsResize) {\n const onFirstEnd = (ev: TransitionEvent) => {\n if (ev.propertyName !== 'transform') return;\n overlay.removeEventListener('transitionend', onFirstEnd);\n const prevTransition = overlay.style.transition;\n overlay.style.transition = 'none';\n const tempWidth = openedImageWidth || \\`\\${frameR.width}px\\`;\n const tempHeight = openedImageHeight || \\`\\${frameR.height}px\\`;\n overlay.style.width = tempWidth;\n overlay.style.height = tempHeight;\n const newRect = overlay.getBoundingClientRect();\n overlay.style.width = frameR.width + 'px';\n overlay.style.height = frameR.height + 'px';\n void overlay.offsetWidth;\n overlay.style.transition = \\`left \\${enlargeTransitionMs}ms ease, top \\${enlargeTransitionMs}ms ease, width \\${enlargeTransitionMs}ms ease, height \\${enlargeTransitionMs}ms ease\\`;\n const centeredLeft = frameR.left - mainR.left + (frameR.width - newRect.width) / 2;\n const centeredTop = frameR.top - mainR.top + (frameR.height - newRect.height) / 2;\n requestAnimationFrame(() => {\n overlay.style.left = \\`\\${centeredLeft}px\\`;\n overlay.style.top = \\`\\${centeredTop}px\\`;\n overlay.style.width = tempWidth;\n overlay.style.height = tempHeight;\n });\n const cleanupSecond = () => {\n overlay.removeEventListener('transitionend', cleanupSecond);\n overlay.style.transition = prevTransition;\n };\n overlay.addEventListener('transitionend', cleanupSecond, {\n once: true\n });\n };\n overlay.addEventListener('transitionend', onFirstEnd);\n }\n };\n\n useEffect(() => {\n return () => {\n document.body.classList.remove('dg-scroll-lock');\n };\n }, []);\n\n const cssStyles = \\`\n .sphere-root {\n --radius: 520px;\n --viewer-pad: 72px;\n --circ: calc(var(--radius) * 3.14);\n --rot-y: calc((360deg / var(--segments-x)) / 2);\n --rot-x: calc((360deg / var(--segments-y)) / 2);\n --item-width: calc(var(--circ) / var(--segments-x));\n --item-height: calc(var(--circ) / var(--segments-y));\n }\n\n .sphere-root * { box-sizing: border-box; }\n .sphere, .sphere-item, .item__image { transform-style: preserve-3d; }\n\n .stage {\n width: 100%;\n height: 100%;\n display: grid;\n place-items: center;\n position: absolute;\n inset: 0;\n margin: auto;\n perspective: calc(var(--radius) * 2);\n perspective-origin: 50% 50%;\n }\n\n .sphere {\n transform: translateZ(calc(var(--radius) * -1));\n will-change: transform;\n position: absolute;\n }\n\n .sphere-item {\n width: calc(var(--item-width) * var(--item-size-x));\n height: calc(var(--item-height) * var(--item-size-y));\n position: absolute;\n top: -999px;\n bottom: -999px;\n left: -999px;\n right: -999px;\n margin: auto;\n transform-origin: 50% 50%;\n backface-visibility: hidden;\n transition: transform 300ms;\n transform: rotateY(calc(var(--rot-y) * (var(--offset-x) + ((var(--item-size-x) - 1) / 2)) + var(--rot-y-delta, 0deg)))\n rotateX(calc(var(--rot-x) * (var(--offset-y) - ((var(--item-size-y) - 1) / 2)) + var(--rot-x-delta, 0deg)))\n translateZ(var(--radius));\n }\n\n .sphere-root[data-enlarging=\"true\"] .scrim {\n opacity: 1 !important;\n pointer-events: all !important;\n }\n\n @media (max-aspect-ratio: 1/1) {\n .viewer-frame {\n height: auto !important;\n width: 100% !important;\n }\n }\n\n .item__image {\n position: absolute;\n inset: 10px;\n border-radius: var(--tile-radius, 12px);\n overflow: hidden;\n cursor: pointer;\n backface-visibility: hidden;\n -webkit-backface-visibility: hidden;\n transition: transform 300ms;\n pointer-events: auto;\n -webkit-transform: translateZ(0);\n transform: translateZ(0);\n }\n .item__image--reference {\n position: absolute;\n inset: 10px;\n pointer-events: none;\n }\n \\`;\n\n return (\n <>\n <style dangerouslySetInnerHTML={{ __html: cssStyles }} />\n <div\n ref={rootRef}\n className=\"sphere-root relative w-full h-full\"\n style={\n {\n ['--segments-x' as string]: segments,\n ['--segments-y' as string]: segments,\n ['--overlay-blur-color' as string]: overlayBlurColor,\n ['--tile-radius' as string]: imageBorderRadius,\n ['--enlarge-radius' as string]: openedImageBorderRadius,\n ['--image-filter' as string]: grayscale ? 'grayscale(1)' : 'none'\n } as React.CSSProperties\n }\n >\n <main\n ref={mainRef}\n className=\"absolute inset-0 grid place-items-center overflow-hidden select-none bg-transparent\"\n style={{\n touchAction: 'none',\n WebkitUserSelect: 'none'\n }}\n >\n <div className=\"stage\">\n <div ref={sphereRef} className=\"sphere\">\n {items.map((it, i) => (\n <div\n key={\\`\\${it.x},\\${it.y},\\${i}\\`}\n className=\"sphere-item absolute m-auto\"\n data-src={it.src}\n data-alt={it.alt}\n data-offset-x={it.x}\n data-offset-y={it.y}\n data-size-x={it.sizeX}\n data-size-y={it.sizeY}\n style={\n {\n ['--offset-x' as string]: it.x,\n ['--offset-y' as string]: it.y,\n ['--item-size-x' as string]: it.sizeX,\n ['--item-size-y' as string]: it.sizeY,\n top: '-999px',\n bottom: '-999px',\n left: '-999px',\n right: '-999px'\n } as React.CSSProperties\n }\n >\n <div\n className={\\`item__image absolute block overflow-hidden bg-gray-200 transition-transform duration-300 \\${disableInteraction ? 'pointer-events-none' : 'cursor-pointer'}\\`}\n role={disableInteraction ? undefined : \"button\"}\n tabIndex={disableInteraction ? -1 : 0}\n aria-label={disableInteraction ? undefined : (it.alt || 'Open image')}\n onClick={disableInteraction ? undefined : (e => {\n if (draggingRef.current) return;\n if (movedRef.current) return;\n if (performance.now() - lastDragEndAt.current < 80) return;\n if (openingRef.current) return;\n openItemFromElement(e.currentTarget as HTMLElement);\n })}\n onPointerUp={disableInteraction ? undefined : (e => {\n if ((e.nativeEvent as PointerEvent).pointerType !== 'touch') return;\n if (draggingRef.current) return;\n if (movedRef.current) return;\n if (performance.now() - lastDragEndAt.current < 80) return;\n if (openingRef.current) return;\n openItemFromElement(e.currentTarget as HTMLElement);\n })}\n style={{\n inset: '10px',\n borderRadius: \\`var(--tile-radius, \\${imageBorderRadius})\\`,\n backfaceVisibility: 'hidden'\n }}\n >\n <img\n src={it.src}\n draggable={false}\n alt={it.alt}\n className=\"w-full h-full object-cover pointer-events-none\"\n style={{\n backfaceVisibility: 'hidden',\n filter: \\`var(--image-filter, \\${grayscale ? 'grayscale(1)' : 'none'})\\`\n }}\n />\n </div>\n </div>\n ))}\n </div>\n </div>\n\n <div\n className=\"absolute inset-0 m-auto z-[3] pointer-events-none\"\n style={{\n backgroundImage: \\`radial-gradient(rgba(235, 235, 235, 0) 65%, var(--overlay-blur-color, \\${overlayBlurColor}) 100%)\\`\n }}\n />\n\n <div\n className=\"absolute inset-0 m-auto z-[3] pointer-events-none\"\n style={{\n WebkitMaskImage: \\`radial-gradient(rgba(235, 235, 235, 0) 70%, var(--overlay-blur-color, \\${overlayBlurColor}) 90%)\\`,\n maskImage: \\`radial-gradient(rgba(235, 235, 235, 0) 70%, var(--overlay-blur-color, \\${overlayBlurColor}) 90%)\\`,\n backdropFilter: 'blur(3px)'\n }}\n />\n\n <div\n className=\"absolute left-0 right-0 top-0 h-[120px] z-[5] pointer-events-none rotate-180\"\n style={{\n background: \\`linear-gradient(to bottom, transparent, var(--overlay-blur-color, \\${overlayBlurColor}))\\`\n }}\n />\n <div\n className=\"absolute left-0 right-0 bottom-0 h-[120px] z-[5] pointer-events-none\"\n style={{\n background: \\`linear-gradient(to bottom, transparent, var(--overlay-blur-color, \\${overlayBlurColor}))\\`\n }}\n />\n\n <div\n ref={viewerRef}\n className=\"absolute inset-0 z-20 pointer-events-none flex items-center justify-center\"\n style={{ padding: 'var(--viewer-pad)' }}\n >\n <div\n ref={scrimRef}\n className=\"scrim absolute inset-0 z-10 pointer-events-none opacity-0 transition-opacity duration-500\"\n style={{\n background: 'rgba(0, 0, 0, 0.4)',\n backdropFilter: 'blur(3px)'\n }}\n />\n <div\n ref={frameRef}\n className=\"viewer-frame h-full aspect-square flex\"\n style={{\n borderRadius: \\`var(--enlarge-radius, \\${openedImageBorderRadius})\\`\n }}\n />\n </div>\n </main>\n </div>\n </>\n );\n}\n`,\n \"components/3d/flip-card\": `\"use client\";\n\nimport { useEffect, useState, useCallback } from \"react\";\nimport { cn } from \"__UTILS_ALIAS__/cn\";\nimport { CardContainer, CardBody, CardItem } from \"__COMPONENTS_ALIAS__/3d-card\";\n\ninterface FlipCardProps {\n isOpen: boolean;\n onClose: () => void;\n title: string;\n subtitle?: string;\n image?: string;\n badges?: Array<{ label: string; color?: string }>;\n quote?: string;\n children?: React.ReactNode;\n className?: string;\n}\n\nexport function FlipCard({ isOpen, onClose, title, subtitle, image, badges, quote, children, className }: FlipCardProps) {\n const [isExiting, setIsExiting] = useState(false);\n const [isVisible, setIsVisible] = useState(false);\n const [isFlipped, setIsFlipped] = useState(false);\n\n useEffect(() => {\n if (isOpen) {\n setIsVisible(true);\n setIsExiting(false);\n setIsFlipped(false);\n document.body.style.overflow = \"hidden\";\n const flipTimer = setTimeout(() => setIsFlipped(true), 50);\n return () => clearTimeout(flipTimer);\n } else if (!isOpen && isVisible) {\n setIsExiting(true);\n setIsFlipped(false);\n const timer = setTimeout(() => {\n setIsVisible(false);\n setIsExiting(false);\n document.body.style.overflow = \"\";\n }, 400);\n return () => clearTimeout(timer);\n }\n }, [isOpen, isVisible]);\n\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\" && isOpen) onClose();\n };\n if (isOpen) {\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => window.removeEventListener(\"keydown\", handleKeyDown);\n }\n }, [isOpen, onClose]);\n\n const handleOverlayClick = useCallback(\n (e: React.MouseEvent<HTMLDivElement>) => {\n if (e.target === e.currentTarget) onClose();\n },\n [onClose]\n );\n\n if (!isVisible) return null;\n\n return (\n <div\n className={cn(\n \"fixed inset-0 z-50 flex items-center justify-center\",\n \"bg-black/80 backdrop-blur-sm\",\n \"transition-opacity duration-300\",\n isExiting ? \"opacity-0\" : \"opacity-100\"\n )}\n onClick={handleOverlayClick}\n role=\"dialog\"\n aria-modal=\"true\"\n style={{ perspective: \"1200px\" }}\n >\n <div\n className=\"transition-transform duration-500 ease-out\"\n style={{\n transformStyle: \"preserve-3d\",\n transform: isFlipped\n ? \"rotateY(0deg)\"\n : isExiting\n ? \"rotateY(-90deg)\"\n : \"rotateY(180deg)\",\n touchAction: \"none\",\n }}\n >\n <CardContainer\n containerClassName={cn(\"!py-0\", \"transition-opacity duration-300\", isExiting ? \"opacity-0\" : \"opacity-100\")}\n >\n <CardBody className={cn(\n \"relative group/card w-[320px] sm:w-[380px] h-auto rounded-2xl p-6 sm:p-8 border border-white/20 bg-gradient-to-br from-white/10 to-white/5 backdrop-blur-xl shadow-2xl\",\n className\n )}>\n <CardItem translateZ={40} className=\"absolute top-3 right-3 z-10\">\n <button onClick={onClose} className=\"p-2 rounded-full bg-white/10 hover:bg-white/20 transition-colors\" aria-label=\"Close\">\n <svg className=\"w-5 h-5 text-white/70\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </CardItem>\n\n {image && (\n <CardItem translateZ={100} className=\"w-full flex justify-center mb-4\">\n <div\n className=\"w-24 h-24 sm:w-28 sm:h-28 rounded-full bg-cover bg-center border-2 border-white/20 shadow-lg\"\n style={{ backgroundImage: \\`url(\\${image})\\` }}\n onContextMenu={(e) => e.preventDefault()}\n />\n </CardItem>\n )}\n\n <CardItem translateZ={60} className=\"w-full text-center\">\n <h3 className=\"text-xl sm:text-2xl font-bold text-white mb-2\">{title}</h3>\n </CardItem>\n\n {badges && badges.length > 0 && (\n <CardItem translateZ={50} className=\"w-full flex flex-wrap justify-center gap-2 mb-3\">\n {badges.map((badge, i) => (\n <span key={i} className=\"px-3 py-1 rounded-full text-xs font-medium border\"\n style={{\n backgroundColor: \\`\\${badge.color || '#00E5A0'}20\\`,\n color: badge.color || '#00E5A0',\n borderColor: \\`\\${badge.color || '#00E5A0'}30\\`,\n }}>\n {badge.label}\n </span>\n ))}\n </CardItem>\n )}\n\n {subtitle && (\n <CardItem translateZ={40} className=\"w-full flex justify-center items-center gap-3 mb-6\">\n <p className=\"text-sm font-medium text-white/90 text-center\">{subtitle}</p>\n </CardItem>\n )}\n\n <CardItem translateZ={20} className=\"w-full mb-6\">\n <div className=\"w-full h-px bg-gradient-to-r from-transparent via-white/20 to-transparent\" />\n </CardItem>\n\n {quote && (\n <CardItem translateZ={80} className=\"w-full\">\n <div className=\"relative\">\n <svg className=\"absolute -top-2 -left-1 w-8 h-8 text-[#00E5A0]/30\" fill=\"currentColor\" viewBox=\"0 0 24 24\">\n <path d=\"M14.017 21v-7.391c0-5.704 3.731-9.57 8.983-10.609l.995 2.151c-2.432.917-3.995 3.638-3.995 5.849h4v10h-9.983zm-14.017 0v-7.391c0-5.704 3.748-9.57 9-10.609l.996 2.151c-2.433.917-3.996 3.638-3.996 5.849h3.983v10h-9.983z\" />\n </svg>\n <p className=\"text-sm sm:text-base text-white/80 leading-relaxed pl-6 italic\">{quote}</p>\n </div>\n </CardItem>\n )}\n\n {children}\n </CardBody>\n </CardContainer>\n </div>\n </div>\n );\n}\n`,\n \"components/3d/glass-surface\": `\"use client\";\n\nimport React, { useEffect, useRef, useState, useId, useCallback } from \"react\";\nimport { cn } from \"__UTILS_ALIAS__/cn\";\n\nexport interface GlassSurfaceProps {\n children?: React.ReactNode;\n width?: number | string;\n height?: number | string;\n borderRadius?: number;\n borderWidth?: number;\n brightness?: number;\n opacity?: number;\n blur?: number;\n displace?: number;\n backgroundOpacity?: number;\n saturation?: number;\n distortionScale?: number;\n redOffset?: number;\n greenOffset?: number;\n blueOffset?: number;\n xChannel?: \"R\" | \"G\" | \"B\";\n yChannel?: \"R\" | \"G\" | \"B\";\n mixBlendMode?:\n | \"normal\"\n | \"multiply\"\n | \"screen\"\n | \"overlay\"\n | \"darken\"\n | \"lighten\"\n | \"color-dodge\"\n | \"color-burn\"\n | \"hard-light\"\n | \"soft-light\"\n | \"difference\"\n | \"exclusion\"\n | \"hue\"\n | \"saturation\"\n | \"color\"\n | \"luminosity\"\n | \"plus-darker\"\n | \"plus-lighter\";\n className?: string;\n style?: React.CSSProperties;\n}\n\nconst GlassSurface: React.FC<GlassSurfaceProps> = ({\n children,\n width = \"auto\",\n height = \"auto\",\n borderRadius = 20,\n borderWidth = 0.07,\n brightness = 50,\n opacity = 0.93,\n blur = 11,\n displace = 0,\n backgroundOpacity = 0.08,\n saturation = 1,\n distortionScale = -180,\n redOffset = 0,\n greenOffset = 10,\n blueOffset = 20,\n xChannel = \"R\",\n yChannel = \"G\",\n mixBlendMode = \"difference\",\n className = \"\",\n style = {},\n}) => {\n const uniqueId = useId().replace(/:/g, \"-\");\n const filterId = \\`glass-filter-\\${uniqueId}\\`;\n const redGradId = \\`red-grad-\\${uniqueId}\\`;\n const blueGradId = \\`blue-grad-\\${uniqueId}\\`;\n\n const [svgSupported, setSvgSupported] = useState<boolean>(false);\n\n const containerRef = useRef<HTMLDivElement>(null);\n const feImageRef = useRef<SVGFEImageElement>(null);\n const redChannelRef = useRef<SVGFEDisplacementMapElement>(null);\n const greenChannelRef = useRef<SVGFEDisplacementMapElement>(null);\n const blueChannelRef = useRef<SVGFEDisplacementMapElement>(null);\n const gaussianBlurRef = useRef<SVGFEGaussianBlurElement>(null);\n\n const generateDisplacementMap = () => {\n const rect = containerRef.current?.getBoundingClientRect();\n const actualWidth = rect?.width || 400;\n const actualHeight = rect?.height || 200;\n const edgeSize = Math.min(actualWidth, actualHeight) * (borderWidth * 0.5);\n\n const svgContent = \\`\n <svg viewBox=\"0 0 \\${actualWidth} \\${actualHeight}\" xmlns=\"http://www.w3.org/2000/svg\">\n <defs>\n <linearGradient id=\"\\${redGradId}\" x1=\"100%\" y1=\"0%\" x2=\"0%\" y2=\"0%\">\n <stop offset=\"0%\" stop-color=\"#0000\"/>\n <stop offset=\"100%\" stop-color=\"red\"/>\n </linearGradient>\n <linearGradient id=\"\\${blueGradId}\" x1=\"0%\" y1=\"0%\" x2=\"0%\" y2=\"100%\">\n <stop offset=\"0%\" stop-color=\"#0000\"/>\n <stop offset=\"100%\" stop-color=\"blue\"/>\n </linearGradient>\n </defs>\n <rect x=\"0\" y=\"0\" width=\"\\${actualWidth}\" height=\"\\${actualHeight}\" fill=\"black\"></rect>\n <rect x=\"0\" y=\"0\" width=\"\\${actualWidth}\" height=\"\\${actualHeight}\" rx=\"\\${borderRadius}\" fill=\"url(#\\${redGradId})\" />\n <rect x=\"0\" y=\"0\" width=\"\\${actualWidth}\" height=\"\\${actualHeight}\" rx=\"\\${borderRadius}\" fill=\"url(#\\${blueGradId})\" style=\"mix-blend-mode: \\${mixBlendMode}\" />\n <rect x=\"\\${edgeSize}\" y=\"\\${edgeSize}\" width=\"\\${actualWidth - edgeSize * 2}\" height=\"\\${actualHeight - edgeSize * 2}\" rx=\"\\${borderRadius}\" fill=\"hsl(0 0% \\${brightness}% / \\${opacity})\" style=\"filter:blur(\\${blur}px)\" />\n </svg>\n \\`;\n\n return \\`data:image/svg+xml,\\${encodeURIComponent(svgContent)}\\`;\n };\n\n const updateDisplacementMap = () => {\n feImageRef.current?.setAttribute(\"href\", generateDisplacementMap());\n };\n\n useEffect(() => {\n updateDisplacementMap();\n [\n { ref: redChannelRef, offset: redOffset },\n { ref: greenChannelRef, offset: greenOffset },\n { ref: blueChannelRef, offset: blueOffset },\n ].forEach(({ ref, offset }) => {\n if (ref.current) {\n ref.current.setAttribute(\"scale\", (distortionScale + offset).toString());\n ref.current.setAttribute(\"xChannelSelector\", xChannel);\n ref.current.setAttribute(\"yChannelSelector\", yChannel);\n }\n });\n\n gaussianBlurRef.current?.setAttribute(\"stdDeviation\", displace.toString());\n }, [\n width,\n height,\n borderRadius,\n borderWidth,\n brightness,\n opacity,\n blur,\n displace,\n distortionScale,\n redOffset,\n greenOffset,\n blueOffset,\n xChannel,\n yChannel,\n mixBlendMode,\n ]);\n\n useEffect(() => {\n setSvgSupported(supportsSVGFilters());\n }, []);\n\n useEffect(() => {\n if (!containerRef.current) return;\n\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n const resizeObserver = new ResizeObserver(() => {\n // Debounce resize updates (150ms)\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(updateDisplacementMap, 150);\n });\n\n resizeObserver.observe(containerRef.current);\n\n return () => {\n if (debounceTimer) clearTimeout(debounceTimer);\n resizeObserver.disconnect();\n };\n }, []);\n\n useEffect(() => {\n setTimeout(updateDisplacementMap, 0);\n }, [width, height]);\n\n const supportsSVGFilters = () => {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return false;\n }\n\n const isWebkit =\n /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);\n const isFirefox = /Firefox/.test(navigator.userAgent);\n\n if (isWebkit || isFirefox) {\n return false;\n }\n\n const div = document.createElement(\"div\");\n div.style.backdropFilter = \\`url(#\\${filterId})\\`;\n\n return div.style.backdropFilter !== \"\";\n };\n\n const supportsBackdropFilter = () => {\n if (typeof window === \"undefined\") return false;\n return CSS.supports(\"backdrop-filter\", \"blur(10px)\");\n };\n\n const getContainerStyles = (): React.CSSProperties => {\n const baseStyles: React.CSSProperties = {\n ...style,\n width: typeof width === \"number\" ? \\`\\${width}px\\` : width,\n height: typeof height === \"number\" ? \\`\\${height}px\\` : height,\n borderRadius: \\`\\${borderRadius}px\\`,\n };\n\n const backdropFilterSupported = supportsBackdropFilter();\n\n if (svgSupported) {\n // Full glass effect with SVG filters (Chrome/Edge desktop)\n return {\n ...baseStyles,\n background: \\`hsl(0 0% 0% / \\${backgroundOpacity})\\`,\n backdropFilter: \\`url(#\\${filterId}) saturate(\\${saturation})\\`,\n boxShadow: \\`0 0 2px 1px color-mix(in oklch, white, transparent 65%) inset,\n 0 0 10px 4px color-mix(in oklch, white, transparent 85%) inset,\n 0px 4px 16px rgba(17, 17, 26, 0.05),\n 0px 8px 24px rgba(17, 17, 26, 0.05),\n 0px 16px 56px rgba(17, 17, 26, 0.05),\n 0px 4px 16px rgba(17, 17, 26, 0.05) inset,\n 0px 8px 24px rgba(17, 17, 26, 0.05) inset,\n 0px 16px 56px rgba(17, 17, 26, 0.05) inset\\`,\n };\n } else {\n // Fallback for Safari, Firefox, mobile\n if (!backdropFilterSupported) {\n return {\n ...baseStyles,\n background: \"rgba(0, 0, 0, 0.4)\",\n border: \"1px solid rgba(255, 255, 255, 0.08)\",\n boxShadow: \\`inset 0 1px 0 0 rgba(255, 255, 255, 0.1),\n inset 0 -1px 0 0 rgba(255, 255, 255, 0.05)\\`,\n };\n } else {\n return {\n ...baseStyles,\n background: \\`rgba(255, 255, 255, \\${backgroundOpacity})\\`,\n backdropFilter: \"blur(20px) saturate(1.8) brightness(1.1)\",\n WebkitBackdropFilter: \"blur(20px) saturate(1.8) brightness(1.1)\",\n border: \"1px solid rgba(255, 255, 255, 0.08)\",\n boxShadow: \\`inset 0 1px 0 0 rgba(255, 255, 255, 0.1),\n inset 0 -1px 0 0 rgba(255, 255, 255, 0.05),\n 0 4px 24px rgba(0, 0, 0, 0.1)\\`,\n };\n }\n }\n };\n\n return (\n <div\n ref={containerRef}\n className={cn(\n \"relative overflow-hidden transition-opacity duration-[260ms] ease-out\",\n className\n )}\n style={getContainerStyles()}\n >\n <svg\n className=\"w-full h-full pointer-events-none absolute inset-0 opacity-0 -z-10\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <defs>\n <filter\n id={filterId}\n colorInterpolationFilters=\"sRGB\"\n x=\"0%\"\n y=\"0%\"\n width=\"100%\"\n height=\"100%\"\n >\n <feImage\n ref={feImageRef}\n x=\"0\"\n y=\"0\"\n width=\"100%\"\n height=\"100%\"\n preserveAspectRatio=\"none\"\n result=\"map\"\n />\n\n <feDisplacementMap\n ref={redChannelRef}\n in=\"SourceGraphic\"\n in2=\"map\"\n id=\"redchannel\"\n result=\"dispRed\"\n />\n <feColorMatrix\n in=\"dispRed\"\n type=\"matrix\"\n values=\"1 0 0 0 0\n 0 0 0 0 0\n 0 0 0 0 0\n 0 0 0 1 0\"\n result=\"red\"\n />\n\n <feDisplacementMap\n ref={greenChannelRef}\n in=\"SourceGraphic\"\n in2=\"map\"\n id=\"greenchannel\"\n result=\"dispGreen\"\n />\n <feColorMatrix\n in=\"dispGreen\"\n type=\"matrix\"\n values=\"0 0 0 0 0\n 0 1 0 0 0\n 0 0 0 0 0\n 0 0 0 1 0\"\n result=\"green\"\n />\n\n <feDisplacementMap\n ref={blueChannelRef}\n in=\"SourceGraphic\"\n in2=\"map\"\n id=\"bluechannel\"\n result=\"dispBlue\"\n />\n <feColorMatrix\n in=\"dispBlue\"\n type=\"matrix\"\n values=\"0 0 0 0 0\n 0 0 0 0 0\n 0 0 1 0 0\n 0 0 0 1 0\"\n result=\"blue\"\n />\n\n <feBlend in=\"red\" in2=\"green\" mode=\"screen\" result=\"rg\" />\n <feBlend in=\"rg\" in2=\"blue\" mode=\"screen\" result=\"output\" />\n <feGaussianBlur ref={gaussianBlurRef} in=\"output\" stdDeviation=\"0.7\" />\n </filter>\n </defs>\n </svg>\n\n {children}\n </div>\n );\n};\n\nexport { GlassSurface };\n`,\n \"components/3d/holo-card\": `\"use client\";\n\nimport { useEffect, useRef, useCallback, useState } from \"react\";\nimport { cn } from \"__UTILS_ALIAS__/cn\";\n\nexport interface HoloCardItem {\n title: string;\n description?: string;\n thumbnail?: string;\n category?: string;\n categoryColor?: string;\n tags?: string[];\n badge?: string;\n}\n\ninterface HoloCardProps {\n item: HoloCardItem | null;\n isOpen: boolean;\n onClose: () => void;\n backLogo?: string;\n backSubtitle?: string;\n footer?: string;\n className?: string;\n}\n\nexport function HoloCard({\n item,\n isOpen,\n onClose,\n backLogo = \"SPARK\",\n backSubtitle = \"UI COMPONENT\",\n footer,\n}: HoloCardProps) {\n const cardRef = useRef<HTMLDivElement>(null);\n const rafRef = useRef<number | null>(null);\n const pendingUpdateRef = useRef<{\n mx: number; my: number; rx: number; ry: number; hyp: number;\n } | null>(null);\n const [isExiting, setIsExiting] = useState(false);\n const [isVisible, setIsVisible] = useState(false);\n const [isAnimating, setIsAnimating] = useState(false);\n\n useEffect(() => {\n if (isOpen && item) {\n setIsVisible(true);\n setIsExiting(false);\n setIsAnimating(true);\n document.body.style.overflow = \"hidden\";\n const animTimer = setTimeout(() => setIsAnimating(false), 700);\n return () => clearTimeout(animTimer);\n } else if (!isOpen && isVisible) {\n setIsExiting(true);\n if (rafRef.current !== null) {\n cancelAnimationFrame(rafRef.current);\n rafRef.current = null;\n }\n const timer = setTimeout(() => {\n setIsVisible(false);\n setIsExiting(false);\n document.body.style.overflow = \"\";\n }, 600);\n return () => clearTimeout(timer);\n }\n return () => {\n if (rafRef.current !== null) {\n cancelAnimationFrame(rafRef.current);\n rafRef.current = null;\n }\n };\n }, [isOpen, item, isVisible]);\n\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\" && isOpen) onClose();\n };\n if (isOpen) {\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => window.removeEventListener(\"keydown\", handleKeyDown);\n }\n }, [isOpen, onClose]);\n\n const applyStyleUpdates = useCallback(() => {\n const card = cardRef.current;\n const update = pendingUpdateRef.current;\n if (!card || !update) return;\n card.style.setProperty(\"--mx\", \\`\\${update.mx}%\\`);\n card.style.setProperty(\"--my\", \\`\\${update.my}%\\`);\n card.style.setProperty(\"--posx\", \\`\\${update.mx}%\\`);\n card.style.setProperty(\"--posy\", \\`\\${update.my}%\\`);\n card.style.setProperty(\"--rx\", \\`\\${update.ry}deg\\`);\n card.style.setProperty(\"--ry\", \\`\\${update.rx}deg\\`);\n card.style.setProperty(\"--hyp\", \\`\\${update.hyp}\\`);\n card.style.setProperty(\"--o\", \"1\");\n pendingUpdateRef.current = null;\n rafRef.current = null;\n }, []);\n\n const handleMouseMove = useCallback((e: React.MouseEvent<HTMLDivElement>) => {\n const card = cardRef.current;\n if (!card) return;\n const rect = card.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const y = e.clientY - rect.top;\n const mx = (x / rect.width) * 100;\n const my = (y / rect.height) * 100;\n const px = mx - 50;\n const py = my - 50;\n const hyp = Math.sqrt(px * px + py * py) / 50;\n const rx = (py / 50) * -15;\n const ry = (px / 50) * 15;\n pendingUpdateRef.current = { mx, my, rx, ry, hyp };\n if (rafRef.current === null) {\n rafRef.current = requestAnimationFrame(applyStyleUpdates);\n }\n }, [applyStyleUpdates]);\n\n const handleMouseLeave = useCallback(() => {\n const card = cardRef.current;\n if (!card) return;\n card.style.setProperty(\"--mx\", \"50%\");\n card.style.setProperty(\"--my\", \"50%\");\n card.style.setProperty(\"--rx\", \"0deg\");\n card.style.setProperty(\"--ry\", \"0deg\");\n card.style.setProperty(\"--hyp\", \"0.5\");\n }, []);\n\n const handleTouchMove = useCallback((e: React.TouchEvent<HTMLDivElement>) => {\n const card = cardRef.current;\n if (!card || !e.touches[0]) return;\n const rect = card.getBoundingClientRect();\n const touch = e.touches[0];\n const x = touch.clientX - rect.left;\n const y = touch.clientY - rect.top;\n const mx = Math.max(0, Math.min(100, (x / rect.width) * 100));\n const my = Math.max(0, Math.min(100, (y / rect.height) * 100));\n const px = mx - 50;\n const py = my - 50;\n const hyp = Math.sqrt(px * px + py * py) / 50;\n const rx = (py / 50) * -10;\n const ry = (px / 50) * 10;\n pendingUpdateRef.current = { mx, my, rx, ry, hyp };\n if (rafRef.current === null) {\n rafRef.current = requestAnimationFrame(applyStyleUpdates);\n }\n }, [applyStyleUpdates]);\n\n const handleOverlayClick = useCallback(\n (e: React.MouseEvent<HTMLDivElement>) => {\n if (e.target === e.currentTarget) onClose();\n },\n [onClose]\n );\n\n if (!isVisible || !item) return null;\n\n const glowColor = item.categoryColor || \"#69d1e9\";\n\n return (\n <div\n className={cn(\n \"holo-card-overlay\",\n isExiting ? \"holo-card-overlay--exiting\" : \"holo-card-overlay--entering\"\n )}\n onClick={handleOverlayClick}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"holo-card-title\"\n >\n <div className=\"holo-card-container\">\n <div\n ref={cardRef}\n className={cn(\n \"holo-card\",\n isExiting && \"holo-card--exiting\",\n isAnimating && !isExiting && \"holo-card--entering\"\n )}\n style={{ \"--glow\": glowColor } as React.CSSProperties}\n onMouseMove={handleMouseMove}\n onMouseLeave={handleMouseLeave}\n onTouchMove={handleTouchMove}\n onTouchEnd={handleMouseLeave}\n >\n <div className=\"holo-card__back\">\n <div className=\"holo-card__back-content\">\n <div className=\"holo-card__back-logo\">{backLogo}</div>\n <div className=\"holo-card__back-subtitle\">{backSubtitle}</div>\n </div>\n </div>\n <div className=\"holo-card__front\">\n <div className=\"holo-card__shine\" />\n <div className=\"holo-card__glare\" />\n <div className=\"holo-card__inner-frame\" />\n <div className=\"holo-card__content\">\n {item.thumbnail && (\n <div className=\"absolute inset-0 overflow-hidden pointer-events-none z-0\">\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <img src={item.thumbnail} alt=\"\" className=\"w-64 h-64 object-contain blur-[60px] opacity-40 saturate-150\" aria-hidden=\"true\" />\n </div>\n </div>\n )}\n <div className=\"relative z-10 p-6 sm:p-8 h-full flex flex-col\">\n <div className=\"flex items-center justify-between flex-shrink-0\">\n {item.category && (\n <span className=\"inline-flex items-center gap-1.5 px-2.5 py-1 rounded text-xs sm:text-sm font-bold uppercase tracking-wide border\"\n style={{ color: glowColor, borderColor: \\`\\${glowColor}30\\`, backgroundColor: \\`\\${glowColor}20\\` }}>\n <span className=\"relative flex h-2 w-2\">\n <span className=\"animate-ping absolute inline-flex h-full w-full rounded-full opacity-75\" style={{ backgroundColor: glowColor }} />\n <span className=\"relative inline-flex rounded-full h-2 w-2\" style={{ backgroundColor: glowColor }} />\n </span>\n {item.category}\n </span>\n )}\n {item.badge && (\n <span className=\"text-xs sm:text-sm font-mono text-white/70\">{item.badge}</span>\n )}\n </div>\n <div className=\"flex-1 flex flex-col justify-center items-center py-4\">\n {item.thumbnail && (\n <div className=\"relative flex-shrink-0 mb-3 flex justify-center\">\n <div className=\"relative w-32 h-32 sm:w-36 sm:h-36 rounded-lg overflow-hidden flex items-center justify-center\">\n <img src={item.thumbnail} alt={item.title} className=\"w-full h-full object-contain\" />\n </div>\n </div>\n )}\n <div className=\"text-center mb-2\">\n <h3 id=\"holo-card-title\" className=\"text-lg sm:text-xl font-bold text-white leading-tight\">{item.title}</h3>\n </div>\n {item.description && (\n <div className=\"mb-2\">\n <p className=\"text-[11px] sm:text-xs text-white/60 leading-relaxed text-center\">{item.description}</p>\n </div>\n )}\n {item.tags && item.tags.length > 0 && (\n <div className=\"flex flex-wrap justify-center gap-1.5\">\n {item.tags.slice(0, 4).map((tag) => (\n <span key={tag} className=\"px-2 py-0.5 bg-white/10 rounded text-[10px] sm:text-xs text-white/70 border border-white/20\">{tag}</span>\n ))}\n {item.tags.length > 4 && (\n <span className=\"px-2 py-0.5 text-[10px] sm:text-xs text-white/50\">+{item.tags.length - 4}</span>\n )}\n </div>\n )}\n </div>\n {footer && (\n <div className=\"pt-2 border-t border-white/20 text-center flex-shrink-0\">\n <p className=\"text-[10px] sm:text-xs text-white/50 tracking-wider\">{footer}</p>\n </div>\n )}\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n}\n`,\n \"components/3d/prismatic-burst\": `\"use client\";\n\nimport React, { useEffect, useRef } from \"react\";\nimport { Renderer, Program, Mesh, Triangle, Texture } from \"ogl\";\n\ntype Offset = { x?: number | string; y?: number | string };\ntype AnimationType = \"rotate\" | \"rotate3d\" | \"hover\";\n\nexport type PrismaticBurstProps = {\n intensity?: number;\n speed?: number;\n animationType?: AnimationType;\n colors?: string[];\n distort?: number;\n paused?: boolean;\n offset?: Offset;\n hoverDampness?: number;\n rayCount?: number;\n mixBlendMode?: React.CSSProperties[\"mixBlendMode\"] | \"none\";\n};\n\nconst vertexShader = \\`#version 300 es\nin vec2 position;\nin vec2 uv;\nout vec2 vUv;\nvoid main() {\n vUv = uv;\n gl_Position = vec4(position, 0.0, 1.0);\n}\n\\`;\n\nconst fragmentShader = \\`#version 300 es\nprecision highp float;\nprecision highp int;\n\nout vec4 fragColor;\n\nuniform vec2 uResolution;\nuniform float uTime;\n\nuniform float uIntensity;\nuniform float uSpeed;\nuniform int uAnimType;\nuniform vec2 uMouse;\nuniform int uColorCount;\nuniform float uDistort;\nuniform vec2 uOffset;\nuniform sampler2D uGradient;\nuniform float uNoiseAmount;\nuniform int uRayCount;\n\nfloat hash21(vec2 p){\n p = floor(p);\n float f = 52.9829189 * fract(dot(p, vec2(0.065, 0.005)));\n return fract(f);\n}\n\nmat2 rot30(){ return mat2(0.8, -0.5, 0.5, 0.8); }\n\nfloat layeredNoise(vec2 fragPx){\n vec2 p = mod(fragPx + vec2(uTime * 30.0, -uTime * 21.0), 1024.0);\n vec2 q = rot30() * p;\n float n = 0.0;\n n += 0.40 * hash21(q);\n n += 0.25 * hash21(q * 2.0 + 17.0);\n n += 0.20 * hash21(q * 4.0 + 47.0);\n n += 0.10 * hash21(q * 8.0 + 113.0);\n n += 0.05 * hash21(q * 16.0 + 191.0);\n return n;\n}\n\nvec3 rayDir(vec2 frag, vec2 res, vec2 offset, float dist){\n float focal = res.y * max(dist, 1e-3);\n return normalize(vec3(2.0 * (frag - offset) - res, focal));\n}\n\nfloat edgeFade(vec2 frag, vec2 res, vec2 offset){\n vec2 toC = frag - 0.5 * res - offset;\n float r = length(toC) / (0.5 * min(res.x, res.y));\n float x = clamp(r, 0.0, 1.0);\n float q = x * x * x * (x * (x * 6.0 - 15.0) + 10.0);\n float s = q * 0.5;\n s = pow(s, 1.5);\n float tail = 1.0 - pow(1.0 - s, 2.0);\n s = mix(s, tail, 0.2);\n float dn = (layeredNoise(frag * 0.15) - 0.5) * 0.0015 * s;\n return clamp(s + dn, 0.0, 1.0);\n}\n\nmat3 rotX(float a){ float c = cos(a), s = sin(a); return mat3(1.0,0.0,0.0, 0.0,c,-s, 0.0,s,c); }\nmat3 rotY(float a){ float c = cos(a), s = sin(a); return mat3(c,0.0,s, 0.0,1.0,0.0, -s,0.0,c); }\nmat3 rotZ(float a){ float c = cos(a), s = sin(a); return mat3(c,-s,0.0, s,c,0.0, 0.0,0.0,1.0); }\n\nvec3 sampleGradient(float t){\n t = clamp(t, 0.0, 1.0);\n return texture(uGradient, vec2(t, 0.5)).rgb;\n}\n\nvec2 rot2(vec2 v, float a){\n float s = sin(a), c = cos(a);\n return mat2(c, -s, s, c) * v;\n}\n\nfloat bendAngle(vec3 q, float t){\n float a = 0.8 * sin(q.x * 0.55 + t * 0.6)\n + 0.7 * sin(q.y * 0.50 - t * 0.5)\n + 0.6 * sin(q.z * 0.60 + t * 0.7);\n return a;\n}\n\nvoid main(){\n vec2 frag = gl_FragCoord.xy;\n float t = uTime * uSpeed;\n float jitterAmp = 0.1 * clamp(uNoiseAmount, 0.0, 1.0);\n vec3 dir = rayDir(frag, uResolution, uOffset, 1.0);\n float marchT = 0.0;\n vec3 col = vec3(0.0);\n float n = layeredNoise(frag);\n vec4 c = cos(t * 0.2 + vec4(0.0, 33.0, 11.0, 0.0));\n mat2 M2 = mat2(c.x, c.y, c.z, c.w);\n float amp = clamp(uDistort, 0.0, 50.0) * 0.15;\n\n mat3 rot3dMat = mat3(1.0);\n if(uAnimType == 1){\n vec3 ang = vec3(t * 0.31, t * 0.21, t * 0.17);\n rot3dMat = rotZ(ang.z) * rotY(ang.y) * rotX(ang.x);\n }\n mat3 hoverMat = mat3(1.0);\n if(uAnimType == 2){\n vec2 m = uMouse * 2.0 - 1.0;\n vec3 ang = vec3(m.y * 0.6, m.x * 0.6, 0.0);\n hoverMat = rotY(ang.y) * rotX(ang.x);\n }\n\n for (int i = 0; i < 44; ++i) {\n vec3 P = marchT * dir;\n P.z -= 2.0;\n float rad = length(P);\n vec3 Pl = P * (10.0 / max(rad, 1e-6));\n\n if(uAnimType == 0){\n Pl.xz *= M2;\n } else if(uAnimType == 1){\n Pl = rot3dMat * Pl;\n } else {\n Pl = hoverMat * Pl;\n }\n\n float stepLen = min(rad - 0.3, n * jitterAmp) + 0.1;\n\n float grow = smoothstep(0.35, 3.0, marchT);\n float a1 = amp * grow * bendAngle(Pl * 0.6, t);\n float a2 = 0.5 * amp * grow * bendAngle(Pl.zyx * 0.5 + 3.1, t * 0.9);\n vec3 Pb = Pl;\n Pb.xz = rot2(Pb.xz, a1);\n Pb.xy = rot2(Pb.xy, a2);\n\n float rayPattern = smoothstep(\n 0.5, 0.7,\n sin(Pb.x + cos(Pb.y) * cos(Pb.z)) *\n sin(Pb.z + sin(Pb.y) * cos(Pb.x + t))\n );\n\n if (uRayCount > 0) {\n float ang = atan(Pb.y, Pb.x);\n float comb = 0.5 + 0.5 * cos(float(uRayCount) * ang);\n comb = pow(comb, 3.0);\n rayPattern *= smoothstep(0.15, 0.95, comb);\n }\n\n vec3 spectralDefault = 1.0 + vec3(\n cos(marchT * 3.0 + 0.0),\n cos(marchT * 3.0 + 1.0),\n cos(marchT * 3.0 + 2.0)\n );\n\n float saw = fract(marchT * 0.25);\n float tRay = saw * saw * (3.0 - 2.0 * saw);\n vec3 userGradient = 2.0 * sampleGradient(tRay);\n vec3 spectral = (uColorCount > 0) ? userGradient : spectralDefault;\n vec3 base = (0.05 / (0.4 + stepLen))\n * smoothstep(5.0, 0.0, rad)\n * spectral;\n\n col += base * rayPattern;\n marchT += stepLen;\n }\n\n col *= edgeFade(frag, uResolution, uOffset);\n col *= uIntensity;\n\n fragColor = vec4(clamp(col, 0.0, 1.0), 1.0);\n}\\`;\n\nconst hexToRgb01 = (hex: string): [number, number, number] => {\n let h = hex.trim();\n if (h.startsWith(\"#\")) h = h.slice(1);\n if (h.length === 3) {\n const r = h[0],\n g = h[1],\n b = h[2];\n h = r + r + g + g + b + b;\n }\n const intVal = parseInt(h, 16);\n if (isNaN(intVal) || (h.length !== 6 && h.length !== 8)) return [1, 1, 1];\n const r = ((intVal >> 16) & 255) / 255;\n const g = ((intVal >> 8) & 255) / 255;\n const b = (intVal & 255) / 255;\n return [r, g, b];\n};\n\nconst toPx = (v: number | string | undefined): number => {\n if (v == null) return 0;\n if (typeof v === \"number\") return v;\n const s = String(v).trim();\n const num = parseFloat(s.replace(\"px\", \"\"));\n return isNaN(num) ? 0 : num;\n};\n\nconst PrismaticBurst = ({\n intensity = 2,\n speed = 0.5,\n animationType = \"rotate3d\",\n colors,\n distort = 0,\n paused = false,\n offset = { x: 0, y: 0 },\n hoverDampness = 0,\n rayCount,\n mixBlendMode = \"lighten\",\n}: PrismaticBurstProps) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const programRef = useRef<Program | null>(null);\n const rendererRef = useRef<Renderer | null>(null);\n const mouseTargetRef = useRef<[number, number]>([0.5, 0.5]);\n const mouseSmoothRef = useRef<[number, number]>([0.5, 0.5]);\n const pausedRef = useRef<boolean>(paused);\n const gradTexRef = useRef<Texture | null>(null);\n const hoverDampRef = useRef<number>(hoverDampness);\n const isVisibleRef = useRef<boolean>(true);\n const isScrollingRef = useRef<boolean>(false);\n const meshRef = useRef<Mesh | null>(null);\n const triRef = useRef<Triangle | null>(null);\n\n useEffect(() => {\n pausedRef.current = paused;\n }, [paused]);\n useEffect(() => {\n hoverDampRef.current = hoverDampness;\n }, [hoverDampness]);\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n // Respect user's motion preferences\n const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n if (prefersReducedMotion) {\n // Skip WebGL rendering entirely for users who prefer reduced motion\n return;\n }\n\n // Performance optimization: Use DPR of 1 for all devices\n // Background effects don't need high resolution\n const dpr = 1;\n const renderer = new Renderer({ dpr, alpha: false, antialias: false });\n rendererRef.current = renderer;\n\n const gl = renderer.gl;\n gl.canvas.style.position = \"absolute\";\n gl.canvas.style.inset = \"0\";\n gl.canvas.style.width = \"100%\";\n gl.canvas.style.height = \"100%\";\n gl.canvas.style.mixBlendMode =\n mixBlendMode && mixBlendMode !== \"none\" ? mixBlendMode : \"\";\n container.appendChild(gl.canvas);\n\n const white = new Uint8Array([255, 255, 255, 255]);\n const gradientTex = new Texture(gl, {\n image: white,\n width: 1,\n height: 1,\n generateMipmaps: false,\n flipY: false,\n });\n\n gradientTex.minFilter = gl.LINEAR;\n gradientTex.magFilter = gl.LINEAR;\n gradientTex.wrapS = gl.CLAMP_TO_EDGE;\n gradientTex.wrapT = gl.CLAMP_TO_EDGE;\n gradTexRef.current = gradientTex;\n\n const program = new Program(gl, {\n vertex: vertexShader,\n fragment: fragmentShader,\n uniforms: {\n uResolution: { value: [1, 1] as [number, number] },\n uTime: { value: 0 },\n\n uIntensity: { value: 1 },\n uSpeed: { value: 1 },\n uAnimType: { value: 0 },\n uMouse: { value: [0.5, 0.5] as [number, number] },\n uColorCount: { value: 0 },\n uDistort: { value: 0 },\n uOffset: { value: [0, 0] as [number, number] },\n uGradient: { value: gradientTex },\n uNoiseAmount: { value: 0.8 },\n uRayCount: { value: 0 },\n },\n });\n\n programRef.current = program;\n\n const triangle = new Triangle(gl);\n const mesh = new Mesh(gl, { geometry: triangle, program });\n triRef.current = triangle;\n meshRef.current = mesh;\n\n const resize = () => {\n const w = container.clientWidth || 1;\n const h = container.clientHeight || 1;\n renderer.setSize(w, h);\n program.uniforms.uResolution.value = [\n gl.drawingBufferWidth,\n gl.drawingBufferHeight,\n ];\n };\n\n let ro: ResizeObserver | null = null;\n if (\"ResizeObserver\" in window) {\n ro = new ResizeObserver(resize);\n ro.observe(container);\n } else {\n (window as Window).addEventListener(\"resize\", resize);\n }\n resize();\n\n const onPointer = (e: PointerEvent) => {\n const rect = container.getBoundingClientRect();\n const x = (e.clientX - rect.left) / Math.max(rect.width, 1);\n const y = (e.clientY - rect.top) / Math.max(rect.height, 1);\n mouseTargetRef.current = [\n Math.min(Math.max(x, 0), 1),\n Math.min(Math.max(y, 0), 1),\n ];\n };\n container.addEventListener(\"pointermove\", onPointer, { passive: true });\n\n let raf = 0;\n let last = performance.now();\n let accumTime = 0;\n let isRunning = false;\n\n const update = (now: number) => {\n if (!isRunning) return;\n const dt = Math.max(0, now - last) * 0.001;\n last = now;\n if (!pausedRef.current) accumTime += dt;\n const tau = 0.02 + Math.max(0, Math.min(1, hoverDampRef.current)) * 0.5;\n const alpha = 1 - Math.exp(-dt / tau);\n const tgt = mouseTargetRef.current;\n const sm = mouseSmoothRef.current;\n sm[0] += (tgt[0] - sm[0]) * alpha;\n sm[1] += (tgt[1] - sm[1]) * alpha;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n program.uniforms.uMouse.value = sm as any;\n program.uniforms.uTime.value = accumTime;\n renderer.render({ scene: meshRef.current! });\n raf = requestAnimationFrame(update);\n };\n\n const startLoop = () => {\n if (isRunning) return;\n isRunning = true;\n last = performance.now();\n raf = requestAnimationFrame(update);\n };\n\n const stopLoop = () => {\n isRunning = false;\n if (raf) {\n cancelAnimationFrame(raf);\n raf = 0;\n }\n };\n\n // Start/stop based on visibility and scroll state\n const checkVisibility = () => {\n const shouldRun = isVisibleRef.current && !document.hidden && !isScrollingRef.current;\n if (shouldRun) {\n startLoop();\n } else {\n stopLoop();\n }\n };\n\n // Pause during scroll for better performance\n let scrollTimeout: ReturnType<typeof setTimeout> | null = null;\n const onScroll = () => {\n isScrollingRef.current = true;\n checkVisibility();\n if (scrollTimeout) clearTimeout(scrollTimeout);\n scrollTimeout = setTimeout(() => {\n isScrollingRef.current = false;\n checkVisibility();\n }, 150);\n };\n window.addEventListener('scroll', onScroll, { passive: true });\n\n let io: IntersectionObserver | null = null;\n if (\"IntersectionObserver\" in window) {\n io = new IntersectionObserver(\n (entries) => {\n if (entries[0]) {\n isVisibleRef.current = entries[0].isIntersecting;\n checkVisibility();\n }\n },\n { root: null, threshold: 0.01 }\n );\n io.observe(container);\n }\n\n const onVis = () => checkVisibility();\n document.addEventListener(\"visibilitychange\", onVis);\n\n // Start loop if initially visible\n checkVisibility();\n\n return () => {\n stopLoop();\n container.removeEventListener(\"pointermove\", onPointer);\n ro?.disconnect();\n if (!ro) window.removeEventListener(\"resize\", resize);\n io?.disconnect();\n document.removeEventListener(\"visibilitychange\", onVis);\n window.removeEventListener('scroll', onScroll);\n if (scrollTimeout) clearTimeout(scrollTimeout);\n try {\n container.removeChild(gl.canvas);\n } catch (e) {\n void e;\n }\n meshRef.current = null;\n triRef.current = null;\n programRef.current = null;\n try {\n const glCtx = rendererRef.current?.gl;\n if (glCtx && gradTexRef.current?.texture)\n glCtx.deleteTexture(gradTexRef.current.texture);\n } catch (e) {\n void e;\n }\n rendererRef.current = null;\n gradTexRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n const canvas = rendererRef.current?.gl?.canvas as\n | HTMLCanvasElement\n | undefined;\n if (canvas) {\n canvas.style.mixBlendMode =\n mixBlendMode && mixBlendMode !== \"none\" ? mixBlendMode : \"\";\n }\n }, [mixBlendMode]);\n\n useEffect(() => {\n const program = programRef.current;\n const renderer = rendererRef.current;\n const gradTex = gradTexRef.current;\n if (!program || !renderer || !gradTex) return;\n\n program.uniforms.uIntensity.value = intensity ?? 1;\n program.uniforms.uSpeed.value = speed ?? 1;\n\n const animTypeMap: Record<AnimationType, number> = {\n rotate: 0,\n rotate3d: 1,\n hover: 2,\n };\n program.uniforms.uAnimType.value = animTypeMap[animationType ?? \"rotate\"];\n\n program.uniforms.uDistort.value = typeof distort === \"number\" ? distort : 0;\n\n const ox = toPx(offset?.x);\n const oy = toPx(offset?.y);\n program.uniforms.uOffset.value = [ox, oy];\n program.uniforms.uRayCount.value = Math.max(0, Math.floor(rayCount ?? 0));\n\n let count = 0;\n if (Array.isArray(colors) && colors.length > 0) {\n const gl = renderer.gl;\n const capped = colors.slice(0, 64);\n count = capped.length;\n const data = new Uint8Array(count * 4);\n for (let i = 0; i < count; i++) {\n const [r, g, b] = hexToRgb01(capped[i]);\n data[i * 4 + 0] = Math.round(r * 255);\n data[i * 4 + 1] = Math.round(g * 255);\n data[i * 4 + 2] = Math.round(b * 255);\n data[i * 4 + 3] = 255;\n }\n gradTex.image = data;\n gradTex.width = count;\n gradTex.height = 1;\n gradTex.minFilter = gl.LINEAR;\n gradTex.magFilter = gl.LINEAR;\n gradTex.wrapS = gl.CLAMP_TO_EDGE;\n gradTex.wrapT = gl.CLAMP_TO_EDGE;\n gradTex.flipY = false;\n gradTex.generateMipmaps = false;\n gradTex.format = gl.RGBA;\n gradTex.type = gl.UNSIGNED_BYTE;\n gradTex.needsUpdate = true;\n } else {\n count = 0;\n }\n program.uniforms.uColorCount.value = count;\n }, [intensity, speed, animationType, colors, distort, offset, rayCount]);\n\n return (\n <div className=\"w-full h-full relative overflow-hidden\" ref={containerRef} />\n );\n};\n\nexport default PrismaticBurst;\n`,\n \"components/glass/button\": `import { cn } from \"__UTILS_ALIAS__/cn\";\nimport { ButtonHTMLAttributes, forwardRef } from \"react\";\n\ninterface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: \"primary\" | \"secondary\" | \"ghost\" | \"gradient\" | \"glow-border\";\n size?: \"sm\" | \"md\" | \"lg\";\n children: React.ReactNode;\n}\n\nconst Button = forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, variant = \"primary\", size = \"md\", children, ...props }, ref) => {\n return (\n <button\n ref={ref}\n className={cn(\n \"inline-flex items-center justify-center gap-2 font-semibold transition-all duration-300 rounded-full focus:outline-none focus:ring-2 focus:ring-primary/50 disabled:opacity-50 disabled:cursor-not-allowed relative overflow-hidden\",\n {\n \"bg-gradient-to-r from-[#00D4FF] to-[#00FF88] text-black hover:shadow-[0_0_30px_rgba(0,229,160,0.4)] hover:-translate-y-0.5\":\n variant === \"gradient\",\n \"bg-[#00E5A0] text-black hover:bg-[#00C488] hover:shadow-[0_0_20px_rgba(0,229,160,0.4)] hover:-translate-y-0.5\":\n variant === \"primary\",\n \"bg-transparent text-white border border-white/10 hover:border-[#00D4FF]/50 hover:text-[#00E5A0] hover:shadow-[0_0_20px_rgba(0,212,255,0.2)] hover:-translate-y-0.5\":\n variant === \"secondary\",\n \"bg-transparent text-white hover:bg-white/5 hover:text-[#00E5A0]\":\n variant === \"ghost\",\n \"btn-glow-border text-white hover:-translate-y-0.5\":\n variant === \"glow-border\",\n },\n {\n \"px-4 py-2 text-sm\": size === \"sm\",\n \"px-6 py-3 text-base\": size === \"md\",\n \"px-8 py-4 text-lg\": size === \"lg\",\n },\n className\n )}\n {...props}\n >\n <span className=\"relative z-10 flex items-center gap-2\">{children}</span>\n </button>\n );\n }\n);\n\nButton.displayName = \"Button\";\n\nexport { Button };\n`,\n \"components/glass/card\": `import { cn } from \"__UTILS_ALIAS__/cn\";\nimport { HTMLAttributes, forwardRef } from \"react\";\n\ninterface CardProps extends HTMLAttributes<HTMLDivElement> {\n variant?: \"default\" | \"strong\" | \"gradient\" | \"outline\";\n hover?: boolean;\n children: React.ReactNode;\n}\n\nconst Card = forwardRef<HTMLDivElement, CardProps>(\n ({ className, variant = \"default\", hover = false, children, ...props }, ref) => {\n return (\n <div\n ref={ref}\n className={cn(\n \"rounded-2xl transition-all duration-300\",\n {\n \"bg-white/[0.06] backdrop-blur-xl border border-white/[0.08] shadow-lg shadow-black/10\":\n variant === \"default\",\n \"bg-white/[0.1] backdrop-blur-xl border border-white/[0.1] shadow-lg shadow-black/15\":\n variant === \"strong\",\n \"bg-gradient-to-br from-[#00D4FF]/[0.08] to-[#00FF88]/[0.05] backdrop-blur-xl border border-white/[0.1] shadow-lg shadow-black/15\":\n variant === \"gradient\",\n \"bg-transparent backdrop-blur-sm border border-white/[0.1] hover:border-[#00D4FF]/30\":\n variant === \"outline\",\n },\n hover && \"hover:bg-white/[0.1] hover:border-[#00D4FF]/20 hover:shadow-[0_8px_32px_rgba(0,212,255,0.1)] hover:-translate-y-1 cursor-pointer\",\n className\n )}\n {...props}\n >\n {children}\n </div>\n );\n }\n);\n\nCard.displayName = \"Card\";\n\nexport { Card };\n`,\n \"components/glass/modal\": `\"use client\";\n\nimport { cn } from \"__UTILS_ALIAS__/cn\";\nimport { useEffect, useCallback } from \"react\";\nimport { createPortal } from \"react-dom\";\n\ninterface ModalProps {\n isOpen: boolean;\n onClose: () => void;\n children: React.ReactNode;\n className?: string;\n}\n\nexport function Modal({ isOpen, onClose, children, className }: ModalProps) {\n const handleEscape = useCallback(\n (e: KeyboardEvent) => {\n if (e.key === \"Escape\") onClose();\n },\n [onClose]\n );\n\n useEffect(() => {\n if (isOpen) {\n document.addEventListener(\"keydown\", handleEscape);\n document.body.style.overflow = \"hidden\";\n }\n return () => {\n document.removeEventListener(\"keydown\", handleEscape);\n document.body.style.overflow = \"\";\n };\n }, [isOpen, handleEscape]);\n\n if (!isOpen) return null;\n\n return createPortal(\n <div className=\"fixed inset-0 z-50 flex items-end sm:items-center justify-center p-0 sm:p-4\">\n <div className=\"absolute inset-0 bg-black/80 backdrop-blur-sm animate-fade-in\" onClick={onClose} />\n <div className={cn(\n \"relative z-10 w-full sm:max-w-2xl max-h-[85vh] sm:max-h-[90vh] overflow-auto bg-[#0a0a0a] sm:glass-strong animate-slide-up rounded-t-2xl sm:rounded-2xl\",\n className\n )}>\n <button\n onClick={onClose}\n className=\"absolute top-3 sm:top-4 right-3 sm:right-4 w-9 sm:w-10 h-9 sm:h-10 flex items-center justify-center rounded-full bg-white/10 hover:bg-white/20 transition-colors text-gray-400 hover:text-white z-10\"\n aria-label=\"Close modal\"\n >\n <svg className=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n <div className=\"sm:hidden flex justify-center pt-3 pb-1\">\n <div className=\"w-10 h-1 rounded-full bg-white/20\" />\n </div>\n {children}\n </div>\n </div>,\n document.body\n );\n}\n`,\n \"components/glass/music-player\": `\"use client\";\n\nimport { useState, useRef, useEffect } from \"react\";\nimport { GlassSurface } from \"__COMPONENTS_ALIAS__/glass-surface\";\n\ninterface MusicPlayerProps {\n src: string;\n initialVolume?: number;\n}\n\nexport function MusicPlayer({ src, initialVolume = 0.5 }: MusicPlayerProps) {\n const [isPlaying, setIsPlaying] = useState(false);\n const [isDesktop, setIsDesktop] = useState(false);\n const audioRef = useRef<HTMLAudioElement>(null);\n\n useEffect(() => {\n if (audioRef.current) {\n audioRef.current.volume = initialVolume;\n }\n }, [initialVolume]);\n\n useEffect(() => {\n const handleResize = () => setIsDesktop(window.innerWidth >= 768);\n handleResize();\n window.addEventListener(\"resize\", handleResize);\n return () => window.removeEventListener(\"resize\", handleResize);\n }, []);\n\n const togglePlay = () => {\n if (audioRef.current) {\n if (isPlaying) {\n audioRef.current.pause();\n } else {\n audioRef.current.play();\n }\n setIsPlaying(!isPlaying);\n }\n };\n\n const buttonContent = (\n <>\n <div className=\"flex items-center gap-[3px] h-5\">\n {[0, 1, 2, 3, 4].map((i) => (\n <div\n key={i}\n className={\\`w-[3px] bg-gradient-to-t from-[#00D4FF] to-[#00FF88] rounded-full transition-all \\${\n isPlaying ? \"animate-soundwave\" : \"h-1\"\n }\\`}\n style={{ animationDelay: isPlaying ? \\`\\${i * 0.1}s\\` : undefined }}\n />\n ))}\n </div>\n <span className=\"text-sm text-white/60 hidden sm:inline\">\n {isPlaying ? \"ON\" : \"OFF\"}\n </span>\n </>\n );\n\n return (\n <>\n <audio ref={audioRef} src={src} loop />\n {!isDesktop && (\n <button\n onClick={togglePlay}\n className={\\`h-14 sm:h-16 w-14 sm:w-auto sm:px-4 flex items-center justify-center sm:justify-start gap-2 rounded-full bg-white/[0.08] backdrop-blur-xl border border-white/[0.08] hover:bg-white/[0.12] transition-all shadow-lg shadow-black/10 \\${\n !isPlaying ? \"animate-attention-ring\" : \"\"\n }\\`}\n aria-label={isPlaying ? \"Pause music\" : \"Play music\"}\n >\n {buttonContent}\n </button>\n )}\n {isDesktop && (\n <GlassSurface\n borderRadius={9999}\n backgroundOpacity={0.1}\n brightness={50}\n blur={11}\n displace={0.5}\n distortionScale={-180}\n redOffset={0}\n greenOffset={10}\n blueOffset={20}\n className={\\`cursor-pointer hover:scale-105 transition-transform \\${\n !isPlaying ? \"animate-attention-ring\" : \"\"\n }\\`}\n >\n <button\n onClick={togglePlay}\n className=\"h-14 sm:h-16 w-14 sm:w-auto sm:px-4 flex items-center justify-center sm:justify-start gap-2\"\n aria-label={isPlaying ? \"Pause music\" : \"Play music\"}\n >\n {buttonContent}\n </button>\n </GlassSurface>\n )}\n </>\n );\n}\n`,\n \"components/motion/countdown-timer\": `\"use client\";\n\nimport { cn } from \"__UTILS_ALIAS__/cn\";\nimport { useCountdown } from \"__HOOKS_ALIAS__/use-countdown\";\nimport { useMemo } from \"react\";\n\ninterface CountdownTimerProps {\n targetDate: Date;\n endDate?: Date;\n className?: string;\n labels?: {\n days?: string;\n hours?: string;\n mins?: string;\n secs?: string;\n };\n endedText?: [string, string];\n liveText?: string;\n todayText?: [string, string]; // [title, subtitle template with {time}]\n}\n\ntype CountdownState = \"normal\" | \"approaching\" | \"imminent\" | \"today-waiting\" | \"live\" | \"ended\";\n\nfunction getCountdownState(targetDate: Date, endDate?: Date): CountdownState {\n const now = new Date();\n const msUntilStart = targetDate.getTime() - now.getTime();\n const msUntilEnd = endDate ? endDate.getTime() - now.getTime() : msUntilStart;\n\n const oneDayMs = 24 * 60 * 60 * 1000;\n const sevenDaysMs = 7 * oneDayMs;\n\n if (msUntilEnd <= 0) return \"ended\";\n if (msUntilStart <= 0 && msUntilEnd > 0) return \"live\";\n\n const isSameDay =\n now.getFullYear() === targetDate.getFullYear() &&\n now.getMonth() === targetDate.getMonth() &&\n now.getDate() === targetDate.getDate();\n\n if (isSameDay && msUntilStart > 0) return \"today-waiting\";\n if (msUntilStart > 0 && msUntilStart <= oneDayMs) return \"imminent\";\n if (msUntilStart > 0 && msUntilStart <= sevenDaysMs) return \"approaching\";\n\n return \"normal\";\n}\n\nfunction TimeUnit({ value, label, state }: { value: number; label: string; state: CountdownState }) {\n const stateStyles = {\n normal: { text: \"gradient-text glow-text-gradient\", glass: \"glass\" },\n approaching: { text: \"animate-ember\", glass: \"glass animate-ember-glow\" },\n imminent: { text: \"text-rose-400 drop-shadow-[0_0_25px_rgba(251,113,133,0.8)] animate-pulse\", glass: \"glass border-rose-500/40 shadow-[0_0_40px_rgba(251,113,133,0.3)] animate-shake\" },\n \"today-waiting\": { text: \"text-emerald-400 drop-shadow-[0_0_20px_rgba(52,211,153,0.7)]\", glass: \"glass border-emerald-500/30 shadow-[0_0_30px_rgba(52,211,153,0.2)]\" },\n live: { text: \"gradient-text\", glass: \"glass\" },\n ended: { text: \"gradient-text\", glass: \"glass\" },\n };\n const styles = stateStyles[state];\n\n return (\n <div className=\"flex flex-col items-center\">\n <div className={cn(\"px-3 py-2 sm:px-5 sm:py-3 md:px-6 md:py-4 min-w-[56px] sm:min-w-[72px] md:min-w-[90px] transition-all duration-300\", styles.glass)}>\n <span className={cn(\"text-2xl sm:text-4xl md:text-5xl font-bold tabular-nums transition-all duration-300\", styles.text)} suppressHydrationWarning>\n {value.toString().padStart(2, \"0\")}\n </span>\n </div>\n <span className={cn(\"text-[10px] sm:text-xs md:text-sm mt-1.5 sm:mt-2 uppercase tracking-wider transition-colors duration-300\",\n state === \"approaching\" ? \"text-orange-500/80\" :\n state === \"imminent\" ? \"text-rose-400/70\" :\n state === \"today-waiting\" ? \"text-emerald-400/70\" :\n \"text-gray-400\"\n )}>\n {label}\n </span>\n </div>\n );\n}\n\nfunction Separator({ state }: { state: CountdownState }) {\n const colorClass =\n state === \"approaching\" ? \"text-orange-500 animate-ember\" :\n state === \"imminent\" ? \"text-rose-400 animate-pulse\" :\n state === \"today-waiting\" ? \"text-emerald-400\" :\n \"gradient-text\";\n\n return (\n <div className={cn(\"flex items-center text-xl sm:text-3xl md:text-4xl self-start mt-3 sm:mt-4 transition-colors duration-300\", colorClass)}>:</div>\n );\n}\n\nfunction LiveIndicator({ text }: { text: string }) {\n return (\n <div className=\"flex items-center justify-center gap-4 sm:gap-6\">\n <span className=\"relative flex h-5 w-5 sm:h-6 sm:w-6 md:h-8 md:w-8\">\n <span className=\"animate-ping absolute inline-flex h-full w-full rounded-full bg-red-500 opacity-75\"></span>\n <span className=\"relative inline-flex rounded-full h-5 w-5 sm:h-6 sm:w-6 md:h-8 md:w-8 bg-red-500 shadow-[0_0_20px_rgba(239,68,68,0.9)]\"></span>\n </span>\n <span className=\"text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-bold text-red-400 tracking-wider animate-pulse drop-shadow-[0_0_30px_rgba(239,68,68,0.8)]\">\n {text}\n </span>\n <span className=\"relative flex h-5 w-5 sm:h-6 sm:w-6 md:h-8 md:w-8\">\n <span className=\"animate-ping absolute inline-flex h-full w-full rounded-full bg-red-500 opacity-75\"></span>\n <span className=\"relative inline-flex rounded-full h-5 w-5 sm:h-6 sm:w-6 md:h-8 md:w-8 bg-red-500 shadow-[0_0_20px_rgba(239,68,68,0.9)]\"></span>\n </span>\n </div>\n );\n}\n\nexport function CountdownTimer({\n targetDate,\n endDate,\n className,\n labels = {},\n endedText = [\"Event\", \"Ended\"],\n liveText = \"LIVE NOW\",\n todayText,\n}: CountdownTimerProps) {\n const timeLeft = useCountdown(targetDate);\n const daysLabel = labels.days ?? \"Days\";\n const hoursLabel = labels.hours ?? \"Hours\";\n const minsLabel = labels.mins ?? \"Mins\";\n const secsLabel = labels.secs ?? \"Secs\";\n\n const state = useMemo(\n () => getCountdownState(targetDate, endDate),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [targetDate, endDate, timeLeft.total]\n );\n\n if (state === \"ended\") {\n return (\n <div className={cn(\"text-center flex flex-col items-center gap-1 sm:gap-2\", className)}>\n <span className=\"text-3xl sm:text-4xl md:text-5xl lg:text-6xl xl:text-7xl font-bold tracking-wide\"\n style={{ background: \"linear-gradient(135deg, #FFD700, #FFA500, #FFE066)\", backgroundClip: \"text\", WebkitBackgroundClip: \"text\", WebkitTextFillColor: \"transparent\", filter: \"drop-shadow(0 0 20px rgba(255,215,0,0.5))\" }}>\n {endedText[0]}\n </span>\n <span className=\"text-3xl sm:text-4xl md:text-5xl lg:text-6xl xl:text-7xl font-bold tracking-wide\"\n style={{ background: \"linear-gradient(135deg, #FFD700, #FFA500, #FFE066)\", backgroundClip: \"text\", WebkitBackgroundClip: \"text\", WebkitTextFillColor: \"transparent\", filter: \"drop-shadow(0 0 20px rgba(255,215,0,0.5))\" }}>\n {endedText[1]}\n </span>\n </div>\n );\n }\n\n if (state === \"live\") {\n return (\n <div className={cn(\"text-center\", className)}>\n <LiveIndicator text={liveText} />\n </div>\n );\n }\n\n if (state === \"today-waiting\") {\n return (\n <div className={cn(\"flex gap-1.5 sm:gap-3 md:gap-4 flex-wrap\", className)}>\n <TimeUnit value={timeLeft.hours} label={hoursLabel} state={state} />\n <Separator state={state} />\n <TimeUnit value={timeLeft.minutes} label={minsLabel} state={state} />\n <Separator state={state} />\n <TimeUnit value={timeLeft.seconds} label={secsLabel} state={state} />\n </div>\n );\n }\n\n return (\n <div className={cn(\"flex flex-col items-center\", className)}>\n <div className=\"flex gap-1.5 sm:gap-3 md:gap-4 flex-wrap justify-center\">\n <TimeUnit value={timeLeft.days} label={daysLabel} state={state} />\n <Separator state={state} />\n <TimeUnit value={timeLeft.hours} label={hoursLabel} state={state} />\n <Separator state={state} />\n <TimeUnit value={timeLeft.minutes} label={minsLabel} state={state} />\n <Separator state={state} />\n <TimeUnit value={timeLeft.seconds} label={secsLabel} state={state} />\n </div>\n </div>\n );\n}\n`,\n \"components/motion/counter\": `\"use client\";\n\nimport { useEffect, useState, useRef } from \"react\";\nimport { useScrollAnimation } from \"__HOOKS_ALIAS__/use-scroll-animation\";\nimport { cn } from \"__UTILS_ALIAS__/cn\";\n\ninterface CounterProps {\n end: number;\n suffix?: string;\n duration?: number;\n className?: string;\n}\n\nconst CYBER_CHARS = \"ア イ ウ エ オ カ キ ク ケ コ 0 1 2 3 4 5 6 7 8 9 A B C D E F # \\$ % & @\".split(\" \");\n\nexport function Counter({ end, suffix = \"\", duration = 2500, className }: CounterProps) {\n const [displayText, setDisplayText] = useState(\"\");\n const [ref, isInView] = useScrollAnimation<HTMLSpanElement>({ threshold: 0.5 });\n const hasAnimated = useRef(false);\n\n useEffect(() => {\n if (isInView && !hasAnimated.current) {\n hasAnimated.current = true;\n\n const targetText = end.toString();\n const totalLength = targetText.length;\n\n const lockedChars: boolean[] = new Array(totalLength).fill(false);\n const currentChars: string[] = new Array(totalLength).fill(\"\");\n\n for (let i = 0; i < totalLength; i++) {\n currentChars[i] = CYBER_CHARS[Math.floor(Math.random() * CYBER_CHARS.length)];\n }\n setDisplayText(currentChars.join(\"\"));\n\n const intervals: ReturnType<typeof setInterval>[] = [];\n const timeouts: ReturnType<typeof setTimeout>[] = [];\n\n const scrambleInterval = setInterval(() => {\n for (let i = 0; i < totalLength; i++) {\n if (!lockedChars[i]) {\n currentChars[i] = CYBER_CHARS[Math.floor(Math.random() * CYBER_CHARS.length)];\n }\n }\n setDisplayText(currentChars.join(\"\"));\n }, 50);\n intervals.push(scrambleInterval);\n\n const lockDelay = duration / (totalLength + 1);\n\n for (let i = 0; i < totalLength; i++) {\n const lockTimeout = setTimeout(() => {\n let findCount = 0;\n const findInterval = setInterval(() => {\n currentChars[i] = CYBER_CHARS[Math.floor(Math.random() * CYBER_CHARS.length)];\n setDisplayText(currentChars.join(\"\"));\n findCount++;\n if (findCount >= 5) {\n clearInterval(findInterval);\n lockedChars[i] = true;\n currentChars[i] = targetText[i];\n setDisplayText(currentChars.join(\"\"));\n }\n }, 60);\n intervals.push(findInterval);\n }, lockDelay * (i + 1));\n timeouts.push(lockTimeout);\n }\n\n const finalTimeout = setTimeout(() => {\n clearInterval(scrambleInterval);\n setDisplayText(targetText);\n }, duration + 100);\n timeouts.push(finalTimeout);\n\n return () => {\n intervals.forEach(clearInterval);\n timeouts.forEach(clearTimeout);\n };\n }\n }, [isInView, end, duration]);\n\n return (\n <span ref={ref} className={cn(\"tabular-nums\", className)}>\n {displayText}{suffix}\n </span>\n );\n}\n`,\n \"components/motion/light-rays\": `\"use client\";\n\nimport { useRef, useEffect, useState } from 'react';\nimport { Renderer, Program, Triangle, Mesh } from 'ogl';\n\nexport type RaysOrigin =\n | 'top-center'\n | 'top-left'\n | 'top-right'\n | 'right'\n | 'left'\n | 'bottom-center'\n | 'bottom-right'\n | 'bottom-left';\n\ninterface LightRaysProps {\n raysOrigin?: RaysOrigin;\n raysColor?: string;\n raysSpeed?: number;\n lightSpread?: number;\n rayLength?: number;\n pulsating?: boolean;\n fadeDistance?: number;\n saturation?: number;\n followMouse?: boolean;\n mouseInfluence?: number;\n noiseAmount?: number;\n distortion?: number;\n className?: string;\n}\n\nconst DEFAULT_COLOR = '#ffffff';\n\nconst hexToRgb = (hex: string): [number, number, number] => {\n const m = /^#?([a-f\\\\d]{2})([a-f\\\\d]{2})([a-f\\\\d]{2})\\$/i.exec(hex);\n return m ? [parseInt(m[1], 16) / 255, parseInt(m[2], 16) / 255, parseInt(m[3], 16) / 255] : [1, 1, 1];\n};\n\nconst getAnchorAndDir = (\n origin: RaysOrigin,\n w: number,\n h: number\n): { anchor: [number, number]; dir: [number, number] } => {\n const outside = 0.2;\n switch (origin) {\n case 'top-left':\n return { anchor: [0, -outside * h], dir: [0, 1] };\n case 'top-right':\n return { anchor: [w, -outside * h], dir: [0, 1] };\n case 'left':\n return { anchor: [-outside * w, 0.5 * h], dir: [1, 0] };\n case 'right':\n return { anchor: [(1 + outside) * w, 0.5 * h], dir: [-1, 0] };\n case 'bottom-left':\n return { anchor: [0, (1 + outside) * h], dir: [0, -1] };\n case 'bottom-center':\n return { anchor: [0.5 * w, (1 + outside) * h], dir: [0, -1] };\n case 'bottom-right':\n return { anchor: [w, (1 + outside) * h], dir: [0, -1] };\n default: // \"top-center\"\n return { anchor: [0.5 * w, -outside * h], dir: [0, 1] };\n }\n};\n\ntype Vec2 = [number, number];\ntype Vec3 = [number, number, number];\n\ninterface Uniforms {\n iTime: { value: number };\n iResolution: { value: Vec2 };\n rayPos: { value: Vec2 };\n rayDir: { value: Vec2 };\n raysColor: { value: Vec3 };\n raysSpeed: { value: number };\n lightSpread: { value: number };\n rayLength: { value: number };\n pulsating: { value: number };\n fadeDistance: { value: number };\n saturation: { value: number };\n mousePos: { value: Vec2 };\n mouseInfluence: { value: number };\n noiseAmount: { value: number };\n distortion: { value: number };\n}\n\nconst LightRays: React.FC<LightRaysProps> = ({\n raysOrigin = 'top-center',\n raysColor = DEFAULT_COLOR,\n raysSpeed = 1,\n lightSpread = 1,\n rayLength = 2,\n pulsating = false,\n fadeDistance = 1.0,\n saturation = 1.0,\n followMouse = true,\n mouseInfluence = 0.1,\n noiseAmount = 0.0,\n distortion = 0.0,\n className = ''\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const uniformsRef = useRef<Uniforms | null>(null);\n const rendererRef = useRef<Renderer | null>(null);\n const mouseRef = useRef({ x: 0.5, y: 0.5 });\n const smoothMouseRef = useRef({ x: 0.5, y: 0.5 });\n const animationIdRef = useRef<number | null>(null);\n const meshRef = useRef<Mesh | null>(null);\n const cleanupFunctionRef = useRef<(() => void) | null>(null);\n const [isVisible, setIsVisible] = useState(false);\n const observerRef = useRef<IntersectionObserver | null>(null);\n\n useEffect(() => {\n if (!containerRef.current) return;\n\n observerRef.current = new IntersectionObserver(\n entries => {\n const entry = entries[0];\n setIsVisible(entry.isIntersecting);\n },\n { threshold: 0.1 }\n );\n\n observerRef.current.observe(containerRef.current);\n\n return () => {\n if (observerRef.current) {\n observerRef.current.disconnect();\n observerRef.current = null;\n }\n };\n }, []);\n\n useEffect(() => {\n if (!isVisible || !containerRef.current) return;\n\n if (cleanupFunctionRef.current) {\n cleanupFunctionRef.current();\n cleanupFunctionRef.current = null;\n }\n\n const initializeWebGL = async () => {\n if (!containerRef.current) return;\n\n await new Promise(resolve => setTimeout(resolve, 10));\n\n if (!containerRef.current) return;\n\n const renderer = new Renderer({\n dpr: Math.min(window.devicePixelRatio, 2),\n alpha: true\n });\n rendererRef.current = renderer;\n\n const gl = renderer.gl;\n gl.canvas.style.width = '100%';\n gl.canvas.style.height = '100%';\n\n while (containerRef.current.firstChild) {\n containerRef.current.removeChild(containerRef.current.firstChild);\n }\n containerRef.current.appendChild(gl.canvas);\n\n const vert = \\`\nattribute vec2 position;\nvarying vec2 vUv;\nvoid main() {\n vUv = position * 0.5 + 0.5;\n gl_Position = vec4(position, 0.0, 1.0);\n}\\`;\n\n const frag = \\`precision highp float;\n\nuniform float iTime;\nuniform vec2 iResolution;\n\nuniform vec2 rayPos;\nuniform vec2 rayDir;\nuniform vec3 raysColor;\nuniform float raysSpeed;\nuniform float lightSpread;\nuniform float rayLength;\nuniform float pulsating;\nuniform float fadeDistance;\nuniform float saturation;\nuniform vec2 mousePos;\nuniform float mouseInfluence;\nuniform float noiseAmount;\nuniform float distortion;\n\nvarying vec2 vUv;\n\nfloat noise(vec2 st) {\n return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123);\n}\n\nfloat rayStrength(vec2 raySource, vec2 rayRefDirection, vec2 coord,\n float seedA, float seedB, float speed) {\n vec2 sourceToCoord = coord - raySource;\n vec2 dirNorm = normalize(sourceToCoord);\n float cosAngle = dot(dirNorm, rayRefDirection);\n\n float distortedAngle = cosAngle + distortion * sin(iTime * 2.0 + length(sourceToCoord) * 0.01) * 0.2;\n\n float spreadFactor = pow(max(distortedAngle, 0.0), 1.0 / max(lightSpread, 0.001));\n\n float distance = length(sourceToCoord);\n float maxDistance = iResolution.x * rayLength;\n float lengthFalloff = clamp((maxDistance - distance) / maxDistance, 0.0, 1.0);\n\n float fadeFalloff = clamp((iResolution.x * fadeDistance - distance) / (iResolution.x * fadeDistance), 0.5, 1.0);\n float pulse = pulsating > 0.5 ? (0.8 + 0.2 * sin(iTime * speed * 3.0)) : 1.0;\n\n float baseStrength = clamp(\n (0.45 + 0.15 * sin(distortedAngle * seedA + iTime * speed)) +\n (0.3 + 0.2 * cos(-distortedAngle * seedB + iTime * speed)),\n 0.0, 1.0\n );\n\n return baseStrength * lengthFalloff * fadeFalloff * spreadFactor * pulse;\n}\n\nvoid mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 coord = vec2(fragCoord.x, iResolution.y - fragCoord.y);\n\n vec2 finalRayDir = rayDir;\n if (mouseInfluence > 0.0) {\n vec2 mouseScreenPos = mousePos * iResolution.xy;\n vec2 mouseDirection = normalize(mouseScreenPos - rayPos);\n finalRayDir = normalize(mix(rayDir, mouseDirection, mouseInfluence));\n }\n\n vec4 rays1 = vec4(1.0) *\n rayStrength(rayPos, finalRayDir, coord, 36.2214, 21.11349,\n 1.5 * raysSpeed);\n vec4 rays2 = vec4(1.0) *\n rayStrength(rayPos, finalRayDir, coord, 22.3991, 18.0234,\n 1.1 * raysSpeed);\n\n fragColor = rays1 * 0.5 + rays2 * 0.4;\n\n if (noiseAmount > 0.0) {\n float n = noise(coord * 0.01 + iTime * 0.1);\n fragColor.rgb *= (1.0 - noiseAmount + noiseAmount * n);\n }\n\n float brightness = 1.0 - (coord.y / iResolution.y);\n fragColor.x *= 0.1 + brightness * 0.8;\n fragColor.y *= 0.3 + brightness * 0.6;\n fragColor.z *= 0.5 + brightness * 0.5;\n\n if (saturation != 1.0) {\n float gray = dot(fragColor.rgb, vec3(0.299, 0.587, 0.114));\n fragColor.rgb = mix(vec3(gray), fragColor.rgb, saturation);\n }\n\n fragColor.rgb *= raysColor;\n}\n\nvoid main() {\n vec4 color;\n mainImage(color, gl_FragCoord.xy);\n gl_FragColor = color;\n}\\`;\n\n const uniforms: Uniforms = {\n iTime: { value: 0 },\n iResolution: { value: [1, 1] },\n\n rayPos: { value: [0, 0] },\n rayDir: { value: [0, 1] },\n\n raysColor: { value: hexToRgb(raysColor) },\n raysSpeed: { value: raysSpeed },\n lightSpread: { value: lightSpread },\n rayLength: { value: rayLength },\n pulsating: { value: pulsating ? 1.0 : 0.0 },\n fadeDistance: { value: fadeDistance },\n saturation: { value: saturation },\n mousePos: { value: [0.5, 0.5] },\n mouseInfluence: { value: mouseInfluence },\n noiseAmount: { value: noiseAmount },\n distortion: { value: distortion }\n };\n uniformsRef.current = uniforms;\n\n const geometry = new Triangle(gl);\n const program = new Program(gl, {\n vertex: vert,\n fragment: frag,\n uniforms\n });\n const mesh = new Mesh(gl, { geometry, program });\n meshRef.current = mesh;\n\n const updatePlacement = () => {\n if (!containerRef.current || !renderer) return;\n\n renderer.dpr = Math.min(window.devicePixelRatio, 2);\n\n const { clientWidth: wCSS, clientHeight: hCSS } = containerRef.current;\n renderer.setSize(wCSS, hCSS);\n\n const dpr = renderer.dpr;\n const w = wCSS * dpr;\n const h = hCSS * dpr;\n\n uniforms.iResolution.value = [w, h];\n\n const { anchor, dir } = getAnchorAndDir(raysOrigin, w, h);\n uniforms.rayPos.value = anchor;\n uniforms.rayDir.value = dir;\n };\n\n const loop = (t: number) => {\n if (!rendererRef.current || !uniformsRef.current || !meshRef.current) {\n return;\n }\n\n uniforms.iTime.value = t * 0.001;\n\n if (followMouse && mouseInfluence > 0.0) {\n const smoothing = 0.92;\n\n smoothMouseRef.current.x = smoothMouseRef.current.x * smoothing + mouseRef.current.x * (1 - smoothing);\n smoothMouseRef.current.y = smoothMouseRef.current.y * smoothing + mouseRef.current.y * (1 - smoothing);\n\n uniforms.mousePos.value = [smoothMouseRef.current.x, smoothMouseRef.current.y];\n }\n\n try {\n renderer.render({ scene: mesh });\n animationIdRef.current = requestAnimationFrame(loop);\n } catch (error) {\n console.warn('WebGL rendering error:', error);\n return;\n }\n };\n\n window.addEventListener('resize', updatePlacement);\n updatePlacement();\n animationIdRef.current = requestAnimationFrame(loop);\n\n cleanupFunctionRef.current = () => {\n if (animationIdRef.current) {\n cancelAnimationFrame(animationIdRef.current);\n animationIdRef.current = null;\n }\n\n window.removeEventListener('resize', updatePlacement);\n\n if (renderer) {\n try {\n const canvas = renderer.gl.canvas;\n const loseContextExt = renderer.gl.getExtension('WEBGL_lose_context');\n if (loseContextExt) {\n loseContextExt.loseContext();\n }\n\n if (canvas && canvas.parentNode) {\n canvas.parentNode.removeChild(canvas);\n }\n } catch (error) {\n console.warn('Error during WebGL cleanup:', error);\n }\n }\n\n rendererRef.current = null;\n uniformsRef.current = null;\n meshRef.current = null;\n };\n };\n\n initializeWebGL();\n\n return () => {\n if (cleanupFunctionRef.current) {\n cleanupFunctionRef.current();\n cleanupFunctionRef.current = null;\n }\n };\n }, [\n isVisible,\n raysOrigin,\n raysColor,\n raysSpeed,\n lightSpread,\n rayLength,\n pulsating,\n fadeDistance,\n saturation,\n followMouse,\n mouseInfluence,\n noiseAmount,\n distortion\n ]);\n\n useEffect(() => {\n if (!uniformsRef.current || !containerRef.current || !rendererRef.current) return;\n\n const u = uniformsRef.current;\n const renderer = rendererRef.current;\n\n u.raysColor.value = hexToRgb(raysColor);\n u.raysSpeed.value = raysSpeed;\n u.lightSpread.value = lightSpread;\n u.rayLength.value = rayLength;\n u.pulsating.value = pulsating ? 1.0 : 0.0;\n u.fadeDistance.value = fadeDistance;\n u.saturation.value = saturation;\n u.mouseInfluence.value = mouseInfluence;\n u.noiseAmount.value = noiseAmount;\n u.distortion.value = distortion;\n\n const { clientWidth: wCSS, clientHeight: hCSS } = containerRef.current;\n const dpr = renderer.dpr;\n const { anchor, dir } = getAnchorAndDir(raysOrigin, wCSS * dpr, hCSS * dpr);\n u.rayPos.value = anchor;\n u.rayDir.value = dir;\n }, [\n raysColor,\n raysSpeed,\n lightSpread,\n raysOrigin,\n rayLength,\n pulsating,\n fadeDistance,\n saturation,\n mouseInfluence,\n noiseAmount,\n distortion\n ]);\n\n useEffect(() => {\n const handleMouseMove = (e: MouseEvent) => {\n if (!containerRef.current || !rendererRef.current) return;\n const rect = containerRef.current.getBoundingClientRect();\n const x = (e.clientX - rect.left) / rect.width;\n const y = (e.clientY - rect.top) / rect.height;\n mouseRef.current = { x, y };\n };\n\n if (followMouse) {\n window.addEventListener('mousemove', handleMouseMove);\n return () => window.removeEventListener('mousemove', handleMouseMove);\n }\n }, [followMouse]);\n\n return (\n <div\n ref={containerRef}\n className={\\`w-full h-full pointer-events-none z-[3] overflow-hidden relative \\${className}\\`.trim()}\n />\n );\n};\n\nexport default LightRays;\n`,\n \"components/motion/shiny-text\": `\"use client\";\n\nimport React, { useState, useCallback, useEffect, useRef } from 'react';\nimport { motion, useMotionValue, useAnimationFrame, useTransform } from 'motion/react';\n\ninterface ShinyTextProps {\n text: string;\n disabled?: boolean;\n speed?: number;\n className?: string;\n color?: string;\n shineColor?: string;\n spread?: number;\n yoyo?: boolean;\n pauseOnHover?: boolean;\n direction?: 'left' | 'right';\n delay?: number;\n}\n\nconst ShinyText: React.FC<ShinyTextProps> = ({\n text,\n disabled = false,\n speed = 2,\n className = '',\n color = '#b5b5b5',\n shineColor = '#ffffff',\n spread = 120,\n yoyo = false,\n pauseOnHover = false,\n direction = 'left',\n delay = 0\n}) => {\n const [isPaused, setIsPaused] = useState(false);\n const progress = useMotionValue(0);\n const elapsedRef = useRef(0);\n const lastTimeRef = useRef<number | null>(null);\n const directionRef = useRef(direction === 'left' ? 1 : -1);\n\n const animationDuration = speed * 1000;\n const delayDuration = delay * 1000;\n\n useAnimationFrame(time => {\n if (disabled || isPaused) {\n lastTimeRef.current = null;\n return;\n }\n\n if (lastTimeRef.current === null) {\n lastTimeRef.current = time;\n return;\n }\n\n const deltaTime = time - lastTimeRef.current;\n lastTimeRef.current = time;\n\n elapsedRef.current += deltaTime;\n\n if (yoyo) {\n const cycleDuration = animationDuration + delayDuration;\n const fullCycle = cycleDuration * 2;\n const cycleTime = elapsedRef.current % fullCycle;\n\n if (cycleTime < animationDuration) {\n const p = (cycleTime / animationDuration) * 100;\n progress.set(directionRef.current === 1 ? p : 100 - p);\n } else if (cycleTime < cycleDuration) {\n progress.set(directionRef.current === 1 ? 100 : 0);\n } else if (cycleTime < cycleDuration + animationDuration) {\n const reverseTime = cycleTime - cycleDuration;\n const p = 100 - (reverseTime / animationDuration) * 100;\n progress.set(directionRef.current === 1 ? p : 100 - p);\n } else {\n progress.set(directionRef.current === 1 ? 0 : 100);\n }\n } else {\n const cycleDuration = animationDuration + delayDuration;\n const cycleTime = elapsedRef.current % cycleDuration;\n\n if (cycleTime < animationDuration) {\n const p = (cycleTime / animationDuration) * 100;\n progress.set(directionRef.current === 1 ? p : 100 - p);\n } else {\n progress.set(directionRef.current === 1 ? 100 : 0);\n }\n }\n });\n\n useEffect(() => {\n directionRef.current = direction === 'left' ? 1 : -1;\n elapsedRef.current = 0;\n progress.set(0);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [direction]);\n\n const backgroundPosition = useTransform(progress, p => \\`\\${150 - p * 2}% center\\`);\n\n const handleMouseEnter = useCallback(() => {\n if (pauseOnHover) setIsPaused(true);\n }, [pauseOnHover]);\n\n const handleMouseLeave = useCallback(() => {\n if (pauseOnHover) setIsPaused(false);\n }, [pauseOnHover]);\n\n const gradientStyle: React.CSSProperties = {\n backgroundImage: \\`linear-gradient(\\${spread}deg, \\${color} 0%, \\${color} 35%, \\${shineColor} 50%, \\${color} 65%, \\${color} 100%)\\`,\n backgroundSize: '200% auto',\n WebkitBackgroundClip: 'text',\n backgroundClip: 'text',\n WebkitTextFillColor: 'transparent'\n };\n\n return (\n <motion.span\n className={\\`inline-block \\${className}\\`}\n style={{ ...gradientStyle, backgroundPosition }}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n >\n {text}\n </motion.span>\n );\n};\n\nexport default ShinyText;\n`,\n \"hooks/use-countdown\": `\"use client\";\n\nimport { useState, useEffect, useCallback } from \"react\";\n\ninterface TimeLeft {\n days: number;\n hours: number;\n minutes: number;\n seconds: number;\n total: number;\n}\n\nexport function useCountdown(targetDate: Date): TimeLeft {\n const calculateTimeLeft = useCallback((): TimeLeft => {\n const difference = targetDate.getTime() - new Date().getTime();\n\n if (difference <= 0) {\n return { days: 0, hours: 0, minutes: 0, seconds: 0, total: 0 };\n }\n\n return {\n days: Math.floor(difference / (1000 * 60 * 60 * 24)),\n hours: Math.floor((difference / (1000 * 60 * 60)) % 24),\n minutes: Math.floor((difference / 1000 / 60) % 60),\n seconds: Math.floor((difference / 1000) % 60),\n total: difference,\n };\n }, [targetDate]);\n\n const [timeLeft, setTimeLeft] = useState<TimeLeft>(calculateTimeLeft);\n\n useEffect(() => {\n const timer = setInterval(() => {\n setTimeLeft(calculateTimeLeft());\n }, 1000);\n\n return () => clearInterval(timer);\n }, [calculateTimeLeft]);\n\n return timeLeft;\n}\n`,\n \"hooks/use-media-query\": `\"use client\";\n\nimport { useState, useEffect } from \"react\";\n\nexport function useMediaQuery(query: string): boolean {\n const [matches, setMatches] = useState(false);\n\n useEffect(() => {\n const media = window.matchMedia(query);\n setMatches(media.matches);\n\n const listener = (event: MediaQueryListEvent) => {\n setMatches(event.matches);\n };\n\n media.addEventListener(\"change\", listener);\n return () => media.removeEventListener(\"change\", listener);\n }, [query]);\n\n return matches;\n}\n\nexport function useIsMobile(): boolean {\n return useMediaQuery(\"(max-width: 767px)\");\n}\n\nexport function useIsTablet(): boolean {\n return useMediaQuery(\"(min-width: 768px) and (max-width: 1023px)\");\n}\n\nexport function useIsDesktop(): boolean {\n return useMediaQuery(\"(min-width: 1024px)\");\n}\n`,\n \"hooks/use-scroll-animation\": `\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\n\ninterface UseScrollAnimationOptions {\n threshold?: number;\n rootMargin?: string;\n triggerOnce?: boolean;\n}\n\nexport function useScrollAnimation<T extends HTMLElement>(\n options: UseScrollAnimationOptions = {}\n): [React.RefObject<T | null>, boolean] {\n const { threshold = 0.1, rootMargin = \"0px\", triggerOnce = true } = options;\n const ref = useRef<T | null>(null);\n const [isInView, setIsInView] = useState(false);\n\n useEffect(() => {\n const element = ref.current;\n if (!element) return;\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n setIsInView(true);\n if (triggerOnce) {\n observer.unobserve(element);\n }\n } else if (!triggerOnce) {\n setIsInView(false);\n }\n },\n { threshold, rootMargin }\n );\n\n observer.observe(element);\n\n return () => observer.disconnect();\n }, [threshold, rootMargin, triggerOnce]);\n\n return [ref, isInView];\n}\n`,\n \"utils/cn\": `import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n`\n};\n","import fs from \"fs-extra\";\nimport { logger } from \"./logger.js\";\n\nconst MARKER_START = (name: string) => `/* glintkit:${name} - start */`;\nconst MARKER_END = (name: string) => `/* glintkit:${name} - end */`;\n\nexport function injectCSSPreset(\n cssFilePath: string,\n presetName: string,\n presetContent: string\n): void {\n if (!fs.existsSync(cssFilePath)) {\n logger.warn(`CSS file not found: ${cssFilePath}. Skipping CSS injection.`);\n return;\n }\n\n let css = fs.readFileSync(cssFilePath, \"utf-8\");\n const startMarker = MARKER_START(presetName);\n const endMarker = MARKER_END(presetName);\n\n // Check if already injected\n if (css.includes(startMarker)) {\n // Replace existing preset\n const startIdx = css.indexOf(startMarker);\n const endIdx = css.indexOf(endMarker);\n if (endIdx !== -1) {\n css = css.slice(0, startIdx) + startMarker + \"\\n\" + presetContent + \"\\n\" + endMarker + css.slice(endIdx + endMarker.length);\n fs.writeFileSync(cssFilePath, css, \"utf-8\");\n return;\n }\n }\n\n // Append to end of file\n const block = `\\n${startMarker}\\n${presetContent}\\n${endMarker}\\n`;\n css += block;\n fs.writeFileSync(cssFilePath, css, \"utf-8\");\n}\n","export const CSS_PRESETS: Record<string, string> = {\n glass: `:root {\n --glass-bg: rgba(255, 255, 255, 0.06);\n --glass-bg-strong: rgba(255, 255, 255, 0.1);\n --glass-border: rgba(255, 255, 255, 0.1);\n --glass-shadow: rgba(0, 0, 0, 0.2);\n}\n\n.glass {\n background: var(--glass-bg);\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n border: 1px solid var(--glass-border);\n border-radius: 16px;\n}\n\n.glass-strong {\n background: var(--glass-bg-strong);\n backdrop-filter: blur(20px);\n -webkit-backdrop-filter: blur(20px);\n border: 1px solid var(--glass-border);\n border-radius: 1rem;\n box-shadow: 0 8px 32px var(--glass-shadow);\n}\n\n.glass-gradient {\n background: linear-gradient(135deg, rgba(0, 212, 255, 0.08), rgba(0, 255, 136, 0.05));\n backdrop-filter: blur(20px);\n -webkit-backdrop-filter: blur(20px);\n border: 1px solid rgba(255, 255, 255, 0.1);\n border-radius: 1rem;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);\n}\n\n@media (max-width: 768px) {\n .glass {\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n }\n .glass-strong {\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n }\n .glass-gradient {\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n }\n}`,\n\n \"gradient-text\": `.gradient-text {\n background: linear-gradient(135deg, #00D4FF, #00FF88);\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent;\n background-clip: text;\n}\n\n@keyframes text-flow {\n 0% { background-position: 0% 50%; }\n 100% { background-position: 200% 50%; }\n}\n\n.gradient-text-animated {\n background: linear-gradient(90deg, #00D4FF, #00FF88, #00D4FF, #00FF88);\n background-size: 200% 100%;\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent;\n background-clip: text;\n animation: text-flow 3s linear infinite;\n}`,\n\n \"glow-border\": `@keyframes border-flow {\n 0% { background-position: 0% 50%; }\n 100% { background-position: 200% 50%; }\n}\n\n.btn-glow-border {\n position: relative;\n background: var(--background, #000);\n z-index: 1;\n}\n\n.btn-glow-border::before {\n content: '';\n position: absolute;\n inset: -2px;\n border-radius: 9999px;\n background: linear-gradient(90deg, rgba(255,255,255,0.1), rgba(255,255,255,0.1), #00D4FF, #00FF88, rgba(255,255,255,0.1), rgba(255,255,255,0.1));\n background-size: 200% 100%;\n z-index: -2;\n animation: border-flow 3s linear infinite;\n}\n\n.btn-glow-border::after {\n content: '';\n position: absolute;\n inset: 1px;\n border-radius: 9999px;\n background: var(--background, #000);\n z-index: -1;\n}\n\n.btn-glow-border:hover::before {\n animation-duration: 1.5s;\n filter: blur(3px);\n}\n\n.btn-glow-border:hover {\n box-shadow: 0 0 30px rgba(0, 212, 255, 0.4), 0 0 60px rgba(0, 255, 136, 0.2);\n}`,\n\n animations: `@keyframes float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-10px); } }\n@keyframes pulse-glow { 0%, 100% { box-shadow: 0 0 20px rgba(0, 212, 255, 0.3), 0 0 40px rgba(0, 255, 136, 0.2); } 50% { box-shadow: 0 0 40px rgba(0, 212, 255, 0.6), 0 0 60px rgba(0, 255, 136, 0.4); } }\n@keyframes slide-up { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }\n@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }\n@keyframes soundwave { 0%, 100% { height: 4px; } 50% { height: 16px; } }\n@keyframes attention-ring { 0%, 100% { box-shadow: 0 0 0 0 rgba(0, 212, 255, 0.4), 0 0 0 0 rgba(0, 255, 136, 0.3); } 50% { box-shadow: 0 0 0 6px rgba(0, 212, 255, 0), 0 0 0 12px rgba(0, 255, 136, 0); } }\n@keyframes countdown-shake { 0%, 100% { transform: translateX(0); } 10%, 30%, 50%, 70%, 90% { transform: translateX(-1px); } 20%, 40%, 60%, 80% { transform: translateX(1px); } }\n\n.animate-float { animation: float 3s ease-in-out infinite; }\n.animate-pulse-glow { animation: pulse-glow 2s ease-in-out infinite; }\n.animate-slide-up { animation: slide-up 0.6s ease-out forwards; }\n.animate-fade-in { animation: fade-in 0.6s ease-out forwards; }\n.animate-soundwave { animation: soundwave 0.6s ease-in-out infinite; }\n.animate-attention-ring { animation: attention-ring 2s ease-in-out infinite; }\n.animate-shake { animation: countdown-shake 0.5s ease-in-out infinite; }`,\n\n \"holo-card\": `/* Overlay Background */\n.holo-card-overlay {\n position: fixed;\n inset: 0;\n z-index: 100;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.85);\n backdrop-filter: blur(12px);\n}\n\n/* Card Container - 3D 공간 설정 */\n.holo-card-container {\n perspective: 600px;\n transform-origin: center;\n}\n\n/* Card Rotator - 3D 회전 + 글로우 */\n.holo-card {\n --glow: #69d1e9;\n --mx: 50%;\n --my: 50%;\n --rx: 0deg;\n --ry: 0deg;\n --posx: 50%;\n --posy: 50%;\n --hyp: 0.5;\n --o: 1;\n\n position: relative;\n width: 320px;\n max-width: 85vw;\n aspect-ratio: 2.5/3.5;\n border-radius: 16px;\n -webkit-transform-style: preserve-3d;\n transform-style: preserve-3d;\n -webkit-transform: rotateY(var(--rx)) rotateX(var(--ry));\n transform: rotateY(var(--rx)) rotateX(var(--ry));\n transition: transform 0.15s ease-out;\n will-change: transform;\n\n /* Active glow effect */\n box-shadow:\n 0 0 10px 0px var(--glow),\n 0 0 10px 0px var(--glow),\n 0 0 30px 0px var(--glow),\n 0px 10px 20px -5px rgba(0, 0, 0, 0.5);\n}\n\n@media (min-width: 640px) {\n .holo-card {\n width: 360px;\n }\n}\n\n@media (min-width: 768px) {\n .holo-card {\n width: 400px;\n }\n}\n\n/* Card Content Layer */\n.holo-card__content {\n position: relative;\n z-index: 10;\n border-radius: 16px;\n overflow: hidden;\n background: linear-gradient(135deg, rgba(20, 20, 30, 0.95), rgba(10, 10, 20, 0.98));\n width: 100%;\n height: 100%;\n /* Fix for iOS Safari - ensure content is visible */\n -webkit-transform: translateZ(0);\n transform: translateZ(0);\n}\n\n/* Inner Frame Border - Pokemon Card Style */\n.holo-card__inner-frame {\n --frame-color: #C0C0C0;\n\n position: absolute;\n inset: 0;\n border-radius: 16px;\n pointer-events: none;\n z-index: 50;\n\n /* Thick inner border - inset shadow draws inside */\n box-shadow:\n inset 0 0 0 10px var(--frame-color),\n inset 0 0 20px 5px var(--frame-color);\n}\n\n@keyframes frame-shimmer {\n 0%, 100% {\n background-position: -200% 0;\n }\n 50% {\n background-position: 200% 0;\n }\n}\n\n/* Shine Layer - 홀로그래픽 무지개 효과 */\n.holo-card__shine {\n --space: 5%;\n --angle: 133deg;\n\n position: absolute;\n inset: 0;\n border-radius: 16px;\n pointer-events: none;\n z-index: 20;\n\n background-image:\n /* Rainbow gradient */\n repeating-linear-gradient(\n 0deg,\n rgb(255, 119, 115) calc(var(--space) * 1),\n rgba(255, 237, 95, 1) calc(var(--space) * 2),\n rgba(168, 255, 95, 1) calc(var(--space) * 3),\n rgba(131, 255, 247, 1) calc(var(--space) * 4),\n rgba(120, 148, 255, 1) calc(var(--space) * 5),\n rgb(216, 117, 255) calc(var(--space) * 6),\n rgb(255, 119, 115) calc(var(--space) * 7)\n ),\n /* Diagonal shine lines */\n repeating-linear-gradient(\n var(--angle),\n #0e152e 0%,\n hsl(180, 10%, 60%) 3.8%,\n hsl(180, 29%, 66%) 4.5%,\n hsl(180, 10%, 60%) 5.2%,\n #0e152e 10%,\n #0e152e 12%\n ),\n /* Radial highlight following mouse */\n radial-gradient(\n farthest-corner circle at var(--mx) var(--my),\n rgba(255, 255, 255, 0.8) 10%,\n rgba(200, 200, 200, 0.3) 20%,\n rgba(0, 0, 0, 0.5) 90%\n );\n\n background-blend-mode: hue, hard-light, normal;\n background-size: 200% 700%, 300% 300%, 200% 200%;\n background-position:\n 0% var(--posy),\n var(--posx) var(--posy),\n var(--posx) var(--posy);\n\n filter: brightness(calc((var(--hyp) * 0.3) + 0.5)) contrast(2.5) saturate(0.65);\n mix-blend-mode: color-dodge;\n opacity: var(--o);\n}\n\n/* Glare Layer - 빛 반사 효과 */\n.holo-card__glare {\n position: absolute;\n inset: 0;\n border-radius: 16px;\n pointer-events: none;\n z-index: 25;\n\n background-image: radial-gradient(\n farthest-corner circle at var(--mx) var(--my),\n rgba(255, 255, 255, 0.8) 0%,\n rgba(255, 255, 255, 0.4) 15%,\n rgba(0, 0, 0, 0.5) 50%,\n rgba(0, 0, 0, 0.7) 90%\n );\n\n mix-blend-mode: overlay;\n opacity: calc(var(--o) * 0.7);\n}\n\n/* Card flip animation - 카드 뒤집기 효과 */\n@keyframes holo-card-flip-in {\n 0% {\n transform: rotateY(180deg) scale(0.6);\n }\n 100% {\n transform: rotateY(0deg) scale(1);\n }\n}\n\n@keyframes holo-card-flip-out {\n 0% {\n transform: rotateY(0deg) scale(1);\n }\n 100% {\n transform: rotateY(-180deg) scale(0.6);\n }\n}\n\n/* Animation states */\n.holo-card--entering {\n animation: holo-card-flip-in 0.7s cubic-bezier(0.34, 1.56, 0.64, 1);\n}\n\n.holo-card--exiting {\n animation: holo-card-flip-out 0.6s ease-in-out !important;\n}\n\n/* Card back side - 카드 뒷면 */\n.holo-card__back {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n border-radius: 16px;\n backface-visibility: hidden;\n -webkit-backface-visibility: hidden;\n -webkit-transform: rotateY(180deg) translateZ(0);\n transform: rotateY(180deg) translateZ(0);\n background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f0f23 100%);\n border: 1px solid rgba(255, 255, 255, 0.1);\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n}\n\n/* Back pattern - 홀로그래픽 패턴 */\n.holo-card__back::before {\n content: '';\n position: absolute;\n inset: 0;\n background:\n repeating-linear-gradient(\n 45deg,\n transparent,\n transparent 10px,\n rgba(0, 212, 255, 0.03) 10px,\n rgba(0, 212, 255, 0.03) 20px\n ),\n repeating-linear-gradient(\n -45deg,\n transparent,\n transparent 10px,\n rgba(0, 255, 136, 0.03) 10px,\n rgba(0, 255, 136, 0.03) 20px\n );\n}\n\n/* Back logo/text */\n.holo-card__back-content {\n position: relative;\n z-index: 1;\n text-align: center;\n}\n\n.holo-card__back-logo {\n font-size: 3rem;\n font-weight: bold;\n background: linear-gradient(135deg, #00D4FF, #00FF88);\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent;\n background-clip: text;\n opacity: 0.8;\n letter-spacing: 0.1em;\n}\n\n.holo-card__back-subtitle {\n font-size: 0.75rem;\n color: rgba(255, 255, 255, 0.4);\n margin-top: 0.5rem;\n letter-spacing: 0.2em;\n text-transform: uppercase;\n}\n\n/* Front side needs backface-visibility */\n.holo-card__front {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n backface-visibility: hidden;\n -webkit-backface-visibility: hidden;\n border-radius: 16px;\n /* Fix iOS Safari rendering issue */\n -webkit-transform: translateZ(1px);\n transform: translateZ(1px);\n}\n\n/* Overlay fade */\n@keyframes holo-overlay-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n@keyframes holo-overlay-out {\n from { opacity: 1; }\n to { opacity: 0; }\n}\n\n.holo-card-overlay--entering {\n animation: holo-overlay-in 0.3s ease-out forwards;\n}\n\n.holo-card-overlay--exiting {\n animation: holo-overlay-out 0.3s ease-in forwards;\n}\n\n/* Accessibility: Reduce motion for users who prefer it */\n@media (prefers-reduced-motion: reduce) {\n .holo-card {\n transition: none !important;\n }\n .holo-card--entering,\n .holo-card--exiting {\n animation: none !important;\n }\n .holo-card-overlay--entering,\n .holo-card-overlay--exiting {\n animation: none !important;\n }\n}`,\n};\n","import { Command } from \"commander\";\nimport pc from \"picocolors\";\nimport { REGISTRY, getAllCategories, getComponentsByCategory } from \"../../registry/components.js\";\nimport { logger } from \"../../utils/logger.js\";\n\nexport const listCommand = new Command(\"list\")\n .description(\"List all available components\")\n .option(\"-c, --category <category>\", \"Filter by category\")\n .action((options) => {\n logger.title(\"\\n glintkit components\\n\");\n\n const categories = options.category\n ? [options.category]\n : getAllCategories();\n\n for (const category of categories) {\n const components = getComponentsByCategory(category as any);\n if (components.length === 0) continue;\n\n console.log(pc.bold(pc.yellow(` ${category.toUpperCase()}`)));\n for (const comp of components) {\n const deps = comp.npmDependencies.length > 0\n ? pc.dim(` (${comp.npmDependencies.join(\", \")})`)\n : \"\";\n console.log(` ${pc.green(comp.name)}${deps}`);\n console.log(` ${pc.dim(comp.description)}`);\n }\n console.log();\n }\n\n console.log(pc.dim(` Total: ${REGISTRY.length} components\\n`));\n });\n"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8MzB,8BAA8B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAi8B9B,2BAA2B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgK3B,+BAA+B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyV/B,2BAA2B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiQ3B,iCAAiC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmhBjC,2BAA2B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA+C3B,yBAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyCzB,0BAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA4D1B,iCAAiC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoGjC,qCAAqC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiLrC,6BAA6B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyF7B,gCAAgC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAychC,gCAAgC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8HhC,uBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA0CvB,yBAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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AAA,EAgBZ,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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","names":["Command","Command","prompts","path","pc","fs","path","fs","path","path","fs","fs","fs","path","Command","execSync","prompts","pc","Command","pc","Command","pc","Command"]}
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "glintkit",
3
+ "version": "0.1.0",
4
+ "description": "Add beautiful 3D, glass, and motion UI components to your project",
5
+ "type": "module",
6
+ "bin": {
7
+ "glintkit": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "prebuild": "tsx scripts/bundle-templates.ts",
14
+ "build": "tsup",
15
+ "dev": "tsup --watch",
16
+ "storybook": "storybook dev -p 6006",
17
+ "build-storybook": "storybook build",
18
+ "test": "vitest run",
19
+ "test:watch": "vitest",
20
+ "test:coverage": "vitest run --coverage"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/tristanjung1006/glintkit"
25
+ },
26
+ "homepage": "https://github.com/tristanjung1006/glintkit#readme",
27
+ "bugs": {
28
+ "url": "https://github.com/tristanjung1006/glintkit/issues"
29
+ },
30
+ "author": "tristanjung1006",
31
+ "dependencies": {
32
+ "commander": "^13.1.0",
33
+ "fs-extra": "^11.2.0",
34
+ "ora": "^8.1.1",
35
+ "picocolors": "^1.1.1",
36
+ "prompts": "^2.4.2"
37
+ },
38
+ "devDependencies": {
39
+ "@storybook/addon-a11y": "^10.2.7",
40
+ "@storybook/addon-themes": "^10.2.7",
41
+ "@storybook/react": "^10.2.7",
42
+ "@storybook/react-vite": "^10.2.7",
43
+ "@tailwindcss/vite": "^4.1.18",
44
+ "@types/fs-extra": "^11.0.4",
45
+ "@types/node": "^22.10.0",
46
+ "@types/prompts": "^2.4.9",
47
+ "@types/react": "^19.2.13",
48
+ "@types/react-dom": "^19.2.3",
49
+ "@use-gesture/react": "^10.3.1",
50
+ "@vitest/coverage-v8": "^3.0.0",
51
+ "clsx": "^2.1.1",
52
+ "motion": "^12.33.0",
53
+ "ogl": "^1.0.11",
54
+ "react": "^19.2.4",
55
+ "react-dom": "^19.2.4",
56
+ "storybook": "^10.2.7",
57
+ "tailwind-merge": "^3.4.0",
58
+ "tailwindcss": "^4.1.18",
59
+ "tsup": "^8.3.5",
60
+ "tsx": "^4.19.2",
61
+ "typescript": "^5.7.0",
62
+ "vitest": "^3.0.0"
63
+ },
64
+ "keywords": [
65
+ "ui",
66
+ "components",
67
+ "3d",
68
+ "glassmorphism",
69
+ "webgl",
70
+ "cli",
71
+ "glintkit"
72
+ ],
73
+ "license": "MIT"
74
+ }