@backbay/glia-three 0.2.0-alpha.3 → 0.2.0-alpha.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.
Files changed (187) hide show
  1. package/dist/chunk-HFSDW5IV.js +1861 -0
  2. package/dist/chunk-HFSDW5IV.js.map +1 -0
  3. package/dist/chunk-PZ5AY32C.js +10 -0
  4. package/dist/chunk-PZ5AY32C.js.map +1 -0
  5. package/dist/chunk-T7Z4PIZ7.js +17606 -0
  6. package/dist/chunk-T7Z4PIZ7.js.map +1 -0
  7. package/dist/environment/index.d.ts +116 -0
  8. package/dist/environment/index.js +26 -0
  9. package/dist/environment/index.js.map +1 -0
  10. package/dist/index.d.ts +9 -0
  11. package/dist/index.js +274 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/three/index.d.ts +3402 -0
  14. package/dist/three/index.js +252 -0
  15. package/dist/three/index.js.map +1 -0
  16. package/package.json +4 -1
  17. package/src/environment/AuroraLayer/AuroraLayer.stories.tsx +0 -43
  18. package/src/environment/AuroraLayer/AuroraLayer.tsx +0 -200
  19. package/src/environment/AuroraLayer/index.ts +0 -2
  20. package/src/environment/AuroraLayer/types.ts +0 -30
  21. package/src/environment/EnvironmentLayer/EnvironmentLayer.stories.tsx +0 -262
  22. package/src/environment/EnvironmentLayer/EnvironmentLayer.tsx +0 -105
  23. package/src/environment/EnvironmentLayer/index.ts +0 -4
  24. package/src/environment/EnvironmentLayer/presets.ts +0 -128
  25. package/src/environment/FogLayer/FogLayer.stories.tsx +0 -83
  26. package/src/environment/FogLayer/FogLayer.tsx +0 -113
  27. package/src/environment/FogLayer/index.ts +0 -2
  28. package/src/environment/FogLayer/types.ts +0 -45
  29. package/src/environment/VolumetricLight/VolumetricLight.stories.tsx +0 -55
  30. package/src/environment/VolumetricLight/VolumetricLight.tsx +0 -188
  31. package/src/environment/VolumetricLight/index.ts +0 -2
  32. package/src/environment/VolumetricLight/types.ts +0 -33
  33. package/src/environment/WeatherLayer/WeatherLayer.stories.tsx +0 -348
  34. package/src/environment/WeatherLayer/WeatherLayer.tsx +0 -266
  35. package/src/environment/WeatherLayer/cinematicCanvas.tsx +0 -809
  36. package/src/environment/WeatherLayer/colors.ts +0 -27
  37. package/src/environment/WeatherLayer/index.ts +0 -4
  38. package/src/environment/WeatherLayer/leafPresets.ts +0 -12
  39. package/src/environment/WeatherLayer/particles.ts +0 -227
  40. package/src/environment/WeatherLayer/types.ts +0 -140
  41. package/src/environment/index.ts +0 -17
  42. package/src/environment/shared/index.ts +0 -2
  43. package/src/environment/shared/noise.ts +0 -10
  44. package/src/environment/shared/performance.ts +0 -33
  45. package/src/environment/shared/types.ts +0 -26
  46. package/src/index.ts +0 -2
  47. package/src/lib/utils.ts +0 -6
  48. package/src/lib/vision-types.ts +0 -84
  49. package/src/three/AgentConsole/AgentConsole.stories.tsx +0 -397
  50. package/src/three/AgentConsole/AgentConsole.tsx +0 -195
  51. package/src/three/AgentConsole/ConsoleChat.tsx +0 -237
  52. package/src/three/AgentConsole/FocusConstellation.tsx +0 -297
  53. package/src/three/AgentConsole/GlyphAvatar.stories.tsx +0 -110
  54. package/src/three/AgentConsole/GlyphAvatar.tsx +0 -117
  55. package/src/three/AgentConsole/QuickActions.tsx +0 -111
  56. package/src/three/AgentConsole/index.ts +0 -41
  57. package/src/three/AgentConsole/types.ts +0 -241
  58. package/src/three/AmbientField/AmbientField.stories.tsx +0 -290
  59. package/src/three/AmbientField/AmbientField.tsx +0 -307
  60. package/src/three/AmbientField/BackbayFieldBus.ts +0 -326
  61. package/src/three/AmbientField/FieldProvider.tsx +0 -83
  62. package/src/three/AmbientField/index.ts +0 -37
  63. package/src/three/AmbientField/shaders/constellation.ts +0 -384
  64. package/src/three/AmbientField/types.ts +0 -174
  65. package/src/three/AttackGraph/AttackGraph.stories.tsx +0 -144
  66. package/src/three/AttackGraph/AttackGraph.tsx +0 -325
  67. package/src/three/AttackGraph/index.ts +0 -19
  68. package/src/three/AttackGraph/types.ts +0 -97
  69. package/src/three/AuditTrail/AuditTrail.stories.tsx +0 -567
  70. package/src/three/AuditTrail/AuditTrail.tsx +0 -644
  71. package/src/three/AuditTrail/index.ts +0 -33
  72. package/src/three/AuditTrail/types.ts +0 -192
  73. package/src/three/CrystallineOrganism/Breadcrumb.tsx +0 -61
  74. package/src/three/CrystallineOrganism/CrystallineOrganism.stories.tsx +0 -509
  75. package/src/three/CrystallineOrganism/CrystallineOrganism.tsx +0 -273
  76. package/src/three/CrystallineOrganism/LatticeEdge.tsx +0 -69
  77. package/src/three/CrystallineOrganism/OrganismLattice.tsx +0 -159
  78. package/src/three/CrystallineOrganism/OrganismParticles.tsx +0 -148
  79. package/src/three/CrystallineOrganism/OrganismShell.tsx +0 -277
  80. package/src/three/CrystallineOrganism/constants.ts +0 -161
  81. package/src/three/CrystallineOrganism/index.ts +0 -17
  82. package/src/three/CrystallineOrganism/layouts/hexGrid.ts +0 -85
  83. package/src/three/CrystallineOrganism/layouts/index.ts +0 -1
  84. package/src/three/CrystallineOrganism/types.ts +0 -167
  85. package/src/three/CrystallineOrganism/useOrganismEmotion.ts +0 -84
  86. package/src/three/FirewallBarrier/FirewallBarrier.stories.tsx +0 -167
  87. package/src/three/FirewallBarrier/FirewallBarrier.tsx +0 -259
  88. package/src/three/FirewallBarrier/index.ts +0 -14
  89. package/src/three/FirewallBarrier/types.ts +0 -52
  90. package/src/three/Glyph/GlyphObject.stories.tsx +0 -577
  91. package/src/three/Glyph/GlyphObject.tsx +0 -422
  92. package/src/three/Glyph/index.ts +0 -10
  93. package/src/three/Glyph/types.ts +0 -36
  94. package/src/three/Glyph/useGlyphController.ts +0 -231
  95. package/src/three/Glyph/useGlyphEmotion.ts +0 -70
  96. package/src/three/Graph3D/Graph3D.stories.tsx +0 -269
  97. package/src/three/Graph3D/Graph3D.tsx +0 -248
  98. package/src/three/Graph3D/GraphEdge.tsx +0 -79
  99. package/src/three/Graph3D/GraphNode.tsx +0 -239
  100. package/src/three/Graph3D/types.ts +0 -66
  101. package/src/three/Graph3D/utils.ts +0 -204
  102. package/src/three/IntelFeed/IntelFeed.stories.tsx +0 -168
  103. package/src/three/IntelFeed/IntelFeed.tsx +0 -284
  104. package/src/three/IntelFeed/index.ts +0 -14
  105. package/src/three/IntelFeed/types.ts +0 -56
  106. package/src/three/MetricsGalaxy/MetricsGalaxy.tsx +0 -484
  107. package/src/three/MetricsGalaxy/index.ts +0 -6
  108. package/src/three/MetricsGalaxy/types.ts +0 -26
  109. package/src/three/NetworkTopology/NetworkTopology.stories.tsx +0 -184
  110. package/src/three/NetworkTopology/NetworkTopology.tsx +0 -421
  111. package/src/three/NetworkTopology/index.ts +0 -34
  112. package/src/three/NetworkTopology/types.ts +0 -128
  113. package/src/three/ParticleField/ParticleField.stories.tsx +0 -162
  114. package/src/three/ParticleField/ParticleField.tsx +0 -81
  115. package/src/three/ParticleField/index.ts +0 -1
  116. package/src/three/PermissionMatrix/PermissionMatrix.stories.tsx +0 -475
  117. package/src/three/PermissionMatrix/PermissionMatrix.tsx +0 -380
  118. package/src/three/PermissionMatrix/index.ts +0 -15
  119. package/src/three/PermissionMatrix/types.ts +0 -54
  120. package/src/three/QuantumField/ConstellationField.tsx +0 -238
  121. package/src/three/QuantumField/FieldBus.ts +0 -349
  122. package/src/three/QuantumField/FieldLayer.tsx +0 -430
  123. package/src/three/QuantumField/FieldProvider.tsx +0 -460
  124. package/src/three/QuantumField/PcbField.tsx +0 -406
  125. package/src/three/QuantumField/QuantumField.stories.tsx +0 -1155
  126. package/src/three/QuantumField/TrailRTT.ts +0 -212
  127. package/src/three/QuantumField/WaterField.tsx +0 -226
  128. package/src/three/QuantumField/WaterSimRTT.ts +0 -283
  129. package/src/three/QuantumField/domMapping.ts +0 -185
  130. package/src/three/QuantumField/index.ts +0 -110
  131. package/src/three/QuantumField/styles/index.ts +0 -9
  132. package/src/three/QuantumField/styles/styleA.ts +0 -526
  133. package/src/three/QuantumField/styles/styleB.ts +0 -1210
  134. package/src/three/QuantumField/styles/styleC.ts +0 -266
  135. package/src/three/QuantumField/themes.ts +0 -211
  136. package/src/three/QuantumField/types.ts +0 -380
  137. package/src/three/SOCCommandCenter/SOCCommandCenter.stories.tsx +0 -591
  138. package/src/three/SOCCommandCenter/SOCCommandCenter.tsx +0 -248
  139. package/src/three/SOCCommandCenter/index.ts +0 -26
  140. package/src/three/SOCCommandCenter/types.ts +0 -201
  141. package/src/three/SecurityDashboard/SecurityDashboard.stories.tsx +0 -508
  142. package/src/three/SecurityDashboard/SecurityDashboard.tsx +0 -507
  143. package/src/three/SecurityDashboard/index.ts +0 -37
  144. package/src/three/SecurityDashboard/types.ts +0 -143
  145. package/src/three/SecurityShield/SecurityShield.stories.tsx +0 -257
  146. package/src/three/SecurityShield/SecurityShield.tsx +0 -502
  147. package/src/three/SecurityShield/index.ts +0 -25
  148. package/src/three/SecurityShield/types.ts +0 -64
  149. package/src/three/Sentinel/AvatarMode.tsx +0 -578
  150. package/src/three/Sentinel/AvatarRenderer.tsx +0 -199
  151. package/src/three/Sentinel/CameraPip.tsx +0 -127
  152. package/src/three/Sentinel/CardinalItem.tsx +0 -83
  153. package/src/three/Sentinel/CardinalMenu.tsx +0 -370
  154. package/src/three/Sentinel/DockedMiniOrb.tsx +0 -146
  155. package/src/three/Sentinel/RadialSubmenu.tsx +0 -273
  156. package/src/three/Sentinel/SentinelConversation.tsx +0 -802
  157. package/src/three/Sentinel/SentinelOrb.tsx +0 -316
  158. package/src/three/Sentinel/SentinelOverlay.tsx +0 -146
  159. package/src/three/Sentinel/SentinelProvider.tsx +0 -145
  160. package/src/three/Sentinel/SentinelTether.tsx +0 -182
  161. package/src/three/Sentinel/SigilPlaceholder.tsx +0 -176
  162. package/src/three/Sentinel/VerticalSubmenu.tsx +0 -150
  163. package/src/three/Sentinel/index.ts +0 -145
  164. package/src/three/Sentinel/sentinelStore.ts +0 -196
  165. package/src/three/Sentinel/types.ts +0 -403
  166. package/src/three/Sentinel/useCameraPermission.ts +0 -153
  167. package/src/three/Sentinel/useThrowPhysics.ts +0 -220
  168. package/src/three/SpatialWorkspace/CyntraWorkspace.tsx +0 -84
  169. package/src/three/SpatialWorkspace/JobCluster.tsx +0 -281
  170. package/src/three/SpatialWorkspace/NodeGraph.tsx +0 -236
  171. package/src/three/SpatialWorkspace/ReceiptOrbit.tsx +0 -368
  172. package/src/three/SpatialWorkspace/SpatialWorkspace.stories.tsx +0 -547
  173. package/src/three/SpatialWorkspace/SpatialWorkspace.tsx +0 -428
  174. package/src/three/SpatialWorkspace/TrustRings.tsx +0 -228
  175. package/src/three/SpatialWorkspace/adapters.ts +0 -353
  176. package/src/three/SpatialWorkspace/index.ts +0 -85
  177. package/src/three/SpatialWorkspace/nexusAdapter.ts +0 -182
  178. package/src/three/SpatialWorkspace/types.ts +0 -389
  179. package/src/three/ThreatRadar/ThreatRadar.stories.tsx +0 -451
  180. package/src/three/ThreatRadar/ThreatRadar.tsx +0 -542
  181. package/src/three/ThreatRadar/index.ts +0 -8
  182. package/src/three/ThreatRadar/types.ts +0 -90
  183. package/src/three/ThreeErrorBoundary/ThreeErrorBoundary.tsx +0 -235
  184. package/src/three/ThreeErrorBoundary/index.ts +0 -5
  185. package/src/three/index.ts +0 -56
  186. package/tsconfig.json +0 -20
  187. package/tsup.config.ts +0 -21
@@ -0,0 +1,1861 @@
1
+ // src/environment/WeatherLayer/WeatherLayer.tsx
2
+ import * as React2 from "react";
3
+ import { useReducedMotion } from "framer-motion";
4
+
5
+ // src/lib/utils.ts
6
+ import { clsx } from "clsx";
7
+ import { twMerge } from "tailwind-merge";
8
+ function cn(...inputs) {
9
+ return twMerge(clsx(inputs));
10
+ }
11
+
12
+ // src/environment/shared/types.ts
13
+ var PERFORMANCE_PRESETS = {
14
+ high: { tier: "high", maxParticles: 2e3, useShaders: true, targetFPS: 60 },
15
+ medium: { tier: "medium", maxParticles: 800, useShaders: true, targetFPS: 60 },
16
+ low: { tier: "low", maxParticles: 300, useShaders: false, targetFPS: 30 },
17
+ minimal: { tier: "minimal", maxParticles: 0, useShaders: false, targetFPS: 0 }
18
+ };
19
+
20
+ // src/environment/shared/performance.ts
21
+ function detectPerformanceTier() {
22
+ if (typeof window === "undefined") return "medium";
23
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
24
+ return "minimal";
25
+ }
26
+ const nav = navigator;
27
+ if (nav.deviceMemory && nav.deviceMemory < 4) {
28
+ return "low";
29
+ }
30
+ if (navigator.hardwareConcurrency && navigator.hardwareConcurrency < 4) {
31
+ return "low";
32
+ }
33
+ const isMobile = /Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
34
+ if (isMobile) return "low";
35
+ return "medium";
36
+ }
37
+ function getPerformanceConfig(tier) {
38
+ const detected = tier ?? detectPerformanceTier();
39
+ return PERFORMANCE_PRESETS[detected];
40
+ }
41
+
42
+ // src/environment/WeatherLayer/types.ts
43
+ var WEATHER_CONFIGS = {
44
+ rain: {
45
+ baseCount: 200,
46
+ sizeRange: [1, 3],
47
+ opacityRange: [0.4, 0.7],
48
+ speedRange: [15, 25],
49
+ motionStyle: "fall",
50
+ shape: "streak",
51
+ colors: ["#a0c4ff", "#bde0fe"]
52
+ },
53
+ snow: {
54
+ baseCount: 150,
55
+ sizeRange: [2, 6],
56
+ opacityRange: [0.6, 0.9],
57
+ speedRange: [1, 3],
58
+ motionStyle: "drift",
59
+ shape: "circle",
60
+ colors: ["#ffffff", "#e8f4ff"]
61
+ },
62
+ dust: {
63
+ baseCount: 80,
64
+ sizeRange: [1, 3],
65
+ opacityRange: [0.2, 0.4],
66
+ speedRange: [0.5, 2],
67
+ motionStyle: "drift",
68
+ shape: "circle",
69
+ colors: ["#d4a574", "#c9b896"]
70
+ },
71
+ leaves: {
72
+ baseCount: 30,
73
+ sizeRange: [6, 12],
74
+ opacityRange: [0.7, 1],
75
+ speedRange: [2, 5],
76
+ motionStyle: "drift",
77
+ shape: "leaf",
78
+ colors: ["#d4a373", "#e07b39", "#bc6c25"]
79
+ },
80
+ embers: {
81
+ baseCount: 60,
82
+ sizeRange: [2, 5],
83
+ opacityRange: [0.5, 0.8],
84
+ speedRange: [2, 5],
85
+ motionStyle: "rise",
86
+ shape: "glow",
87
+ colors: ["#ff6b35", "#f7931e", "#ffcc00"]
88
+ },
89
+ fireflies: {
90
+ baseCount: 40,
91
+ sizeRange: [3, 6],
92
+ opacityRange: [0.3, 1],
93
+ speedRange: [0.3, 1],
94
+ motionStyle: "drift",
95
+ shape: "glow",
96
+ colors: ["#90ee90", "#adff2f", "#7fff00"]
97
+ },
98
+ ash: {
99
+ baseCount: 100,
100
+ sizeRange: [2, 5],
101
+ opacityRange: [0.3, 0.6],
102
+ speedRange: [1, 3],
103
+ motionStyle: "fall",
104
+ shape: "circle",
105
+ colors: ["#4a4a4a", "#6b6b6b", "#8b8b8b"]
106
+ },
107
+ sakura: {
108
+ baseCount: 50,
109
+ sizeRange: [6, 12],
110
+ opacityRange: [0.6, 0.9],
111
+ speedRange: [1, 3],
112
+ motionStyle: "drift",
113
+ shape: "petal",
114
+ colors: ["#ffb7c5", "#ff69b4", "#ffc0cb"]
115
+ },
116
+ sparks: {
117
+ baseCount: 40,
118
+ sizeRange: [1, 3],
119
+ opacityRange: [0.7, 1],
120
+ speedRange: [8, 15],
121
+ motionStyle: "erratic",
122
+ shape: "glow",
123
+ colors: ["#ffd700", "#ffffff", "#fffacd"]
124
+ },
125
+ spores: {
126
+ baseCount: 60,
127
+ sizeRange: [4, 8],
128
+ opacityRange: [0.4, 0.7],
129
+ speedRange: [0.5, 1.5],
130
+ motionStyle: "rise",
131
+ shape: "glow",
132
+ colors: ["#9370db", "#8a2be2", "#da70d6"]
133
+ }
134
+ };
135
+
136
+ // src/environment/WeatherLayer/particles.ts
137
+ function smoothstep(edge0, edge1, x) {
138
+ const t = Math.max(0, Math.min(1, (x - edge0) / (edge1 - edge0)));
139
+ return t * t * (3 - 2 * t);
140
+ }
141
+ function getSpawnPosition(bounds, velocity) {
142
+ const margin = 40;
143
+ if (Math.abs(velocity.x) > Math.abs(velocity.y)) {
144
+ return {
145
+ x: velocity.x > 0 ? -margin : bounds.width + margin,
146
+ y: Math.random() * bounds.height
147
+ };
148
+ }
149
+ return {
150
+ x: Math.random() * bounds.width,
151
+ y: velocity.y > 0 ? -margin : bounds.height + margin
152
+ };
153
+ }
154
+ function getLifeRangeMs(type) {
155
+ switch (type) {
156
+ case "rain":
157
+ return [900, 2200];
158
+ case "snow":
159
+ return [3200, 7200];
160
+ case "dust":
161
+ return [5e3, 11e3];
162
+ case "leaves":
163
+ return [6e3, 14e3];
164
+ case "embers":
165
+ return [2600, 6500];
166
+ case "fireflies":
167
+ return [4500, 9e3];
168
+ case "ash":
169
+ return [4500, 1e4];
170
+ case "sakura":
171
+ return [6500, 15e3];
172
+ case "sparks":
173
+ return [450, 1200];
174
+ case "spores":
175
+ return [5e3, 12e3];
176
+ default:
177
+ return [3e3, 8e3];
178
+ }
179
+ }
180
+ function createParticle(id, type, config, wind, bounds, stylePreset = "ui", colorsOverride) {
181
+ const [minSize, maxSize] = config.sizeRange;
182
+ const [minOpacity, maxOpacity] = config.opacityRange;
183
+ const [minSpeed, maxSpeed] = config.speedRange;
184
+ const speed = minSpeed + Math.random() * (maxSpeed - minSpeed);
185
+ let vx = wind.x * speed;
186
+ let vy = wind.y * speed;
187
+ const depth = stylePreset === "cinematic" ? Math.sqrt(Math.random()) : 0;
188
+ const sizeScale = stylePreset === "cinematic" ? 0.55 + (1 - depth) * 0.45 : 1;
189
+ const speedScale = stylePreset === "cinematic" ? 0.6 + (1 - depth) * 0.4 : 1;
190
+ const opacityScale = stylePreset === "cinematic" ? 0.45 + (1 - depth) * 0.55 : 1;
191
+ switch (config.motionStyle) {
192
+ case "fall":
193
+ vy = speed;
194
+ vx = wind.x * 2 + (Math.random() - 0.5) * 0.5;
195
+ break;
196
+ case "rise":
197
+ vy = -speed;
198
+ vx = wind.x + (Math.random() - 0.5) * 2;
199
+ break;
200
+ case "drift": {
201
+ const allowUpdraft = type === "dust" || type === "fireflies";
202
+ const verticalSign = allowUpdraft ? Math.random() > 0.5 ? 1 : -1 : 1;
203
+ const driftFallScale = type === "snow" ? 0.35 : type === "leaves" ? 0.55 : type === "sakura" ? 0.45 : 0.3;
204
+ vy = speed * driftFallScale * verticalSign;
205
+ vx = wind.x + (Math.random() - 0.5) * 2;
206
+ break;
207
+ }
208
+ case "erratic":
209
+ vy = (Math.random() - 0.3) * speed;
210
+ vx = (Math.random() - 0.5) * speed * 2;
211
+ break;
212
+ }
213
+ vx *= speedScale;
214
+ vy *= speedScale;
215
+ const [minLife, maxLife] = getLifeRangeMs(type);
216
+ const maxLifeMs = minLife + Math.random() * (maxLife - minLife);
217
+ const palette = colorsOverride && colorsOverride.length > 0 ? colorsOverride : config.colors;
218
+ const color = palette.length > 0 ? palette[Math.floor(Math.random() * palette.length)] : "#ffffff";
219
+ const baseOpacity = (minOpacity + Math.random() * (maxOpacity - minOpacity)) * opacityScale;
220
+ const baseSize = (minSize + Math.random() * (maxSize - minSize)) * sizeScale;
221
+ const twinkleAmountBase = type === "fireflies" ? 0.55 : type === "sparks" ? 0.35 : type === "embers" ? 0.18 : type === "spores" ? 0.12 : 0;
222
+ const twinkleAmount = stylePreset === "cinematic" ? twinkleAmountBase * 0.65 : twinkleAmountBase;
223
+ const twinkleSpeed = type === "sparks" ? 0.03 : type === "fireflies" ? 8e-3 : type === "embers" ? 0.01 : type === "spores" ? 7e-3 : 0;
224
+ const driftPhase = Math.random() * Math.PI * 2;
225
+ const twinklePhase = Math.random() * Math.PI * 2;
226
+ const velocity = { x: vx, y: vy };
227
+ const spawn = getSpawnPosition(bounds, velocity);
228
+ const rotation = config.shape === "streak" ? Math.atan2(velocity.y, velocity.x) * 180 / Math.PI - 90 : Math.random() * 360;
229
+ const rotationSpeed = config.shape === "leaf" || config.shape === "petal" ? (stylePreset === "cinematic" ? 0.03 : 0.06) * (Math.random() > 0.5 ? 1 : -1) : 0;
230
+ return {
231
+ id,
232
+ x: spawn.x,
233
+ y: spawn.y,
234
+ size: baseSize,
235
+ opacity: baseOpacity,
236
+ rotation,
237
+ rotationSpeed,
238
+ velocity,
239
+ life: 0,
240
+ maxLife: maxLifeMs,
241
+ color,
242
+ baseOpacity,
243
+ depth,
244
+ driftPhase,
245
+ twinklePhase,
246
+ twinkleSpeed,
247
+ twinkleAmount
248
+ };
249
+ }
250
+ function updateParticle(particle, config, deltaTime) {
251
+ const newLife = particle.life + deltaTime;
252
+ const lifeRatio = newLife / particle.maxLife;
253
+ const fadeIn = smoothstep(0, 0.08, lifeRatio);
254
+ const fadeOut = 1 - smoothstep(0.82, 1, lifeRatio);
255
+ let opacity = particle.baseOpacity * fadeIn * fadeOut;
256
+ if (particle.twinkleAmount > 0 && particle.twinkleSpeed > 0) {
257
+ const tw = Math.sin(newLife * particle.twinkleSpeed + particle.twinklePhase);
258
+ opacity *= Math.max(0.15, 1 + tw * particle.twinkleAmount * 0.5);
259
+ }
260
+ let vx = particle.velocity.x;
261
+ let vy = particle.velocity.y;
262
+ if (config.motionStyle === "drift") {
263
+ vx += Math.sin(newLife * 2e-3 + particle.driftPhase) * 0.18;
264
+ vy += Math.cos(newLife * 16e-4 + particle.driftPhase) * 0.07;
265
+ } else if (config.motionStyle === "erratic") {
266
+ vx += Math.sin(newLife * 0.01 + particle.driftPhase) * 0.25;
267
+ vy += Math.cos(newLife * 0.012 + particle.driftPhase) * 0.18;
268
+ }
269
+ return {
270
+ ...particle,
271
+ x: particle.x + vx * deltaTime * 0.1,
272
+ y: particle.y + vy * deltaTime * 0.1,
273
+ rotation: particle.rotation + particle.rotationSpeed * deltaTime,
274
+ life: newLife,
275
+ opacity
276
+ };
277
+ }
278
+ function getParticleCount(type, intensity, maxParticles, stylePreset = "ui") {
279
+ const config = WEATHER_CONFIGS[type];
280
+ const styleScale = stylePreset === "cinematic" ? type === "snow" ? 1.8 : type === "leaves" ? 1.4 : 0.7 : 1;
281
+ const desired = Math.floor(config.baseCount * intensity * styleScale);
282
+ return Math.min(desired, maxParticles);
283
+ }
284
+
285
+ // src/environment/WeatherLayer/cinematicCanvas.tsx
286
+ import * as React from "react";
287
+
288
+ // src/environment/WeatherLayer/leafPresets.ts
289
+ var AUTUMN_LEAF_COLOR_PRESETS = {
290
+ "early-autumn": ["#6b8f3f", "#a9b84a", "#d8c35a", "#e5b04a", "#c97a2c"],
291
+ "peak-autumn": ["#b63a2b", "#d86b2c", "#e5a93a", "#c56b2b", "#7a3e1b"],
292
+ "late-autumn": ["#5a3d2b", "#7a563e", "#9a6f4b", "#b68a5e", "#8a6b4f"],
293
+ golden: ["#f2d27a", "#e9c15c", "#d8a93b", "#b9872a", "#e8b04a"],
294
+ rust: ["#a34a1f", "#c2602d", "#d6782f", "#e07b39", "#bc6c25"],
295
+ "maple-red": ["#7a1f1f", "#a02b2b", "#c0302b", "#d44a2c", "#e36a3a"],
296
+ muted: ["#d4a373", "#c08a5a", "#a66a3f", "#8c5a36", "#e3b07a"]
297
+ };
298
+
299
+ // src/environment/WeatherLayer/colors.ts
300
+ function resolveWeatherColors({
301
+ type,
302
+ configColors,
303
+ colors,
304
+ leafColorPreset
305
+ }) {
306
+ if (colors && colors.length > 0) return colors;
307
+ if (type === "leaves" && leafColorPreset) {
308
+ return AUTUMN_LEAF_COLOR_PRESETS[leafColorPreset] ?? configColors;
309
+ }
310
+ return configColors;
311
+ }
312
+ function colorsKey(colors) {
313
+ if (!colors || colors.length === 0) return "";
314
+ return colors.join("|");
315
+ }
316
+
317
+ // src/environment/WeatherLayer/cinematicCanvas.tsx
318
+ import { jsx } from "react/jsx-runtime";
319
+ function clamp(min, value, max) {
320
+ return Math.max(min, Math.min(max, value));
321
+ }
322
+ function parseHexColor(color) {
323
+ const hex = color.trim();
324
+ if (!hex.startsWith("#")) return null;
325
+ const raw = hex.slice(1);
326
+ if (raw.length === 3) {
327
+ const r = Number.parseInt(raw[0] + raw[0], 16);
328
+ const g = Number.parseInt(raw[1] + raw[1], 16);
329
+ const b = Number.parseInt(raw[2] + raw[2], 16);
330
+ return { r, g, b };
331
+ }
332
+ if (raw.length === 6) {
333
+ const r = Number.parseInt(raw.slice(0, 2), 16);
334
+ const g = Number.parseInt(raw.slice(2, 4), 16);
335
+ const b = Number.parseInt(raw.slice(4, 6), 16);
336
+ return { r, g, b };
337
+ }
338
+ return null;
339
+ }
340
+ function mix(a, b, t) {
341
+ const tt = clamp(0, t, 1);
342
+ return {
343
+ r: Math.round(a.r + (b.r - a.r) * tt),
344
+ g: Math.round(a.g + (b.g - a.g) * tt),
345
+ b: Math.round(a.b + (b.b - a.b) * tt)
346
+ };
347
+ }
348
+ function rgba(c, a) {
349
+ return `rgba(${c.r}, ${c.g}, ${c.b}, ${clamp(0, a, 1)})`;
350
+ }
351
+ function getSeededNoise(t, phase) {
352
+ return Math.sin(t * 7e-4 + phase) * 0.6 + Math.sin(t * 13e-4 + phase * 1.7) * 0.3 + Math.sin(t * 21e-4 + phase * 2.3) * 0.1;
353
+ }
354
+ var SNOW_SPRITE_VARIANTS = 4;
355
+ var LEAF_SPRITE_VARIANTS = 11;
356
+ function makeSnowSprite(color, variant) {
357
+ const size = 64;
358
+ const canvas = document.createElement("canvas");
359
+ canvas.width = size;
360
+ canvas.height = size;
361
+ const ctx = canvas.getContext("2d");
362
+ if (!ctx) return canvas;
363
+ ctx.clearRect(0, 0, size, size);
364
+ const v = (variant % SNOW_SPRITE_VARIANTS + SNOW_SPRITE_VARIANTS) % SNOW_SPRITE_VARIANTS;
365
+ const cx = size / 2;
366
+ const cy = size / 2;
367
+ const r = size * 0.46;
368
+ const highlightX = cx - r * (0.16 + v * 0.04);
369
+ const highlightY = cy - r * (0.18 - v * 0.03);
370
+ const softness = v === 3 ? 0.62 : v === 2 ? 0.56 : v === 1 ? 0.5 : 0.46;
371
+ const g = ctx.createRadialGradient(cx - r * 0.1, cy - r * 0.1, 0, cx, cy, r);
372
+ g.addColorStop(0, "rgba(255,255,255,0.95)");
373
+ g.addColorStop(0.45, `rgba(255,255,255,${0.38 + v * 0.02})`);
374
+ g.addColorStop(1, "rgba(255,255,255,0)");
375
+ ctx.fillStyle = g;
376
+ ctx.beginPath();
377
+ ctx.arc(cx, cy, r, 0, Math.PI * 2);
378
+ ctx.fill();
379
+ const h = ctx.createRadialGradient(highlightX, highlightY, 0, highlightX, highlightY, r * softness);
380
+ h.addColorStop(0, `rgba(255,255,255,${0.26 + v * 0.05})`);
381
+ h.addColorStop(1, "rgba(255,255,255,0)");
382
+ ctx.fillStyle = h;
383
+ ctx.beginPath();
384
+ ctx.arc(cx, cy, r, 0, Math.PI * 2);
385
+ ctx.fill();
386
+ ctx.globalCompositeOperation = "source-in";
387
+ ctx.fillStyle = color;
388
+ ctx.fillRect(0, 0, size, size);
389
+ ctx.globalCompositeOperation = "source-over";
390
+ const s = ctx.createRadialGradient(highlightX, highlightY, 0, highlightX, highlightY, r * 0.34);
391
+ s.addColorStop(0, `rgba(255,255,255,${0.18 + v * 0.04})`);
392
+ s.addColorStop(1, "rgba(255,255,255,0)");
393
+ ctx.fillStyle = s;
394
+ ctx.beginPath();
395
+ ctx.arc(cx, cy, r, 0, Math.PI * 2);
396
+ ctx.fill();
397
+ return canvas;
398
+ }
399
+ function makeLeafSprite(color, variant) {
400
+ const size = 160;
401
+ const canvas = document.createElement("canvas");
402
+ canvas.width = size;
403
+ canvas.height = size;
404
+ const ctx = canvas.getContext("2d");
405
+ if (!ctx) return canvas;
406
+ ctx.clearRect(0, 0, size, size);
407
+ const base = parseHexColor(color) ?? { r: 180, g: 120, b: 70 };
408
+ const dark = mix(base, { r: 0, g: 0, b: 0 }, 0.35);
409
+ const light = mix(base, { r: 255, g: 255, b: 255 }, 0.25);
410
+ const rim = mix(base, { r: 0, g: 0, b: 0 }, 0.2);
411
+ const cx = size / 2;
412
+ const cy = size / 2 - 4;
413
+ const shape = (variant % LEAF_SPRITE_VARIANTS + LEAF_SPRITE_VARIANTS) % LEAF_SPRITE_VARIANTS;
414
+ const drawRadialPath = (radii, rx, ry) => {
415
+ const pts = radii.map((r, i) => {
416
+ const a = -Math.PI / 2 + i / radii.length * Math.PI * 2;
417
+ return {
418
+ x: cx + Math.cos(a) * rx * r,
419
+ y: cy + Math.sin(a) * ry * r
420
+ };
421
+ });
422
+ const mid = (a, b) => ({
423
+ x: (a.x + b.x) / 2,
424
+ y: (a.y + b.y) / 2
425
+ });
426
+ ctx.beginPath();
427
+ const start = mid(pts[pts.length - 1], pts[0]);
428
+ ctx.moveTo(start.x, start.y);
429
+ for (let i = 0; i < pts.length; i++) {
430
+ const p0 = pts[i];
431
+ const p1 = pts[(i + 1) % pts.length];
432
+ const m = mid(p0, p1);
433
+ ctx.quadraticCurveTo(p0.x, p0.y, m.x, m.y);
434
+ }
435
+ ctx.closePath();
436
+ };
437
+ let w = 56;
438
+ let h = 78;
439
+ let skew = 0;
440
+ let veinStyle = "midrib";
441
+ let carveNotch = false;
442
+ let drawKind = "ovate";
443
+ let radialRadii = null;
444
+ switch (shape) {
445
+ case 0:
446
+ w = 52;
447
+ h = 78;
448
+ skew = -0.04;
449
+ drawKind = "ovate";
450
+ break;
451
+ case 1:
452
+ w = 66;
453
+ h = 74;
454
+ drawKind = "radial";
455
+ radialRadii = [1, 0.58, 0.96, 0.52, 0.92, 0.6, 0.86, 0.62, 0.96, 0.58];
456
+ break;
457
+ case 2:
458
+ w = 60;
459
+ h = 80;
460
+ drawKind = "radial";
461
+ radialRadii = [1, 0.82, 0.95, 0.84, 0.92, 0.86, 0.9, 0.86, 0.92, 0.84, 0.95, 0.82];
462
+ break;
463
+ case 3:
464
+ w = 74;
465
+ h = 70;
466
+ veinStyle = "fan";
467
+ carveNotch = true;
468
+ drawKind = "ginkgo";
469
+ break;
470
+ case 4:
471
+ w = 60;
472
+ h = 78;
473
+ drawKind = "heart";
474
+ break;
475
+ case 5:
476
+ w = 34;
477
+ h = 80;
478
+ skew = 0.08;
479
+ drawKind = "willow";
480
+ break;
481
+ case 6:
482
+ w = 54;
483
+ h = 76;
484
+ drawKind = "deltoid";
485
+ break;
486
+ case 7:
487
+ w = 74;
488
+ h = 78;
489
+ drawKind = "radial";
490
+ radialRadii = [1, 0.74, 0.96, 0.72, 0.92, 0.72, 0.96, 0.74, 1, 0.74];
491
+ break;
492
+ case 8:
493
+ w = 50;
494
+ h = 74;
495
+ skew = 0.02;
496
+ drawKind = "ovate";
497
+ break;
498
+ case 9:
499
+ w = 66;
500
+ h = 74;
501
+ drawKind = "heart";
502
+ break;
503
+ case 10:
504
+ w = 56;
505
+ h = 80;
506
+ drawKind = "radial";
507
+ radialRadii = [1, 0.92, 0.99, 0.91, 0.98, 0.9, 0.97, 0.9, 0.98, 0.91, 0.99, 0.92];
508
+ break;
509
+ default:
510
+ w = 52;
511
+ h = 78;
512
+ skew = -0.04;
513
+ drawKind = "ovate";
514
+ break;
515
+ }
516
+ const drawLeafPath = () => {
517
+ switch (drawKind) {
518
+ case "ovate":
519
+ ctx.beginPath();
520
+ ctx.moveTo(cx, cy - h);
521
+ ctx.bezierCurveTo(cx + w, cy - h * 0.55, cx + w * (1 - skew), cy + h * 0.55, cx, cy + h);
522
+ ctx.bezierCurveTo(cx - w * (1 + skew), cy + h * 0.55, cx - w, cy - h * 0.55, cx, cy - h);
523
+ ctx.closePath();
524
+ return;
525
+ case "radial":
526
+ drawRadialPath(radialRadii, w, h);
527
+ return;
528
+ case "ginkgo":
529
+ ctx.beginPath();
530
+ ctx.moveTo(cx, cy + h * 0.78);
531
+ ctx.bezierCurveTo(
532
+ cx + w * 0.55,
533
+ cy + h * 0.55,
534
+ cx + w * 1.05,
535
+ cy + h * 0.1,
536
+ cx + w * 0.92,
537
+ cy - h * 0.12
538
+ );
539
+ ctx.quadraticCurveTo(cx + w * 0.5, cy - h * 1.02, cx, cy - h * 0.88);
540
+ ctx.quadraticCurveTo(cx - w * 0.5, cy - h * 1.02, cx - w * 0.92, cy - h * 0.12);
541
+ ctx.bezierCurveTo(
542
+ cx - w * 1.05,
543
+ cy + h * 0.1,
544
+ cx - w * 0.55,
545
+ cy + h * 0.55,
546
+ cx,
547
+ cy + h * 0.78
548
+ );
549
+ ctx.closePath();
550
+ return;
551
+ case "heart":
552
+ ctx.beginPath();
553
+ ctx.moveTo(cx, cy + h);
554
+ ctx.bezierCurveTo(cx + w * 0.95, cy + h * 0.45, cx + w, cy + h * 0.05, cx + w * 0.62, cy - h * 0.38);
555
+ ctx.bezierCurveTo(cx + w * 0.25, cy - h * 0.78, cx, cy - h * 0.62, cx, cy - h * 0.48);
556
+ ctx.bezierCurveTo(cx, cy - h * 0.62, cx - w * 0.25, cy - h * 0.78, cx - w * 0.62, cy - h * 0.38);
557
+ ctx.bezierCurveTo(cx - w, cy + h * 0.05, cx - w * 0.95, cy + h * 0.45, cx, cy + h);
558
+ ctx.closePath();
559
+ return;
560
+ case "deltoid":
561
+ ctx.beginPath();
562
+ ctx.moveTo(cx, cy - h);
563
+ ctx.quadraticCurveTo(cx + w * 1.05, cy - h * 0.15, cx + w * 0.36, cy + h);
564
+ ctx.quadraticCurveTo(cx, cy + h * 0.78, cx - w * 0.36, cy + h);
565
+ ctx.quadraticCurveTo(cx - w * 1.05, cy - h * 0.15, cx, cy - h);
566
+ ctx.closePath();
567
+ return;
568
+ case "willow":
569
+ ctx.beginPath();
570
+ ctx.moveTo(cx, cy - h);
571
+ ctx.bezierCurveTo(cx + w, cy - h * 0.35, cx + w * (1 - skew), cy + h * 0.55, cx, cy + h);
572
+ ctx.bezierCurveTo(cx - w * (1 + skew), cy + h * 0.55, cx - w, cy - h * 0.35, cx, cy - h);
573
+ ctx.closePath();
574
+ return;
575
+ }
576
+ };
577
+ ctx.save();
578
+ ctx.translate(2, 4);
579
+ drawLeafPath();
580
+ ctx.fillStyle = "rgba(0,0,0,0.22)";
581
+ ctx.fill();
582
+ ctx.restore();
583
+ drawLeafPath();
584
+ const grad = ctx.createLinearGradient(cx - w, cy - h, cx + w, cy + h);
585
+ grad.addColorStop(0, rgba(dark, 0.95));
586
+ grad.addColorStop(0.45, rgba(base, 0.95));
587
+ grad.addColorStop(1, rgba(light, 0.95));
588
+ ctx.fillStyle = grad;
589
+ ctx.fill();
590
+ drawLeafPath();
591
+ ctx.strokeStyle = rgba(rim, 0.55);
592
+ ctx.lineWidth = 2;
593
+ ctx.stroke();
594
+ ctx.save();
595
+ ctx.strokeStyle = rgba(dark, 0.5);
596
+ ctx.lineWidth = 3;
597
+ ctx.lineCap = "round";
598
+ ctx.beginPath();
599
+ ctx.moveTo(cx, cy + h * 0.95);
600
+ ctx.quadraticCurveTo(cx + w * 0.18, cy + h * 1.08, cx + w * 0.1, cy + h * 1.22);
601
+ ctx.stroke();
602
+ ctx.restore();
603
+ ctx.save();
604
+ drawLeafPath();
605
+ ctx.clip();
606
+ ctx.fillStyle = rgba(mix(dark, { r: 0, g: 0, b: 0 }, 0.1), 0.18);
607
+ for (let i = 0; i < 14; i++) {
608
+ const px = cx + (Math.random() - 0.5) * w * 1.5;
609
+ const py = cy + (Math.random() - 0.25) * h * 1.4;
610
+ const pr = 1.2 + Math.random() * 3.8;
611
+ ctx.globalAlpha = 0.06 + Math.random() * 0.08;
612
+ ctx.beginPath();
613
+ ctx.arc(px, py, pr, 0, Math.PI * 2);
614
+ ctx.fill();
615
+ }
616
+ ctx.globalAlpha = 0.45;
617
+ ctx.strokeStyle = rgba(mix(base, { r: 255, g: 255, b: 255 }, 0.6), 0.55);
618
+ ctx.lineWidth = 1.15;
619
+ if (veinStyle === "fan") {
620
+ const y0 = cy + h * 0.55;
621
+ for (let i = -7; i <= 7; i++) {
622
+ const t = i / 7;
623
+ const x1 = cx + t * w * 0.82;
624
+ const y1 = cy - h * 0.86 + Math.abs(t) * h * 0.18;
625
+ ctx.globalAlpha = 0.1 + (1 - Math.abs(t)) * 0.12;
626
+ ctx.beginPath();
627
+ ctx.moveTo(cx, y0);
628
+ ctx.quadraticCurveTo(cx + t * w * 0.25, cy - h * 0.1, x1, y1);
629
+ ctx.stroke();
630
+ }
631
+ } else {
632
+ ctx.globalAlpha = 0.34;
633
+ ctx.beginPath();
634
+ ctx.moveTo(cx, cy + h * 0.9);
635
+ ctx.quadraticCurveTo(cx + w * 0.05, cy, cx, cy - h * 0.9);
636
+ ctx.stroke();
637
+ const veinCount = shape === 5 ? 4 : 6;
638
+ for (let i = 0; i < veinCount; i++) {
639
+ const t = (i + 1) / (veinCount + 1);
640
+ const y = cy + h * 0.78 - t * h * 1.35;
641
+ const veinW = w * (0.12 + t * 0.6);
642
+ ctx.globalAlpha = 0.12;
643
+ ctx.beginPath();
644
+ ctx.moveTo(cx, y);
645
+ ctx.quadraticCurveTo(cx + veinW, y + h * 0.08, cx + veinW * 0.9, y + h * 0.22);
646
+ ctx.stroke();
647
+ ctx.beginPath();
648
+ ctx.moveTo(cx, y);
649
+ ctx.quadraticCurveTo(cx - veinW, y + h * 0.08, cx - veinW * 0.9, y + h * 0.22);
650
+ ctx.stroke();
651
+ }
652
+ }
653
+ ctx.restore();
654
+ if (carveNotch) {
655
+ ctx.save();
656
+ ctx.globalCompositeOperation = "destination-out";
657
+ ctx.beginPath();
658
+ ctx.arc(cx, cy - h * 0.78, w * 0.12, 0, Math.PI * 2);
659
+ ctx.fill();
660
+ ctx.restore();
661
+ }
662
+ return canvas;
663
+ }
664
+ function getSprite(cache, key, build) {
665
+ const existing = cache.get(key);
666
+ if (existing) return existing;
667
+ const created = build();
668
+ cache.set(key, created);
669
+ return created;
670
+ }
671
+ function createSnowParticles(count, config, bounds, options) {
672
+ const particles = [];
673
+ const palette = options?.colorsOverride && options.colorsOverride.length > 0 ? options.colorsOverride : config.colors;
674
+ for (let i = 0; i < count; i++) {
675
+ const z = Math.pow(Math.random(), 0.55);
676
+ let size = 1.1 + (1 - z) * 4.4 + Math.random() * 0.8;
677
+ const speed = 16 + (1 - z) * 68 + Math.random() * 12;
678
+ const driftAmp = 6 + (1 - z) * 24 + Math.random() * 10;
679
+ const driftFreq = 0.7 + Math.random() * 1.6;
680
+ const alpha = 0.24 + (1 - z) * 0.56 + Math.random() * 0.1;
681
+ const spriteIndex = Math.floor(Math.random() * SNOW_SPRITE_VARIANTS);
682
+ if (z < 0.22 && Math.random() < 0.06) {
683
+ size *= 1.8;
684
+ }
685
+ const spriteColor = options?.colorOverride ?? (palette.length > 0 ? palette[Math.floor(Math.random() * palette.length)] : "#ffffff");
686
+ particles.push({
687
+ x: Math.random() * bounds.width,
688
+ y: Math.random() * bounds.height,
689
+ z,
690
+ speed,
691
+ driftAmp,
692
+ driftFreq,
693
+ phase: Math.random() * Math.PI * 2,
694
+ spriteColor,
695
+ spriteIndex,
696
+ alpha,
697
+ size
698
+ });
699
+ }
700
+ return particles;
701
+ }
702
+ function createLeafParticles(count, config, bounds, options) {
703
+ const particles = [];
704
+ const palette = options?.colorsOverride && options.colorsOverride.length > 0 ? options.colorsOverride : config.colors;
705
+ for (let i = 0; i < count; i++) {
706
+ const z = Math.pow(Math.random(), 0.6);
707
+ const size = 6.5 + (1 - z) * 13.5 + Math.random() * 2.2;
708
+ const speed = 18 + (1 - z) * 48 + Math.random() * 9;
709
+ const alpha = 0.55 + (1 - z) * 0.35;
710
+ const rotation = Math.random() * Math.PI * 2;
711
+ const rotationSpeed = (Math.random() * 0.9 + 0.4) * (Math.random() > 0.5 ? 1 : -1);
712
+ const flutterSpeed = 1.2 + Math.random() * 1.6;
713
+ const windScale = 0.4 + Math.random() * 0.9;
714
+ const spriteColor = options?.colorOverride ?? (palette.length > 0 ? palette[Math.floor(Math.random() * palette.length)] : "#d4a373");
715
+ const spriteIndex = Math.floor(Math.random() * LEAF_SPRITE_VARIANTS);
716
+ particles.push({
717
+ x: Math.random() * bounds.width,
718
+ y: Math.random() * bounds.height,
719
+ z,
720
+ speed,
721
+ windScale,
722
+ phase: Math.random() * Math.PI * 2,
723
+ rotation,
724
+ rotationSpeed,
725
+ flutterPhase: Math.random() * Math.PI * 2,
726
+ flutterSpeed,
727
+ spriteColor,
728
+ spriteIndex,
729
+ alpha,
730
+ size
731
+ });
732
+ }
733
+ return particles;
734
+ }
735
+ function updateSnow(particles, dtSec, tMs, bounds, wind) {
736
+ const margin = 50;
737
+ for (const p of particles) {
738
+ const turb = getSeededNoise(tMs, p.phase);
739
+ const vx = wind.x * (20 + (1 - p.z) * 35) + turb * p.driftAmp * 0.9;
740
+ const vy = p.speed + wind.y * 10 + turb * 8;
741
+ p.x += vx * dtSec;
742
+ p.y += vy * dtSec;
743
+ p.x += Math.sin(tMs * 1e-3 * p.driftFreq + p.phase) * p.driftAmp * dtSec * 1.2;
744
+ if (p.y > bounds.height + margin) {
745
+ p.y = -margin - Math.random() * 40;
746
+ p.x = Math.random() * bounds.width;
747
+ }
748
+ if (p.x < -margin) p.x = bounds.width + margin;
749
+ if (p.x > bounds.width + margin) p.x = -margin;
750
+ }
751
+ }
752
+ function updateLeaves(particles, dtSec, tMs, bounds, wind) {
753
+ const margin = 80;
754
+ for (const p of particles) {
755
+ const turb = getSeededNoise(tMs, p.phase);
756
+ const sway = Math.sin(tMs * 12e-4 + p.phase) * (14 + p.z * 18);
757
+ const vx = wind.x * (28 + (1 - p.z) * 40) * p.windScale + turb * 22 + sway;
758
+ const vy = p.speed + wind.y * 10 + Math.cos(tMs * 1e-3 + p.phase) * 10;
759
+ p.x += vx * dtSec;
760
+ p.y += vy * dtSec;
761
+ p.rotation += (p.rotationSpeed + turb * 0.6) * dtSec;
762
+ if (p.y > bounds.height + margin) {
763
+ p.y = -margin - Math.random() * 80;
764
+ p.x = Math.random() * bounds.width;
765
+ }
766
+ if (p.x < -margin) p.x = bounds.width + margin;
767
+ if (p.x > bounds.width + margin) p.x = -margin;
768
+ }
769
+ }
770
+ function drawSnow(ctx, particles, opacity, spriteCache) {
771
+ for (const p of particles) {
772
+ const sprite = getSprite(
773
+ spriteCache,
774
+ `snow:${p.spriteColor}:${p.spriteIndex}`,
775
+ () => makeSnowSprite(p.spriteColor, p.spriteIndex)
776
+ );
777
+ const alpha = clamp(0, p.alpha * opacity, 1);
778
+ ctx.globalAlpha = alpha;
779
+ const s = p.size;
780
+ const baseX = p.x - s / 2;
781
+ const baseY = p.y - s / 2;
782
+ ctx.drawImage(sprite, baseX, baseY, s, s);
783
+ if (p.z < 0.2 && s > 6) {
784
+ ctx.globalAlpha = alpha * 0.14;
785
+ ctx.drawImage(sprite, baseX - s * 0.25, baseY - s * 0.25, s * 1.5, s * 1.5);
786
+ }
787
+ if (p.z < 0.25 && s > 3.2) {
788
+ ctx.globalAlpha = alpha * 0.28;
789
+ ctx.drawImage(sprite, baseX, baseY - s * 0.35, s, s);
790
+ }
791
+ }
792
+ }
793
+ function drawLeaves(ctx, particles, opacity, tMs, config, spriteCache) {
794
+ for (const p of particles) {
795
+ const leafColor = p.spriteColor || config.colors[0] || "#d4a373";
796
+ const key = `leaf:${leafColor}:${p.spriteIndex}`;
797
+ const sprite = getSprite(spriteCache, key, () => makeLeafSprite(leafColor, p.spriteIndex));
798
+ const alpha = clamp(0, p.alpha * opacity, 1);
799
+ const flutter = Math.sin(tMs * 1e-3 * p.flutterSpeed + p.flutterPhase);
800
+ const tilt = flutter * 0.35;
801
+ const scale = p.size / 52 * (0.78 + (1 - p.z) * 0.34);
802
+ const squash = 0.75 + Math.abs(flutter) * 0.25;
803
+ ctx.save();
804
+ ctx.globalAlpha = alpha;
805
+ ctx.translate(p.x, p.y);
806
+ ctx.rotate(p.rotation + tilt);
807
+ ctx.scale(scale, scale * squash);
808
+ ctx.drawImage(sprite, -sprite.width / 2, -sprite.height / 2);
809
+ ctx.restore();
810
+ }
811
+ }
812
+ function getCinematicCount(type, intensity, maxParticles) {
813
+ return getParticleCount(type, intensity, maxParticles, "cinematic");
814
+ }
815
+ function WeatherLayerCinematicCanvas({
816
+ type,
817
+ intensity = 0.5,
818
+ stylePreset = "cinematic",
819
+ wind = { x: 0, y: 0 },
820
+ color,
821
+ colors,
822
+ leafColorPreset,
823
+ opacity = 1,
824
+ blur = false,
825
+ maxParticles: propMaxParticles,
826
+ enabled = true,
827
+ className,
828
+ style
829
+ }) {
830
+ const perfConfig = getPerformanceConfig();
831
+ const maxParticles = propMaxParticles ?? perfConfig.maxParticles;
832
+ const colorsKeyProp = colorsKey(colors);
833
+ const config = WEATHER_CONFIGS[type];
834
+ const particleColors = React.useMemo(
835
+ () => resolveWeatherColors({ type, colors, leafColorPreset, configColors: config.colors }),
836
+ [type, leafColorPreset, colorsKeyProp, config]
837
+ );
838
+ const containerRef = React.useRef(null);
839
+ const canvasRef = React.useRef(null);
840
+ const frameRef = React.useRef(0);
841
+ const lastRef = React.useRef(0);
842
+ const boundsRef = React.useRef({ width: 0, height: 0 });
843
+ const snowRef = React.useRef([]);
844
+ const leafRef = React.useRef([]);
845
+ const spriteCacheRef = React.useRef(/* @__PURE__ */ new Map());
846
+ React.useEffect(() => {
847
+ if (stylePreset !== "cinematic" || !enabled) return;
848
+ const container = containerRef.current;
849
+ const canvas = canvasRef.current;
850
+ if (!container || !canvas) return;
851
+ const ctx = canvas.getContext("2d", { alpha: true });
852
+ if (!ctx) return;
853
+ lastRef.current = 0;
854
+ const dpr = clamp(1, window.devicePixelRatio ?? 1, 2);
855
+ const resize = () => {
856
+ const width = Math.max(1, Math.floor(container.clientWidth));
857
+ const height = Math.max(1, Math.floor(container.clientHeight));
858
+ boundsRef.current = { width, height };
859
+ canvas.width = Math.floor(width * dpr);
860
+ canvas.height = Math.floor(height * dpr);
861
+ canvas.style.width = `${width}px`;
862
+ canvas.style.height = `${height}px`;
863
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
864
+ };
865
+ resize();
866
+ let ro = null;
867
+ if (typeof ResizeObserver !== "undefined") {
868
+ ro = new ResizeObserver(resize);
869
+ ro.observe(container);
870
+ } else {
871
+ window.addEventListener("resize", resize);
872
+ }
873
+ const bounds = boundsRef.current;
874
+ const count = getCinematicCount(type, intensity, maxParticles);
875
+ if (type === "snow") {
876
+ snowRef.current = createSnowParticles(count, config, bounds, { colorsOverride: particleColors, colorOverride: color });
877
+ leafRef.current = [];
878
+ } else if (type === "leaves") {
879
+ leafRef.current = createLeafParticles(count, config, bounds, { colorsOverride: particleColors, colorOverride: color });
880
+ snowRef.current = [];
881
+ } else {
882
+ snowRef.current = [];
883
+ leafRef.current = [];
884
+ }
885
+ const animate = (tMs) => {
886
+ if (!lastRef.current) lastRef.current = tMs;
887
+ const dtMs = Math.min(tMs - lastRef.current, 40);
888
+ lastRef.current = tMs;
889
+ const dtSec = dtMs / 1e3;
890
+ const b = boundsRef.current;
891
+ if (type === "snow") {
892
+ updateSnow(snowRef.current, dtSec, tMs, b, wind);
893
+ } else if (type === "leaves") {
894
+ updateLeaves(leafRef.current, dtSec, tMs, b, wind);
895
+ }
896
+ ctx.clearRect(0, 0, b.width, b.height);
897
+ ctx.globalCompositeOperation = "source-over";
898
+ if (type === "snow") {
899
+ drawSnow(ctx, snowRef.current, opacity, spriteCacheRef.current);
900
+ } else if (type === "leaves") {
901
+ drawLeaves(ctx, leafRef.current, opacity, tMs, WEATHER_CONFIGS[type], spriteCacheRef.current);
902
+ }
903
+ frameRef.current = requestAnimationFrame(animate);
904
+ };
905
+ frameRef.current = requestAnimationFrame(animate);
906
+ return () => {
907
+ if (ro) ro.disconnect();
908
+ else window.removeEventListener("resize", resize);
909
+ if (frameRef.current) cancelAnimationFrame(frameRef.current);
910
+ };
911
+ }, [type, intensity, wind.x, wind.y, color, opacity, maxParticles, enabled, stylePreset, leafColorPreset, colorsKeyProp]);
912
+ return /* @__PURE__ */ jsx(
913
+ "div",
914
+ {
915
+ ref: containerRef,
916
+ className: cn("pointer-events-none absolute inset-0 overflow-hidden", className),
917
+ style: {
918
+ opacity: 1,
919
+ filter: blur ? "blur(0.8px)" : void 0,
920
+ ...style
921
+ },
922
+ "aria-hidden": "true",
923
+ children: /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "absolute inset-0", "aria-hidden": "true" })
924
+ }
925
+ );
926
+ }
927
+
928
+ // src/environment/WeatherLayer/WeatherLayer.tsx
929
+ import { jsx as jsx2 } from "react/jsx-runtime";
930
+ var ParticleRenderer = React2.memo(function ParticleRenderer2({
931
+ particle,
932
+ config,
933
+ colorOverride
934
+ }) {
935
+ const translate = `translate3d(${particle.x}px, ${particle.y}px, 0)`;
936
+ const rotate = `rotate(${particle.rotation}deg)`;
937
+ const particleColor = colorOverride ?? particle.color;
938
+ const baseStyle = {
939
+ position: "absolute",
940
+ left: 0,
941
+ top: 0,
942
+ opacity: particle.opacity,
943
+ transform: `${translate} ${rotate}`,
944
+ willChange: "transform, opacity",
945
+ pointerEvents: "none"
946
+ };
947
+ switch (config.shape) {
948
+ case "streak":
949
+ return /* @__PURE__ */ jsx2(
950
+ "div",
951
+ {
952
+ style: {
953
+ ...baseStyle,
954
+ width: 1,
955
+ height: particle.size * 8,
956
+ background: `linear-gradient(to bottom, transparent, ${particleColor}, transparent)`
957
+ }
958
+ }
959
+ );
960
+ case "glow":
961
+ return /* @__PURE__ */ jsx2(
962
+ "div",
963
+ {
964
+ style: {
965
+ ...baseStyle,
966
+ width: particle.size,
967
+ height: particle.size,
968
+ borderRadius: "50%",
969
+ backgroundColor: particleColor,
970
+ boxShadow: `0 0 ${particle.size * 2}px ${particleColor}`
971
+ }
972
+ }
973
+ );
974
+ case "leaf":
975
+ return /* @__PURE__ */ jsx2(
976
+ "div",
977
+ {
978
+ style: {
979
+ ...baseStyle,
980
+ width: particle.size,
981
+ height: particle.size * 0.6,
982
+ borderRadius: "50% 0 50% 0",
983
+ backgroundColor: particleColor
984
+ }
985
+ }
986
+ );
987
+ case "petal":
988
+ return /* @__PURE__ */ jsx2(
989
+ "div",
990
+ {
991
+ style: {
992
+ ...baseStyle,
993
+ width: particle.size,
994
+ height: particle.size * 0.7,
995
+ borderRadius: "50% 50% 50% 50%",
996
+ backgroundColor: particleColor,
997
+ transform: `${translate} ${rotate} scale(1, 0.6)`
998
+ }
999
+ }
1000
+ );
1001
+ default:
1002
+ return /* @__PURE__ */ jsx2(
1003
+ "div",
1004
+ {
1005
+ style: {
1006
+ ...baseStyle,
1007
+ width: particle.size,
1008
+ height: particle.size,
1009
+ borderRadius: "50%",
1010
+ backgroundColor: particleColor
1011
+ }
1012
+ }
1013
+ );
1014
+ }
1015
+ });
1016
+ function WeatherLayer({
1017
+ type,
1018
+ intensity = 0.5,
1019
+ stylePreset = "ui",
1020
+ wind = { x: 0, y: 0 },
1021
+ color,
1022
+ colors,
1023
+ leafColorPreset,
1024
+ opacity = 1,
1025
+ blur = false,
1026
+ maxParticles: propMaxParticles,
1027
+ enabled = true,
1028
+ className,
1029
+ style
1030
+ }) {
1031
+ const reducedMotion = useReducedMotion();
1032
+ const containerRef = React2.useRef(null);
1033
+ const [particles, setParticles] = React2.useState([]);
1034
+ const frameRef = React2.useRef(0);
1035
+ const lastTimeRef = React2.useRef(0);
1036
+ const particleIdRef = React2.useRef(0);
1037
+ const config = WEATHER_CONFIGS[type];
1038
+ const perfConfig = getPerformanceConfig();
1039
+ const maxParticles = propMaxParticles ?? perfConfig.maxParticles;
1040
+ const colorsKeyProp = colorsKey(colors);
1041
+ const particleColors = React2.useMemo(
1042
+ () => resolveWeatherColors({ type, colors, leafColorPreset, configColors: config.colors }),
1043
+ [type, leafColorPreset, colorsKeyProp]
1044
+ );
1045
+ const shouldRender = enabled && !reducedMotion && perfConfig.tier !== "minimal";
1046
+ const useCinematicCanvas = shouldRender && stylePreset === "cinematic" && perfConfig.useShaders && (type === "snow" || type === "leaves");
1047
+ if (useCinematicCanvas) {
1048
+ return /* @__PURE__ */ jsx2(
1049
+ WeatherLayerCinematicCanvas,
1050
+ {
1051
+ type,
1052
+ intensity,
1053
+ stylePreset,
1054
+ wind,
1055
+ color,
1056
+ colors,
1057
+ leafColorPreset,
1058
+ opacity,
1059
+ blur,
1060
+ maxParticles,
1061
+ enabled,
1062
+ className,
1063
+ style
1064
+ }
1065
+ );
1066
+ }
1067
+ React2.useEffect(() => {
1068
+ if (!shouldRender || !containerRef.current) {
1069
+ setParticles([]);
1070
+ return;
1071
+ }
1072
+ lastTimeRef.current = 0;
1073
+ const container = containerRef.current;
1074
+ const bounds = {
1075
+ width: container.offsetWidth,
1076
+ height: container.offsetHeight
1077
+ };
1078
+ const particleCount = getParticleCount(type, intensity, maxParticles, stylePreset);
1079
+ const initialParticles = [];
1080
+ for (let i = 0; i < particleCount; i++) {
1081
+ let p = createParticle(particleIdRef.current++, type, config, wind, bounds, stylePreset, particleColors);
1082
+ p.y = Math.random() * bounds.height;
1083
+ p.x = Math.random() * bounds.width;
1084
+ p.life = Math.random() * p.maxLife * 0.7;
1085
+ p = updateParticle(p, config, 0);
1086
+ initialParticles.push(p);
1087
+ }
1088
+ setParticles(initialParticles);
1089
+ const animate = (time) => {
1090
+ if (!lastTimeRef.current) lastTimeRef.current = time;
1091
+ const deltaTime = Math.min(time - lastTimeRef.current, 50);
1092
+ lastTimeRef.current = time;
1093
+ setParticles((prev) => {
1094
+ const updated = [];
1095
+ const currentBounds = {
1096
+ width: container.offsetWidth,
1097
+ height: container.offsetHeight
1098
+ };
1099
+ for (const p of prev) {
1100
+ const newP = updateParticle(p, config, deltaTime);
1101
+ if (newP.life < newP.maxLife && newP.y > -50 && newP.y < currentBounds.height + 50 && newP.x > -50 && newP.x < currentBounds.width + 50) {
1102
+ updated.push(newP);
1103
+ }
1104
+ }
1105
+ const targetCount = getParticleCount(type, intensity, maxParticles, stylePreset);
1106
+ while (updated.length < targetCount) {
1107
+ updated.push(
1108
+ createParticle(particleIdRef.current++, type, config, wind, currentBounds, stylePreset, particleColors)
1109
+ );
1110
+ }
1111
+ return updated;
1112
+ });
1113
+ frameRef.current = requestAnimationFrame(animate);
1114
+ };
1115
+ frameRef.current = requestAnimationFrame(animate);
1116
+ return () => {
1117
+ if (frameRef.current) {
1118
+ cancelAnimationFrame(frameRef.current);
1119
+ }
1120
+ };
1121
+ }, [shouldRender, type, intensity, stylePreset, wind.x, wind.y, maxParticles, config, leafColorPreset, colorsKeyProp]);
1122
+ if (!shouldRender) {
1123
+ return null;
1124
+ }
1125
+ return /* @__PURE__ */ jsx2(
1126
+ "div",
1127
+ {
1128
+ ref: containerRef,
1129
+ className: cn(
1130
+ "pointer-events-none absolute inset-0 overflow-hidden",
1131
+ className
1132
+ ),
1133
+ style: {
1134
+ opacity,
1135
+ filter: blur ? "blur(1px)" : void 0,
1136
+ ...style
1137
+ },
1138
+ "aria-hidden": "true",
1139
+ children: particles.map((p) => /* @__PURE__ */ jsx2(
1140
+ ParticleRenderer,
1141
+ {
1142
+ particle: p,
1143
+ config,
1144
+ colorOverride: color
1145
+ },
1146
+ p.id
1147
+ ))
1148
+ }
1149
+ );
1150
+ }
1151
+
1152
+ // src/environment/FogLayer/FogLayer.tsx
1153
+ import * as React3 from "react";
1154
+ import { useReducedMotion as useReducedMotion2 } from "framer-motion";
1155
+
1156
+ // src/environment/shared/noise.ts
1157
+ var NOISE_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200">
1158
+ <filter id="n">
1159
+ <feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="3" seed="2" stitchTiles="stitch" />
1160
+ <feColorMatrix type="saturate" values="0" />
1161
+ </filter>
1162
+ <rect width="200" height="200" filter="url(#n)" opacity="0.55" />
1163
+ </svg>`;
1164
+ var NOISE_DATA_URL = `data:image/svg+xml,${encodeURIComponent(NOISE_SVG)}`;
1165
+
1166
+ // src/environment/FogLayer/types.ts
1167
+ var FOG_CONFIGS = {
1168
+ depth: {
1169
+ defaultColor: "#0a0a0f",
1170
+ defaultDensity: 0.5,
1171
+ animationSpeed: 0,
1172
+ gradientStops: 2
1173
+ },
1174
+ ground: {
1175
+ defaultColor: "#ffffff",
1176
+ defaultDensity: 0.7,
1177
+ animationSpeed: 0.02,
1178
+ gradientStops: 3
1179
+ },
1180
+ volumetric: {
1181
+ defaultColor: "#e8e8ff",
1182
+ defaultDensity: 0.4,
1183
+ animationSpeed: 0.01,
1184
+ gradientStops: 4
1185
+ },
1186
+ mist: {
1187
+ defaultColor: "#f0f0ff",
1188
+ defaultDensity: 0.3,
1189
+ animationSpeed: 0.015,
1190
+ gradientStops: 5
1191
+ }
1192
+ };
1193
+
1194
+ // src/environment/FogLayer/FogLayer.tsx
1195
+ import { jsx as jsx3 } from "react/jsx-runtime";
1196
+ function FogLayer({
1197
+ type,
1198
+ density: propDensity,
1199
+ color: propColor,
1200
+ height = 0.4,
1201
+ animated = true,
1202
+ intensity = 1,
1203
+ stylePreset = "ui",
1204
+ enabled = true,
1205
+ className,
1206
+ style
1207
+ }) {
1208
+ const reducedMotion = useReducedMotion2();
1209
+ const perfConfig = getPerformanceConfig();
1210
+ const [offset, setOffset] = React3.useState(0);
1211
+ const config = FOG_CONFIGS[type];
1212
+ const density = propDensity ?? config.defaultDensity;
1213
+ const color = propColor ?? config.defaultColor;
1214
+ const shouldAnimate = animated && !reducedMotion && perfConfig.tier !== "minimal";
1215
+ React3.useEffect(() => {
1216
+ if (!shouldAnimate || !enabled || config.animationSpeed === 0) return;
1217
+ let frame;
1218
+ const animate = () => {
1219
+ setOffset((prev) => (prev + config.animationSpeed) % 100);
1220
+ frame = requestAnimationFrame(animate);
1221
+ };
1222
+ frame = requestAnimationFrame(animate);
1223
+ return () => cancelAnimationFrame(frame);
1224
+ }, [shouldAnimate, enabled, config.animationSpeed]);
1225
+ if (!enabled) return null;
1226
+ const gradientStyle = React3.useMemo(() => {
1227
+ const opacity = density * intensity;
1228
+ switch (type) {
1229
+ case "depth":
1230
+ return {
1231
+ background: `linear-gradient(to top, ${color}${Math.round(opacity * 255).toString(16).padStart(2, "0")}, transparent)`
1232
+ };
1233
+ case "ground":
1234
+ return {
1235
+ background: `linear-gradient(to top,
1236
+ ${color}${Math.round(opacity * 255).toString(16).padStart(2, "0")} 0%,
1237
+ ${color}${Math.round(opacity * 0.5 * 255).toString(16).padStart(2, "0")} ${height * 50}%,
1238
+ transparent ${height * 100}%)`
1239
+ };
1240
+ case "volumetric":
1241
+ return {
1242
+ background: `
1243
+ radial-gradient(ellipse 120% 60% at 50% ${100 - offset}%, ${color}${Math.round(opacity * 0.6 * 255).toString(16).padStart(2, "0")}, transparent),
1244
+ radial-gradient(ellipse 100% 40% at ${30 + offset * 0.2}% 80%, ${color}${Math.round(opacity * 0.4 * 255).toString(16).padStart(2, "0")}, transparent),
1245
+ radial-gradient(ellipse 80% 50% at ${70 - offset * 0.15}% 90%, ${color}${Math.round(opacity * 0.5 * 255).toString(16).padStart(2, "0")}, transparent)
1246
+ `
1247
+ };
1248
+ case "mist":
1249
+ return {
1250
+ background: `
1251
+ radial-gradient(ellipse 150% 80% at ${40 + Math.sin(offset * 0.1) * 20}% ${60 + Math.cos(offset * 0.08) * 10}%, ${color}${Math.round(opacity * 0.3 * 255).toString(16).padStart(2, "0")}, transparent),
1252
+ radial-gradient(ellipse 120% 60% at ${60 + Math.cos(offset * 0.12) * 15}% ${40 + Math.sin(offset * 0.09) * 15}%, ${color}${Math.round(opacity * 0.25 * 255).toString(16).padStart(2, "0")}, transparent),
1253
+ linear-gradient(to top, ${color}${Math.round(opacity * 0.15 * 255).toString(16).padStart(2, "0")}, transparent 50%)
1254
+ `
1255
+ };
1256
+ default:
1257
+ return {};
1258
+ }
1259
+ }, [type, color, density, intensity, height, offset]);
1260
+ return /* @__PURE__ */ jsx3(
1261
+ "div",
1262
+ {
1263
+ className: cn(
1264
+ "pointer-events-none absolute inset-0",
1265
+ className
1266
+ ),
1267
+ style: {
1268
+ ...gradientStyle,
1269
+ ...style
1270
+ },
1271
+ "aria-hidden": "true",
1272
+ children: stylePreset === "cinematic" && /* @__PURE__ */ jsx3(
1273
+ "div",
1274
+ {
1275
+ className: "absolute inset-0",
1276
+ style: {
1277
+ backgroundImage: `url("${NOISE_DATA_URL}")`,
1278
+ backgroundRepeat: "repeat",
1279
+ backgroundSize: `${260 - Math.round(density * 80)}px ${260 - Math.round(density * 80)}px`,
1280
+ backgroundPosition: `${offset * 30}px ${offset * 20}px`,
1281
+ opacity: Math.min(0.18, 0.04 + density * intensity * 0.12),
1282
+ mixBlendMode: "soft-light",
1283
+ filter: "blur(0.6px) contrast(1.2)"
1284
+ },
1285
+ "aria-hidden": "true"
1286
+ }
1287
+ )
1288
+ }
1289
+ );
1290
+ }
1291
+
1292
+ // src/environment/VolumetricLight/VolumetricLight.tsx
1293
+ import * as React4 from "react";
1294
+ import { useReducedMotion as useReducedMotion3 } from "framer-motion";
1295
+
1296
+ // src/environment/VolumetricLight/types.ts
1297
+ var LIGHT_CONFIGS = {
1298
+ godrays: { defaultColor: "#fffbe6", defaultIntensity: 0.6, animated: true, blendMode: "screen" },
1299
+ shaft: { defaultColor: "#ffffff", defaultIntensity: 0.5, animated: false, blendMode: "screen" },
1300
+ bloom: { defaultColor: "#ffffff", defaultIntensity: 0.4, animated: false, blendMode: "screen" },
1301
+ flare: { defaultColor: "#ffe4b5", defaultIntensity: 0.7, animated: true, blendMode: "screen" },
1302
+ caustics: { defaultColor: "#00d4ff", defaultIntensity: 0.4, animated: true, blendMode: "overlay" },
1303
+ scanner: { defaultColor: "#00ff00", defaultIntensity: 0.6, animated: true, blendMode: "screen" },
1304
+ neon: { defaultColor: "#ff00ff", defaultIntensity: 0.8, animated: true, blendMode: "screen" },
1305
+ spotlight: { defaultColor: "#ffffff", defaultIntensity: 0.7, animated: false, blendMode: "screen" },
1306
+ rim: { defaultColor: "#00f0ff", defaultIntensity: 0.5, animated: false, blendMode: "screen" },
1307
+ laser: { defaultColor: "#ff0000", defaultIntensity: 0.9, animated: true, blendMode: "screen" }
1308
+ };
1309
+
1310
+ // src/environment/VolumetricLight/VolumetricLight.tsx
1311
+ import { jsx as jsx4 } from "react/jsx-runtime";
1312
+ function VolumetricLight({
1313
+ type,
1314
+ source = { x: 0.5, y: 0 },
1315
+ color: propColor,
1316
+ intensity = 0.5,
1317
+ decay = 0.5,
1318
+ angle = 0,
1319
+ width = 0.3,
1320
+ animated: propAnimated,
1321
+ stylePreset = "ui",
1322
+ enabled = true,
1323
+ className,
1324
+ style
1325
+ }) {
1326
+ const reducedMotion = useReducedMotion3();
1327
+ const perfConfig = getPerformanceConfig();
1328
+ const [time, setTime] = React4.useState(0);
1329
+ const config = LIGHT_CONFIGS[type];
1330
+ const color = propColor ?? config.defaultColor;
1331
+ const shouldAnimate = (propAnimated ?? config.animated) && !reducedMotion && perfConfig.tier !== "minimal";
1332
+ React4.useEffect(() => {
1333
+ if (!shouldAnimate || !enabled) return;
1334
+ let frame;
1335
+ const animate = () => {
1336
+ setTime((prev) => prev + 0.016);
1337
+ frame = requestAnimationFrame(animate);
1338
+ };
1339
+ frame = requestAnimationFrame(animate);
1340
+ return () => cancelAnimationFrame(frame);
1341
+ }, [shouldAnimate, enabled]);
1342
+ if (!enabled) return null;
1343
+ const lightStyle = React4.useMemo(() => {
1344
+ const opacity = intensity * config.defaultIntensity;
1345
+ const d = Math.max(0, Math.min(1, decay));
1346
+ switch (type) {
1347
+ case "godrays":
1348
+ return {
1349
+ background: `conic-gradient(from ${angle}deg at ${source.x * 100}% ${source.y * 100}%,
1350
+ ${color}${Math.round(opacity * 0.4 * 255).toString(16).padStart(2, "0")} 0deg,
1351
+ transparent 15deg,
1352
+ ${color}${Math.round(opacity * 0.3 * 255).toString(16).padStart(2, "0")} 30deg,
1353
+ transparent 45deg,
1354
+ ${color}${Math.round(opacity * 0.35 * 255).toString(16).padStart(2, "0")} 60deg,
1355
+ transparent 75deg,
1356
+ ${color}${Math.round(opacity * 0.25 * 255).toString(16).padStart(2, "0")} 90deg,
1357
+ transparent 105deg
1358
+ )`,
1359
+ mixBlendMode: config.blendMode
1360
+ };
1361
+ case "shaft":
1362
+ return {
1363
+ background: `linear-gradient(${angle}deg,
1364
+ transparent ${(source.x - width / 2) * 100}%,
1365
+ ${color}${Math.round(opacity * 0.5 * 255).toString(16).padStart(2, "0")} ${source.x * 100}%,
1366
+ transparent ${(source.x + width / 2) * 100}%
1367
+ )`,
1368
+ mixBlendMode: config.blendMode
1369
+ };
1370
+ case "bloom": {
1371
+ const bloomInner = 18 + d * 22;
1372
+ const bloomMid = 42 + d * 24;
1373
+ return {
1374
+ background: `radial-gradient(circle at ${source.x * 100}% ${source.y * 100}%,
1375
+ ${color}${Math.round(opacity * 0.6 * 255).toString(16).padStart(2, "0")},
1376
+ ${color}${Math.round(opacity * 0.3 * 255).toString(16).padStart(2, "0")} ${bloomInner}%,
1377
+ transparent ${bloomMid}%
1378
+ )`,
1379
+ mixBlendMode: config.blendMode,
1380
+ filter: "blur(20px)"
1381
+ };
1382
+ }
1383
+ case "flare":
1384
+ const flareOffset = Math.sin(time * 2) * 5;
1385
+ return {
1386
+ background: `
1387
+ radial-gradient(ellipse 20% 5% at ${source.x * 100}% ${source.y * 100 + flareOffset}%, ${color}${Math.round(opacity * 0.8 * 255).toString(16).padStart(2, "0")}, transparent),
1388
+ radial-gradient(ellipse 5% 20% at ${source.x * 100}% ${source.y * 100}%, ${color}${Math.round(opacity * 0.6 * 255).toString(16).padStart(2, "0")}, transparent),
1389
+ radial-gradient(circle at ${source.x * 100}% ${source.y * 100}%, ${color}${Math.round(opacity * 0.4 * 255).toString(16).padStart(2, "0")}, transparent 20%)
1390
+ `,
1391
+ mixBlendMode: config.blendMode
1392
+ };
1393
+ case "caustics":
1394
+ const causticPhase = time * 0.5;
1395
+ return {
1396
+ background: `
1397
+ radial-gradient(ellipse at ${50 + Math.sin(causticPhase) * 20}% ${50 + Math.cos(causticPhase * 1.3) * 20}%, ${color}${Math.round(opacity * 0.3 * 255).toString(16).padStart(2, "0")}, transparent 40%),
1398
+ radial-gradient(ellipse at ${50 + Math.cos(causticPhase * 0.7) * 25}% ${50 + Math.sin(causticPhase * 1.1) * 25}%, ${color}${Math.round(opacity * 0.25 * 255).toString(16).padStart(2, "0")}, transparent 35%)
1399
+ `,
1400
+ mixBlendMode: config.blendMode
1401
+ };
1402
+ case "scanner":
1403
+ const scanAngle = time * 60 % 360;
1404
+ return {
1405
+ background: `conic-gradient(from ${scanAngle}deg at ${source.x * 100}% ${source.y * 100}%,
1406
+ ${color}${Math.round(opacity * 0.6 * 255).toString(16).padStart(2, "0")} 0deg,
1407
+ transparent 30deg,
1408
+ transparent 360deg
1409
+ )`,
1410
+ mixBlendMode: config.blendMode
1411
+ };
1412
+ case "neon":
1413
+ const flickerIntensity = 0.9 + Math.sin(time * 20) * 0.1;
1414
+ return {
1415
+ background: `linear-gradient(${angle}deg, transparent 40%, ${color}${Math.round(opacity * flickerIntensity * 0.8 * 255).toString(16).padStart(2, "0")} 50%, transparent 60%)`,
1416
+ boxShadow: `0 0 30px ${color}${Math.round(opacity * flickerIntensity * 0.5 * 255).toString(16).padStart(2, "0")}, 0 0 60px ${color}${Math.round(opacity * flickerIntensity * 0.3 * 255).toString(16).padStart(2, "0")}`,
1417
+ mixBlendMode: config.blendMode
1418
+ };
1419
+ case "spotlight": {
1420
+ const spotMid = 40 + d * 35;
1421
+ return {
1422
+ background: `radial-gradient(ellipse ${width * 100}% ${width * 150}% at ${source.x * 100}% ${source.y * 100}%,
1423
+ ${color}${Math.round(opacity * 0.7 * 255).toString(16).padStart(2, "0")},
1424
+ ${color}${Math.round(opacity * 0.3 * 255).toString(16).padStart(2, "0")} ${spotMid}%,
1425
+ transparent 100%
1426
+ )`,
1427
+ mixBlendMode: config.blendMode
1428
+ };
1429
+ }
1430
+ case "rim":
1431
+ return {
1432
+ boxShadow: `inset 0 0 ${60 * intensity}px ${color}${Math.round(opacity * 0.6 * 255).toString(16).padStart(2, "0")}`,
1433
+ mixBlendMode: config.blendMode
1434
+ };
1435
+ case "laser":
1436
+ const laserOffset = Math.sin(time * 3) * 10;
1437
+ return {
1438
+ background: `linear-gradient(${angle + laserOffset}deg,
1439
+ transparent 49%,
1440
+ ${color}${Math.round(opacity * 255).toString(16).padStart(2, "0")} 49.5%,
1441
+ ${color}${Math.round(opacity * 255).toString(16).padStart(2, "0")} 50.5%,
1442
+ transparent 51%
1443
+ )`,
1444
+ mixBlendMode: config.blendMode
1445
+ };
1446
+ default:
1447
+ return {};
1448
+ }
1449
+ }, [type, source, color, intensity, decay, angle, width, time, config]);
1450
+ return /* @__PURE__ */ jsx4(
1451
+ "div",
1452
+ {
1453
+ className: cn("pointer-events-none absolute inset-0", className),
1454
+ style: { ...lightStyle, ...style },
1455
+ "aria-hidden": "true",
1456
+ children: stylePreset === "cinematic" && /* @__PURE__ */ jsx4(
1457
+ "div",
1458
+ {
1459
+ className: "absolute inset-0",
1460
+ style: {
1461
+ backgroundImage: `url("${NOISE_DATA_URL}")`,
1462
+ backgroundRepeat: "repeat",
1463
+ backgroundSize: "240px 240px",
1464
+ backgroundPosition: `${time * 160}px ${time * 110}px`,
1465
+ opacity: Math.min(0.12, 0.03 + intensity * 0.06),
1466
+ mixBlendMode: "overlay",
1467
+ filter: "blur(0.4px) contrast(1.25)"
1468
+ },
1469
+ "aria-hidden": "true"
1470
+ }
1471
+ )
1472
+ }
1473
+ );
1474
+ }
1475
+
1476
+ // src/environment/AuroraLayer/AuroraLayer.tsx
1477
+ import * as React5 from "react";
1478
+ import { useReducedMotion as useReducedMotion4 } from "framer-motion";
1479
+
1480
+ // src/environment/AuroraLayer/types.ts
1481
+ var AURORA_CONFIGS = {
1482
+ aurora: { colors: ["#00ff87", "#60efff", "#ff00aa"], animationSpeed: 8e-3, complexity: 3 },
1483
+ nebula: { colors: ["#1a0533", "#4b0082", "#ff006e"], animationSpeed: 5e-3, complexity: 4 },
1484
+ gradient: { colors: ["#667eea", "#764ba2"], animationSpeed: 3e-3, complexity: 1 },
1485
+ stars: { colors: ["#ffffff", "#fffacd", "#e6e6fa"], animationSpeed: 0.01, complexity: 5 },
1486
+ clouds: { colors: ["#f0f0f0", "#d0d0d0", "#e8e8e8"], animationSpeed: 4e-3, complexity: 3 },
1487
+ sunset: { colors: ["#ff6b35", "#f7c59f", "#2e294e"], animationSpeed: 2e-3, complexity: 2 },
1488
+ storm: { colors: ["#1c1c1c", "#363636", "#4a4a4a"], animationSpeed: 0.015, complexity: 4 },
1489
+ cosmic: { colors: ["#0d0221", "#0f084b", "#26408b", "#a663cc"], animationSpeed: 3e-3, complexity: 5 },
1490
+ heat: { colors: ["#ff4500", "#ff6347", "#ffa500"], animationSpeed: 0.02, complexity: 2 },
1491
+ underwater: { colors: ["#006994", "#00a8cc", "#40e0d0"], animationSpeed: 6e-3, complexity: 3 }
1492
+ };
1493
+
1494
+ // src/environment/AuroraLayer/AuroraLayer.tsx
1495
+ import { jsx as jsx5 } from "react/jsx-runtime";
1496
+ function AuroraLayer({
1497
+ type,
1498
+ colors: propColors,
1499
+ intensity = 1,
1500
+ speed = 1,
1501
+ stylePreset = "ui",
1502
+ complexity: propComplexity,
1503
+ direction = "vertical",
1504
+ blend = "normal",
1505
+ enabled = true,
1506
+ className,
1507
+ style
1508
+ }) {
1509
+ const reducedMotion = useReducedMotion4();
1510
+ const perfConfig = getPerformanceConfig();
1511
+ const [time, setTime] = React5.useState(0);
1512
+ const lightningRef = React5.useRef(0);
1513
+ const config = AURORA_CONFIGS[type];
1514
+ const colors = propColors ?? config.colors;
1515
+ const complexity = propComplexity ?? config.complexity;
1516
+ const shouldAnimate = !reducedMotion && perfConfig.tier !== "minimal";
1517
+ React5.useEffect(() => {
1518
+ if (!shouldAnimate || !enabled) return;
1519
+ lightningRef.current = 0;
1520
+ let frame;
1521
+ const animate = () => {
1522
+ if (type === "storm") {
1523
+ const chance = stylePreset === "cinematic" ? 3e-3 : 7e-3;
1524
+ if (Math.random() < chance) lightningRef.current = 1;
1525
+ lightningRef.current *= stylePreset === "cinematic" ? 0.92 : 0.88;
1526
+ if (lightningRef.current < 0.02) lightningRef.current = 0;
1527
+ }
1528
+ setTime((prev) => prev + config.animationSpeed * speed);
1529
+ frame = requestAnimationFrame(animate);
1530
+ };
1531
+ frame = requestAnimationFrame(animate);
1532
+ return () => cancelAnimationFrame(frame);
1533
+ }, [shouldAnimate, enabled, type, stylePreset, config.animationSpeed, speed]);
1534
+ if (!enabled) return null;
1535
+ const lightning = type === "storm" ? lightningRef.current : 0;
1536
+ const auroraStyle = React5.useMemo(() => {
1537
+ const opacity = intensity;
1538
+ const c = colors.map((color) => `${color}${Math.round(opacity * 200).toString(16).padStart(2, "0")}`);
1539
+ switch (type) {
1540
+ case "aurora":
1541
+ return {
1542
+ background: `
1543
+ linear-gradient(${direction === "horizontal" ? "90deg" : "180deg"},
1544
+ ${c[0]} ${Math.sin(time) * 20 + 10}%,
1545
+ ${c[1]} ${50 + Math.cos(time * 1.3) * 20}%,
1546
+ ${c[2]} ${90 + Math.sin(time * 0.7) * 10}%
1547
+ )
1548
+ `,
1549
+ filter: `blur(${40 + Math.sin(time * 2) * 10}px)`,
1550
+ mixBlendMode: blend
1551
+ };
1552
+ case "nebula":
1553
+ return {
1554
+ background: `
1555
+ radial-gradient(ellipse at ${30 + Math.sin(time) * 20}% ${40 + Math.cos(time * 0.8) * 20}%, ${c[0]}, transparent 50%),
1556
+ radial-gradient(ellipse at ${70 + Math.cos(time * 1.2) * 15}% ${60 + Math.sin(time * 0.9) * 15}%, ${c[1]}, transparent 40%),
1557
+ radial-gradient(ellipse at ${50 + Math.sin(time * 0.7) * 25}% ${50 + Math.cos(time * 1.1) * 25}%, ${c[2]}, transparent 45%)
1558
+ `,
1559
+ mixBlendMode: blend
1560
+ };
1561
+ case "gradient":
1562
+ return {
1563
+ background: `linear-gradient(${direction === "horizontal" ? "90deg" : direction === "radial" ? "135deg" : "180deg"}, ${c.join(", ")})`,
1564
+ mixBlendMode: blend
1565
+ };
1566
+ case "stars":
1567
+ const starGradients = Array.from({ length: Math.min(complexity * 10, 50) }, (_, i) => {
1568
+ const x = (i * 17 + Math.sin(i) * 30) % 100;
1569
+ const y = (i * 23 + Math.cos(i) * 40) % 100;
1570
+ const twinkle = Math.sin(time * (2 + i * 0.1)) * 0.5 + 0.5;
1571
+ const size = 1 + i % 3;
1572
+ return `radial-gradient(circle ${size}px at ${x}% ${y}%, ${colors[i % colors.length]}${Math.round(twinkle * opacity * 255).toString(16).padStart(2, "0")}, transparent ${size}px)`;
1573
+ });
1574
+ return {
1575
+ background: starGradients.join(", "),
1576
+ mixBlendMode: blend
1577
+ };
1578
+ case "clouds":
1579
+ return {
1580
+ background: `
1581
+ radial-gradient(ellipse 80% 50% at ${20 + Math.sin(time) * 10}% ${30 + Math.cos(time * 0.5) * 10}%, ${c[0]}, transparent),
1582
+ radial-gradient(ellipse 60% 40% at ${60 + Math.cos(time * 0.7) * 15}% ${50 + Math.sin(time * 0.6) * 10}%, ${c[1]}, transparent),
1583
+ radial-gradient(ellipse 70% 45% at ${40 + Math.sin(time * 0.8) * 12}% ${70 + Math.cos(time * 0.4) * 8}%, ${c[2]}, transparent)
1584
+ `,
1585
+ filter: "blur(30px)",
1586
+ mixBlendMode: blend
1587
+ };
1588
+ case "sunset":
1589
+ return {
1590
+ background: `linear-gradient(180deg,
1591
+ ${c[2]} 0%,
1592
+ ${c[1]} ${40 + Math.sin(time) * 5}%,
1593
+ ${c[0]} ${70 + Math.cos(time * 0.5) * 5}%,
1594
+ ${c[0]}00 100%
1595
+ )`,
1596
+ mixBlendMode: blend
1597
+ };
1598
+ case "storm":
1599
+ return {
1600
+ background: `
1601
+ radial-gradient(ellipse 100% 60% at ${30 + Math.sin(time * 2) * 20}% ${20 + Math.cos(time) * 10}%, ${c[1]}, transparent),
1602
+ radial-gradient(ellipse 80% 50% at ${70 + Math.cos(time * 1.5) * 25}% ${40 + Math.sin(time * 1.2) * 15}%, ${c[2]}, transparent),
1603
+ linear-gradient(180deg, ${c[0]}, ${c[1]})
1604
+ `,
1605
+ filter: lightning ? `brightness(${1 + lightning * (stylePreset === "cinematic" ? 1.8 : 2.6)}) contrast(${1 + lightning * 0.35})` : void 0,
1606
+ mixBlendMode: blend
1607
+ };
1608
+ case "cosmic":
1609
+ return {
1610
+ background: `
1611
+ radial-gradient(ellipse at center, ${c[3] ?? c[0]} 0%, transparent 50%),
1612
+ radial-gradient(ellipse at ${30 + Math.sin(time * 0.5) * 20}% ${30 + Math.cos(time * 0.3) * 20}%, ${c[1]}, transparent 40%),
1613
+ radial-gradient(ellipse at ${70 + Math.cos(time * 0.4) * 15}% ${70 + Math.sin(time * 0.6) * 15}%, ${c[2]}, transparent 35%),
1614
+ linear-gradient(180deg, ${c[0]}, ${c[1]})
1615
+ `,
1616
+ mixBlendMode: blend
1617
+ };
1618
+ case "heat":
1619
+ return {
1620
+ background: `
1621
+ radial-gradient(ellipse at 50% 100%, ${c[0]}, transparent 60%),
1622
+ radial-gradient(ellipse at ${30 + Math.sin(time * 3) * 20}% 80%, ${c[1]}, transparent 40%),
1623
+ radial-gradient(ellipse at ${70 + Math.cos(time * 2.5) * 20}% 90%, ${c[2]}, transparent 45%)
1624
+ `,
1625
+ filter: `blur(${10 + Math.sin(time * 5) * 5}px)`,
1626
+ mixBlendMode: blend
1627
+ };
1628
+ case "underwater":
1629
+ return {
1630
+ background: `
1631
+ linear-gradient(180deg, ${c[0]} 0%, ${c[1]} 50%, ${c[2]} 100%),
1632
+ radial-gradient(ellipse at ${50 + Math.sin(time) * 30}% ${30 + Math.cos(time * 0.8) * 20}%, ${c[2]}40, transparent 40%)
1633
+ `,
1634
+ mixBlendMode: blend
1635
+ };
1636
+ default:
1637
+ return {};
1638
+ }
1639
+ }, [type, colors, intensity, complexity, direction, blend, time, lightning, stylePreset]);
1640
+ return /* @__PURE__ */ jsx5(
1641
+ "div",
1642
+ {
1643
+ className: cn("pointer-events-none absolute inset-0", className),
1644
+ style: { ...auroraStyle, ...style },
1645
+ "aria-hidden": "true",
1646
+ children: stylePreset === "cinematic" && /* @__PURE__ */ jsx5(
1647
+ "div",
1648
+ {
1649
+ className: "absolute inset-0",
1650
+ style: {
1651
+ backgroundImage: `url("${NOISE_DATA_URL}")`,
1652
+ backgroundRepeat: "repeat",
1653
+ backgroundSize: `${200 + complexity * 16}px ${200 + complexity * 16}px`,
1654
+ backgroundPosition: `${time * 120}px ${time * 80}px`,
1655
+ opacity: Math.min(0.1, 0.04 + intensity * 0.03),
1656
+ mixBlendMode: "overlay",
1657
+ filter: "blur(0.4px) contrast(1.1)"
1658
+ },
1659
+ "aria-hidden": "true"
1660
+ }
1661
+ )
1662
+ }
1663
+ );
1664
+ }
1665
+
1666
+ // src/environment/EnvironmentLayer/EnvironmentLayer.tsx
1667
+ import { useReducedMotion as useReducedMotion5 } from "framer-motion";
1668
+
1669
+ // src/environment/EnvironmentLayer/presets.ts
1670
+ var ENVIRONMENT_PRESETS = {
1671
+ "enchanted-forest": {
1672
+ weather: { type: "fireflies", intensity: 0.7 },
1673
+ fog: { type: "ground", intensity: 0.5, color: "#1a3a2a" },
1674
+ light: { type: "godrays", intensity: 0.6, color: "#d4f0c4" },
1675
+ sky: { type: "aurora", intensity: 0.4, colors: ["#00ff87", "#004d2f", "#001a0f"] }
1676
+ },
1677
+ "cyberpunk-city": {
1678
+ weather: { type: "rain", intensity: 0.8, wind: { x: -0.3, y: 0 } },
1679
+ fog: { type: "volumetric", intensity: 0.6, color: "#1a0033" },
1680
+ light: { type: "neon", intensity: 0.8, color: "#ff00ff", angle: 90 },
1681
+ sky: { type: "gradient", intensity: 0.7, colors: ["#0a001a", "#1a0033", "#330066"] }
1682
+ },
1683
+ "deep-space": {
1684
+ weather: { type: "spores", intensity: 0.3, color: "#ffffff" },
1685
+ fog: void 0,
1686
+ light: void 0,
1687
+ sky: { type: "nebula", intensity: 0.8, colors: ["#0d0221", "#1a0533", "#4b0082", "#9400d3"] }
1688
+ },
1689
+ underwater: {
1690
+ weather: { type: "spores", intensity: 0.4, color: "#40e0d0" },
1691
+ fog: { type: "mist", intensity: 0.5, color: "#006994" },
1692
+ light: { type: "caustics", intensity: 0.7, color: "#00d4ff" },
1693
+ sky: { type: "underwater", intensity: 0.9 }
1694
+ },
1695
+ apocalypse: {
1696
+ weather: { type: "ash", intensity: 0.8 },
1697
+ fog: { type: "volumetric", intensity: 0.7, color: "#2a1a0a" },
1698
+ light: void 0,
1699
+ sky: { type: "storm", intensity: 0.9, colors: ["#1c1c1c", "#363636", "#4a3020"] }
1700
+ },
1701
+ "cozy-night": {
1702
+ weather: { type: "fireflies", intensity: 0.4 },
1703
+ fog: { type: "mist", intensity: 0.3, color: "#0a0a20" },
1704
+ light: { type: "bloom", intensity: 0.5, color: "#fff5e6", source: { x: 0.5, y: 0.3 } },
1705
+ sky: { type: "stars", intensity: 0.6, colors: ["#ffffff", "#fffacd", "#e6e6fa"] }
1706
+ },
1707
+ haunted: {
1708
+ weather: { type: "embers", intensity: 0.3, color: "#4a90a4" },
1709
+ fog: { type: "ground", intensity: 0.8, color: "#1a1a2e" },
1710
+ light: { type: "godrays", intensity: 0.4, color: "#4a6fa5" },
1711
+ sky: { type: "storm", intensity: 0.7, colors: ["#0a0a0f", "#1a1a2e", "#2a2a4e"] }
1712
+ },
1713
+ synthwave: {
1714
+ weather: { type: "sparks", intensity: 0.4, color: "#ff00ff" },
1715
+ fog: void 0,
1716
+ light: { type: "scanner", intensity: 0.6, color: "#00ffff", source: { x: 0.5, y: 1 } },
1717
+ sky: { type: "gradient", intensity: 0.9, colors: ["#ff00ff", "#ff6ec7", "#0a001a"] }
1718
+ },
1719
+ "zen-garden": {
1720
+ weather: { type: "sakura", intensity: 0.5, wind: { x: 0.2, y: 0 } },
1721
+ fog: { type: "mist", intensity: 0.4, color: "#f0f0f0" },
1722
+ light: { type: "godrays", intensity: 0.5, color: "#ffe4b5" },
1723
+ sky: { type: "sunset", intensity: 0.7, colors: ["#ff6b35", "#f7c59f", "#2e294e"] }
1724
+ },
1725
+ volcanic: {
1726
+ weather: { type: "embers", intensity: 0.8 },
1727
+ fog: { type: "volumetric", intensity: 0.6, color: "#1a0a00" },
1728
+ light: void 0,
1729
+ sky: { type: "heat", intensity: 0.9, colors: ["#ff4500", "#ff6347", "#1a0500"] }
1730
+ },
1731
+ arctic: {
1732
+ weather: { type: "snow", intensity: 0.7, wind: { x: 0.2, y: 0 } },
1733
+ fog: { type: "mist", intensity: 0.5, color: "#e8f4ff" },
1734
+ light: { type: "rim", intensity: 0.4, color: "#00f0ff" },
1735
+ sky: { type: "aurora", intensity: 0.6, colors: ["#00ff87", "#60efff", "#0a1a2f"] }
1736
+ },
1737
+ noir: {
1738
+ weather: { type: "rain", intensity: 0.6, wind: { x: -0.2, y: 0 } },
1739
+ fog: { type: "depth", intensity: 0.7, color: "#0a0a0f" },
1740
+ light: { type: "spotlight", intensity: 0.8, color: "#ffffff", source: { x: 0.3, y: 0 }, width: 0.4 },
1741
+ sky: { type: "clouds", intensity: 0.5, colors: ["#1a1a1a", "#2a2a2a", "#0a0a0a"] }
1742
+ },
1743
+ ethereal: {
1744
+ weather: { type: "spores", intensity: 0.5, color: "#da70d6" },
1745
+ fog: void 0,
1746
+ light: { type: "bloom", intensity: 0.6, color: "#e6e6fa", source: { x: 0.5, y: 0.5 } },
1747
+ sky: { type: "nebula", intensity: 0.7, colors: ["#4b0082", "#8a2be2", "#da70d6"] }
1748
+ },
1749
+ matrix: {
1750
+ weather: { type: "rain", intensity: 0.9, color: "#00ff00", wind: { x: 0, y: 0 } },
1751
+ fog: void 0,
1752
+ light: { type: "scanner", intensity: 0.4, color: "#00ff00", source: { x: 0.5, y: 0 } },
1753
+ sky: { type: "gradient", intensity: 0.8, colors: ["#001a00", "#003300", "#000a00"] }
1754
+ },
1755
+ rave: {
1756
+ weather: { type: "sparks", intensity: 0.6 },
1757
+ fog: void 0,
1758
+ light: { type: "laser", intensity: 0.8, color: "#ff0000", angle: 45 },
1759
+ sky: { type: "cosmic", intensity: 0.7, colors: ["#0d0221", "#ff00ff", "#00ffff", "#ff00ff"] }
1760
+ },
1761
+ "alien-world": {
1762
+ weather: { type: "spores", intensity: 0.6, color: "#7fff00" },
1763
+ fog: { type: "volumetric", intensity: 0.5, color: "#1a0033" },
1764
+ light: { type: "caustics", intensity: 0.5, color: "#7fff00" },
1765
+ sky: { type: "nebula", intensity: 0.8, colors: ["#1a0033", "#4b0082", "#7fff00"] }
1766
+ }
1767
+ };
1768
+
1769
+ // src/environment/EnvironmentLayer/EnvironmentLayer.tsx
1770
+ import { jsx as jsx6, jsxs } from "react/jsx-runtime";
1771
+ function EnvironmentLayer({
1772
+ preset,
1773
+ enabled = true,
1774
+ intensity = 1,
1775
+ stylePreset,
1776
+ className,
1777
+ style,
1778
+ weatherOverride,
1779
+ fogOverride,
1780
+ lightOverride,
1781
+ skyOverride
1782
+ }) {
1783
+ const reducedMotion = useReducedMotion5();
1784
+ const perfConfig = getPerformanceConfig();
1785
+ const resolvedStylePreset = stylePreset ?? (perfConfig.useShaders ? "cinematic" : "ui");
1786
+ if (!enabled || reducedMotion || perfConfig.tier === "minimal") {
1787
+ return null;
1788
+ }
1789
+ const config = ENVIRONMENT_PRESETS[preset];
1790
+ if (!config) {
1791
+ console.warn(
1792
+ `EnvironmentLayer: Unknown preset "${preset}". Available presets: ${Object.keys(ENVIRONMENT_PRESETS).join(", ")}`
1793
+ );
1794
+ return null;
1795
+ }
1796
+ return /* @__PURE__ */ jsxs(
1797
+ "div",
1798
+ {
1799
+ className: cn("pointer-events-none absolute inset-0 overflow-hidden", className),
1800
+ style,
1801
+ "aria-hidden": "true",
1802
+ children: [
1803
+ config.sky && /* @__PURE__ */ jsx6(
1804
+ AuroraLayer,
1805
+ {
1806
+ enabled,
1807
+ ...config.sky,
1808
+ stylePreset: resolvedStylePreset,
1809
+ intensity: (config.sky.intensity ?? 1) * intensity,
1810
+ ...skyOverride
1811
+ }
1812
+ ),
1813
+ config.fog && /* @__PURE__ */ jsx6(
1814
+ FogLayer,
1815
+ {
1816
+ enabled,
1817
+ ...config.fog,
1818
+ stylePreset: resolvedStylePreset,
1819
+ intensity: (config.fog.intensity ?? 1) * intensity,
1820
+ ...fogOverride
1821
+ }
1822
+ ),
1823
+ config.light && /* @__PURE__ */ jsx6(
1824
+ VolumetricLight,
1825
+ {
1826
+ enabled,
1827
+ ...config.light,
1828
+ stylePreset: resolvedStylePreset,
1829
+ intensity: (config.light.intensity ?? 1) * intensity,
1830
+ ...lightOverride
1831
+ }
1832
+ ),
1833
+ config.weather && /* @__PURE__ */ jsx6(
1834
+ WeatherLayer,
1835
+ {
1836
+ enabled,
1837
+ ...config.weather,
1838
+ stylePreset: resolvedStylePreset,
1839
+ intensity: (config.weather.intensity ?? 1) * intensity,
1840
+ ...weatherOverride
1841
+ }
1842
+ )
1843
+ ]
1844
+ }
1845
+ );
1846
+ }
1847
+ EnvironmentLayer.displayName = "EnvironmentLayer";
1848
+
1849
+ export {
1850
+ PERFORMANCE_PRESETS,
1851
+ detectPerformanceTier,
1852
+ getPerformanceConfig,
1853
+ AUTUMN_LEAF_COLOR_PRESETS,
1854
+ WeatherLayer,
1855
+ FogLayer,
1856
+ VolumetricLight,
1857
+ AuroraLayer,
1858
+ ENVIRONMENT_PRESETS,
1859
+ EnvironmentLayer
1860
+ };
1861
+ //# sourceMappingURL=chunk-HFSDW5IV.js.map