feat: WebGL cinematic countdown — 3D atom with circular orbital text
- Three.js + React Three Fiber + postprocessing (bloom, chromatic aberration, glitch, scanlines) - Nucleus with pulsing energy core, wireframe icosahedron shell - 3 electron orbits with trail particles + energy arcs - 600 particle cloud with organic movement - Circular orbital text: title, countdown timer, subtitle curved on 3D rings - Mouse interactions: camera parallax, atom speed boost, nucleus glow on proximity - Camera smooth lerp follow with gentle auto-drift - Font fix: woff2 → ttf for troika-three-text compatibility - Logo: static mark instead of missing sprite frames Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Generated
+665
-10
@@ -11,13 +11,18 @@
|
||||
"@astrojs/node": "^9.4.5",
|
||||
"@astrojs/react": "^4.4.1",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"@react-three/drei": "^10.7.7",
|
||||
"@react-three/fiber": "^9.5.0",
|
||||
"@react-three/postprocessing": "^3.0.4",
|
||||
"astro": "^5.13.8",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"motion": "^12.23.24",
|
||||
"postprocessing": "^6.39.0",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"tailwind-merge": "^3.3.1"
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"three": "^0.183.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/check": "^0.9.6",
|
||||
@@ -1013,9 +1018,7 @@
|
||||
"version": "7.28.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz",
|
||||
"integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
@@ -1077,6 +1080,12 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@dimforge/rapier3d-compat": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz",
|
||||
"integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@emmetio/abbreviation": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@emmetio/abbreviation/-/abbreviation-2.3.3.tgz",
|
||||
@@ -2189,6 +2198,24 @@
|
||||
"react": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/@mediapipe/tasks-vision": {
|
||||
"version": "0.10.17",
|
||||
"resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.17.tgz",
|
||||
"integrity": "sha512-CZWV/q6TTe8ta61cZXjfnnHsfWIdFhms03M9T7Cnd5y2mdpylJM0rF1qRq+wsQVRMLz1OYPVEBU9ph2Bx8cxrg==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@monogrid/gainmap-js": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@monogrid/gainmap-js/-/gainmap-js-3.4.0.tgz",
|
||||
"integrity": "sha512-2Z0FATFHaoYJ8b+Y4y4Hgfn3FRFwuU5zRrk+9dFWp4uGAdHGqVEdP7HP+gLA3X469KXHmfupJaUbKo1b/aDKIg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"promise-worker-transferable": "^1.0.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"three": ">= 0.159.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@oslojs/encoding": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz",
|
||||
@@ -2239,6 +2266,120 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@react-three/drei": {
|
||||
"version": "10.7.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-10.7.7.tgz",
|
||||
"integrity": "sha512-ff+J5iloR0k4tC++QtD/j9u3w5fzfgFAWDtAGQah9pF2B1YgOq/5JxqY0/aVoQG5r3xSZz0cv5tk2YuBob4xEQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.26.0",
|
||||
"@mediapipe/tasks-vision": "0.10.17",
|
||||
"@monogrid/gainmap-js": "^3.0.6",
|
||||
"@use-gesture/react": "^10.3.1",
|
||||
"camera-controls": "^3.1.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"detect-gpu": "^5.0.56",
|
||||
"glsl-noise": "^0.0.0",
|
||||
"hls.js": "^1.5.17",
|
||||
"maath": "^0.10.8",
|
||||
"meshline": "^3.3.1",
|
||||
"stats-gl": "^2.2.8",
|
||||
"stats.js": "^0.17.0",
|
||||
"suspend-react": "^0.1.3",
|
||||
"three-mesh-bvh": "^0.8.3",
|
||||
"three-stdlib": "^2.35.6",
|
||||
"troika-three-text": "^0.52.4",
|
||||
"tunnel-rat": "^0.1.2",
|
||||
"use-sync-external-store": "^1.4.0",
|
||||
"utility-types": "^3.11.0",
|
||||
"zustand": "^5.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-three/fiber": "^9.0.0",
|
||||
"react": "^19",
|
||||
"react-dom": "^19",
|
||||
"three": ">=0.159"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@react-three/fiber": {
|
||||
"version": "9.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-9.5.0.tgz",
|
||||
"integrity": "sha512-FiUzfYW4wB1+PpmsE47UM+mCads7j2+giRBltfwH7SNhah95rqJs3ltEs9V3pP8rYdS0QlNne+9Aj8dS/SiaIA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.17.8",
|
||||
"@types/webxr": "*",
|
||||
"base64-js": "^1.5.1",
|
||||
"buffer": "^6.0.3",
|
||||
"its-fine": "^2.0.0",
|
||||
"react-use-measure": "^2.1.7",
|
||||
"scheduler": "^0.27.0",
|
||||
"suspend-react": "^0.1.3",
|
||||
"use-sync-external-store": "^1.4.0",
|
||||
"zustand": "^5.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"expo": ">=43.0",
|
||||
"expo-asset": ">=8.4",
|
||||
"expo-file-system": ">=11.0",
|
||||
"expo-gl": ">=11.0",
|
||||
"react": ">=19 <19.3",
|
||||
"react-dom": ">=19 <19.3",
|
||||
"react-native": ">=0.78",
|
||||
"three": ">=0.156"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"expo": {
|
||||
"optional": true
|
||||
},
|
||||
"expo-asset": {
|
||||
"optional": true
|
||||
},
|
||||
"expo-file-system": {
|
||||
"optional": true
|
||||
},
|
||||
"expo-gl": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
},
|
||||
"react-native": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@react-three/postprocessing": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@react-three/postprocessing/-/postprocessing-3.0.4.tgz",
|
||||
"integrity": "sha512-e4+F5xtudDYvhxx3y0NtWXpZbwvQ0x1zdOXWTbXMK6fFLVDd4qucN90YaaStanZGS4Bd5siQm0lGL/5ogf8iDQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"maath": "^0.6.0",
|
||||
"n8ao": "^1.9.4",
|
||||
"postprocessing": "^6.36.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-three/fiber": "^9.0.0",
|
||||
"react": "^19.0",
|
||||
"three": ">= 0.156.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-three/postprocessing/node_modules/maath": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/maath/-/maath-0.6.0.tgz",
|
||||
"integrity": "sha512-dSb2xQuP7vDnaYqfoKzlApeRcR2xtN8/f7WV/TMAkBC8552TwTLtOO0JTcSygkYMjNDPoo6V01jTw/aPi4JrMw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/three": ">=0.144.0",
|
||||
"three": ">=0.144.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/pluginutils": {
|
||||
"version": "1.0.0-beta.27",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
|
||||
@@ -3230,6 +3371,12 @@
|
||||
"@testing-library/dom": ">=7.21.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tweenjs/tween.js": {
|
||||
"version": "23.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz",
|
||||
"integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/aria-query": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
|
||||
@@ -3313,6 +3460,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/draco3d": {
|
||||
"version": "1.4.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/draco3d/-/draco3d-1.4.10.tgz",
|
||||
"integrity": "sha512-AX22jp8Y7wwaBgAixaSvkoG4M/+PlAcm3Qs4OW8yT9DM4xUpWKeFhLueTAyZF39pviAdcDdeJoACapiAceqNcw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
||||
@@ -3359,6 +3512,12 @@
|
||||
"@types/unist": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/offscreencanvas": {
|
||||
"version": "2019.7.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz",
|
||||
"integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "19.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
|
||||
@@ -3379,6 +3538,15 @@
|
||||
"@types/react": "^19.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-reconciler": {
|
||||
"version": "0.28.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.9.tgz",
|
||||
"integrity": "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/resolve": {
|
||||
"version": "1.20.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.6.tgz",
|
||||
@@ -3386,18 +3554,63 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/stats.js": {
|
||||
"version": "0.17.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz",
|
||||
"integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/three": {
|
||||
"version": "0.183.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.183.1.tgz",
|
||||
"integrity": "sha512-f2Pu5Hrepfgavttdye3PsH5RWyY/AvdZQwIVhrc4uNtvF7nOWJacQKcoVJn0S4f0yYbmAE6AR+ve7xDcuYtMGw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@dimforge/rapier3d-compat": "~0.12.0",
|
||||
"@tweenjs/tween.js": "~23.1.3",
|
||||
"@types/stats.js": "*",
|
||||
"@types/webxr": ">=0.5.17",
|
||||
"@webgpu/types": "*",
|
||||
"fflate": "~0.8.2",
|
||||
"meshoptimizer": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/unist": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
|
||||
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/webxr": {
|
||||
"version": "0.5.24",
|
||||
"resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz",
|
||||
"integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@ungap/structured-clone": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
|
||||
"integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@use-gesture/core": {
|
||||
"version": "10.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz",
|
||||
"integrity": "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@use-gesture/react": {
|
||||
"version": "10.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.1.tgz",
|
||||
"integrity": "sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@use-gesture/core": "10.3.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitejs/plugin-react": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
|
||||
@@ -3611,6 +3824,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@webgpu/types": {
|
||||
"version": "0.1.69",
|
||||
"resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.69.tgz",
|
||||
"integrity": "sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.16.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
|
||||
@@ -4472,6 +4691,26 @@
|
||||
"integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/baseline-browser-mapping": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz",
|
||||
@@ -4497,6 +4736,15 @@
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bidi-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
|
||||
"integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"require-from-string": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/boolbase": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
||||
@@ -4568,6 +4816,30 @@
|
||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/camelcase": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz",
|
||||
@@ -4580,6 +4852,19 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/camera-controls": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-3.1.2.tgz",
|
||||
"integrity": "sha512-xkxfpG2ECZ6Ww5/9+kf4mfg1VEYAoe9aDSY+IwF0UEs7qEzwy0aVRfs2grImIECs/PoBtWFrh7RXsQkwG922JA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=22.0.0",
|
||||
"npm": ">=10.5.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"three": ">=0.126.1"
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001775",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001775.tgz",
|
||||
@@ -4896,11 +5181,28 @@
|
||||
"integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cross-env": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
|
||||
"integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cross-spawn": "^7.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"cross-env": "src/bin/cross-env.js",
|
||||
"cross-env-shell": "src/bin/cross-env-shell.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.14",
|
||||
"npm": ">=6",
|
||||
"yarn": ">=1"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"path-key": "^3.1.0",
|
||||
@@ -5100,6 +5402,15 @@
|
||||
"integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/detect-gpu": {
|
||||
"version": "5.0.70",
|
||||
"resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.70.tgz",
|
||||
"integrity": "sha512-bqerEP1Ese6nt3rFkwPnGbsUF9a4q+gMmpTVVOEzoCyeCc+y7/RvJnQZJx1JwhgQI5Ntg0Kgat8Uu7XpBqnz1w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"webgl-constants": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||
@@ -5244,6 +5555,12 @@
|
||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/draco3d": {
|
||||
"version": "1.5.7",
|
||||
"resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.7.tgz",
|
||||
"integrity": "sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/dset": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz",
|
||||
@@ -5509,6 +5826,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/fflate": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
|
||||
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/find-up": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz",
|
||||
@@ -5693,6 +6016,12 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/glsl-noise": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/glsl-noise/-/glsl-noise-0.0.0.tgz",
|
||||
"integrity": "sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/graceful-fs": {
|
||||
"version": "4.2.11",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
@@ -5907,6 +6236,12 @@
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/hls.js": {
|
||||
"version": "1.6.15",
|
||||
"resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.6.15.tgz",
|
||||
"integrity": "sha512-E3a5VwgXimGHwpRGV+WxRTKeSp2DW5DI5MWv34ulL3t5UNmyJWCQ1KmLEHbYzcfThfXG8amBL+fCYPneGHC4VA==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/html-escaper": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz",
|
||||
@@ -5949,6 +6284,32 @@
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/immediate": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/import-meta-resolve": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz",
|
||||
@@ -6054,6 +6415,12 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-promise": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz",
|
||||
"integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-wsl": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz",
|
||||
@@ -6073,9 +6440,20 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/its-fine": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/its-fine/-/its-fine-2.0.0.tgz",
|
||||
"integrity": "sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/react-reconciler": "^0.28.9"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jackspeak": {
|
||||
"version": "3.4.3",
|
||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
|
||||
@@ -6167,6 +6545,15 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/lie": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
||||
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"immediate": "~3.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss": {
|
||||
"version": "1.31.1",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.31.1.tgz",
|
||||
@@ -6470,6 +6857,16 @@
|
||||
"lz-string": "bin/bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/maath": {
|
||||
"version": "0.10.8",
|
||||
"resolved": "https://registry.npmjs.org/maath/-/maath-0.10.8.tgz",
|
||||
"integrity": "sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/three": ">=0.134.0",
|
||||
"three": ">=0.134.0"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.21",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||
@@ -6731,6 +7128,21 @@
|
||||
"integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==",
|
||||
"license": "CC0-1.0"
|
||||
},
|
||||
"node_modules/meshline": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/meshline/-/meshline-3.3.1.tgz",
|
||||
"integrity": "sha512-/TQj+JdZkeSUOl5Mk2J7eLcYTLiQm2IDzmlSvYm7ov15anEcDJ92GHqqazxTSreeNgfnYu24kiEvvv0WlbCdFQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"three": ">=0.137"
|
||||
}
|
||||
},
|
||||
"node_modules/meshoptimizer": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-1.0.1.tgz",
|
||||
"integrity": "sha512-Vix+QlA1YYT3FwmBBZ+49cE5y/b+pRrcXKqGpS5ouh33d3lSp2PoTpCw19E0cKDFWalembrHnIaZetf27a+W2g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/micromark": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz",
|
||||
@@ -7428,6 +7840,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/n8ao": {
|
||||
"version": "1.10.1",
|
||||
"resolved": "https://registry.npmjs.org/n8ao/-/n8ao-1.10.1.tgz",
|
||||
"integrity": "sha512-hhI1pC+BfOZBV1KMwynBrVlIm8wqLxj/abAWhF2nZ0qQKyzTSQa1QtLVS2veRiuoBQXojxobcnp0oe+PUoxf/w==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"postprocessing": ">=6.30.0",
|
||||
"three": ">=0.137"
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.11",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||
@@ -7739,7 +8161,6 @@
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -7838,6 +8259,21 @@
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/postprocessing": {
|
||||
"version": "6.39.0",
|
||||
"resolved": "https://registry.npmjs.org/postprocessing/-/postprocessing-6.39.0.tgz",
|
||||
"integrity": "sha512-/G6JY8hs426lcto/pBZlnFSkyEo1fHsh4gy7FPJtq1SaSUOzJgDW6f6f1K/+aMOYzK/eQEefyOb3++jPPIUeDA==",
|
||||
"license": "Zlib",
|
||||
"peerDependencies": {
|
||||
"three": ">= 0.168.0 < 0.184.0"
|
||||
}
|
||||
},
|
||||
"node_modules/potpack": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz",
|
||||
"integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz",
|
||||
@@ -7879,6 +8315,16 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/promise-worker-transferable": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/promise-worker-transferable/-/promise-worker-transferable-1.0.4.tgz",
|
||||
"integrity": "sha512-bN+0ehEnrXfxV2ZQvU2PetO0n4gqBD4ulq3MI1WOPLgr7/Mg9yRQkX5+0v1vagr74ZTsl7XtzlaYDo2EuCeYJw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"is-promise": "^2.1.0",
|
||||
"lie": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/prompts": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
|
||||
@@ -7987,6 +8433,21 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-use-measure": {
|
||||
"version": "2.1.7",
|
||||
"resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.7.tgz",
|
||||
"integrity": "sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.13",
|
||||
"react-dom": ">=16.13"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz",
|
||||
@@ -8231,7 +8692,6 @@
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -8487,7 +8947,6 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"shebang-regex": "^3.0.0"
|
||||
@@ -8500,7 +8959,6 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -8582,6 +9040,32 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/stats-gl": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/stats-gl/-/stats-gl-2.4.2.tgz",
|
||||
"integrity": "sha512-g5O9B0hm9CvnM36+v7SFl39T7hmAlv541tU81ME8YeSb3i1CIP5/QdDeSB3A0la0bKNHpxpwxOVRo2wFTYEosQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/three": "*",
|
||||
"three": "^0.170.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/three": "*",
|
||||
"three": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/stats-gl/node_modules/three": {
|
||||
"version": "0.170.0",
|
||||
"resolved": "https://registry.npmjs.org/three/-/three-0.170.0.tgz",
|
||||
"integrity": "sha512-FQK+LEpYc0fBD+J8g6oSEyyNzjp+Q7Ks1C568WWaoMRLW+TkNNWmenWeGgJjV105Gd+p/2ql1ZcjYvNiPZBhuQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/stats.js": {
|
||||
"version": "0.17.0",
|
||||
"resolved": "https://registry.npmjs.org/stats.js/-/stats.js-0.17.0.tgz",
|
||||
"integrity": "sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
||||
@@ -9268,6 +9752,15 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/suspend-react": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.1.3.tgz",
|
||||
"integrity": "sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": ">=17.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svgo": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.0.tgz",
|
||||
@@ -9324,6 +9817,44 @@
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/three": {
|
||||
"version": "0.183.2",
|
||||
"resolved": "https://registry.npmjs.org/three/-/three-0.183.2.tgz",
|
||||
"integrity": "sha512-di3BsL2FEQ1PA7Hcvn4fyJOlxRRgFYBpMTcyOgkwJIaDOdJMebEFPA+t98EvjuljDx4hNulAGwF6KIjtwI5jgQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/three-mesh-bvh": {
|
||||
"version": "0.8.3",
|
||||
"resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.8.3.tgz",
|
||||
"integrity": "sha512-4G5lBaF+g2auKX3P0yqx+MJC6oVt6sB5k+CchS6Ob0qvH0YIhuUk1eYr7ktsIpY+albCqE80/FVQGV190PmiAg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"three": ">= 0.159.0"
|
||||
}
|
||||
},
|
||||
"node_modules/three-stdlib": {
|
||||
"version": "2.36.1",
|
||||
"resolved": "https://registry.npmjs.org/three-stdlib/-/three-stdlib-2.36.1.tgz",
|
||||
"integrity": "sha512-XyGQrFmNQ5O/IoKm556ftwKsBg11TIb301MB5dWNicziQBEs2g3gtOYIf7pFiLa0zI2gUwhtCjv9fmjnxKZ1Cg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/draco3d": "^1.4.0",
|
||||
"@types/offscreencanvas": "^2019.6.4",
|
||||
"@types/webxr": "^0.5.2",
|
||||
"draco3d": "^1.4.1",
|
||||
"fflate": "^0.6.9",
|
||||
"potpack": "^1.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"three": ">=0.128.0"
|
||||
}
|
||||
},
|
||||
"node_modules/three-stdlib/node_modules/fflate": {
|
||||
"version": "0.6.10",
|
||||
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.6.10.tgz",
|
||||
"integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tiny-inflate": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
|
||||
@@ -9401,6 +9932,36 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/troika-three-text": {
|
||||
"version": "0.52.4",
|
||||
"resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.52.4.tgz",
|
||||
"integrity": "sha512-V50EwcYGruV5rUZ9F4aNsrytGdKcXKALjEtQXIOBfhVoZU9VAqZNIoGQ3TMiooVqFAbR1w15T+f+8gkzoFzawg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bidi-js": "^1.0.2",
|
||||
"troika-three-utils": "^0.52.4",
|
||||
"troika-worker-utils": "^0.52.0",
|
||||
"webgl-sdf-generator": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"three": ">=0.125.0"
|
||||
}
|
||||
},
|
||||
"node_modules/troika-three-utils": {
|
||||
"version": "0.52.4",
|
||||
"resolved": "https://registry.npmjs.org/troika-three-utils/-/troika-three-utils-0.52.4.tgz",
|
||||
"integrity": "sha512-NORAStSVa/BDiG52Mfudk4j1FG4jC4ILutB3foPnfGbOeIs9+G5vZLa0pnmnaftZUGm4UwSoqEpWdqvC7zms3A==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"three": ">=0.125.0"
|
||||
}
|
||||
},
|
||||
"node_modules/troika-worker-utils": {
|
||||
"version": "0.52.0",
|
||||
"resolved": "https://registry.npmjs.org/troika-worker-utils/-/troika-worker-utils-0.52.0.tgz",
|
||||
"integrity": "sha512-W1CpvTHykaPH5brv5VHLfQo9D1OYuo0cSBEUQFFT/nBUzM8iD6Lq2/tgG/f1OelbAS1WtaTPQzE5uM49egnngw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/trough": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz",
|
||||
@@ -9462,6 +10023,43 @@
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/tunnel-rat": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-rat/-/tunnel-rat-0.1.2.tgz",
|
||||
"integrity": "sha512-lR5VHmkPhzdhrM092lI2nACsLO4QubF0/yoOhzX7c+wIpbN1GjHNzCc91QlpxBi+cnx8vVJ+Ur6vL5cEoQPFpQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"zustand": "^4.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/tunnel-rat/node_modules/zustand": {
|
||||
"version": "4.5.7",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz",
|
||||
"integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"use-sync-external-store": "^1.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.7.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": ">=16.8",
|
||||
"immer": ">=9.0.6",
|
||||
"react": ">=16.8"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"immer": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/type-fest": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
|
||||
@@ -9850,6 +10448,24 @@
|
||||
"browserslist": ">= 4.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
|
||||
"integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/utility-types": {
|
||||
"version": "3.11.0",
|
||||
"resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz",
|
||||
"integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/vfile": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
|
||||
@@ -10264,6 +10880,17 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/webgl-constants": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/webgl-constants/-/webgl-constants-1.1.1.tgz",
|
||||
"integrity": "sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg=="
|
||||
},
|
||||
"node_modules/webgl-sdf-generator": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz",
|
||||
"integrity": "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/webpack-virtual-modules": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
|
||||
@@ -10275,7 +10902,6 @@
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"isexe": "^2.0.0"
|
||||
@@ -10642,6 +11268,35 @@
|
||||
"zod": "^3"
|
||||
}
|
||||
},
|
||||
"node_modules/zustand": {
|
||||
"version": "5.0.12",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.12.tgz",
|
||||
"integrity": "sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": ">=18.0.0",
|
||||
"immer": ">=9.0.6",
|
||||
"react": ">=18.0.0",
|
||||
"use-sync-external-store": ">=1.2.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"immer": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"use-sync-external-store": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/zwitch": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
|
||||
|
||||
+6
-1
@@ -26,13 +26,18 @@
|
||||
"@astrojs/node": "^9.4.5",
|
||||
"@astrojs/react": "^4.4.1",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"@react-three/drei": "^10.7.7",
|
||||
"@react-three/fiber": "^9.5.0",
|
||||
"@react-three/postprocessing": "^3.0.4",
|
||||
"astro": "^5.13.8",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"motion": "^12.23.24",
|
||||
"postprocessing": "^6.39.0",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"tailwind-merge": "^3.3.1"
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"three": "^0.183.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/check": "^0.9.6",
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,526 @@
|
||||
import { useRef, useMemo, useEffect, useState, useCallback } from 'react';
|
||||
import { Canvas, useFrame, useThree } from '@react-three/fiber';
|
||||
import { Text } from '@react-three/drei';
|
||||
import { EffectComposer, Bloom, ChromaticAberration, Scanline, Glitch as GlitchEffect } from '@react-three/postprocessing';
|
||||
import { GlitchMode, BlendFunction } from 'postprocessing';
|
||||
import * as THREE from 'three';
|
||||
|
||||
/* ===================================================================
|
||||
ATOM GLITCH — cinematic WebGL countdown for L'Electron Rare
|
||||
- Nucleus core with pulsing energy
|
||||
- 3 electron orbits with trail particles
|
||||
- Particle cloud (electrons as sparks)
|
||||
- Post-processing: bloom, chromatic aberration, glitch, scanlines
|
||||
- Mouse-reactive + auto-orbit
|
||||
=================================================================== */
|
||||
|
||||
const COLORS = {
|
||||
cyan: new THREE.Color('#5bd1d8'),
|
||||
amber: new THREE.Color('#f1c27a'),
|
||||
green: new THREE.Color('#b6d18f'),
|
||||
electric: new THREE.Color('#0071e3'),
|
||||
white: new THREE.Color('#ffffff'),
|
||||
nucleus: new THREE.Color('#ff6b35'),
|
||||
};
|
||||
|
||||
/* ---------- Particle cloud around the atom ---------- */
|
||||
function ParticleField({ count = 600 }) {
|
||||
const ref = useRef<THREE.Points>(null);
|
||||
|
||||
const { positions, velocities, colors } = useMemo(() => {
|
||||
const pos = new Float32Array(count * 3);
|
||||
const vel = new Float32Array(count * 3);
|
||||
const col = new Float32Array(count * 3);
|
||||
const palette = [COLORS.cyan, COLORS.amber, COLORS.green, COLORS.electric];
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const r = 2 + Math.random() * 6;
|
||||
const theta = Math.random() * Math.PI * 2;
|
||||
const phi = Math.acos(2 * Math.random() - 1);
|
||||
pos[i * 3] = r * Math.sin(phi) * Math.cos(theta);
|
||||
pos[i * 3 + 1] = r * Math.sin(phi) * Math.sin(theta);
|
||||
pos[i * 3 + 2] = r * Math.cos(phi);
|
||||
|
||||
vel[i * 3] = (Math.random() - 0.5) * 0.004;
|
||||
vel[i * 3 + 1] = (Math.random() - 0.5) * 0.004;
|
||||
vel[i * 3 + 2] = (Math.random() - 0.5) * 0.004;
|
||||
|
||||
const c = palette[Math.floor(Math.random() * palette.length)];
|
||||
col[i * 3] = c.r;
|
||||
col[i * 3 + 1] = c.g;
|
||||
col[i * 3 + 2] = c.b;
|
||||
}
|
||||
return { positions: pos, velocities: vel, colors: col };
|
||||
}, [count]);
|
||||
|
||||
useFrame(({ clock }) => {
|
||||
if (!ref.current) return;
|
||||
const posAttr = ref.current.geometry.attributes.position as THREE.BufferAttribute;
|
||||
const arr = posAttr.array as Float32Array;
|
||||
const t = clock.getElapsedTime();
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
arr[i * 3] += velocities[i * 3] + Math.sin(t * 0.5 + i) * 0.002;
|
||||
arr[i * 3 + 1] += velocities[i * 3 + 1] + Math.cos(t * 0.3 + i) * 0.002;
|
||||
arr[i * 3 + 2] += velocities[i * 3 + 2];
|
||||
|
||||
// respawn far particles
|
||||
const dist = Math.sqrt(arr[i * 3] ** 2 + arr[i * 3 + 1] ** 2 + arr[i * 3 + 2] ** 2);
|
||||
if (dist > 8) {
|
||||
const r = 2 + Math.random() * 2;
|
||||
const theta = Math.random() * Math.PI * 2;
|
||||
const phi = Math.acos(2 * Math.random() - 1);
|
||||
arr[i * 3] = r * Math.sin(phi) * Math.cos(theta);
|
||||
arr[i * 3 + 1] = r * Math.sin(phi) * Math.sin(theta);
|
||||
arr[i * 3 + 2] = r * Math.cos(phi);
|
||||
}
|
||||
}
|
||||
posAttr.needsUpdate = true;
|
||||
});
|
||||
|
||||
return (
|
||||
<points ref={ref}>
|
||||
<bufferGeometry>
|
||||
<bufferAttribute attach="attributes-position" args={[positions, 3]} />
|
||||
<bufferAttribute attach="attributes-color" args={[colors, 3]} />
|
||||
</bufferGeometry>
|
||||
<pointsMaterial size={0.04} vertexColors transparent opacity={0.7} sizeAttenuation depthWrite={false} blending={THREE.AdditiveBlending} />
|
||||
</points>
|
||||
);
|
||||
}
|
||||
|
||||
/* ---------- Orbit trail (instanced tube of spheres) ---------- */
|
||||
function OrbitTrail({ tilt, speed, color, trailCount = 30 }: { tilt: number[]; speed: number; color: THREE.Color; trailCount?: number }) {
|
||||
const meshRef = useRef<THREE.InstancedMesh>(null);
|
||||
const phase = useMemo(() => Math.random() * Math.PI * 2, []);
|
||||
const dummy = useMemo(() => new THREE.Object3D(), []);
|
||||
const orbitA = 3.2;
|
||||
const orbitB = orbitA * 0.38;
|
||||
|
||||
useFrame(({ clock }) => {
|
||||
if (!meshRef.current) return;
|
||||
const t = clock.getElapsedTime() * speed + phase;
|
||||
|
||||
for (let i = 0; i < trailCount; i++) {
|
||||
const age = i / trailCount;
|
||||
const angle = t - age * 0.8;
|
||||
dummy.position.set(Math.cos(angle) * orbitA, Math.sin(angle) * orbitB, 0);
|
||||
const s = (1 - age) * 0.14 + 0.02;
|
||||
dummy.scale.setScalar(s);
|
||||
dummy.updateMatrix();
|
||||
meshRef.current.setMatrixAt(i, dummy.matrix);
|
||||
}
|
||||
meshRef.current.instanceMatrix.needsUpdate = true;
|
||||
});
|
||||
|
||||
return (
|
||||
<group rotation={tilt as [number, number, number]}>
|
||||
<instancedMesh ref={meshRef} args={[undefined, undefined, trailCount]}>
|
||||
<sphereGeometry args={[1, 8, 8]} />
|
||||
<meshStandardMaterial color={color} emissive={color} emissiveIntensity={2} transparent opacity={0.9} />
|
||||
</instancedMesh>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
/* ---------- Orbit ring (thin glowing line) ---------- */
|
||||
function OrbitRing({ tilt, color }: { tilt: number[]; color: THREE.Color }) {
|
||||
const points = useMemo(() => {
|
||||
const pts: THREE.Vector3[] = [];
|
||||
for (let i = 0; i <= 128; i++) {
|
||||
const t = (i / 128) * Math.PI * 2;
|
||||
pts.push(new THREE.Vector3(Math.cos(t) * 3.2, Math.sin(t) * 3.2 * 0.38, 0));
|
||||
}
|
||||
return pts;
|
||||
}, []);
|
||||
const geo = useMemo(() => new THREE.BufferGeometry().setFromPoints(points), [points]);
|
||||
|
||||
return (
|
||||
<group rotation={tilt as [number, number, number]}>
|
||||
<line geometry={geo}>
|
||||
<lineBasicMaterial color={color} transparent opacity={0.12} />
|
||||
</line>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
/* ---------- Nucleus: pulsing energy core ---------- */
|
||||
function Nucleus() {
|
||||
const coreRef = useRef<THREE.Mesh>(null);
|
||||
const glowRef = useRef<THREE.Mesh>(null);
|
||||
const outerRef = useRef<THREE.Mesh>(null);
|
||||
|
||||
useFrame(({ clock, pointer }) => {
|
||||
const t = clock.getElapsedTime();
|
||||
const mouseDist = Math.sqrt(pointer.x ** 2 + pointer.y ** 2);
|
||||
const mouseProximity = Math.max(0, 1 - mouseDist); // 1 at center, 0 at edges
|
||||
|
||||
if (coreRef.current) {
|
||||
const s = 1 + Math.sin(t * 3) * 0.08 + Math.sin(t * 7.3) * 0.03 + mouseProximity * 0.15;
|
||||
coreRef.current.scale.setScalar(s);
|
||||
(coreRef.current.material as THREE.MeshStandardMaterial).emissiveIntensity = 2 + mouseProximity * 3;
|
||||
}
|
||||
if (glowRef.current) {
|
||||
const s = 1 + Math.sin(t * 2) * 0.15 + mouseProximity * 0.3;
|
||||
glowRef.current.scale.setScalar(s);
|
||||
(glowRef.current.material as THREE.MeshStandardMaterial).opacity = 0.15 + Math.sin(t * 4) * 0.08 + mouseProximity * 0.2;
|
||||
}
|
||||
if (outerRef.current) {
|
||||
outerRef.current.rotation.y = t * (0.5 + mouseProximity * 2);
|
||||
outerRef.current.rotation.x = t * (0.3 + mouseProximity * 1.5);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<group>
|
||||
{/* outer energy shell */}
|
||||
<mesh ref={outerRef}>
|
||||
<icosahedronGeometry args={[0.9, 1]} />
|
||||
<meshStandardMaterial color={COLORS.electric} wireframe transparent opacity={0.08} emissive={COLORS.electric} emissiveIntensity={0.5} />
|
||||
</mesh>
|
||||
{/* glow sphere */}
|
||||
<mesh ref={glowRef}>
|
||||
<sphereGeometry args={[0.7, 32, 32]} />
|
||||
<meshStandardMaterial color={COLORS.nucleus} transparent opacity={0.15} emissive={COLORS.nucleus} emissiveIntensity={1.2} />
|
||||
</mesh>
|
||||
{/* solid core */}
|
||||
<mesh ref={coreRef}>
|
||||
<sphereGeometry args={[0.32, 32, 32]} />
|
||||
<meshStandardMaterial color="#ffffff" emissive={COLORS.amber} emissiveIntensity={2} metalness={0.6} roughness={0.2} />
|
||||
</mesh>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
/* ---------- Energy arcs (random lightning) ---------- */
|
||||
function EnergyArc({ color }: { color: THREE.Color }) {
|
||||
const ref = useRef<THREE.Line>(null);
|
||||
const [visible, setVisible] = useState(true);
|
||||
|
||||
useFrame(({ clock }) => {
|
||||
const t = clock.getElapsedTime();
|
||||
// flash randomly
|
||||
setVisible(Math.sin(t * 12 + Math.random() * 100) > 0.7);
|
||||
|
||||
if (ref.current && visible) {
|
||||
const geo = ref.current.geometry;
|
||||
const pts: THREE.Vector3[] = [];
|
||||
const segments = 8;
|
||||
const startAngle = Math.sin(t * 2) * Math.PI;
|
||||
for (let i = 0; i <= segments; i++) {
|
||||
const frac = i / segments;
|
||||
const r = 0.4 + frac * 2.5;
|
||||
const angle = startAngle + frac * 1.5;
|
||||
pts.push(new THREE.Vector3(
|
||||
Math.cos(angle) * r + (Math.random() - 0.5) * 0.3,
|
||||
Math.sin(angle) * r * 0.4 + (Math.random() - 0.5) * 0.2,
|
||||
(Math.random() - 0.5) * 0.4,
|
||||
));
|
||||
}
|
||||
geo.setFromPoints(pts);
|
||||
}
|
||||
});
|
||||
|
||||
if (!visible) return null;
|
||||
|
||||
return (
|
||||
<line ref={ref}>
|
||||
<bufferGeometry />
|
||||
<lineBasicMaterial color={color} transparent opacity={0.4} blending={THREE.AdditiveBlending} />
|
||||
</line>
|
||||
);
|
||||
}
|
||||
|
||||
/* ---------- 3D Circular / Orbital text ---------- */
|
||||
const LAUNCH = new Date('2026-05-01T00:00:00+02:00').getTime();
|
||||
const FONT_URL = '/assets/fonts/manrope-regular.ttf';
|
||||
|
||||
function pad(n: number) { return String(n).padStart(2, '0'); }
|
||||
|
||||
/* Each character placed on a circle arc, rotating together */
|
||||
function CircularText({ text, radius, speed, tilt, fontSize, color, emissive, opacity = 0.9, spread = 0.8 }: {
|
||||
text: string; radius: number; speed: number; tilt: number[];
|
||||
fontSize: number; color: string; emissive: string; opacity?: number; spread?: number;
|
||||
}) {
|
||||
const groupRef = useRef<THREE.Group>(null);
|
||||
const chars = useMemo(() => text.split(''), [text]);
|
||||
const charWidth = fontSize * 0.55;
|
||||
|
||||
useFrame(({ clock }) => {
|
||||
if (!groupRef.current) return;
|
||||
const t = clock.getElapsedTime() * speed;
|
||||
groupRef.current.rotation.y = t;
|
||||
});
|
||||
|
||||
return (
|
||||
<group rotation={tilt as [number, number, number]}>
|
||||
<group ref={groupRef}>
|
||||
{chars.map((char, i) => {
|
||||
const angle = ((i - chars.length / 2) * charWidth * spread) / radius;
|
||||
const x = Math.sin(angle) * radius;
|
||||
const z = Math.cos(angle) * radius;
|
||||
return (
|
||||
<Text
|
||||
key={i}
|
||||
position={[x, 0, z]}
|
||||
rotation={[0, -angle, 0]}
|
||||
fontSize={fontSize}
|
||||
font={FONT_URL}
|
||||
anchorX="center"
|
||||
anchorY="middle"
|
||||
>
|
||||
{char}
|
||||
<meshStandardMaterial
|
||||
color={color}
|
||||
emissive={emissive}
|
||||
emissiveIntensity={0.6}
|
||||
transparent
|
||||
opacity={opacity}
|
||||
side={THREE.DoubleSide}
|
||||
/>
|
||||
</Text>
|
||||
);
|
||||
})}
|
||||
</group>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
/* Countdown — characters on a spinning ring */
|
||||
function CountdownRing() {
|
||||
const [time, setTime] = useState('');
|
||||
const groupRef = useRef<THREE.Group>(null);
|
||||
const radius = 4.2;
|
||||
|
||||
useEffect(() => {
|
||||
function tick() {
|
||||
const diff = Math.max(0, LAUNCH - Date.now());
|
||||
const d = Math.floor(diff / 86400000);
|
||||
const h = Math.floor((diff % 86400000) / 3600000);
|
||||
const m = Math.floor((diff % 3600000) / 60000);
|
||||
const s = Math.floor((diff % 60000) / 1000);
|
||||
setTime(`${pad(d)}J ${pad(h)}H ${pad(m)}M ${pad(s)}S`);
|
||||
}
|
||||
tick();
|
||||
const id = setInterval(tick, 1000);
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
|
||||
useFrame(({ clock }) => {
|
||||
if (!groupRef.current) return;
|
||||
groupRef.current.rotation.y = clock.getElapsedTime() * -0.2;
|
||||
});
|
||||
|
||||
const chars = time.split('');
|
||||
const fontSize = 0.28;
|
||||
const charWidth = fontSize * 0.5;
|
||||
|
||||
return (
|
||||
<group rotation={[0.3, 0, 0.1]}>
|
||||
<group ref={groupRef}>
|
||||
{chars.map((char, i) => {
|
||||
const angle = ((i - chars.length / 2) * charWidth * 0.9) / radius;
|
||||
const x = Math.sin(angle) * radius;
|
||||
const z = Math.cos(angle) * radius;
|
||||
return (
|
||||
<Text
|
||||
key={i}
|
||||
position={[x, 0, z]}
|
||||
rotation={[0, -angle, 0]}
|
||||
fontSize={fontSize}
|
||||
font={FONT_URL}
|
||||
anchorX="center"
|
||||
anchorY="middle"
|
||||
>
|
||||
{char}
|
||||
<meshStandardMaterial
|
||||
color="#ffffff"
|
||||
emissive="#5bd1d8"
|
||||
emissiveIntensity={0.8}
|
||||
transparent
|
||||
opacity={0.95}
|
||||
side={THREE.DoubleSide}
|
||||
/>
|
||||
</Text>
|
||||
);
|
||||
})}
|
||||
</group>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
/* All circular texts assembled */
|
||||
function CountdownText() {
|
||||
return (
|
||||
<group>
|
||||
{/* "L'electron" — large ring, slow spin */}
|
||||
<CircularText
|
||||
text={"L'\u00e9lectron"}
|
||||
radius={3.8}
|
||||
speed={0.15}
|
||||
tilt={[-0.3, 0, 0]}
|
||||
fontSize={0.55}
|
||||
color="#ffffff"
|
||||
emissive="#ffffff"
|
||||
spread={0.9}
|
||||
/>
|
||||
|
||||
{/* "rare" — opposite tilt, bigger */}
|
||||
<CircularText
|
||||
text="rare"
|
||||
radius={3.5}
|
||||
speed={-0.2}
|
||||
tilt={[0.5, 0.2, 0]}
|
||||
fontSize={0.85}
|
||||
color="#ffffff"
|
||||
emissive="#5bd1d8"
|
||||
spread={1.0}
|
||||
/>
|
||||
|
||||
{/* Subtitle — wider ring, slow */}
|
||||
<CircularText
|
||||
text="SYSTEMES ELECTRONIQUES SPECIFIQUES"
|
||||
radius={5.2}
|
||||
speed={0.08}
|
||||
tilt={[1.3, 0.2, 0.4]}
|
||||
fontSize={0.11}
|
||||
color="#ffffff"
|
||||
emissive="#5bd1d8"
|
||||
opacity={0.35}
|
||||
spread={0.7}
|
||||
/>
|
||||
|
||||
{/* "LANCEMENT DANS" — small ring */}
|
||||
<CircularText
|
||||
text="LANCEMENT DANS"
|
||||
radius={3.2}
|
||||
speed={0.25}
|
||||
tilt={[-0.7, -0.3, 0.2]}
|
||||
fontSize={0.1}
|
||||
color="#5bd1d8"
|
||||
emissive="#5bd1d8"
|
||||
opacity={0.5}
|
||||
spread={0.8}
|
||||
/>
|
||||
|
||||
{/* Countdown timer — spinning ring */}
|
||||
<CountdownRing />
|
||||
|
||||
{/* Extra decorative text rings */}
|
||||
<CircularText
|
||||
text="electronique automatisme energie stockage prototypage formation"
|
||||
radius={5.8}
|
||||
speed={-0.05}
|
||||
tilt={[0.8, -0.4, 0.6]}
|
||||
fontSize={0.07}
|
||||
color="#5bd1d8"
|
||||
emissive="#5bd1d8"
|
||||
opacity={0.2}
|
||||
spread={0.5}
|
||||
/>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
/* ---------- Main scene ---------- */
|
||||
function AtomScene() {
|
||||
const groupRef = useRef<THREE.Group>(null);
|
||||
|
||||
const orbits = useMemo(() => [
|
||||
{ tilt: [-0.5, 0, 0], speed: 1.0, color: COLORS.cyan },
|
||||
{ tilt: [0.5, 0.3, 0], speed: 0.72, color: COLORS.amber },
|
||||
{ tilt: [1.57, 0.2, 0.4], speed: 0.55, color: COLORS.green },
|
||||
], []);
|
||||
|
||||
useFrame(({ clock, pointer }) => {
|
||||
if (!groupRef.current) return;
|
||||
const t = clock.getElapsedTime();
|
||||
// Mouse distance from center = speed boost
|
||||
const mouseDist = Math.sqrt(pointer.x ** 2 + pointer.y ** 2);
|
||||
const speedMul = 1 + mouseDist * 0.8;
|
||||
groupRef.current.rotation.y = t * 0.12 * speedMul + pointer.x * 0.6;
|
||||
groupRef.current.rotation.x = -0.2 + pointer.y * 0.4;
|
||||
groupRef.current.rotation.z = pointer.x * 0.1;
|
||||
});
|
||||
|
||||
return (
|
||||
<group ref={groupRef}>
|
||||
<Nucleus />
|
||||
{orbits.map((o, i) => (
|
||||
<OrbitRing key={`ring-${i}`} tilt={o.tilt} color={o.color} />
|
||||
))}
|
||||
{orbits.map((o, i) => (
|
||||
<OrbitTrail key={`trail-${i}`} tilt={o.tilt} speed={o.speed} color={o.color} />
|
||||
))}
|
||||
<EnergyArc color={COLORS.cyan} />
|
||||
<EnergyArc color={COLORS.amber} />
|
||||
<ParticleField />
|
||||
{/* Textes orbitants — dans le groupe, tournent avec l'atome */}
|
||||
<CountdownText />
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
/* ---------- Camera with mouse parallax ---------- */
|
||||
function CameraRig() {
|
||||
const { camera } = useThree();
|
||||
const mouse = useRef({ x: 0, y: 0 });
|
||||
const smooth = useRef({ x: 0, y: 0 });
|
||||
|
||||
useFrame(({ clock, pointer }) => {
|
||||
const t = clock.getElapsedTime();
|
||||
|
||||
// Smooth mouse follow (lerp)
|
||||
mouse.current.x = pointer.x;
|
||||
mouse.current.y = pointer.y;
|
||||
smooth.current.x += (mouse.current.x - smooth.current.x) * 0.05;
|
||||
smooth.current.y += (mouse.current.y - smooth.current.y) * 0.05;
|
||||
|
||||
// Camera orbits gently + follows mouse
|
||||
camera.position.x = Math.sin(t * 0.08) * 0.5 + smooth.current.x * 2.5;
|
||||
camera.position.y = Math.cos(t * 0.06) * 0.3 + smooth.current.y * 1.5;
|
||||
camera.position.z = 9 + Math.sin(t * 0.1) * 0.3;
|
||||
camera.lookAt(smooth.current.x * 0.5, smooth.current.y * 0.3, 0);
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/* ---------- Exported component ---------- */
|
||||
export default function AtomGlitch() {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
}}
|
||||
aria-label="Atome 3D animé — L'Electron Rare"
|
||||
role="img"
|
||||
>
|
||||
<Canvas
|
||||
camera={{ position: [0, -0.5, 9], fov: 45 }}
|
||||
dpr={[1, 2]}
|
||||
gl={{ antialias: true, alpha: true, powerPreference: 'high-performance' }}
|
||||
style={{ background: 'transparent' }}
|
||||
>
|
||||
<color attach="background" args={['#000000']} />
|
||||
<fog attach="fog" args={['#000000', 8, 18]} />
|
||||
|
||||
<ambientLight intensity={0.15} />
|
||||
<pointLight position={[5, 3, 5]} intensity={0.8} color="#ffffff" />
|
||||
<pointLight position={[-4, -2, 3]} intensity={0.4} color="#5bd1d8" />
|
||||
<pointLight position={[0, 4, -3]} intensity={0.3} color="#f1c27a" />
|
||||
|
||||
<CameraRig />
|
||||
<AtomScene />
|
||||
|
||||
<EffectComposer>
|
||||
<Bloom luminanceThreshold={0.2} luminanceSmoothing={0.9} intensity={1.8} mipmapBlur />
|
||||
<ChromaticAberration blendFunction={BlendFunction.NORMAL} offset={new THREE.Vector2(0.002, 0.002)} />
|
||||
<GlitchEffect delay={new THREE.Vector2(3, 8)} duration={new THREE.Vector2(0.1, 0.4)} strength={new THREE.Vector2(0.05, 0.15)} mode={GlitchMode.SPORADIC} />
|
||||
<Scanline blendFunction={BlendFunction.OVERLAY} density={1.8} opacity={0.05} />
|
||||
</EffectComposer>
|
||||
</Canvas>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+120
-190
@@ -1,63 +1,42 @@
|
||||
---
|
||||
import BaseLayout from '@/layouts/BaseLayout.astro';
|
||||
import AtomGlitch from '@/components/AtomGlitch';
|
||||
import '@/styles/global.css';
|
||||
---
|
||||
|
||||
<BaseLayout title="L'Electron Rare — Bientôt" description="L'Electron Rare arrive. Systèmes électroniques spécifiques, formations, prototypage.">
|
||||
<main class="countdown-page">
|
||||
<div class="logo-sprite" id="logo-sprite" aria-label="L'Electron Rare — logo animé"></div>
|
||||
|
||||
<svg class="atom" viewBox="0 0 300 300" xmlns="http://www.w3.org/2000/svg">
|
||||
<ellipse cx="150" cy="150" rx="120" ry="45" fill="none" stroke="var(--trace-cyan-soft, #5bd1d866)" stroke-width="0.8" transform="rotate(-30 150 150)"/>
|
||||
<ellipse cx="150" cy="150" rx="120" ry="45" fill="none" stroke="var(--trace-amber-soft, #f1c27a66)" stroke-width="0.8" transform="rotate(30 150 150)"/>
|
||||
<ellipse cx="150" cy="150" rx="120" ry="45" fill="none" stroke="var(--trace-green-soft, #b6d18f44)" stroke-width="0.8" transform="rotate(90 150 150)"/>
|
||||
<circle cx="150" cy="150" r="18" fill="url(#nucleusGlow)"/>
|
||||
<circle cx="150" cy="150" r="8" fill="var(--accent, #f1c27a)"/>
|
||||
<circle r="5" fill="var(--electric, #5bd1d8)"><animateMotion dur="3s" repeatCount="indefinite"><mpath href="#o1"/></animateMotion></circle>
|
||||
<circle r="10" fill="var(--electric, #5bd1d8)" opacity="0.3"><animateMotion dur="3s" repeatCount="indefinite"><mpath href="#o1"/></animateMotion></circle>
|
||||
<circle r="4" fill="var(--accent, #f1c27a)"><animateMotion dur="4.2s" repeatCount="indefinite"><mpath href="#o2"/></animateMotion></circle>
|
||||
<circle r="9" fill="var(--accent, #f1c27a)" opacity="0.25"><animateMotion dur="4.2s" repeatCount="indefinite"><mpath href="#o2"/></animateMotion></circle>
|
||||
<circle r="3" fill="var(--trace-green, #b6d18f)"><animateMotion dur="5.8s" repeatCount="indefinite"><mpath href="#o3"/></animateMotion></circle>
|
||||
<circle r="8" fill="var(--trace-green, #b6d18f)" opacity="0.2"><animateMotion dur="5.8s" repeatCount="indefinite"><mpath href="#o3"/></animateMotion></circle>
|
||||
<defs>
|
||||
<ellipse id="o1" cx="150" cy="150" rx="120" ry="45" transform="rotate(-30 150 150)" fill="none"/>
|
||||
<ellipse id="o2" cx="150" cy="150" rx="120" ry="45" transform="rotate(30 150 150)" fill="none"/>
|
||||
<ellipse id="o3" cx="150" cy="150" rx="120" ry="45" transform="rotate(90 150 150)" fill="none"/>
|
||||
<radialGradient id="nucleusGlow">
|
||||
<stop offset="0%" stop-color="var(--accent, #f1c27a)" stop-opacity="0.6"/>
|
||||
<stop offset="100%" stop-color="var(--accent, #f1c27a)" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
<div class="title-bubble">
|
||||
<h1 class="title" data-text="L'électron rare">L'<span class="electric">é</span>lectron rare</h1>
|
||||
<!-- WebGL background -->
|
||||
<div class="webgl-bg">
|
||||
<AtomGlitch client:load />
|
||||
</div>
|
||||
|
||||
<div class="timer-bubble">
|
||||
<div class="timer" id="timer">
|
||||
<div class="unit"><span class="num" id="days">--</span><span class="label">jours</span></div>
|
||||
<div class="sep">:</div>
|
||||
<div class="unit"><span class="num" id="hours">--</span><span class="label">heures</span></div>
|
||||
<div class="sep">:</div>
|
||||
<div class="unit"><span class="num" id="minutes">--</span><span class="label">minutes</span></div>
|
||||
<div class="sep">:</div>
|
||||
<div class="unit"><span class="num" id="seconds">--</span><span class="label">secondes</span></div>
|
||||
<!-- Overlay content -->
|
||||
<div class="overlay">
|
||||
<!-- Top bar -->
|
||||
<div class="top-bar">
|
||||
<div class="logo-sprite" id="logo-sprite" aria-label="L'Electron Rare — logo animé"></div>
|
||||
<span class="brand-mark">L'ELECTRON RARE</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tagline-bubble" id="tagline-bubble" aria-live="polite">
|
||||
<span class="tagline-text" id="tagline"></span>
|
||||
</div>
|
||||
<!-- Center — spacer (text is in WebGL) -->
|
||||
<div class="center-spacer">
|
||||
<div class="tagline-area" id="tagline-bubble" aria-live="polite">
|
||||
<span class="tagline-cursor">></span>
|
||||
<span class="tagline-text" id="tagline"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footnote-bubble">
|
||||
<span>Systèmes électroniques spécifiques · Formations · Prototypage</span>
|
||||
<!-- Bottom bar -->
|
||||
<div class="bottom-bar">
|
||||
<span class="bottom-left">electronique · automatisme · energie</span>
|
||||
<span class="bottom-right">mai 2026</span>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</BaseLayout>
|
||||
|
||||
<script>
|
||||
const LAUNCH = new Date('2026-05-01T00:00:00+02:00').getTime();
|
||||
const messages = [
|
||||
"Charge des condensateurs en cours...",
|
||||
"Calibration de l'oscilloscope...",
|
||||
@@ -77,17 +56,9 @@ import '@/styles/global.css';
|
||||
];
|
||||
let msgIndex = Math.floor(Math.random() * messages.length);
|
||||
|
||||
function pad(n) { return String(n).padStart(2, '0'); }
|
||||
function update() {
|
||||
const diff = Math.max(0, LAUNCH - Date.now());
|
||||
document.getElementById('days').textContent = pad(Math.floor(diff / 86400000));
|
||||
document.getElementById('hours').textContent = pad(Math.floor((diff % 86400000) / 3600000));
|
||||
document.getElementById('minutes').textContent = pad(Math.floor((diff % 3600000) / 60000));
|
||||
document.getElementById('seconds').textContent = pad(Math.floor((diff % 60000) / 1000));
|
||||
}
|
||||
function rotateMessage() {
|
||||
const bubble = document.getElementById('tagline-bubble');
|
||||
const el = document.getElementById('tagline');
|
||||
const bubble = document.getElementById('tagline-bubble')!;
|
||||
const el = document.getElementById('tagline')!;
|
||||
bubble.style.opacity = '0';
|
||||
bubble.style.transform = 'translateY(4px)';
|
||||
setTimeout(() => {
|
||||
@@ -98,180 +69,139 @@ import '@/styles/global.css';
|
||||
}, 400);
|
||||
}
|
||||
|
||||
// Logo sprite animation - cycle through 4 frames
|
||||
const frames = [
|
||||
'/assets/brand/logo-frame-1.png',
|
||||
'/assets/brand/logo-frame-2.png',
|
||||
'/assets/brand/logo-frame-3.png',
|
||||
'/assets/brand/logo-frame-4.png',
|
||||
'/assets/brand/logo-frame-3.png',
|
||||
'/assets/brand/logo-frame-2.png',
|
||||
];
|
||||
let frameIdx = 0;
|
||||
const sprite = document.getElementById('logo-sprite');
|
||||
const sprite = document.getElementById('logo-sprite')!;
|
||||
sprite.style.backgroundImage = `url(/assets/brand/logo-mark.png)`;
|
||||
|
||||
// Preload frames
|
||||
frames.forEach(f => { const img = new Image(); img.src = f; });
|
||||
|
||||
function animateLogo() {
|
||||
sprite.style.opacity = '0.85';
|
||||
setTimeout(() => {
|
||||
sprite.style.backgroundImage = `url(${frames[frameIdx]})`;
|
||||
sprite.style.opacity = '1';
|
||||
frameIdx = (frameIdx + 1) % frames.length;
|
||||
}, 150);
|
||||
}
|
||||
|
||||
update(); rotateMessage();
|
||||
sprite.style.backgroundImage = `url(${frames[0]})`;
|
||||
setInterval(update, 1000);
|
||||
rotateMessage();
|
||||
setInterval(rotateMessage, 8000);
|
||||
setInterval(animateLogo, 2000);
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.countdown-page {
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
overflow: hidden;
|
||||
background: #000;
|
||||
font-family: 'Manrope', -apple-system, 'SF Pro Display', 'Helvetica Neue', sans-serif;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* WebGL canvas — full background */
|
||||
.webgl-bg {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
/* Overlay — full viewport, vertical layout */
|
||||
.overlay {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: clamp(32px, 6vw, 80px) clamp(20px, 4vw, 48px);
|
||||
background-color: #ffffff;
|
||||
font-family: 'Manrope', -apple-system, 'SF Pro Display', 'Helvetica Neue', sans-serif;
|
||||
justify-content: space-between;
|
||||
padding: clamp(20px, 3vw, 40px) clamp(24px, 5vw, 60px);
|
||||
pointer-events: none;
|
||||
}
|
||||
.overlay > * { pointer-events: auto; }
|
||||
|
||||
/* Logo — clean, static display */
|
||||
/* ===== TOP BAR ===== */
|
||||
.top-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
animation: fade-in 0.8s ease both;
|
||||
}
|
||||
.logo-sprite {
|
||||
width: clamp(120px, 18vw, 180px);
|
||||
height: clamp(120px, 18vw, 180px);
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
margin-bottom: clamp(16px, 3vw, 32px);
|
||||
transition: opacity 0.3s ease;
|
||||
animation: gentle-float 6s ease-in-out infinite;
|
||||
filter: brightness(1.2) drop-shadow(0 0 12px rgba(91, 209, 216, 0.4));
|
||||
}
|
||||
@keyframes gentle-float {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-6px); }
|
||||
.brand-mark {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.25em;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.atom {
|
||||
width: clamp(100px, 16vw, 150px);
|
||||
height: auto;
|
||||
margin-bottom: clamp(16px, 2vw, 24px);
|
||||
opacity: 0.12;
|
||||
}
|
||||
|
||||
/* Title bubble */
|
||||
.title-bubble {
|
||||
margin-bottom: clamp(24px, 4vw, 40px);
|
||||
padding: 0;
|
||||
animation: fade-in 0.8s ease both;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-family: 'Manrope', -apple-system, 'SF Pro Display', sans-serif;
|
||||
font-size: clamp(32px, 6vw, 56px);
|
||||
font-weight: 800;
|
||||
color: #1d1d1f;
|
||||
margin: 0;
|
||||
letter-spacing: -0.03em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
.electric {
|
||||
color: #0071e3;
|
||||
}
|
||||
|
||||
/* Timer */
|
||||
.timer-bubble {
|
||||
background: #f5f5f7;
|
||||
border: none;
|
||||
border-radius: 20px;
|
||||
padding: clamp(16px, 3vw, 28px) clamp(24px, 4vw, 48px);
|
||||
margin-bottom: clamp(20px, 3vw, 32px);
|
||||
animation: fade-in 0.8s 0.2s ease both;
|
||||
}
|
||||
.timer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: clamp(8px, 2vw, 16px);
|
||||
}
|
||||
.unit {
|
||||
/* ===== CENTER SPACER ===== */
|
||||
.center-spacer {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.num {
|
||||
font-family: 'Manrope', -apple-system, 'SF Pro Display', sans-serif;
|
||||
font-size: clamp(36px, 8vw, 72px);
|
||||
font-weight: 300;
|
||||
color: #1d1d1f;
|
||||
line-height: 1;
|
||||
min-width: 2ch;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.label {
|
||||
font-family: 'Manrope', -apple-system, 'SF Pro Display', sans-serif;
|
||||
font-size: clamp(9px, 1.2vw, 12px);
|
||||
color: #86868b;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
margin-top: 8px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.sep {
|
||||
font-family: 'Manrope', -apple-system, 'SF Pro Display', sans-serif;
|
||||
font-size: clamp(24px, 5vw, 48px);
|
||||
color: #d2d2d7;
|
||||
font-weight: 200;
|
||||
margin-bottom: 18px;
|
||||
justify-content: flex-end;
|
||||
padding-bottom: clamp(20px, 3vw, 40px);
|
||||
}
|
||||
|
||||
/* Tagline */
|
||||
.tagline-bubble {
|
||||
padding: 12px 24px;
|
||||
margin-bottom: clamp(16px, 2vw, 24px);
|
||||
min-height: 44px;
|
||||
/* Tagline — terminal style */
|
||||
.tagline-area {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
min-height: 36px;
|
||||
padding: 10px 20px;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
border-radius: 8px;
|
||||
transition: opacity 0.4s ease, transform 0.4s ease;
|
||||
animation: fade-in 0.8s 0.4s ease both;
|
||||
animation: fade-in 0.8s 1.1s ease both;
|
||||
}
|
||||
.tagline-cursor {
|
||||
font-family: 'JetBrains Mono', 'SF Mono', 'Fira Code', monospace;
|
||||
font-size: 14px;
|
||||
color: #5bd1d8;
|
||||
animation: blink 1s step-end infinite;
|
||||
}
|
||||
@keyframes blink {
|
||||
50% { opacity: 0; }
|
||||
}
|
||||
.tagline-text {
|
||||
font-family: 'Manrope', -apple-system, 'SF Pro Display', sans-serif;
|
||||
font-size: clamp(13px, 1.6vw, 16px);
|
||||
color: #86868b;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
/* Footnote */
|
||||
.footnote-bubble {
|
||||
padding: clamp(8px, 1.5vw, 12px) 20px;
|
||||
animation: fade-in 0.8s 0.6s ease both;
|
||||
}
|
||||
.footnote-bubble span {
|
||||
font-family: 'Manrope', -apple-system, 'SF Pro Display', sans-serif;
|
||||
font-size: clamp(11px, 1.2vw, 13px);
|
||||
color: #aeaeb2;
|
||||
letter-spacing: 0.02em;
|
||||
font-family: 'JetBrains Mono', 'SF Mono', 'Fira Code', monospace;
|
||||
font-size: clamp(11px, 1.3vw, 14px);
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
/* ===== BOTTOM BAR ===== */
|
||||
.bottom-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
animation: fade-in 0.8s 1.2s ease both;
|
||||
}
|
||||
.bottom-left, .bottom-right {
|
||||
font-size: clamp(10px, 1.1vw, 12px);
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.08em;
|
||||
color: rgba(255, 255, 255, 0.15);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* ===== Animations ===== */
|
||||
@keyframes fade-in {
|
||||
from { opacity: 0; transform: translateY(12px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.timer-hero { gap: 2px; }
|
||||
.sep { margin: 0; }
|
||||
.bottom-bar { flex-direction: column; align-items: center; gap: 4px; }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.logo-sprite { animation: none; }
|
||||
.title-bubble, .timer-bubble, .tagline-bubble, .footnote-bubble {
|
||||
animation: none;
|
||||
opacity: 1;
|
||||
}
|
||||
.logo-sprite, .title, .timer-hero, .divider,
|
||||
.tagline-area, .subtitle, .overline,
|
||||
.top-bar, .bottom-bar { animation: none; opacity: 1; }
|
||||
.webgl-bg { display: none; }
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user