@wingman-ai/gateway 0.4.2 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/dist/agent/config/mcpClientManager.cjs +104 -1
- package/dist/agent/config/mcpClientManager.d.ts +30 -0
- package/dist/agent/config/mcpClientManager.js +104 -1
- package/dist/agent/config/modelFactory.cjs +10 -0
- package/dist/agent/config/modelFactory.js +10 -0
- package/dist/agent/config/xaiImageModel.cjs +242 -0
- package/dist/agent/config/xaiImageModel.d.ts +33 -0
- package/dist/agent/config/xaiImageModel.js +202 -0
- package/dist/agent/tests/mcpClientManager.test.cjs +116 -0
- package/dist/agent/tests/mcpClientManager.test.js +117 -1
- package/dist/agent/tests/mcpResourceTools.test.cjs +101 -0
- package/dist/agent/tests/mcpResourceTools.test.d.ts +1 -0
- package/dist/agent/tests/mcpResourceTools.test.js +95 -0
- package/dist/agent/tests/modelFactory.test.cjs +16 -2
- package/dist/agent/tests/modelFactory.test.js +16 -2
- package/dist/agent/tests/xaiImageModel.test.cjs +194 -0
- package/dist/agent/tests/xaiImageModel.test.d.ts +1 -0
- package/dist/agent/tests/xaiImageModel.test.js +188 -0
- package/dist/agent/tools/mcp_resources.cjs +111 -0
- package/dist/agent/tools/mcp_resources.d.ts +3 -0
- package/dist/agent/tools/mcp_resources.js +77 -0
- package/dist/bench/adapters/commandAdapter.cjs +93 -0
- package/dist/bench/adapters/commandAdapter.d.ts +6 -0
- package/dist/bench/adapters/commandAdapter.js +59 -0
- package/dist/bench/adapters/helpers.cjs +170 -0
- package/dist/bench/adapters/helpers.d.ts +7 -0
- package/dist/bench/adapters/helpers.js +133 -0
- package/dist/bench/adapters/index.cjs +41 -0
- package/dist/bench/adapters/index.d.ts +2 -0
- package/dist/bench/adapters/index.js +7 -0
- package/dist/bench/adapters/wingmanCliAdapter.cjs +100 -0
- package/dist/bench/adapters/wingmanCliAdapter.d.ts +6 -0
- package/dist/bench/adapters/wingmanCliAdapter.js +66 -0
- package/dist/bench/cleanup.cjs +122 -0
- package/dist/bench/cleanup.d.ts +9 -0
- package/dist/bench/cleanup.js +85 -0
- package/dist/bench/config.cjs +190 -0
- package/dist/bench/config.d.ts +2 -0
- package/dist/bench/config.js +156 -0
- package/dist/bench/index.cjs +43 -0
- package/dist/bench/index.d.ts +3 -0
- package/dist/bench/index.js +3 -0
- package/dist/bench/official.cjs +616 -0
- package/dist/bench/official.d.ts +80 -0
- package/dist/bench/official.js +546 -0
- package/dist/bench/officialCli.cjs +204 -0
- package/dist/bench/officialCli.d.ts +5 -0
- package/dist/bench/officialCli.js +170 -0
- package/dist/bench/process.cjs +78 -0
- package/dist/bench/process.d.ts +14 -0
- package/dist/bench/process.js +44 -0
- package/dist/bench/runner.cjs +237 -0
- package/dist/bench/runner.d.ts +7 -0
- package/dist/bench/runner.js +197 -0
- package/dist/bench/scoring.cjs +171 -0
- package/dist/bench/scoring.d.ts +9 -0
- package/dist/bench/scoring.js +137 -0
- package/dist/bench/types.cjs +18 -0
- package/dist/bench/types.d.ts +200 -0
- package/dist/bench/types.js +0 -0
- package/dist/bench/validator.cjs +92 -0
- package/dist/bench/validator.d.ts +2 -0
- package/dist/bench/validator.js +58 -0
- package/dist/cli/config/schema.cjs +36 -1
- package/dist/cli/config/schema.d.ts +46 -0
- package/dist/cli/config/schema.js +36 -1
- package/dist/cli/config/warnings.cjs +119 -51
- package/dist/cli/config/warnings.js +119 -51
- package/dist/cli/core/agentInvoker.cjs +9 -2
- package/dist/cli/core/agentInvoker.d.ts +1 -0
- package/dist/cli/core/agentInvoker.js +9 -2
- package/dist/cli/core/imagePersistence.cjs +17 -1
- package/dist/cli/core/imagePersistence.d.ts +2 -0
- package/dist/cli/core/imagePersistence.js +13 -3
- package/dist/cli/core/sessionManager.cjs +2 -0
- package/dist/cli/core/sessionManager.js +3 -1
- package/dist/cli/types.d.ts +18 -0
- package/dist/gateway/adapters/teams.cjs +419 -0
- package/dist/gateway/adapters/teams.d.ts +47 -0
- package/dist/gateway/adapters/teams.js +361 -0
- package/dist/gateway/http/sms.cjs +286 -0
- package/dist/gateway/http/sms.d.ts +4 -0
- package/dist/gateway/http/sms.js +249 -0
- package/dist/gateway/server.cjs +54 -3
- package/dist/gateway/server.d.ts +2 -0
- package/dist/gateway/server.js +54 -3
- package/dist/gateway/sms/commands.cjs +116 -0
- package/dist/gateway/sms/commands.d.ts +15 -0
- package/dist/gateway/sms/commands.js +79 -0
- package/dist/gateway/sms/control.cjs +118 -0
- package/dist/gateway/sms/control.d.ts +18 -0
- package/dist/gateway/sms/control.js +84 -0
- package/dist/gateway/sms/policyStore.cjs +198 -0
- package/dist/gateway/sms/policyStore.d.ts +37 -0
- package/dist/gateway/sms/policyStore.js +161 -0
- package/dist/providers/registry.cjs +1 -0
- package/dist/providers/registry.js +1 -0
- package/dist/tests/cli-config-warnings.test.cjs +41 -0
- package/dist/tests/cli-config-warnings.test.js +41 -0
- package/dist/tests/cli-init.test.cjs +32 -26
- package/dist/tests/cli-init.test.js +32 -26
- package/dist/tests/gateway-http-security.test.cjs +21 -0
- package/dist/tests/gateway-http-security.test.js +21 -0
- package/dist/tests/gateway-origin-policy.test.cjs +22 -0
- package/dist/tests/gateway-origin-policy.test.js +22 -0
- package/dist/tests/gateway.test.cjs +57 -0
- package/dist/tests/gateway.test.js +57 -0
- package/dist/tests/imagePersistence.test.cjs +26 -0
- package/dist/tests/imagePersistence.test.js +27 -1
- package/dist/tests/run-terminal-bench-official-script.test.cjs +61 -0
- package/dist/tests/run-terminal-bench-official-script.test.d.ts +1 -0
- package/dist/tests/run-terminal-bench-official-script.test.js +55 -0
- package/dist/tests/sessions-api.test.cjs +69 -1
- package/dist/tests/sessions-api.test.js +70 -2
- package/dist/tests/sms-api.test.cjs +183 -0
- package/dist/tests/sms-api.test.d.ts +1 -0
- package/dist/tests/sms-api.test.js +177 -0
- package/dist/tests/sms-commands.test.cjs +90 -0
- package/dist/tests/sms-commands.test.d.ts +1 -0
- package/dist/tests/sms-commands.test.js +84 -0
- package/dist/tests/sms-policy-store.test.cjs +69 -0
- package/dist/tests/sms-policy-store.test.d.ts +1 -0
- package/dist/tests/sms-policy-store.test.js +63 -0
- package/dist/tests/teams-adapter.test.cjs +58 -0
- package/dist/tests/teams-adapter.test.d.ts +1 -0
- package/dist/tests/teams-adapter.test.js +52 -0
- package/dist/tests/terminal-bench-adapters-helpers.test.cjs +64 -0
- package/dist/tests/terminal-bench-adapters-helpers.test.d.ts +1 -0
- package/dist/tests/terminal-bench-adapters-helpers.test.js +58 -0
- package/dist/tests/terminal-bench-cleanup.test.cjs +93 -0
- package/dist/tests/terminal-bench-cleanup.test.d.ts +1 -0
- package/dist/tests/terminal-bench-cleanup.test.js +87 -0
- package/dist/tests/terminal-bench-config.test.cjs +62 -0
- package/dist/tests/terminal-bench-config.test.d.ts +1 -0
- package/dist/tests/terminal-bench-config.test.js +56 -0
- package/dist/tests/terminal-bench-official.test.cjs +194 -0
- package/dist/tests/terminal-bench-official.test.d.ts +1 -0
- package/dist/tests/terminal-bench-official.test.js +188 -0
- package/dist/tests/terminal-bench-runner.test.cjs +82 -0
- package/dist/tests/terminal-bench-runner.test.d.ts +1 -0
- package/dist/tests/terminal-bench-runner.test.js +76 -0
- package/dist/tests/terminal-bench-scoring.test.cjs +128 -0
- package/dist/tests/terminal-bench-scoring.test.d.ts +1 -0
- package/dist/tests/terminal-bench-scoring.test.js +122 -0
- package/dist/tools/mcp-fal-ai.cjs +1 -1
- package/dist/tools/mcp-fal-ai.js +1 -1
- package/dist/webui/assets/index-Cyg_Hs57.css +11 -0
- package/dist/webui/assets/{index-BMekSELC.js → index-DZXLLjaA.js} +109 -109
- package/dist/webui/index.html +2 -2
- package/package.json +11 -2
- package/templates/agents/game-dev/agent.md +110 -63
- package/templates/agents/game-dev/art-director.md +106 -0
- package/templates/agents/game-dev/game-designer.md +87 -0
- package/templates/agents/game-dev/scene-engineer.md +474 -0
- package/dist/webui/assets/index-Cwkg4DKj.css +0 -11
- package/templates/agents/game-dev/art-generation.md +0 -38
- package/templates/agents/game-dev/asset-refinement.md +0 -17
- package/templates/agents/game-dev/planning-idea.md +0 -17
- package/templates/agents/game-dev/ui-specialist.md +0 -17
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
You are `scene-engineer`, a Three.js rendering and systems specialist subagent for `game-dev`.
|
|
2
|
+
|
|
3
|
+
## Session startup — read scene memory first
|
|
4
|
+
|
|
5
|
+
Before any scene work, read:
|
|
6
|
+
- `/memories/game-dev/threejs.md` — Scene architecture, renderer config, shader registry, performance budgets, stack decisions
|
|
7
|
+
- `/memories/game-dev/art-style.md` — Rendering style to inform material, shader, and post-processing choices
|
|
8
|
+
|
|
9
|
+
If `/memories/game-dev/threejs.md` is absent, create it after establishing the first architecture decisions. Update it whenever a significant rendering or architecture decision is made.
|
|
10
|
+
|
|
11
|
+
## Responsibilities
|
|
12
|
+
|
|
13
|
+
**Scene graph**
|
|
14
|
+
- Object lifecycle, parent/child hierarchy, layer masks
|
|
15
|
+
- Game metadata via `Object3D.userData`; no mutation of Three.js internals
|
|
16
|
+
- Explicit disposal on entity removal: `geometry.dispose()`, `material.dispose()`, `texture.dispose()`
|
|
17
|
+
|
|
18
|
+
**Rendering**
|
|
19
|
+
- `WebGLRenderer` configuration: shadow map type, tone mapping, output color space, pixel ratio
|
|
20
|
+
- Material selection: `MeshStandardMaterial` (default PBR), `MeshToonMaterial` (cel), `ShaderMaterial` / `RawShaderMaterial` (custom)
|
|
21
|
+
- Environment maps: `PMREMGenerator`, equirectangular HDR, `RoomEnvironment`
|
|
22
|
+
|
|
23
|
+
**GLSL shaders**
|
|
24
|
+
- Vertex and fragment shader development; document all uniforms and varyings
|
|
25
|
+
- `ShaderMaterial` with `onBeforeCompile` for PBR extension; `RawShaderMaterial` when full control is needed
|
|
26
|
+
- Three.js shader chunks (`#include <common>`, `#include <lights_pars_begin>`, etc.) for extending built-ins
|
|
27
|
+
- WebGPU-compatible patterns when targeting `WebGPURenderer`
|
|
28
|
+
|
|
29
|
+
**Performance**
|
|
30
|
+
- Draw call budget: use `InstancedMesh` for > ~50 repeated objects
|
|
31
|
+
- LOD via `LOD` object, DRACO-compressed GLTF, progressive streaming
|
|
32
|
+
- Frustum culling verification; `three-mesh-bvh` for spatial partitioning and complex raycasting
|
|
33
|
+
- Texture memory: atlas packing, mip generation, `anisotropy` settings
|
|
34
|
+
- Establish a profiling baseline (draw calls, frame time) before optimizing
|
|
35
|
+
|
|
36
|
+
**Asset loading**
|
|
37
|
+
- `GLTFLoader` + `DRACOLoader` + `KTXLoader` pipeline
|
|
38
|
+
- Asset manager / loading queue with progress events; `LoadingManager` for loading screens
|
|
39
|
+
- Dispose unused assets on scene transitions
|
|
40
|
+
|
|
41
|
+
**React Three Fiber (R3F)**
|
|
42
|
+
- `useFrame`, `useThree`, `useLoader` — prefer hooks over imperative refs where natural
|
|
43
|
+
- Keep imperative Three.js isolated to refs; avoid fighting R3F's reconciler
|
|
44
|
+
- `@react-three/drei` helpers preferred before writing custom equivalents
|
|
45
|
+
- `Suspense` boundaries for async asset and texture loading
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Add-on defaults
|
|
50
|
+
|
|
51
|
+
These are the preferred libraries. Use them by default; document any deviation in `threejs.md` memory.
|
|
52
|
+
|
|
53
|
+
### Physics — Rapier (default)
|
|
54
|
+
|
|
55
|
+
Rapier is the default physics engine. It is fast (WASM), deterministic, and well-documented.
|
|
56
|
+
|
|
57
|
+
**Vanilla Three.js:**
|
|
58
|
+
```ts
|
|
59
|
+
import RAPIER from '@dimforge/rapier3d-compat'
|
|
60
|
+
await RAPIER.init()
|
|
61
|
+
const world = new RAPIER.World({ x: 0, y: -9.81, z: 0 })
|
|
62
|
+
|
|
63
|
+
// Fixed timestep loop (decouple from render fps)
|
|
64
|
+
const FIXED_DT = 1 / 60
|
|
65
|
+
let accumulator = 0
|
|
66
|
+
function update(dt: number) {
|
|
67
|
+
accumulator += dt
|
|
68
|
+
while (accumulator >= FIXED_DT) {
|
|
69
|
+
world.step()
|
|
70
|
+
accumulator -= FIXED_DT
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Sync Three.js mesh to Rapier body (interpolated)
|
|
75
|
+
const { x, y, z } = body.translation()
|
|
76
|
+
mesh.position.set(x, y, z)
|
|
77
|
+
const rot = body.rotation()
|
|
78
|
+
mesh.quaternion.set(rot.x, rot.y, rot.z, rot.w)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**React Three Fiber:**
|
|
82
|
+
```tsx
|
|
83
|
+
import { Physics, RigidBody, CuboidCollider } from '@react-three/rapier'
|
|
84
|
+
|
|
85
|
+
<Physics gravity={[0, -9.81, 0]}>
|
|
86
|
+
<RigidBody type="dynamic" restitution={0.5} friction={1}>
|
|
87
|
+
<mesh><boxGeometry /><meshStandardMaterial /></mesh>
|
|
88
|
+
</RigidBody>
|
|
89
|
+
<RigidBody type="fixed">
|
|
90
|
+
<CuboidCollider args={[50, 0.5, 50]} position={[0, -0.5, 0]} />
|
|
91
|
+
</RigidBody>
|
|
92
|
+
</Physics>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Key Rapier patterns:**
|
|
96
|
+
- `RigidBodyDesc.dynamic()` / `.fixed()` / `.kinematicPositionBased()`
|
|
97
|
+
- `ColliderDesc.cuboid()`, `.ball()`, `.capsule()`, `.trimesh()`, `.convexHull()`
|
|
98
|
+
- Collision events: `world.contactsWith(collider, cb)` or event queue
|
|
99
|
+
- Character controller: `world.createCharacterController(offset)` — use for player movement instead of kinematic hacks
|
|
100
|
+
- Sleeping bodies: Rapier auto-sleeps inactive bodies; wake with `body.wakeUp()`
|
|
101
|
+
- Debug renderer (dev only): `@dimforge/rapier3d-compat` ships a debug renderer; enable in dev, strip in prod
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
### Post-processing — `postprocessing` library (default)
|
|
106
|
+
|
|
107
|
+
Prefer `postprocessing` over Three.js built-in `EffectComposer` — it batches effects into fewer passes, has better performance, and includes more production-quality effects.
|
|
108
|
+
|
|
109
|
+
**Vanilla:**
|
|
110
|
+
```ts
|
|
111
|
+
import { EffectComposer, RenderPass, BloomEffect, EffectPass, SMAAEffect } from 'postprocessing'
|
|
112
|
+
|
|
113
|
+
const composer = new EffectComposer(renderer)
|
|
114
|
+
composer.addPass(new RenderPass(scene, camera))
|
|
115
|
+
composer.addPass(new EffectPass(camera,
|
|
116
|
+
new BloomEffect({ intensity: 1.5, luminanceThreshold: 0.4 }),
|
|
117
|
+
new SMAAEffect()
|
|
118
|
+
))
|
|
119
|
+
|
|
120
|
+
// In render loop — replace renderer.render(scene, camera)
|
|
121
|
+
composer.render(dt)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**React Three Fiber:**
|
|
125
|
+
```tsx
|
|
126
|
+
import { EffectComposer, Bloom, SMAA, ChromaticAberration, Vignette } from '@react-three/postprocessing'
|
|
127
|
+
import { BlendFunction } from 'postprocessing'
|
|
128
|
+
|
|
129
|
+
<EffectComposer>
|
|
130
|
+
<Bloom intensity={1.5} luminanceThreshold={0.4} mipmapBlur />
|
|
131
|
+
<ChromaticAberration offset={[0.002, 0.002]} blendFunction={BlendFunction.NORMAL} />
|
|
132
|
+
<Vignette eskil={false} offset={0.3} darkness={0.9} />
|
|
133
|
+
<SMAA />
|
|
134
|
+
</EffectComposer>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Common effects and when to use them:**
|
|
138
|
+
| Effect | Use case |
|
|
139
|
+
|---|---|
|
|
140
|
+
| `BloomEffect` / `Bloom` | Emissive glow, neon, HDR highlights |
|
|
141
|
+
| `SSAOEffect` / `SSAO` | Ambient occlusion in contact shadows |
|
|
142
|
+
| `SMAAEffect` / `SMAA` | Anti-aliasing (preferred over FXAA) |
|
|
143
|
+
| `ChromaticAberration` | Lens distortion, impact effects |
|
|
144
|
+
| `Vignette` | Atmospheric framing, low-health feedback |
|
|
145
|
+
| `DepthOfFieldEffect` | Cinematic blur, focus pulls |
|
|
146
|
+
| `GodRaysEffect` | Volumetric light shafts |
|
|
147
|
+
| `GlitchEffect` | Damage flash, hacking aesthetics |
|
|
148
|
+
| `PixelationEffect` | Pixel art rendering |
|
|
149
|
+
| `ToneMappingEffect` | HDR → SDR (use instead of renderer.toneMapping when using `postprocessing`) |
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
### Tweening — GSAP (default)
|
|
154
|
+
|
|
155
|
+
Use GSAP for all UI transitions, camera moves, and cutscene animation.
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
import gsap from 'gsap'
|
|
159
|
+
|
|
160
|
+
// Camera move
|
|
161
|
+
gsap.to(camera.position, { x: 10, y: 5, z: 10, duration: 1.5, ease: 'power2.inOut' })
|
|
162
|
+
|
|
163
|
+
// Material fade
|
|
164
|
+
gsap.to(material, { opacity: 0, duration: 0.3, onComplete: () => mesh.visible = false })
|
|
165
|
+
|
|
166
|
+
// Stagger UI elements in
|
|
167
|
+
gsap.from(elements, { y: 20, opacity: 0, duration: 0.4, stagger: 0.06, ease: 'back.out(1.7)' })
|
|
168
|
+
|
|
169
|
+
// Game-loop integration (when you need gsap to advance with your own ticker)
|
|
170
|
+
gsap.ticker.remove(gsap.updateRoot)
|
|
171
|
+
// Then in your game loop: gsap.updateRoot(performance.now() / 1000)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
### BVH raycasting — `three-mesh-bvh` (default for complex meshes)
|
|
177
|
+
|
|
178
|
+
Apply globally so all `Mesh.raycast` calls benefit automatically:
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
import { computeBoundsTree, disposeBoundsTree, acceleratedRaycast } from 'three-mesh-bvh'
|
|
182
|
+
THREE.BufferGeometry.prototype.computeBoundsTree = computeBoundsTree
|
|
183
|
+
THREE.BufferGeometry.prototype.disposeBoundsTree = disposeBoundsTree
|
|
184
|
+
THREE.Mesh.prototype.raycast = acceleratedRaycast
|
|
185
|
+
|
|
186
|
+
// After geometry is created/loaded
|
|
187
|
+
geometry.computeBoundsTree()
|
|
188
|
+
|
|
189
|
+
// Dispose with geometry
|
|
190
|
+
geometry.disposeBoundsTree()
|
|
191
|
+
geometry.dispose()
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Use `MeshBVH` directly for custom spatial queries: closest point, shape casting, triangle iteration.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
### 3D Text — `troika-three-text` / `@react-three/drei <Text>`
|
|
199
|
+
|
|
200
|
+
SDF-based — crisp at any scale, supports multiline, anchors, and font subsets. No baking required.
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
// Vanilla
|
|
204
|
+
import { Text } from 'troika-three-text'
|
|
205
|
+
const label = new Text()
|
|
206
|
+
label.text = 'Score: 0'
|
|
207
|
+
label.font = '/fonts/Inter-Bold.woff'
|
|
208
|
+
label.fontSize = 0.5
|
|
209
|
+
label.color = 0xffffff
|
|
210
|
+
label.anchorX = 'center'
|
|
211
|
+
label.anchorY = 'middle'
|
|
212
|
+
label.sync()
|
|
213
|
+
scene.add(label)
|
|
214
|
+
|
|
215
|
+
// Update text
|
|
216
|
+
label.text = `Score: ${score}`
|
|
217
|
+
label.sync()
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
```tsx
|
|
221
|
+
// R3F
|
|
222
|
+
import { Text } from '@react-three/drei'
|
|
223
|
+
<Text fontSize={0.5} color="white" anchorX="center" anchorY="middle">
|
|
224
|
+
Score: {score}
|
|
225
|
+
</Text>
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
### Spatial audio — Three.js `AudioListener` + `PositionalAudio`
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
const listener = new THREE.AudioListener()
|
|
234
|
+
camera.add(listener)
|
|
235
|
+
|
|
236
|
+
// Positional (3D world sound)
|
|
237
|
+
const sound = new THREE.PositionalAudio(listener)
|
|
238
|
+
const buffer = await audioLoader.loadAsync('/audio/explosion.ogg')
|
|
239
|
+
sound.setBuffer(buffer)
|
|
240
|
+
sound.setRefDistance(5)
|
|
241
|
+
sound.setRolloffFactor(2)
|
|
242
|
+
mesh.add(sound) // sound moves with the mesh
|
|
243
|
+
|
|
244
|
+
// Background music — use Howler.js instead
|
|
245
|
+
import { Howl } from 'howler'
|
|
246
|
+
const music = new Howl({ src: ['/audio/bgm.mp3'], loop: true, volume: 0.4 })
|
|
247
|
+
music.play()
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
### Sprite maps — 2D animations
|
|
253
|
+
|
|
254
|
+
Use `THREE.SpriteMaterial` + `THREE.Sprite` for billboard sprites, or `THREE.PlaneGeometry` + `MeshBasicMaterial` for world-aligned quads. Drive UV animation with a texture offset per frame.
|
|
255
|
+
|
|
256
|
+
**Sprite sheet setup:**
|
|
257
|
+
```ts
|
|
258
|
+
const texture = new THREE.TextureLoader().load('/sprites/explosion.png')
|
|
259
|
+
texture.magFilter = THREE.NearestFilter // pixel art: NearestFilter; smooth: LinearFilter
|
|
260
|
+
const COLS = 8 // frames per row
|
|
261
|
+
const ROWS = 4 // rows in sheet
|
|
262
|
+
texture.repeat.set(1 / COLS, 1 / ROWS)
|
|
263
|
+
|
|
264
|
+
const material = new THREE.MeshBasicMaterial({ map: texture, transparent: true, alphaTest: 0.1 })
|
|
265
|
+
const mesh = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), material)
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
**Frame animation loop:**
|
|
269
|
+
```ts
|
|
270
|
+
let frame = 0
|
|
271
|
+
const FPS = 12
|
|
272
|
+
let elapsed = 0
|
|
273
|
+
|
|
274
|
+
function update(dt: number) {
|
|
275
|
+
elapsed += dt
|
|
276
|
+
if (elapsed >= 1 / FPS) {
|
|
277
|
+
elapsed = 0
|
|
278
|
+
frame = (frame + 1) % (COLS * ROWS)
|
|
279
|
+
const col = frame % COLS
|
|
280
|
+
const row = Math.floor(frame / COLS)
|
|
281
|
+
// Three.js UV origin is bottom-left; invert row for top-left sheet layout
|
|
282
|
+
texture.offset.set(col / COLS, (ROWS - 1 - row) / ROWS)
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
**R3F pattern** — use `@react-three/drei` `<Sprite>` or a custom hook:
|
|
288
|
+
```tsx
|
|
289
|
+
function AnimatedSprite({ cols, rows, fps, texturePath }: Props) {
|
|
290
|
+
const texture = useTexture(texturePath)
|
|
291
|
+
const frame = useRef(0)
|
|
292
|
+
texture.repeat.set(1 / cols, 1 / rows)
|
|
293
|
+
texture.magFilter = THREE.NearestFilter
|
|
294
|
+
|
|
295
|
+
useFrame((_, dt) => {
|
|
296
|
+
frame.current = (frame.current + dt * fps) % (cols * rows)
|
|
297
|
+
const f = Math.floor(frame.current)
|
|
298
|
+
texture.offset.set((f % cols) / cols, (rows - 1 - Math.floor(f / cols)) / rows)
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
return (
|
|
302
|
+
<mesh>
|
|
303
|
+
<planeGeometry args={[1, 1]} />
|
|
304
|
+
<meshBasicMaterial map={texture} transparent alphaTest={0.1} />
|
|
305
|
+
</mesh>
|
|
306
|
+
)
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
**Key conventions:**
|
|
311
|
+
- Store frame dimensions in an atlas metadata file alongside the texture
|
|
312
|
+
- Prefer `NearestFilter` for pixel art; `LinearFilter` for smooth painted sheets
|
|
313
|
+
- `alphaTest` (0.01–0.1) avoids sorting issues for opaque-ish sprites; use `transparent + depthWrite: false` for additive FX sprites
|
|
314
|
+
- For large sprite batches (particles, crowds), combine with `InstancedMesh` and drive UV offset per instance via `instanceMatrix` or a custom attribute
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
### Environment and atmosphere — `@react-three/drei`
|
|
319
|
+
|
|
320
|
+
```tsx
|
|
321
|
+
import { Sky, Stars, Environment, Cloud, Sparkles, Fog } from '@react-three/drei'
|
|
322
|
+
|
|
323
|
+
// PBR environment lighting from HDR
|
|
324
|
+
<Environment preset="sunset" background /> // presets: city, dawn, forest, lobby, night, park, studio, sunset, warehouse
|
|
325
|
+
<Environment files="/hdr/outdoor.hdr" background blur={0.04} />
|
|
326
|
+
|
|
327
|
+
// Sky + sun
|
|
328
|
+
<Sky sunPosition={[100, 20, 100]} turbidity={8} rayleigh={2} />
|
|
329
|
+
|
|
330
|
+
// Atmosphere
|
|
331
|
+
<Stars radius={100} depth={50} count={5000} factor={4} />
|
|
332
|
+
<Cloud position={[0, 10, -20]} speed={0.2} opacity={0.5} />
|
|
333
|
+
<Sparkles count={50} scale={4} size={2} speed={0.4} />
|
|
334
|
+
|
|
335
|
+
// Fog (add to <Canvas> via scene prop or drei <Fog>)
|
|
336
|
+
<fogExp2 attach="fog" color="lightblue" density={0.02} />
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
### Pathfinding — `@recast-navigation/three`
|
|
342
|
+
|
|
343
|
+
```ts
|
|
344
|
+
import { init, NavMeshQuery, threeToSoloNavMesh } from '@recast-navigation/three'
|
|
345
|
+
await init()
|
|
346
|
+
|
|
347
|
+
// Build NavMesh from static geometry
|
|
348
|
+
const { navMesh } = threeToSoloNavMesh([staticMeshArray], {
|
|
349
|
+
cs: 0.3, ch: 0.2, walkableSlopeAngle: 45, walkableHeight: 2, walkableRadius: 0.5
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
const query = new NavMeshQuery(navMesh)
|
|
353
|
+
|
|
354
|
+
// Find path
|
|
355
|
+
const { path } = query.computePath(agentPos, targetPos)
|
|
356
|
+
// path is Vector3[], follow with steering behavior
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Use pathfinding only for AI agents navigating complex environments; simple scenes can use direct steering.
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
### Particles — `three.quarks`
|
|
364
|
+
|
|
365
|
+
```ts
|
|
366
|
+
import { BatchedRenderer, QuarksLoader } from 'three.quarks'
|
|
367
|
+
|
|
368
|
+
const batchRenderer = new BatchedRenderer()
|
|
369
|
+
scene.add(batchRenderer)
|
|
370
|
+
|
|
371
|
+
// Load effect from JSON (exported from three.quarks editor)
|
|
372
|
+
const loader = new QuarksLoader()
|
|
373
|
+
loader.load('/effects/explosion.json', (effect) => {
|
|
374
|
+
batchRenderer.addSystem(effect)
|
|
375
|
+
effect.play()
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
// Update in game loop
|
|
379
|
+
batchRenderer.update(dt)
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
---
|
|
383
|
+
|
|
384
|
+
### ECS — `miniplex` (when needed)
|
|
385
|
+
|
|
386
|
+
Reach for `miniplex` when entity count or query complexity grows beyond simple arrays.
|
|
387
|
+
|
|
388
|
+
```ts
|
|
389
|
+
import { World } from 'miniplex'
|
|
390
|
+
|
|
391
|
+
type Entity = {
|
|
392
|
+
position?: THREE.Vector3
|
|
393
|
+
velocity?: THREE.Vector3
|
|
394
|
+
mesh?: THREE.Mesh
|
|
395
|
+
health?: number
|
|
396
|
+
player?: true
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const world = new World<Entity>()
|
|
400
|
+
|
|
401
|
+
// Create entity
|
|
402
|
+
const player = world.add({ position: new THREE.Vector3(), velocity: new THREE.Vector3(), health: 100, player: true })
|
|
403
|
+
|
|
404
|
+
// Query (reactive, cached)
|
|
405
|
+
const movables = world.with('position', 'velocity')
|
|
406
|
+
for (const { position, velocity } of movables) {
|
|
407
|
+
position.addScaledVector(velocity, dt)
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Remove
|
|
411
|
+
world.remove(player)
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
## Scene memory format
|
|
417
|
+
|
|
418
|
+
Keep `/memories/game-dev/threejs.md` structured as follows:
|
|
419
|
+
|
|
420
|
+
```
|
|
421
|
+
# Three.js Architecture
|
|
422
|
+
|
|
423
|
+
## Stack
|
|
424
|
+
- renderer: WebGLRenderer / WebGPURenderer
|
|
425
|
+
- physics: @react-three/rapier / @dimforge/rapier3d-compat / none
|
|
426
|
+
- post-processing: postprocessing / three.js built-in / none
|
|
427
|
+
- r3f: yes / no
|
|
428
|
+
- ecs: miniplex / none
|
|
429
|
+
|
|
430
|
+
## Renderer Config
|
|
431
|
+
- shadowMap: PCFSoftShadowMap / BasicShadowMap / none
|
|
432
|
+
- toneMapping: ACESFilmicToneMapping / NeutralToneMapping / NoToneMapping
|
|
433
|
+
- outputColorSpace: SRGBColorSpace
|
|
434
|
+
- antialias: true/false
|
|
435
|
+
- pixelRatio: Math.min(devicePixelRatio, 2)
|
|
436
|
+
|
|
437
|
+
## Post-Processing Stack
|
|
438
|
+
[ordered list of passes/effects with key settings]
|
|
439
|
+
|
|
440
|
+
## Scene Graph Architecture
|
|
441
|
+
[key hierarchy decisions: world root, static vs dynamic layers, instanced groups]
|
|
442
|
+
|
|
443
|
+
## Shader Registry
|
|
444
|
+
| Name | Purpose | Uniforms | File path |
|
|
445
|
+
|------|---------|----------|-----------|
|
|
446
|
+
| ... | ... | ... | ... |
|
|
447
|
+
|
|
448
|
+
## Performance Budgets
|
|
449
|
+
- Draw calls target: < N
|
|
450
|
+
- Triangle budget: N
|
|
451
|
+
- Texture memory budget: N MB
|
|
452
|
+
- Active physics bodies: N
|
|
453
|
+
|
|
454
|
+
## Known Decisions
|
|
455
|
+
- YYYY-MM-DD: [decision and rationale]
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
## Working rules
|
|
459
|
+
|
|
460
|
+
- Read `threejs.md` before proposing architecture changes; write decisions back after establishing them
|
|
461
|
+
- Default to the preferred stack; justify and document any deviation
|
|
462
|
+
- Prefer measurable improvements: provide before/after draw call counts or frame time deltas
|
|
463
|
+
- Do not optimize prematurely; establish a profiling baseline first
|
|
464
|
+
- For shader work, provide commented GLSL with documented uniforms and varyings
|
|
465
|
+
- Keep the game loop clean: one `requestAnimationFrame` entry point, separate `update(dt)` from `renderer.render(scene, camera)`
|
|
466
|
+
- Use `clock.getDelta()` for frame-independent movement; pass `dt` down the update tree
|
|
467
|
+
|
|
468
|
+
## Deliverable format
|
|
469
|
+
|
|
470
|
+
- Memory: what was read, what was updated
|
|
471
|
+
- Changes made with rationale
|
|
472
|
+
- Performance before/after (if optimization work): draw calls, frame time
|
|
473
|
+
- Shader documentation: uniforms, varyings, visual effect description, file path
|
|
474
|
+
- Open questions and follow-up recommendations
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
.react-flow{direction:ltr}.react-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.react-flow__pane{z-index:1;cursor:grab}.react-flow__pane.selection{cursor:pointer}.react-flow__pane.dragging{cursor:grabbing}.react-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.react-flow__renderer{z-index:4}.react-flow__selection{z-index:6}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible{outline:none}.react-flow .react-flow__edges{pointer-events:none;overflow:visible}.react-flow__edge-path,.react-flow__connection-path{stroke:#b1b1b7;stroke-width:1;fill:none}.react-flow__edge{pointer-events:visibleStroke;cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.react-flow__edge.animated path.react-flow__edge-interaction{stroke-dasharray:none;animation:none}.react-flow__edge.inactive{pointer-events:none}.react-flow__edge.selected,.react-flow__edge:focus,.react-flow__edge:focus-visible{outline:none}.react-flow__edge.selected .react-flow__edge-path,.react-flow__edge:focus .react-flow__edge-path,.react-flow__edge:focus-visible .react-flow__edge-path{stroke:#555}.react-flow__edge-textwrapper{pointer-events:all}.react-flow__edge-textbg{fill:#fff}.react-flow__edge .react-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__connection{pointer-events:none}.react-flow__connection .animated{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.react-flow__connectionline{z-index:1001}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:grab}.react-flow__node.dragging{cursor:grabbing}.react-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.react-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:grab}.react-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background:#1a192b;border:1px solid white;border-radius:100%}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;left:50%;bottom:-4px;transform:translate(-50%)}.react-flow__handle-top{left:50%;top:-4px;transform:translate(-50%)}.react-flow__handle-left{top:50%;left:-4px;transform:translateY(-50%)}.react-flow__handle-right{right:-4px;top:50%;transform:translateY(-50%)}.react-flow__edgeupdater{cursor:move;pointer-events:all}.react-flow__panel{position:absolute;z-index:5;margin:15px}.react-flow__panel.top{top:0}.react-flow__panel.bottom{bottom:0}.react-flow__panel.left{left:0}.react-flow__panel.right{right:0}.react-flow__panel.center{left:50%;transform:translate(-50%)}.react-flow__attribution{font-size:10px;background:#ffffff80;padding:2px 3px;margin:0}.react-flow__attribution a{text-decoration:none;color:#999}@keyframes dashdraw{0%{stroke-dashoffset:10}}.react-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__edge.updating .react-flow__edge-path{stroke:#777}.react-flow__edge-text{font-size:10px}.react-flow__node.selectable:focus,.react-flow__node.selectable:focus-visible{outline:none}.react-flow__node-default,.react-flow__node-input,.react-flow__node-output,.react-flow__node-group{padding:10px;border-radius:3px;width:150px;font-size:12px;color:#222;text-align:center;border-width:1px;border-style:solid;border-color:#1a192b;background-color:#fff}.react-flow__node-default.selectable:hover,.react-flow__node-input.selectable:hover,.react-flow__node-output.selectable:hover,.react-flow__node-group.selectable:hover{box-shadow:0 1px 4px 1px #00000014}.react-flow__node-default.selectable.selected,.react-flow__node-default.selectable:focus,.react-flow__node-default.selectable:focus-visible,.react-flow__node-input.selectable.selected,.react-flow__node-input.selectable:focus,.react-flow__node-input.selectable:focus-visible,.react-flow__node-output.selectable.selected,.react-flow__node-output.selectable:focus,.react-flow__node-output.selectable:focus-visible,.react-flow__node-group.selectable.selected,.react-flow__node-group.selectable:focus,.react-flow__node-group.selectable:focus-visible{box-shadow:0 0 0 .5px #1a192b}.react-flow__node-group{background-color:#f0f0f040}.react-flow__nodesselection-rect,.react-flow__selection{background:#0059dc14;border:1px dotted rgba(0,89,220,.8)}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible,.react-flow__selection:focus,.react-flow__selection:focus-visible{outline:none}.react-flow__controls{box-shadow:0 0 2px 1px #00000014}.react-flow__controls-button{border:none;background:#fefefe;border-bottom:1px solid #eee;box-sizing:content-box;display:flex;justify-content:center;align-items:center;width:16px;height:16px;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;padding:5px}.react-flow__controls-button:hover{background:#f4f4f4}.react-flow__controls-button svg{width:100%;max-width:12px;max-height:12px}.react-flow__controls-button:disabled{pointer-events:none}.react-flow__controls-button:disabled svg{fill-opacity:.4}.react-flow__minimap{background-color:#fff}.react-flow__minimap svg{display:block}.react-flow__resize-control{position:absolute}.react-flow__resize-control.left,.react-flow__resize-control.right{cursor:ew-resize}.react-flow__resize-control.top,.react-flow__resize-control.bottom{cursor:ns-resize}.react-flow__resize-control.top.left,.react-flow__resize-control.bottom.right{cursor:nwse-resize}.react-flow__resize-control.bottom.left,.react-flow__resize-control.top.right{cursor:nesw-resize}.react-flow__resize-control.handle{width:4px;height:4px;border:1px solid #fff;border-radius:1px;background-color:#3367d9;transform:translate(-50%,-50%)}.react-flow__resize-control.handle.left{left:0;top:50%}.react-flow__resize-control.handle.right{left:100%;top:50%}.react-flow__resize-control.handle.top{left:50%;top:0}.react-flow__resize-control.handle.bottom{left:50%;top:100%}.react-flow__resize-control.handle.top.left,.react-flow__resize-control.handle.bottom.left{left:0}.react-flow__resize-control.handle.top.right,.react-flow__resize-control.handle.bottom.right{left:100%}.react-flow__resize-control.line{border-color:#3367d9;border-width:0;border-style:solid}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.react-flow__resize-control.line.left{left:0;border-left-width:1px}.react-flow__resize-control.line.right{left:100%;border-right-width:1px}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.react-flow__resize-control.line.top{top:0;border-top-width:1px}.react-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
|
|
2
|
-
Theme: GitHub Dark
|
|
3
|
-
Description: Dark theme as seen on github.com
|
|
4
|
-
Author: github.com
|
|
5
|
-
Maintainer: @Hirse
|
|
6
|
-
Updated: 2021-05-15
|
|
7
|
-
|
|
8
|
-
Outdated base version: https://github.com/primer/github-syntax-dark
|
|
9
|
-
Current colors taken from GitHub's CSS
|
|
10
|
-
*/.hljs{color:#c9d1d9;background:#0d1117}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-variable,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id{color:#79c0ff}.hljs-regexp,.hljs-string,.hljs-meta .hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-comment,.hljs-code,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com
|
|
11
|
-
*/*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:JetBrains Mono,ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}:root{color-scheme:dark}body{margin:0;min-height:100vh;font-family:Sora,system-ui,-apple-system,sans-serif;color:#e2e8f0;background:radial-gradient(1200px 600px at 12% 12%,rgba(56,189,248,.14) 0%,transparent 60%),radial-gradient(900px 620px at 88% 8%,rgba(59,130,246,.2) 0%,transparent 55%),radial-gradient(1200px 900px at 42% 100%,rgba(15,23,42,.85) 0%,transparent 60%),linear-gradient(135deg,#0b0f14,#101826 55%,#0b1120)}.\!container{width:100%!important}.container{width:100%}@media (min-width: 640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media (min-width: 768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media (min-width: 1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media (min-width: 1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media (min-width: 1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.glass-edge{border-width:1px;border-color:#ffffff26;background-color:#0f172a99;--tw-shadow: 0 25px 80px rgba(10,25,60,.35);--tw-shadow-colored: 0 25px 80px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);--tw-backdrop-blur: blur(40px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.panel-card{border-radius:1rem;border-width:1px;border-color:#ffffff1a;background-color:#0f172a99;--tw-shadow: 0 12px 24px rgba(2,10,25,.45);--tw-shadow-colored: 0 12px 24px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);--tw-backdrop-blur: blur(24px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.stat-card{border-radius:1rem;border-width:1px;border-color:#ffffff1a;background-image:linear-gradient(to bottom right,var(--tw-gradient-stops));--tw-gradient-from: rgb(15 23 42 / .8) var(--tw-gradient-from-position);--tw-gradient-to: rgb(15 23 42 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);--tw-gradient-to: rgb(2 6 23 / .7) var(--tw-gradient-to-position);--tw-shadow: 0 12px 24px rgba(2,10,25,.45);--tw-shadow-colored: 0 12px 24px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.text-gradient{background-image:linear-gradient(to right,var(--tw-gradient-stops));--tw-gradient-from: #7dd3fc var(--tw-gradient-from-position);--tw-gradient-to: rgb(125 211 252 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);--tw-gradient-to: rgb(56 189 248 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), #38bdf8 var(--tw-gradient-via-position), var(--tw-gradient-to);--tw-gradient-to: #3b82f6 var(--tw-gradient-to-position);-webkit-background-clip:text;background-clip:text;color:transparent}.pill{border-radius:9999px;border-width:1px;border-style:dashed;border-color:#fff3;background-color:#0f172a99;padding:.25rem .75rem;font-size:.75rem;line-height:1rem;font-weight:500;--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity, 1));font-family:JetBrains Mono,ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.button-primary{border-radius:9999px;background-image:linear-gradient(to right,var(--tw-gradient-stops));--tw-gradient-from: #0ea5e9 var(--tw-gradient-from-position);--tw-gradient-to: rgb(14 165 233 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);--tw-gradient-to: #2563eb var(--tw-gradient-to-position);padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem;font-weight:600;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1));--tw-shadow: 0 14px 30px rgba(37,99,235,.35);--tw-shadow-colored: 0 14px 30px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.button-secondary{border-radius:9999px;border-width:1px;border-color:#ffffff1a;background-color:#0f172ab3;padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem;font-weight:600;--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity, 1));--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.button-ghost{border-radius:9999px;border-width:1px;border-style:dashed;border-color:#fff3;background-color:transparent;padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem;font-weight:600;--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity, 1));--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.markdown-content{overflow-wrap:break-word;overflow-wrap:anywhere}.markdown-content h1,.markdown-content h2,.markdown-content h3,.markdown-content h4{margin-top:1rem;margin-bottom:.5rem;font-weight:600;--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity, 1))}.markdown-content p{margin-bottom:.5rem}.markdown-content p:last-child{margin-bottom:0}.markdown-content h1{font-size:1.125rem;line-height:1.75rem}.markdown-content h2{font-size:1rem;line-height:1.5rem}.markdown-content h3,.markdown-content h4{font-size:.875rem;line-height:1.25rem}.markdown-content hr{margin-top:1rem;margin-bottom:1rem;border-color:#ffffff1a}.markdown-content pre code.hljs{background-color:transparent;padding:0}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.fixed{position:fixed}.absolute{position:absolute}.\!relative{position:relative!important}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.inset-y-0{top:0;bottom:0}.bottom-0{bottom:0}.bottom-6{bottom:1.5rem}.left-0{left:0}.left-\[13px\]{left:13px}.left-\[9px\]{left:9px}.right-2{right:.5rem}.right-6{right:1.5rem}.top-0{top:0}.top-2{top:.5rem}.top-6{top:1.5rem}.z-0{z-index:0}.z-10{z-index:10}.z-40{z-index:40}.z-50{z-index:50}.z-\[120\]{z-index:120}.z-\[1\]{z-index:1}.z-\[60\]{z-index:60}.z-\[70\]{z-index:70}.order-last{order:9999}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.ml-5{margin-left:1.25rem}.mt-0{margin-top:0}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-auto{margin-top:auto}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.contents{display:contents}.hidden{display:none}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-12{height:3rem}.h-14{height:3.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-36{height:9rem}.h-4{height:1rem}.h-40{height:10rem}.h-5{height:1.25rem}.h-56{height:14rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\[520px\]{height:520px}.h-full{height:100%}.max-h-40{max-height:10rem}.max-h-52{max-height:13rem}.max-h-56{max-height:14rem}.max-h-60{max-height:15rem}.max-h-\[45vh\]{max-height:45vh}.max-h-\[85vh\]{max-height:85vh}.max-h-\[90vh\]{max-height:90vh}.min-h-0{min-height:0px}.min-h-\[1200px\]{min-height:1200px}.min-h-\[44px\]{min-height:44px}.min-h-full{min-height:100%}.min-h-screen{min-height:100vh}.w-1\.5{width:.375rem}.w-10{width:2.5rem}.w-12{width:3rem}.w-14{width:3.5rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-\[200px\]{width:200px}.w-\[280px\]{width:280px}.w-auto{width:auto}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.w-px{width:1px}.min-w-0{min-width:0px}.min-w-\[180px\]{min-width:180px}.min-w-\[200px\]{min-width:200px}.min-w-\[220px\]{min-width:220px}.min-w-full{min-width:100%}.max-w-2xl{max-width:42rem}.max-w-\[160px\]{max-width:160px}.max-w-\[360px\]{max-width:360px}.max-w-\[420px\]{max-width:420px}.max-w-\[90\%\]{max-width:90%}.max-w-\[90vw\]{max-width:90vw}.max-w-full{max-width:100%}.max-w-screen-2xl{max-width:1536px}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}@keyframes drift{0%,to{transform:translateZ(0)}50%{transform:translate3d(40px,-30px,0)}}.animate-drift{animation:drift 14s ease-in-out infinite}@keyframes floatIn{0%{opacity:0;transform:translateY(18px)}to{opacity:1;transform:translateY(0)}}.animate-floatIn{animation:floatIn .7s cubic-bezier(.22,.61,.36,1)}@keyframes ping{75%,to{transform:scale(2);opacity:0}}.animate-ping{animation:ping 1s cubic-bezier(0,0,.2,1) infinite}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes pulseSoft{0%{box-shadow:0 0 #38bdf880}70%{box-shadow:0 0 0 8px #38bdf800}to{box-shadow:0 0 #38bdf800}}.animate-pulseSoft{animation:pulseSoft 1.6s ease-in-out infinite}@keyframes rise{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}.animate-rise{animation:rise .6s cubic-bezier(.22,.61,.36,1)}@keyframes slideInFromLeft{0%{transform:translate(-100%);opacity:0}to{transform:translate(0);opacity:1}}.animate-slideInFromLeft{animation:slideInFromLeft .3s cubic-bezier(.22,.61,.36,1)}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.cursor-zoom-in{cursor:zoom-in}.resize-none{resize:none}.resize{resize:both}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.list-none{list-style-type:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.items-stretch{align-items:stretch}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.gap-x-3{-moz-column-gap:.75rem;column-gap:.75rem}.gap-x-4{-moz-column-gap:1rem;column-gap:1rem}.gap-y-1{row-gap:.25rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-2\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.625rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.625rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.self-center{align-self:center}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-3xl{border-radius:1.5rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-0{border-width:0px}.border-b{border-bottom-width:1px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-amber-300\/60{border-color:#fcd34d99}.border-amber-400\/35{border-color:#fbbf2459}.border-amber-400\/40{border-color:#fbbf2466}.border-amber-400\/45{border-color:#fbbf2473}.border-amber-400\/50{border-color:#fbbf2480}.border-emerald-400\/30{border-color:#34d3994d}.border-emerald-400\/35{border-color:#34d39959}.border-emerald-400\/40{border-color:#34d39966}.border-emerald-400\/45{border-color:#34d39973}.border-emerald-400\/60{border-color:#34d39999}.border-emerald-500\/30{border-color:#10b9814d}.border-red-500\/30{border-color:#ef44444d}.border-rose-400\/35{border-color:#fb718559}.border-rose-400\/40{border-color:#fb718566}.border-rose-400\/45{border-color:#fb718573}.border-rose-400\/50{border-color:#fb718580}.border-rose-400\/60{border-color:#fb718599}.border-rose-500\/40{border-color:#f43f5e66}.border-sky-400\/35{border-color:#38bdf859}.border-sky-400\/40{border-color:#38bdf866}.border-sky-400\/50{border-color:#38bdf880}.border-sky-400\/60{border-color:#38bdf899}.border-sky-500\/20{border-color:#0ea5e933}.border-sky-500\/50{border-color:#0ea5e980}.border-transparent{border-color:transparent}.border-violet-400\/35{border-color:#a78bfa59}.border-white\/10{border-color:#ffffff1a}.border-white\/15{border-color:#ffffff26}.border-white\/5{border-color:#ffffff0d}.bg-amber-400{--tw-bg-opacity: 1;background-color:rgb(251 191 36 / var(--tw-bg-opacity, 1))}.bg-amber-500\/10{background-color:#f59e0b1a}.bg-amber-500\/15{background-color:#f59e0b26}.bg-black\/0{background-color:#0000}.bg-black\/30{background-color:#0000004d}.bg-black\/70{background-color:#000000b3}.bg-blue-400{--tw-bg-opacity: 1;background-color:rgb(96 165 250 / var(--tw-bg-opacity, 1))}.bg-emerald-400{--tw-bg-opacity: 1;background-color:rgb(52 211 153 / var(--tw-bg-opacity, 1))}.bg-emerald-500\/10{background-color:#10b9811a}.bg-emerald-500\/15{background-color:#10b98126}.bg-emerald-950\/30{background-color:#022c224d}.bg-red-500\/10{background-color:#ef44441a}.bg-rose-400{--tw-bg-opacity: 1;background-color:rgb(251 113 133 / var(--tw-bg-opacity, 1))}.bg-rose-500\/10{background-color:#f43f5e1a}.bg-rose-500\/15{background-color:#f43f5e26}.bg-rose-500\/20{background-color:#f43f5e33}.bg-sky-300{--tw-bg-opacity: 1;background-color:rgb(125 211 252 / var(--tw-bg-opacity, 1))}.bg-sky-400{--tw-bg-opacity: 1;background-color:rgb(56 189 248 / var(--tw-bg-opacity, 1))}.bg-sky-500{--tw-bg-opacity: 1;background-color:rgb(14 165 233 / var(--tw-bg-opacity, 1))}.bg-sky-500\/10{background-color:#0ea5e91a}.bg-sky-500\/100{background-color:#0ea5e9}.bg-sky-500\/15{background-color:#0ea5e926}.bg-sky-500\/20{background-color:#0ea5e933}.bg-sky-500\/5{background-color:#0ea5e90d}.bg-slate-400{--tw-bg-opacity: 1;background-color:rgb(148 163 184 / var(--tw-bg-opacity, 1))}.bg-slate-600{--tw-bg-opacity: 1;background-color:rgb(71 85 105 / var(--tw-bg-opacity, 1))}.bg-slate-800\/80{background-color:#1e293bcc}.bg-slate-900\/55{background-color:#0f172a8c}.bg-slate-900\/60{background-color:#0f172a99}.bg-slate-900\/65{background-color:#0f172aa6}.bg-slate-900\/70{background-color:#0f172ab3}.bg-slate-900\/95{background-color:#0f172af2}.bg-slate-950{--tw-bg-opacity: 1;background-color:rgb(2 6 23 / var(--tw-bg-opacity, 1))}.bg-slate-950\/40{background-color:#02061766}.bg-slate-950\/50{background-color:#02061780}.bg-slate-950\/60{background-color:#02061799}.bg-slate-950\/70{background-color:#020617b3}.bg-slate-950\/80{background-color:#020617cc}.bg-slate-950\/90{background-color:#020617e6}.bg-transparent{background-color:transparent}.bg-violet-500\/10{background-color:#8b5cf61a}.bg-white\/10{background-color:#ffffff1a}.bg-white\/5{background-color:#ffffff0d}.bg-gradient-to-b{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-cyan-400{--tw-gradient-from: #22d3ee var(--tw-gradient-from-position);--tw-gradient-to: rgb(34 211 238 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to) }.from-rose-500\/10{--tw-gradient-from: rgb(244 63 94 / .1) var(--tw-gradient-from-position);--tw-gradient-to: rgb(244 63 94 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to) }.from-sky-500{--tw-gradient-from: #0ea5e9 var(--tw-gradient-from-position);--tw-gradient-to: rgb(14 165 233 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to) }.from-slate-950\/80{--tw-gradient-from: rgb(2 6 23 / .8) var(--tw-gradient-from-position);--tw-gradient-to: rgb(2 6 23 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to) }.from-slate-950\/90{--tw-gradient-from: rgb(2 6 23 / .9) var(--tw-gradient-from-position);--tw-gradient-to: rgb(2 6 23 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to) }.via-slate-900\/70{--tw-gradient-to: rgb(15 23 42 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), rgb(15 23 42 / .7) var(--tw-gradient-via-position), var(--tw-gradient-to) }.via-slate-900\/80{--tw-gradient-to: rgb(15 23 42 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), rgb(15 23 42 / .8) var(--tw-gradient-via-position), var(--tw-gradient-to) }.to-blue-500{--tw-gradient-to: #3b82f6 var(--tw-gradient-to-position) }.to-blue-600{--tw-gradient-to: #2563eb var(--tw-gradient-to-position) }.to-slate-900\/70{--tw-gradient-to: rgb(15 23 42 / .7) var(--tw-gradient-to-position) }.to-slate-900\/80{--tw-gradient-to: rgb(15 23 42 / .8) var(--tw-gradient-to-position) }.to-slate-950\/70{--tw-gradient-to: rgb(2 6 23 / .7) var(--tw-gradient-to-position) }.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0{padding:0}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-\[10px\]{padding-top:10px;padding-bottom:10px}.pb-1{padding-bottom:.25rem}.pb-12{padding-bottom:3rem}.pb-16{padding-bottom:4rem}.pb-2{padding-bottom:.5rem}.pl-2{padding-left:.5rem}.pl-3{padding-left:.75rem}.pl-7{padding-left:1.75rem}.pr-1{padding-right:.25rem}.pr-1\.5{padding-right:.375rem}.pr-2{padding-right:.5rem}.pr-4{padding-right:1rem}.pt-2{padding-top:.5rem}.pt-3{padding-top:.75rem}.pt-8{padding-top:2rem}.pt-9{padding-top:2.25rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:JetBrains Mono,ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-\[0\.85em\]{font-size:.85em}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[9px\]{font-size:9px}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.ordinal{--tw-ordinal: ordinal;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-6{line-height:1.5rem}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.tracking-\[0\.12em\]{letter-spacing:.12em}.tracking-\[0\.14em\]{letter-spacing:.14em}.tracking-\[0\.18em\]{letter-spacing:.18em}.tracking-\[0\.25em\]{letter-spacing:.25em}.tracking-\[0\.2em\]{letter-spacing:.2em}.tracking-\[0\.3em\]{letter-spacing:.3em}.tracking-tight{letter-spacing:-.025em}.text-amber-100{--tw-text-opacity: 1;color:rgb(254 243 199 / var(--tw-text-opacity, 1))}.text-amber-200{--tw-text-opacity: 1;color:rgb(253 230 138 / var(--tw-text-opacity, 1))}.text-amber-200\/80{color:#fde68acc}.text-amber-300{--tw-text-opacity: 1;color:rgb(252 211 77 / var(--tw-text-opacity, 1))}.text-blue-300{--tw-text-opacity: 1;color:rgb(147 197 253 / var(--tw-text-opacity, 1))}.text-emerald-100{--tw-text-opacity: 1;color:rgb(209 250 229 / var(--tw-text-opacity, 1))}.text-emerald-200{--tw-text-opacity: 1;color:rgb(167 243 208 / var(--tw-text-opacity, 1))}.text-emerald-300{--tw-text-opacity: 1;color:rgb(110 231 183 / var(--tw-text-opacity, 1))}.text-emerald-300\/60{color:#6ee7b799}.text-emerald-300\/80{color:#6ee7b7cc}.text-ink{--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity, 1))}.text-red-200{--tw-text-opacity: 1;color:rgb(254 202 202 / var(--tw-text-opacity, 1))}.text-red-300{--tw-text-opacity: 1;color:rgb(252 165 165 / var(--tw-text-opacity, 1))}.text-rose-100{--tw-text-opacity: 1;color:rgb(255 228 230 / var(--tw-text-opacity, 1))}.text-rose-200{--tw-text-opacity: 1;color:rgb(254 205 211 / var(--tw-text-opacity, 1))}.text-rose-300{--tw-text-opacity: 1;color:rgb(253 164 175 / var(--tw-text-opacity, 1))}.text-rose-500{--tw-text-opacity: 1;color:rgb(244 63 94 / var(--tw-text-opacity, 1))}.text-sky-100{--tw-text-opacity: 1;color:rgb(224 242 254 / var(--tw-text-opacity, 1))}.text-sky-200{--tw-text-opacity: 1;color:rgb(186 230 253 / var(--tw-text-opacity, 1))}.text-sky-300{--tw-text-opacity: 1;color:rgb(125 211 252 / var(--tw-text-opacity, 1))}.text-slate-100{--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity, 1))}.text-slate-200{--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity, 1))}.text-slate-300{--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity, 1))}.text-slate-400{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity, 1))}.text-slate-500{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity, 1))}.text-violet-100{--tw-text-opacity: 1;color:rgb(237 233 254 / var(--tw-text-opacity, 1))}.text-violet-200{--tw-text-opacity: 1;color:rgb(221 214 254 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.underline{text-decoration-line:underline}.decoration-sky-400\/40{text-decoration-color:#38bdf866}.decoration-slate-300{text-decoration-color:#cbd5e1}.underline-offset-4{text-underline-offset:4px}.opacity-95{opacity:.95}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_0_0_1px_rgba\(0\,0\,0\,0\.03\)\]{--tw-shadow: 0 0 0 1px rgba(0,0,0,.03);--tw-shadow-colored: 0 0 0 1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_0_18px_rgba\(56\,189\,248\,0\.2\)\]{--tw-shadow: 0 0 18px rgba(56,189,248,.2);--tw-shadow-colored: 0 0 18px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_10px_18px_rgba\(18\,14\,12\,0\.08\)\]{--tw-shadow: 0 10px 18px rgba(18,14,12,.08);--tw-shadow-colored: 0 10px 18px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_10px_24px_rgba\(18\,14\,12\,0\.12\)\]{--tw-shadow: 0 10px 24px rgba(18,14,12,.12);--tw-shadow-colored: 0 10px 24px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_12px_26px_rgba\(3\,9\,28\,0\.35\)\]{--tw-shadow: 0 12px 26px rgba(3,9,28,.35);--tw-shadow-colored: 0 12px 26px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_14px_30px_rgba\(37\,99\,235\,0\.45\)\]{--tw-shadow: 0 14px 30px rgba(37,99,235,.45);--tw-shadow-colored: 0 14px 30px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_25px_80px_rgba\(10\,25\,60\,0\.5\)\]{--tw-shadow: 0 25px 80px rgba(10,25,60,.5);--tw-shadow-colored: 0 25px 80px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_8px_20px_rgba\(2\,8\,20\,0\.35\)\]{--tw-shadow: 0 8px 20px rgba(2,8,20,.35);--tw-shadow-colored: 0 8px 20px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-2xl{--tw-backdrop-blur: blur(40px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-sm{--tw-backdrop-blur: blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-xl{--tw-backdrop-blur: blur(24px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.\[animation-delay\:150ms\]{animation-delay:.15s}.\[animation-delay\:160ms\]{animation-delay:.16s}.\[animation-delay\:300ms\]{animation-delay:.3s}.\[animation-delay\:320ms\]{animation-delay:.32s}.\[overflow-wrap\:anywhere\]{overflow-wrap:anywhere}.gridlines{position:absolute;top:0;right:0;bottom:0;left:0;background-image:linear-gradient(transparent 0%,#94a3b814),linear-gradient(to right,rgba(148,163,184,.12) 1px,transparent 1px),linear-gradient(to bottom,rgba(148,163,184,.12) 1px,transparent 1px);background-size:100% 100%,64px 64px,64px 64px;-webkit-mask-image:radial-gradient(circle at top left,rgba(2,6,23,.8),transparent 70%);mask-image:radial-gradient(circle at top left,rgba(2,6,23,.8),transparent 70%)}.noise{position:fixed;top:0;right:0;bottom:0;left:0;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.18'/%3E%3C/svg%3E");mix-blend-mode:soft-light;opacity:.35;pointer-events:none}.aurora{position:absolute;top:-20%;right:-10%;bottom:-20%;left:-10%;background:conic-gradient(from 180deg,#38bdf838,#3b82f61f,#0ea5e933,#fff0);filter:blur(60px);opacity:.55;animation:auroraShift 18s ease-in-out infinite}.orb{position:absolute;border-radius:999px;filter:blur(32px);opacity:.65;mix-blend-mode:multiply}.orb-a{width:420px;height:420px;background:radial-gradient(circle,rgba(56,189,248,.5) 0%,transparent 70%);top:-120px;left:-80px}.orb-b{width:440px;height:440px;background:radial-gradient(circle,rgba(37,99,235,.4) 0%,transparent 70%);bottom:-180px;right:-120px;animation-delay:-3s}.orb-c{width:320px;height:320px;background:radial-gradient(circle,rgba(14,165,233,.4) 0%,transparent 70%);top:120px;right:10%;animation-delay:-6s}@media (prefers-reduced-motion: reduce){*{animation:none!important;transition:none!important}}@keyframes auroraShift{0%,to{transform:translateZ(0) rotate(0)}50%{transform:translate3d(40px,-30px,0) rotate(30deg)}}.placeholder\:text-slate-400::-moz-placeholder{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity, 1))}.placeholder\:text-slate-400::placeholder{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity, 1))}.last\:border-0:last-child{border-width:0px}.hover\:border-rose-300\/80:hover{border-color:#fda4afcc}.hover\:border-rose-400\/40:hover{border-color:#fb718566}.hover\:border-rose-400\/60:hover{border-color:#fb718599}.hover\:border-sky-400\/50:hover{border-color:#38bdf880}.hover\:border-sky-400\/60:hover{border-color:#38bdf899}.hover\:from-cyan-300:hover{--tw-gradient-from: #67e8f9 var(--tw-gradient-from-position);--tw-gradient-to: rgb(103 232 249 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to) }.hover\:to-blue-400:hover{--tw-gradient-to: #60a5fa var(--tw-gradient-to-position) }.hover\:text-red-200:hover{--tw-text-opacity: 1;color:rgb(254 202 202 / var(--tw-text-opacity, 1))}.hover\:text-rose-500:hover{--tw-text-opacity: 1;color:rgb(244 63 94 / var(--tw-text-opacity, 1))}.hover\:text-sky-100:hover{--tw-text-opacity: 1;color:rgb(224 242 254 / var(--tw-text-opacity, 1))}.hover\:text-sky-300:hover{--tw-text-opacity: 1;color:rgb(125 211 252 / var(--tw-text-opacity, 1))}.hover\:text-slate-100:hover{--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity, 1))}.hover\:text-slate-300:hover{--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity, 1))}.hover\:shadow-\[0_18px_36px_rgba\(37\,99\,235\,0\.55\)\]:hover{--tw-shadow: 0 18px 36px rgba(37,99,235,.55);--tw-shadow-colored: 0 18px 36px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.focus\:shadow-glow:focus{--tw-shadow: 0 0 0 1px rgba(56, 189, 248, .15), 0 18px 42px rgba(59, 130, 246, .24);--tw-shadow-colored: 0 0 0 1px var(--tw-shadow-color), 0 18px 42px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.active\:scale-95:active{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.group[open] .group-open\:rotate-180{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group[open] .group-open\:text-slate-300{--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity, 1))}.group:hover .group-hover\:scale-\[1\.02\]{--tw-scale-x: 1.02;--tw-scale-y: 1.02;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:bg-white\/5{background-color:#ffffff0d}@media (min-width: 640px){.sm\:inline{display:inline}.sm\:min-w-\[240px\]{min-width:240px}.sm\:max-w-\[78\%\]{max-width:78%}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:gap-4{gap:1rem}.sm\:p-4{padding:1rem}}@media (min-width: 768px){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:text-\[13px\]{font-size:13px}}@media (min-width: 1024px){.lg\:order-none{order:0}.lg\:block{display:block}.lg\:hidden{display:none}.lg\:h-dvh{height:100dvh}.lg\:h-full{height:100%}.lg\:max-h-\[420px\]{max-height:420px}.lg\:min-h-0{min-height:0px}.lg\:w-\[280px\]{width:280px}.lg\:grid-cols-\[280px_1fr\]{grid-template-columns:280px 1fr}.lg\:grid-cols-\[360px_1fr\]{grid-template-columns:360px 1fr}.lg\:grid-cols-\[minmax\(0\,1\.2fr\)_minmax\(0\,0\.8fr\)\]{grid-template-columns:minmax(0,1.2fr) minmax(0,.8fr)}.lg\:flex-row{flex-direction:row}.lg\:overflow-hidden{overflow:hidden}.lg\:overflow-y-auto{overflow-y:auto}.lg\:rounded-\[32px\]{border-radius:32px}.lg\:px-7{padding-left:1.75rem;padding-right:1.75rem}.lg\:py-6{padding-top:1.5rem;padding-bottom:1.5rem}.lg\:py-7{padding-top:1.75rem;padding-bottom:1.75rem}}@media (min-width: 1280px){.xl\:grid-cols-\[minmax\(420px\,1\.4fr\)_minmax\(360px\,1fr\)\]{grid-template-columns:minmax(420px,1.4fr) minmax(360px,1fr)}.xl\:grid-cols-\[minmax\(420px\,1fr\)_minmax\(360px\,1fr\)\]{grid-template-columns:minmax(420px,1fr) minmax(360px,1fr)}}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
You are `art-generation`, a specialized subagent for `game-dev`.
|
|
2
|
-
|
|
3
|
-
Focus:
|
|
4
|
-
- Generate or define production-ready game art assets: textures, 2D imagery, video concepts, and audio concepts.
|
|
5
|
-
- Keep style direction coherent across assets.
|
|
6
|
-
|
|
7
|
-
Working rules:
|
|
8
|
-
- Prefer existing generation tools available in the environment, especially FAL MCP tools:
|
|
9
|
-
- `generate_image_or_texture`
|
|
10
|
-
- `generate_image_edit`
|
|
11
|
-
- `generate_audio_or_music`
|
|
12
|
-
- `generate_video_from_image`
|
|
13
|
-
- `fal_generation_status`
|
|
14
|
-
- When generation tooling is unavailable, provide high-quality prompts, reference constraints, and executable fallback commands.
|
|
15
|
-
- Use `browser_control` for browser-based visual QA or reference capture when the task depends on JS-rendered pages (for example hosted model demos, web galleries, or WebGL previews).
|
|
16
|
-
- For texture work, gather or infer geometry context first: mesh/part name, material slot(s), UV set(s) or UDIM tiles, mirrored/overlapped islands, and whether the asset uses tiling, trim sheets, or unique unwraps.
|
|
17
|
-
- For Three.js targets, plan UV requirements explicitly: textured meshes need `uv`, and `aoMap`/`lightMap` need `uv2`.
|
|
18
|
-
- For textures, default to power-of-two dimensions and call out tiling/seamless requirements explicitly.
|
|
19
|
-
- Choose texture size from expected on-screen usage and texel density targets; do not default everything to 4K.
|
|
20
|
-
- Keep related assets in consistent texel density bands, and call out intentional exceptions (for example hero props).
|
|
21
|
-
- When scaling is needed, preserve a high-quality source and produce explicit output variants per target platform or LOD tier.
|
|
22
|
-
- Specify map conventions per output: map type (albedo/baseColor, normal, roughness, metallic, AO, height, emissive, opacity), color space, and normal map orientation expectations.
|
|
23
|
-
- For Three.js PBR outputs, map generated textures to `MeshStandardMaterial` slots (`map`, `normalMap`, `roughnessMap`, `metalnessMap`, `aoMap`, `emissiveMap`, `alphaMap`) and call out omitted slots intentionally.
|
|
24
|
-
- In Three.js, keep albedo/baseColor in sRGB and data maps in linear color space; flag mismatches before final delivery.
|
|
25
|
-
- For Three.js + glTF workflows, call out when textures should use `flipY = false` and document tiling transforms (`RepeatWrapping`, repeat/offset/rotation).
|
|
26
|
-
- Ensure generated texture details align intentionally with geometry and UVs (major features aligned to UV islands, seams, and orientation where relevant).
|
|
27
|
-
- For audio/video concept outputs, include exact tool commands and encoding parameters when possible.
|
|
28
|
-
- For async FAL jobs, always follow through by checking `fal_generation_status` (or waiting) before concluding.
|
|
29
|
-
- If review is enabled and status is `awaiting_review`, present options clearly and proceed with `accept`/`deny` when instructed.
|
|
30
|
-
- If geometry/UV details are missing, state assumptions explicitly and provide a clear "needed from user/engineer" list.
|
|
31
|
-
|
|
32
|
-
Deliverable format:
|
|
33
|
-
- Asset goal and target runtime/engine constraints.
|
|
34
|
-
- Generation prompts and/or commands used.
|
|
35
|
-
- Output file paths and naming convention.
|
|
36
|
-
- Texture-to-geometry mapping notes: mesh/part, material slot, UV set/UDIM, map type, resolution, tiling scale/offset, and destination file path.
|
|
37
|
-
- For Three.js targets, include a concise material binding map and UV requirements (`uv`/`uv2`) per mesh/material.
|
|
38
|
-
- Quick quality checklist (resolution, compression, loopability, style consistency, UV/geometry fit).
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
You are `asset-refinement`, a media processing subagent for `game-dev`.
|
|
2
|
-
|
|
3
|
-
Focus:
|
|
4
|
-
- Refine, split, normalize, convert, and optimize game assets.
|
|
5
|
-
- Use command-line tooling with reproducible pipelines (especially `ffmpeg` for audio/video work).
|
|
6
|
-
|
|
7
|
-
Working rules:
|
|
8
|
-
- Never destructively overwrite original source assets unless explicitly requested.
|
|
9
|
-
- Prefer writing transformed assets to clearly named output paths.
|
|
10
|
-
- For audio splitting tasks, document segment boundaries and include exact command invocations.
|
|
11
|
-
- Validate output with lightweight probes (for example `ffprobe`) when available.
|
|
12
|
-
|
|
13
|
-
Deliverable format:
|
|
14
|
-
- Input assets and requested transformation.
|
|
15
|
-
- Commands executed.
|
|
16
|
-
- Output assets produced.
|
|
17
|
-
- Validation notes (duration/channel/sample rate/bitrate or other relevant checks).
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
You are `planning-idea`, a gameplay design and systems planning subagent for `game-dev`.
|
|
2
|
-
|
|
3
|
-
Focus:
|
|
4
|
-
- Design and iterate gameplay loops, mechanics, progression systems, and player feedback structures.
|
|
5
|
-
- Translate game concepts into implementation-ready plans.
|
|
6
|
-
|
|
7
|
-
Working rules:
|
|
8
|
-
- Start with a concise core loop definition: player goal, action cycle, reward cadence.
|
|
9
|
-
- Identify 2-3 mechanic variants with tradeoffs and implementation complexity.
|
|
10
|
-
- Keep recommendations grounded in the current codebase, engine constraints, and content scope.
|
|
11
|
-
- Include telemetry hooks or playtest checkpoints when proposing balancing changes.
|
|
12
|
-
|
|
13
|
-
Deliverable format:
|
|
14
|
-
- Core loop summary.
|
|
15
|
-
- Proposed mechanics/options with tradeoffs.
|
|
16
|
-
- Implementation plan (phased tasks).
|
|
17
|
-
- Risks and validation plan (playtest criteria).
|