PageSourceSearch

https://www.hamienet.com/assets/StagePuppetEngine-BtPqoUG7.js

js hamienet.com collected 2026-09-24 19:01:17 UTC 232,977 bytes, 4,985 lines download raw bytes

1import { r as reactExports, j as jsxRuntimeExports } from "./vendor-react-Bay9Y7Lb.js";
2import { s as synth } from "./synth-BE1TSTD4.js";
3import { s as solve2BoneIK } from "./IkMath-D6fi1ppd.js";
4import { g as gsapWithCSS } from "./vendor-gsap-50Lx19ak.js";
5import { u as useAnimationFrame, m as motion } from "./vendor-framer-C8eEgkdL.js";
6const family_0_pianosSVG = `
7  <!-- Legs -->
8  <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2">
9    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#5D4037" />
10    <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
11  </g>
12  <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2">
13    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#5D4037" />
14    <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
15  </g>
16  <!-- Torso & Head -->
17  <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round">
18    <g id="body">
19      <!-- Torso -->
20      <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#5D4037" />
21      <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#D7CCC8" />
22      <!-- Head -->
23      <g id="head" transform="translate(0, -60)">
24        <circle cx="-15" cy="-30" r="8" fill="#5D4037" /><circle cx="15" cy="-30" r="8" fill="#5D4037" />
25        <circle cx="0" cy="-15" r="22" fill="#5D4037" />
26        <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" />
27        <circle cx="0" cy="-5" r="8" fill="#D7CCC8" /><path d="M0,-8 L0,-5 M-3,-5 Q0,-2 3,-5" stroke="#000" stroke-width="1.5" fill="none"/>
28      </g>
29    </g>
30  </g>
31  <g id="mount_front_body"></g>
32  <!-- Left Arm Hierarchy -->
33  <g id="shoulder_l" opacity="0"></g>
34  <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#5D4037" /></g>
35  <g id="elbow_l" opacity="0"></g>
36  <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#5D4037" /></g>
37  <g id="hand_l" stroke="#333" stroke-width="2">
38    <circle cx="0" cy="0" r="8" fill="#D7CCC8" />
39    <!-- Dynamic Instrument Injection Point -->
40    <g id="instrument" transform="translate(0,0)"></g>
41  </g>
42  <!-- Right Arm Hierarchy -->
43  <g id="shoulder_r" opacity="0"></g>
44  <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#5D4037" /></g>
45  <g id="elbow_r" opacity="0"></g>
46  <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#5D4037" /></g>
47  <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#D7CCC8" /></g>
48`;
49const family_1_chromatic_percussionSVG = `
50  <!-- Legs -->
51  <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2">
52    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#E64A19" />
53    <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
54  </g>
55  <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2">
56    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#E64A19" />
57    <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
58  </g>
59  <!-- Torso & Head -->
60  <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round">
61    <g id="body">
62      <!-- Torso -->
63      <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#E64A19" />
64      <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#FFFFFF" />
65      <!-- Head -->
66      <g id="head" transform="translate(0, -60)">
67        <polygon points="-10,-20 -25,-40 -20,-15" fill="#E64A19" /><polygon points="10,-20 25,-40 20,-15" fill="#E64A19" />
68        <circle cx="0" cy="-15" r="22" fill="#E64A19" />
69        <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" />
70        <polygon points="-15,-5 15,-5 0,10" fill="#FFFFFF" /><circle cx="0" cy="8" r="3" fill="#000" />
71      </g>
72    </g>
73  </g>
74  <g id="mount_front_body"></g>
75  <!-- Left Arm Hierarchy -->
76  <g id="shoulder_l" opacity="0"></g>
77  <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#E64A19" /></g>
78  <g id="elbow_l" opacity="0"></g>
79  <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#E64A19" /></g>
80  <g id="hand_l" stroke="#333" stroke-width="2">
81    <circle cx="0" cy="0" r="8" fill="#FFFFFF" />
82    <!-- Dynamic Instrument Injection Point -->
83    <g id="instrument" transform="translate(0,0)"></g>
84  </g>
85  <!-- Right Arm Hierarchy -->
86  <g id="shoulder_r" opacity="0"></g>
87  <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#E64A19" /></g>
88  <g id="elbow_r" opacity="0"></g>
89  <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#E64A19" /></g>
90  <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#FFFFFF" /></g>
91`;
92const family_2_organsSVG = `
93  <!-- Legs -->
94  <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2">
95    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#FBC02D" />
96    <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
97  </g>
98  <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2">
99    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#FBC02D" />
100    <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
101  </g>
102  <!-- Torso & Head -->
103  <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round">
104    <g id="body">
105      <!-- Torso -->
106      <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#FBC02D" />
107      <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#FFF9C4" />
108      <!-- Head -->
109      <g id="head" transform="translate(0, -60)">
110        <circle cx="0" cy="-15" r="30" fill="#E65100" />
111        <circle cx="0" cy="-15" r="22" fill="#FBC02D" />
112        <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" />
113        <circle cx="0" cy="-5" r="10" fill="#FFF9C4" /><circle cx="0" cy="-8" r="3" fill="#000" />
114      </g>
115    </g>
116  </g>
117  <g id="mount_front_body"></g>
118  <!-- Left Arm Hierarchy -->
119  <g id="shoulder_l" opacity="0"></g>
120  <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#FBC02D" /></g>
121  <g id="elbow_l" opacity="0"></g>
122  <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#FBC02D" /></g>
123  <g id="hand_l" stroke="#333" stroke-width="2">
124    <circle cx="0" cy="0" r="8" fill="#FFF9C4" />
125    <!-- Dynamic Instrument Injection Point -->
126    <g id="instrument" transform="translate(0,0)"></g>
127  </g>
128  <!-- Right Arm Hierarchy -->
129  <g id="shoulder_r" opacity="0"></g>
130  <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#FBC02D" /></g>
131  <g id="elbow_r" opacity="0"></g>
132  <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#FBC02D" /></g>
133  <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#FFF9C4" /></g>
134`;
135const family_3_guitarsSVG = `
136  <!-- Legs -->
137  <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2">
138    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#78909C" />
139    <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
140  </g>
141  <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2">
142    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#78909C" />
143    <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
144  </g>
145  <!-- Torso & Head -->
146  <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round">
147    <g id="body">
148      <!-- Torso -->
149      <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#78909C" />
150      <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#CFD8DC" />
151      <!-- Head -->
152      <g id="head" transform="translate(0, -60)">
153        <ellipse cx="-25" cy="-10" rx="15" ry="25" fill="#78909C" /><ellipse cx="25" cy="-10" rx="15" ry="25" fill="#78909C" />
154        <circle cx="0" cy="-15" r="22" fill="#78909C" />
155        <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" />
156        <path d="M-5,-5 L5,-5 L3,20 Q0,25 -5,15 Z" fill="#78909C" />
157      </g>
158    </g>
159  </g>
160  <g id="mount_front_body"></g>
161  <!-- Left Arm Hierarchy -->
162  <g id="shoulder_l" opacity="0"></g>
163  <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#78909C" /></g>
164  <g id="elbow_l" opacity="0"></g>
165  <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#78909C" /></g>
166  <g id="hand_l" stroke="#333" stroke-width="2">
167    <circle cx="0" cy="0" r="8" fill="#CFD8DC" />
168    <!-- Dynamic Instrument Injection Point -->
169    <g id="instrument" transform="translate(0,0)"></g>
170  </g>
171  <!-- Right Arm Hierarchy -->
172  <g id="shoulder_r" opacity="0"></g>
173  <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#78909C" /></g>
174  <g id="elbow_r" opacity="0"></g>
175  <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#78909C" /></g>
176  <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#CFD8DC" /></g>
177`;
178const family_4_bassesSVG = `
179  <!-- Legs -->
180  <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2">
181    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#212121" />
182    <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
183  </g>
184  <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2">
185    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#212121" />
186    <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
187  </g>
188  <!-- Torso & Head -->
189  <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round">
190    <g id="body">
191      <!-- Torso -->
192      <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#212121" />
193      <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#FFFFFF" />
194      <!-- Head -->
195      <g id="head" transform="translate(0, -60)">
196        
197        <circle cx="0" cy="-15" r="22" fill="#212121" />
198        <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" />
199        <ellipse cx="0" cy="0" rx="15" ry="10" fill="#FFF" /><polygon points="-5,-5 5,-5 0,5" fill="#FFB300" />
200      </g>
201    </g>
202  </g>
203  <g id="mount_front_body"></g>
204  <!-- Left Arm Hierarchy -->
205  <g id="shoulder_l" opacity="0"></g>
206  <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#212121" /></g>
207  <g id="elbow_l" opacity="0"></g>
208  <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#212121" /></g>
209  <g id="hand_l" stroke="#333" stroke-width="2">
210    <circle cx="0" cy="0" r="8" fill="#FFFFFF" />
211    <!-- Dynamic Instrument Injection Point -->
212    <g id="instrument" transform="translate(0,0)"></g>
213  </g>
214  <!-- Right Arm Hierarchy -->
215  <g id="shoulder_r" opacity="0"></g>
216  <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#212121" /></g>
217  <g id="elbow_r" opacity="0"></g>
218  <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#212121" /></g>
219  <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#FFFFFF" /></g>
220`;
221const family_5_stringsSVG = `
222  <!-- Legs -->
223  <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2">
224    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#43A047" />
225    <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
226  </g>
227  <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2">
228    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#43A047" />
229    <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
230  </g>
231  <!-- Torso & Head -->
232  <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round">
233    <g id="body">
234      <!-- Torso -->
235      <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#43A047" />
236      <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#C8E6C9" />
237      <!-- Head -->
238      <g id="head" transform="translate(0, -60)">
239        <circle cx="-15" cy="-25" r="10" fill="#43A047" /><circle cx="15" cy="-25" r="10" fill="#43A047" />
240        <circle cx="0" cy="-15" r="22" fill="#43A047" />
241        <circle cx="-15" cy="-25" r="3" fill="#000" /><circle cx="15" cy="-25" r="3" fill="#000" />
242        <path d="M-10,0 Q0,10 10,0" stroke="#000" stroke-width="2" fill="none" />
243      </g>
244    </g>
245  </g>
246  <g id="mount_front_body"></g>
247  <!-- Left Arm Hierarchy -->
248  <g id="shoulder_l" opacity="0"></g>
249  <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#43A047" /></g>
250  <g id="elbow_l" opacity="0"></g>
251  <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#43A047" /></g>
252  <g id="hand_l" stroke="#333" stroke-width="2">
253    <circle cx="0" cy="0" r="8" fill="#C8E6C9" />
254    <!-- Dynamic Instrument Injection Point -->
255    <g id="instrument" transform="translate(0,0)"></g>
256  </g>
257  <!-- Right Arm Hierarchy -->
258  <g id="shoulder_r" opacity="0"></g>
259  <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#43A047" /></g>
260  <g id="elbow_r" opacity="0"></g>
261  <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#43A047" /></g>
262  <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#C8E6C9" /></g>
263`;
264const family_6_ensembleSVG = `
265  <!-- Legs -->
266  <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2">
267    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#795548" />
268    <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
269  </g>
270  <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2">
271    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#795548" />
272    <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
273  </g>
274  <!-- Torso & Head -->
275  <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round">
276    <g id="body">
277      <!-- Torso -->
278      <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#795548" />
279      <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#FFCC80" />
280      <!-- Head -->
281      <g id="head" transform="translate(0, -60)">
282        <circle cx="-25" cy="-15" r="10" fill="#FFCC80" /><circle cx="25" cy="-15" r="10" fill="#FFCC80" />
283        <circle cx="0" cy="-15" r="22" fill="#795548" />
284        <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" />
285        <ellipse cx="0" cy="0" rx="18" ry="12" fill="#FFCC80" /><circle cx="0" cy="-3" r="2" fill="#000" /><path d="M-8,3 Q0,8 8,3" stroke="#000" stroke-width="2" fill="none" />
286      </g>
287    </g>
288  </g>
289  <g id="mount_front_body"></g>
290  <!-- Left Arm Hierarchy -->
291  <g id="shoulder_l" opacity="0"></g>
292  <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#795548" /></g>
293  <g id="elbow_l" opacity="0"></g>
294  <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#795548" /></g>
295  <g id="hand_l" stroke="#333" stroke-width="2">
296    <circle cx="0" cy="0" r="8" fill="#FFCC80" />
297    <!-- Dynamic Instrument Injection Point -->
298    <g id="instrument" transform="translate(0,0)"></g>
299  </g>
300  <!-- Right Arm Hierarchy -->
301  <g id="shoulder_r" opacity="0"></g>
302  <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#795548" /></g>
303  <g id="elbow_r" opacity="0"></g>
304  <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#795548" /></g>
305  <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#FFCC80" /></g>
306`;
307const family_7_brassSVG = `
308  <!-- Legs -->
309  <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2">
310    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#FF9800" />
311    <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
312  </g>
313  <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2">
314    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#FF9800" />
315    <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
316  </g>
317  <!-- Torso & Head -->
318  <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round">
319    <g id="body">
320      <!-- Torso -->
321      <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#FF9800" />
322      <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#FFE082" />
323      <!-- Head -->
324      <g id="head" transform="translate(0, -60)">
325        <circle cx="-15" cy="-30" r="6" fill="#FF9800" /><circle cx="15" cy="-30" r="6" fill="#FF9800" />
326        <circle cx="0" cy="-15" r="22" fill="#FF9800" />
327        <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" />
328        <circle cx="0" cy="-5" r="10" fill="#FFE082" /><circle cx="0" cy="-8" r="3" fill="#000" /><path d="M-15,-20 L-5,-20 M15,-20 L5,-20 M-20,-10 L-10,-10 M20,-10 L10,-10" stroke="#000" stroke-width="2" fill="none" />
329      </g>
330    </g>
331  </g>
332  <g id="mount_front_body"></g>
333  <!-- Left Arm Hierarchy -->
334  <g id="shoulder_l" opacity="0"></g>
335  <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#FF9800" /></g>
336  <g id="elbow_l" opacity="0"></g>
337  <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#FF9800" /></g>
338  <g id="hand_l" stroke="#333" stroke-width="2">
339    <circle cx="0" cy="0" r="8" fill="#FFE082" />
340    <!-- Dynamic Instrument Injection Point -->
341    <g id="instrument" transform="translate(0,0)"></g>
342  </g>
343  <!-- Right Arm Hierarchy -->
344  <g id="shoulder_r" opacity="0"></g>
345  <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#FF9800" /></g>
346  <g id="elbow_r" opacity="0"></g>
347  <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#FF9800" /></g>
348  <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#FFE082" /></g>
349`;
350const family_8_reedSVG = `
351  <!-- Legs -->
352  <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2">
353    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#607D8B" />
354    <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
355  </g>
356  <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2">
357    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#607D8B" />
358    <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
359  </g>
360  <!-- Torso & Head -->
361  <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round">
362    <g id="body">
363      <!-- Torso -->
364      <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#607D8B" />
365      <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#ECEFF1" />
366      <!-- Head -->
367      <g id="head" transform="translate(0, -60)">
368        <polygon points="-10,-20 -20,-35 -20,-15" fill="#607D8B" /><polygon points="10,-20 20,-35 20,-15" fill="#607D8B" />
369        <circle cx="0" cy="-15" r="22" fill="#607D8B" />
370        <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" />
371        <ellipse cx="0" cy="-2" rx="12" ry="8" fill="#ECEFF1" /><circle cx="0" cy="-5" r="3" fill="#000" />
372      </g>
373    </g>
374  </g>
375  <g id="mount_front_body"></g>
376  <!-- Left Arm Hierarchy -->
377  <g id="shoulder_l" opacity="0"></g>
378  <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#607D8B" /></g>
379  <g id="elbow_l" opacity="0"></g>
380  <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#607D8B" /></g>
381  <g id="hand_l" stroke="#333" stroke-width="2">
382    <circle cx="0" cy="0" r="8" fill="#ECEFF1" />
383    <!-- Dynamic Instrument Injection Point -->
384    <g id="instrument" transform="translate(0,0)"></g>
385  </g>
386  <!-- Right Arm Hierarchy -->
387  <g id="shoulder_r" opacity="0"></g>
388  <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#607D8B" /></g>
389  <g id="elbow_r" opacity="0"></g>
390  <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#607D8B" /></g>
391  <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#ECEFF1" /></g>
392`;
393const family_9_pipeSVG = `
394  <!-- Legs -->
395  <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2">
396    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#9C27B0" />
397    <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
398  </g>
399  <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2">
400    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#9C27B0" />
401    <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
402  </g>
403  <!-- Torso & Head -->
404  <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round">
405    <g id="body">
406      <!-- Torso -->
407      <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#9C27B0" />
408      <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#E1BEE7" />
409      <!-- Head -->
410      <g id="head" transform="translate(0, -60)">
411        <circle cx="-15" cy="-25" r="5" fill="#9C27B0" /><circle cx="15" cy="-25" r="5" fill="#9C27B0" />
412        <circle cx="0" cy="-15" r="22" fill="#9C27B0" />
413        <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" />
414        <ellipse cx="0" cy="5" rx="20" ry="15" fill="#9C27B0" /><circle cx="-8" cy="2" r="2" fill="#000" /><circle cx="8" cy="2" r="2" fill="#000" />
415      </g>
416    </g>
417  </g>
418  <g id="mount_front_body"></g>
419  <!-- Left Arm Hierarchy -->
420  <g id="shoulder_l" opacity="0"></g>
421  <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#9C27B0" /></g>
422  <g id="elbow_l" opacity="0"></g>
423  <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#9C27B0" /></g>
424  <g id="hand_l" stroke="#333" stroke-width="2">
425    <circle cx="0" cy="0" r="8" fill="#E1BEE7" />
426    <!-- Dynamic Instrument Injection Point -->
427    <g id="instrument" transform="translate(0,0)"></g>
428  </g>
429  <!-- Right Arm Hierarchy -->
430  <g id="shoulder_r" opacity="0"></g>
431  <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#9C27B0" /></g>
432  <g id="elbow_r" opacity="0"></g>
433  <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#9C27B0" /></g>
434  <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#E1BEE7" /></g>
435`;
436const family_10_synth_leadSVG = `
437  <!-- Legs -->
438  <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2">
439    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#9E9E9E" />
440    <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
441  </g>
442  <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2">
443    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#9E9E9E" />
444    <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
445  </g>
446  <!-- Torso & Head -->
447  <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round">
448    <g id="body">
449      <!-- Torso -->
450      <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#9E9E9E" />
451      <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#F5F5F5" />
452      <!-- Head -->
453      <g id="head" transform="translate(0, -60)">
454        <polygon points="-10,-20 -15,-30 -20,-15" fill="#9E9E9E" /><polygon points="10,-20 15,-30 20,-15" fill="#9E9E9E" />
455        <circle cx="0" cy="-15" r="22" fill="#9E9E9E" />
456        <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" />
457        <ellipse cx="0" cy="5" rx="15" ry="10" fill="#F5F5F5" /><polygon points="-3,0 3,0 0,-15" fill="#FFF" />
458      </g>
459    </g>
460  </g>
461  <g id="mount_front_body"></g>
462  <!-- Left Arm Hierarchy -->
463  <g id="shoulder_l" opacity="0"></g>
464  <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#9E9E9E" /></g>
465  <g id="elbow_l" opacity="0"></g>
466  <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#9E9E9E" /></g>
467  <g id="hand_l" stroke="#333" stroke-width="2">
468    <circle cx="0" cy="0" r="8" fill="#F5F5F5" />
469    <!-- Dynamic Instrument Injection Point -->
470    <g id="instrument" transform="translate(0,0)"></g>
471  </g>
472  <!-- Right Arm Hierarchy -->
473  <g id="shoulder_r" opacity="0"></g>
474  <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#9E9E9E" /></g>
475  <g id="elbow_r" opacity="0"></g>
476  <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#9E9E9E" /></g>
477  <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#F5F5F5" /></g>
478`;
479const family_11_synth_padSVG = `
480  <!-- Legs -->
481  <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2">
482    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#F06292" />
483    <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
484  </g>
485  <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2">
486    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#F06292" />
487    <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
488  </g>
489  <!-- Torso & Head -->
490  <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round">
491    <g id="body">
492      <!-- Torso -->
493      <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#F06292" />
494      <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#F8BBD0" />
495      <!-- Head -->
496      <g id="head" transform="translate(0, -60)">
497        <polygon points="-10,-20 -20,-30 -20,-15" fill="#F06292" /><polygon points="10,-20 20,-30 20,-15" fill="#F06292" />
498        <circle cx="0" cy="-15" r="22" fill="#F06292" />
499        <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" />
500        <ellipse cx="0" cy="-5" rx="12" ry="8" fill="#F48FB1" /><circle cx="-4" cy="-5" r="2" fill="#000" /><circle cx="4" cy="-5" r="2" fill="#000" />
501      </g>
502    </g>
503  </g>
504  <g id="mount_front_body"></g>
505  <!-- Left Arm Hierarchy -->
506  <g id="shoulder_l" opacity="0"></g>
507  <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#F06292" /></g>
508  <g id="elbow_l" opacity="0"></g>
509  <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#F06292" /></g>
510  <g id="hand_l" stroke="#333" stroke-width="2">
511    <circle cx="0" cy="0" r="8" fill="#F8BBD0" />
512    <!-- Dynamic Instrument Injection Point -->
513    <g id="instrument" transform="translate(0,0)"></g>
514  </g>
515  <!-- Right Arm Hierarchy -->
516  <g id="shoulder_r" opacity="0"></g>
517  <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#F06292" /></g>
518  <g id="elbow_r" opacity="0"></g>
519  <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#F06292" /></g>
520  <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#F8BBD0" /></g>
521`;
522const family_12_synth_effectsSVG = `
523  <!-- Legs -->
524  <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2">
525    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#424242" />
526    <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
527  </g>
528  <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2">
529    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#424242" />
530    <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
531  </g>
532  <!-- Torso & Head -->
533  <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round">
534    <g id="body">
535      <!-- Torso -->
536      <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#424242" />
537      <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#BDBDBD" />
538      <!-- Head -->
539      <g id="head" transform="translate(0, -60)">
540        <polygon points="-10,-20 -20,-35 -20,-15" fill="#424242" /><polygon points="10,-20 20,-35 20,-15" fill="#424242" />
541        <circle cx="0" cy="-15" r="22" fill="#424242" />
542        <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" />
543        <path d="M-15,-5 L-25,-8 M-15,-2 L-25,-2 M15,-5 L25,-8 M15,-2 L25,-2" stroke="#000" stroke-width="1" fill="none" /><circle cx="0" cy="-5" r="2" fill="#F48FB1" />
544      </g>
545    </g>
546  </g>
547  <g id="mount_front_body"></g>
548  <!-- Left Arm Hierarchy -->
549  <g id="shoulder_l" opacity="0"></g>
550  <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#424242" /></g>
551  <g id="elbow_l" opacity="0"></g>
552  <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#424242" /></g>
553  <g id="hand_l" stroke="#333" stroke-width="2">
554    <circle cx="0" cy="0" r="8" fill="#BDBDBD" />
555    <!-- Dynamic Instrument Injection Point -->
556    <g id="instrument" transform="translate(0,0)"></g>
557  </g>
558  <!-- Right Arm Hierarchy -->
559  <g id="shoulder_r" opacity="0"></g>
560  <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#424242" /></g>
561  <g id="elbow_r" opacity="0"></g>
562  <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#424242" /></g>
563  <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#BDBDBD" /></g>
564`;
565const family_13_ethnicSVG = `
566  <!-- Legs -->
567  <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2">
568    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#8D6E63" />
569    <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
570  </g>
571  <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2">
572    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#8D6E63" />
573    <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
574  </g>
575  <!-- Torso & Head -->
576  <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round">
577    <g id="body">
578      <!-- Torso -->
579      <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#8D6E63" />
580      <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#EFEBE9" />
581      <!-- Head -->
582      <g id="head" transform="translate(0, -60)">
583        <ellipse cx="-20" cy="-15" rx="8" ry="20" fill="#8D6E63" /><ellipse cx="20" cy="-15" rx="8" ry="20" fill="#8D6E63" />
584        <circle cx="0" cy="-15" r="22" fill="#8D6E63" />
585        <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" />
586        <ellipse cx="0" cy="-2" rx="14" ry="10" fill="#EFEBE9" /><circle cx="0" cy="-5" r="4" fill="#000" /><path d="M0,-5 L0,2 M-5,2 Q0,5 5,2" stroke="#000" stroke-width="1.5" fill="none"/>
587      </g>
588    </g>
589  </g>
590  <g id="mount_front_body"></g>
591  <!-- Left Arm Hierarchy -->
592  <g id="shoulder_l" opacity="0"></g>
593  <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#8D6E63" /></g>
594  <g id="elbow_l" opacity="0"></g>
595  <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#8D6E63" /></g>
596  <g id="hand_l" stroke="#333" stroke-width="2">
597    <circle cx="0" cy="0" r="8" fill="#EFEBE9" />
598    <!-- Dynamic Instrument Injection Point -->
599    <g id="instrument" transform="translate(0,0)"></g>
600  </g>
601  <!-- Right Arm Hierarchy -->
602  <g id="shoulder_r" opacity="0"></g>
603  <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#8D6E63" /></g>
604  <g id="elbow_r" opacity="0"></g>
605  <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#8D6E63" /></g>
606  <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#EFEBE9" /></g>
607`;
608const family_14_percussiveSVG = `
609  <!-- Legs -->
610  <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2">
611    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#90CAF9" />
612    <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
613  </g>
614  <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2">
615    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#90CAF9" />
616    <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
617  </g>
618  <!-- Torso & Head -->
619  <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round">
620    <g id="body">
621      <!-- Torso -->
622      <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#90CAF9" />
623      <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#FFFFFF" />
624      <!-- Head -->
625      <g id="head" transform="translate(0, -60)">
626        <ellipse cx="-10" cy="-40" rx="6" ry="25" fill="#90CAF9" /><ellipse cx="10" cy="-40" rx="6" ry="25" fill="#90CAF9" />
627        <circle cx="0" cy="-15" r="22" fill="#90CAF9" />
628        <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" />
629        <circle cx="0" cy="-5" r="3" fill="#F48FB1" /><rect x="-3" y="-2" width="6" height="6" fill="#FFF" />
630      </g>
631    </g>
632  </g>
633  <g id="mount_front_body"></g>
634  <!-- Left Arm Hierarchy -->
635  <g id="shoulder_l" opacity="0"></g>
636  <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#90CAF9" /></g>
637  <g id="elbow_l" opacity="0"></g>
638  <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#90CAF9" /></g>
639  <g id="hand_l" stroke="#333" stroke-width="2">
640    <circle cx="0" cy="0" r="8" fill="#FFFFFF" />
641    <!-- Dynamic Instrument Injection Point -->
642    <g id="instrument" transform="translate(0,0)"></g>
643  </g>
644  <!-- Right Arm Hierarchy -->
645  <g id="shoulder_r" opacity="0"></g>
646  <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#90CAF9" /></g>
647  <g id="elbow_r" opacity="0"></g>
648  <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#90CAF9" /></g>
649  <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#FFFFFF" /></g>
650`;
651const family_15_sound_effectsSVG = `
652  <!-- Legs -->
653  <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2">
654    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#F57C00" />
655    <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
656  </g>
657  <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2">
658    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#F57C00" />
659    <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
660  </g>
661  <!-- Torso & Head -->
662  <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round">
663    <g id="body">
664      <!-- Torso -->
665      <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#F57C00" />
666      <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#FFF" />
667      <!-- Head -->
668      <g id="head" transform="translate(0, -60)">
669        <circle cx="-15" cy="-30" r="8" fill="#F57C00" /><circle cx="15" cy="-30" r="8" fill="#F57C00" />
670        <circle cx="0" cy="-15" r="22" fill="#F57C00" />
671        <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" />
672        <circle cx="0" cy="-5" r="10" fill="#FFF" /><circle cx="0" cy="-8" r="3" fill="#F48FB1" />
673      </g>
674    </g>
675  </g>
676  <g id="mount_front_body"></g>
677  <!-- Left Arm Hierarchy -->
678  <g id="shoulder_l" opacity="0"></g>
679  <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#F57C00" /></g>
680  <g id="elbow_l" opacity="0"></g>
681  <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#F57C00" /></g>
682  <g id="hand_l" stroke="#333" stroke-width="2">
683    <circle cx="0" cy="0" r="8" fill="#FFF" />
684    <!-- Dynamic Instrument Injection Point -->
685    <g id="instrument" transform="translate(0,0)"></g>
686  </g>
687  <!-- Right Arm Hierarchy -->
688  <g id="shoulder_r" opacity="0"></g>
689  <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#F57C00" /></g>
690  <g id="elbow_r" opacity="0"></g>
691  <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#F57C00" /></g>
692  <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#FFF" /></g>
693`;
694const percussionSVG = `
695  <!-- Legs -->
696  <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2">
697    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#4E342E" />
698    <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
699  </g>
700  <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2">
701    <rect x="-6" y="0" width="12" height="30" rx="4" fill="#4E342E" />
702    <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g>
703  </g>
704  <!-- Torso & Head -->
705  <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round">
706    <g id="body">
707      <!-- Torso -->
708      <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#4E342E" />
709      <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#D7CCC8" />
710      <!-- Head -->
711      <g id="head" transform="translate(0, -60)">
712        
713        <circle cx="0" cy="-15" r="22" fill="#4E342E" />
714        <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" />
715        <ellipse cx="0" cy="-5" rx="15" ry="12" fill="#D7CCC8" /><circle cx="-6" cy="-8" r="2" fill="#000" /><circle cx="6" cy="-8" r="2" fill="#000" />
716      </g>
717    </g>
718  </g>
719  <g id="mount_front_body"></g>
720  <!-- Left Arm Hierarchy -->
721  <g id="shoulder_l" opacity="0"></g>
722  <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#4E342E" /></g>
723  <g id="elbow_l" opacity="0"></g>
724  <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#4E342E" /></g>
725  <g id="hand_l" stroke="#333" stroke-width="2">
726    <circle cx="0" cy="0" r="8" fill="#D7CCC8" />
727    <!-- Dynamic Instrument Injection Point -->
728    <g id="instrument" transform="translate(0,0)"></g>
729  </g>
730  <!-- Right Arm Hierarchy -->
731  <g id="shoulder_r" opacity="0"></g>
732  <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#4E342E" /></g>
733  <g id="elbow_r" opacity="0"></g>
734  <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#4E342E" /></g>
735  <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#D7CCC8" /></g>
736`;
737const defaultPuppetConfigs = {
738  "family_0_pianos": {
739    id: "family_0_pianos",
740    name: "Bear (Pianos)",
741    family: "keys",
742    svgContent: family_0_pianosSVG,
743    anchorX: 0,
744    anchorY: 100,
745    scale: 0.5,
746    noteMap: {
747      default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 }
748    },
749    ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 },
750    ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 }
751  },
752  "family_1_chromatic_percussion": {
753    id: "family_1_chromatic_percussion",
754    name: "Fox (Chromatic Perc.)",
755    family: "keys",
756    svgContent: family_1_chromatic_percussionSVG,
757    anchorX: 0,
758    anchorY: 100,
759    scale: 0.5,
760    noteMap: {
761      default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 }
762    },
763    ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 },
764    ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 }
765  },
766  "family_2_organs": {
767    id: "family_2_organs",
768    name: "Lion (Organs)",
769    family: "keys",
770    svgContent: family_2_organsSVG,
771    anchorX: 0,
772    anchorY: 100,
773    scale: 0.5,
774    noteMap: {
775      default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 }
776    },
777    ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 },
778    ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 }
779  },
780  "family_3_guitars": {
781    id: "family_3_guitars",
782    name: "Elephant (Guitars)",
783    family: "strings",
784    svgContent: family_3_guitarsSVG,
785    anchorX: 0,
786    anchorY: 100,
787    scale: 0.5,
788    noteMap: {
789      default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 }
790    },
791    ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 },
792    ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 }
793  },
794  "family_4_basses": {
795    id: "family_4_basses",
796    name: "Penguin (Basses)",
797    family: "strings",
798    svgContent: family_4_bassesSVG,
799    anchorX: 0,
800    anchorY: 100,
801    scale: 0.5,
802    noteMap: {
803      default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 }
804    },
805    ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 },
806    ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 }
807  },
808  "family_5_strings": {
809    id: "family_5_strings",
810    name: "Frog (Strings)",
811    family: "strings",
812    svgContent: family_5_stringsSVG,
813    anchorX: 0,
814    anchorY: 100,
815    scale: 0.5,
816    noteMap: {
817      default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 }
818    },
819    ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 },
820    ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 }
821  },
822  "family_6_ensemble": {
823    id: "family_6_ensemble",
824    name: "Monkey (Ensemble)",
825    family: "other",
826    svgContent: family_6_ensembleSVG,
827    anchorX: 0,
828    anchorY: 100,
829    scale: 0.5,
830    noteMap: {
831      default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 }
832    },
833    ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 },
834    ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 }
835  },
836  "family_7_brass": {
837    id: "family_7_brass",
838    name: "Tiger (Brass)",
839    family: "winds",
840    svgContent: family_7_brassSVG,
841    anchorX: 0,
842    anchorY: 100,
843    scale: 0.5,
844    noteMap: {
845      default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 }
846    },
847    ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 },
848    ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 }
849  },
850  "family_8_reed": {
851    id: "family_8_reed",
852    name: "Wolf (Reed)",
853    family: "winds",
854    svgContent: family_8_reedSVG,
855    anchorX: 0,
856    anchorY: 100,
857    scale: 0.5,
858    noteMap: {
859      default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 }
860    },
861    ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 },
862    ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 }
863  },
864  "family_9_pipe": {
865    id: "family_9_pipe",
866    name: "Hippo (Pipe)",
867    family: "winds",
868    svgContent: family_9_pipeSVG,
869    anchorX: 0,
870    anchorY: 100,
871    scale: 0.5,
872    noteMap: {
873      default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 }
874    },
875    ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 },
876    ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 }
877  },
878  "family_10_synth_lead": {
879    id: "family_10_synth_lead",
880    name: "Rhino (Synth Lead)",
881    family: "keys",
882    svgContent: family_10_synth_leadSVG,
883    anchorX: 0,
884    anchorY: 100,
885    scale: 0.5,
886    noteMap: {
887      default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 }
888    },
889    ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 },
890    ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 }
891  },
892  "family_11_synth_pad": {
893    id: "family_11_synth_pad",
894    name: "Pig (Synth Pad)",
895    family: "keys",
896    svgContent: family_11_synth_padSVG,
897    anchorX: 0,
898    anchorY: 100,
899    scale: 0.5,
900    noteMap: {
901      default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 }
902    },
903    ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 },
904    ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 }
905  },
906  "family_12_synth_effects": {
907    id: "family_12_synth_effects",
908    name: "Cat (Synth Effects)",
909    family: "other",
910    svgContent: family_12_synth_effectsSVG,
911    anchorX: 0,
912    anchorY: 100,
913    scale: 0.5,
914    noteMap: {
915      default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 }
916    },
917    ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 },
918    ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 }
919  },
920  "family_13_ethnic": {
921    id: "family_13_ethnic",
922    name: "Dog (Ethnic)",
923    family: "other",
924    svgContent: family_13_ethnicSVG,
925    anchorX: 0,
926    anchorY: 100,
927    scale: 0.5,
928    noteMap: {
929      default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 }
930    },
931    ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 },
932    ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 }
933  },
934  "family_14_percussive": {
935    id: "family_14_percussive",
936    name: "Rabbit (Percussive)",
937    family: "keys",
938    svgContent: family_14_percussiveSVG,
939    anchorX: 0,
940    anchorY: 100,
941    scale: 0.5,
942    noteMap: {
943      default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 }
944    },
945    ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 },
946    ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 }
947  },
948  "family_15_sound_effects": {
949    id: "family_15_sound_effects",
950    name: "Hamster (Sound FX)",
951    family: "other",
952    svgContent: family_15_sound_effectsSVG,
953    anchorX: 0,
954    anchorY: 100,
955    scale: 0.5,
956    noteMap: {
957      default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 }
958    },
959    ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 },
960    ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 }
961  },
962  "percussion": {
963    id: "percussion",
964    name: "Gorilla (Percussion)",
965    family: "percussion",
966    svgContent: percussionSVG,
967    anchorX: 0,
968    anchorY: 100,
969    scale: 0.5,
970    noteMap: {
971      default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 }
972    },
973    ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 },
974    ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 },
975    sockets: {
976      mouth: { x: 0, y: -65 },
977      shoulder_l: { x: -20, y: -50 },
978      shoulder_r: { x: 20, y: -50 },
979      elbow_l: { x: -50, y: -20 },
980      elbow_r: { x: 50, y: -20 },
981      hand_l: { x: -50, y: 15 },
982      hand_r: { x: 50, y: 15 }
983    }
984  }
985};
986function mergeWithGorillaConfig(config, allConfigs) {
987  if (!config) return config;
988  const gorillaFromAll = allConfigs && allConfigs["percussion"];
989  const gorillaDefault = defaultPuppetConfigs["percussion"];
990  const gorilla = gorillaFromAll ? {
991    ...gorillaDefault,
992    ...gorillaFromAll,
993    sockets: {
994      ...gorillaDefault.sockets || {},
995      ...gorillaFromAll.sockets || {}
996    },
997    ikLeftArm: {
998      ...gorillaDefault.ikLeftArm || {},
999      ...gorillaFromAll.ikLeftArm || {}
1000    },
1001    ikRightArm: {
1002      ...gorillaDefault.ikRightArm || {},
1003      ...gorillaFromAll.ikRightArm || {}
1004    }
1005  } : gorillaDefault;
1006  if (config.id === "percussion") return gorilla;
1007  return {
1008    ...config,
1009    anchorX: config.anchorX !== void 0 ? config.anchorX : (gorilla == null ? void 0 : gorilla.anchorX) ?? 0,
1010    anchorY: config.anchorY !== void 0 ? config.anchorY : (gorilla == null ? void 0 : gorilla.anchorY) ?? 100,
1011    scale: config.scale !== void 0 ? config.scale : (gorilla == null ? void 0 : gorilla.scale) ?? 0.5,
1012    instrumentScale: config.instrumentScale !== void 0 ? config.instrumentScale : gorilla == null ? void 0 : gorilla.instrumentScale,
1013    instrumentRotation: config.instrumentRotation !== void 0 ? config.instrumentRotation : gorilla == null ? void 0 : gorilla.instrumentRotation,
1014    ikLeftArm: (gorilla == null ? void 0 : gorilla.ikLeftArm) || config.ikLeftArm ? {
1015      ...(gorilla == null ? void 0 : gorilla.ikLeftArm) || {},
1016      ...config.ikLeftArm || {}
1017    } : void 0,
1018    ikRightArm: (gorilla == null ? void 0 : gorilla.ikRightArm) || config.ikRightArm ? {
1019      ...(gorilla == null ? void 0 : gorilla.ikRightArm) || {},
1020      ...config.ikRightArm || {}
1021    } : void 0,
1022    noteMap: config.noteMap && Object.keys(config.noteMap).length > 0 ? config.noteMap : (gorilla == null ? void 0 : gorilla.noteMap) || {},
1023    transforms: (gorilla == null ? void 0 : gorilla.transforms) || config.transforms ? {
1024      ...(gorilla == null ? void 0 : gorilla.transforms) || {},
1025      ...config.transforms || {}
1026    } : void 0,
1027    sockets: (gorilla == null ? void 0 : gorilla.sockets) || config.sockets ? {
1028      ...(gorilla == null ? void 0 : gorilla.sockets) || {},
1029      ...config.sockets || {}
1030    } : void 0,
1031    baseZOrder: (gorilla == null ? void 0 : gorilla.baseZOrder) || config.baseZOrder ? {
1032      ...(gorilla == null ? void 0 : gorilla.baseZOrder) || {},
1033      ...config.baseZOrder || {}
1034    } : void 0
1035  };
1036}
1037function mergeAllConfigsWithGorilla(configs) {
1038  const merged = {};
1039  const keys = Object.keys(configs);
1040  keys.forEach((id) => {
1041    merged[id] = mergeWithGorillaConfig(configs[id], configs);
1042  });
1043  return merged;
1044}
1045const defaultBackgroundConfig = {
1046  type: "image",
1047  color: "#0c0c14",
1048  gradient: {
1049    type: "linear",
1050    angle: 135,
1051    stops: [
1052      { color: "#0f172a", offset: 0 },
1053      { color: "#1e1b4b", offset: 50 },
1054      { color: "#311042", offset: 100 }
1055    ]
1056  },
1057  image: {
1058    url: "/assets/visualizers/orchestra/background-forest.webp",
1059    size: "cover",
1060    position: "center center",
1061    repeat: "no-repeat",
1062    blur: 0
1063  },
1064  particles: {
1065    enabled: false,
1066    type: "stars",
1067    count: 50,
1068    speed: 1,
1069    color: "#ffffff",
1070    size: 2
1071  },
1072  overlay: {
1073    enabled: true,
1074    color: "#000000",
1075    opacity: 0.15
1076  }
1077};
1078const defaultBackgroundPresets = [
1079  {
1080    id: "alt_rock",
1081    name: "Alternative Rock",
1082    tags: ["alternative rock"],
1083    config: {
1084      type: "image",
1085      color: "#000000",
1086      gradient: { type: "linear", angle: 0, stops: [] },
1087      image: { url: "/assets/visualizers/orchestra/bkg/alt_rock.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 },
1088      particles: { enabled: true, type: "dust", count: 50, speed: 1, color: "#ffffff", size: 2 },
1089      overlay: { enabled: true, color: "#000000", opacity: 0.1 }
1090    }
1091  },
1092  {
1093    id: "anime",
1094    name: "Anime",
1095    tags: ["anime", "animation"],
1096    config: {
1097      type: "image",
1098      color: "#000000",
1099      gradient: { type: "linear", angle: 0, stops: [] },
1100      image: { url: "/assets/visualizers/orchestra/bkg/anime.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 },
1101      particles: { enabled: true, type: "fireflies", count: 80, speed: 1, color: "#ffffff", size: 2 },
1102      overlay: { enabled: true, color: "#000000", opacity: 0.1 }
1103    }
1104  },
1105  {
1106    id: "classical",
1107    name: "Classical & Orchestral",
1108    tags: ["classical", "neo-classical", "orchestral", "piano", "soundtrack", "piano solo"],
1109    config: {
1110      type: "image",
1111      color: "#000000",
1112      gradient: { type: "linear", angle: 0, stops: [] },
1113      image: { url: "/assets/visualizers/orchestra/bkg/classical.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 },
1114      particles: { enabled: true, type: "dust", count: 60, speed: 1, color: "#ffffff", size: 2 },
1115      overlay: { enabled: true, color: "#000000", opacity: 0.1 }
1116    }
1117  },
1118  {
1119    id: "country",
1120    name: "Country",
1121    tags: ["country"],
1122    config: {
1123      type: "image",
1124      color: "#000000",
1125      gradient: { type: "linear", angle: 0, stops: [] },
1126      image: { url: "/assets/visualizers/orchestra/bkg/country.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 },
1127      particles: { enabled: true, type: "dust", count: 40, speed: 1, color: "#ffffff", size: 2 },
1128      overlay: { enabled: true, color: "#000000", opacity: 0.1 }
1129    }
1130  },
1131  {
1132    id: "folk",
1133    name: "Folk",
1134    tags: ["folk"],
1135    config: {
1136      type: "image",
1137      color: "#000000",
1138      gradient: { type: "linear", angle: 0, stops: [] },
1139      image: { url: "/assets/visualizers/orchestra/bkg/folk.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 },
1140      particles: { enabled: true, type: "fireflies", count: 60, speed: 1, color: "#ffffff", size: 2 },
1141      overlay: { enabled: true, color: "#000000", opacity: 0.1 }
1142    }
1143  },
1144  {
1145    id: "game_music",
1146    name: "Game Music",
1147    tags: ["game music", "chiptune", "8-bit"],
1148    config: {
1149      type: "image",
1150      color: "#000000",
1151      gradient: { type: "linear", angle: 0, stops: [] },
1152      image: { url: "/assets/visualizers/orchestra/bkg/game_music.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 },
1153      particles: { enabled: true, type: "floating_orbs", count: 30, speed: 1, color: "#ffffff", size: 2 },
1154      overlay: { enabled: true, color: "#000000", opacity: 0.1 }
1155    }
1156  },
1157  {
1158    id: "hip_hop",
1159    name: "Hip Hop",
1160    tags: ["hip hop"],
1161    config: {
1162      type: "image",
1163      color: "#000000",
1164      gradient: { type: "linear", angle: 0, stops: [] },
1165      image: { url: "/assets/visualizers/orchestra/bkg/hip_hop.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 },
1166      particles: { enabled: true, type: "rain", count: 100, speed: 1, color: "#ffffff", size: 2 },
1167      overlay: { enabled: true, color: "#000000", opacity: 0.1 }
1168    }
1169  },
1170  {
1171    id: "indie",
1172    name: "Indie",
1173    tags: ["indie"],
1174    config: {
1175      type: "image",
1176      color: "#000000",
1177      gradient: { type: "linear", angle: 0, stops: [] },
1178      image: { url: "/assets/visualizers/orchestra/bkg/indie.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 },
1179      particles: { enabled: true, type: "floating_orbs", count: 40, speed: 1, color: "#ffffff", size: 2 },
1180      overlay: { enabled: true, color: "#000000", opacity: 0.1 }
1181    }
1182  },
1183  {
1184    id: "jazz",
1185    name: "Jazz & Blues",
1186    tags: ["jazz", "blues"],
1187    config: {
1188      type: "image",
1189      color: "#000000",
1190      gradient: { type: "linear", angle: 0, stops: [] },
1191      image: { url: "/assets/visualizers/orchestra/bkg/jazz.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 },
1192      particles: { enabled: true, type: "dust", count: 50, speed: 1, color: "#ffffff", size: 2 },
1193      overlay: { enabled: true, color: "#000000", opacity: 0.1 }
1194    }
1195  },
1196  {
1197    id: "karaoke",
1198    name: "Karaoke",
1199    tags: ["karaoke"],
1200    config: {
1201      type: "image",
1202      color: "#000000",
1203      gradient: { type: "linear", angle: 0, stops: [] },
1204      image: { url: "/assets/visualizers/orchestra/bkg/karaoke.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 },
1205      particles: { enabled: true, type: "confetti", count: 100, speed: 1, color: "#ffffff", size: 2 },
1206      overlay: { enabled: true, color: "#000000", opacity: 0.1 }
1207    }
1208  },
1209  {
1210    id: "latin",
1211    name: "Latin",
1212    tags: ["latin"],
1213    config: {
1214      type: "image",
1215      color: "#000000",
1216      gradient: { type: "linear", angle: 0, stops: [] },
1217      image: { url: "/assets/visualizers/orchestra/bkg/latin.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 },
1218      particles: { enabled: true, type: "hearts", count: 30, speed: 1, color: "#ffffff", size: 2 },
1219      overlay: { enabled: true, color: "#000000", opacity: 0.1 }
1220    }
1221  },
1222  {
1223    id: "metal",
1224    name: "Metal",
1225    tags: ["metal"],
1226    config: {
1227      type: "image",
1228      color: "#000000",
1229      gradient: { type: "linear", angle: 0, stops: [] },
1230      image: { url: "/assets/visualizers/orchestra/bkg/metal.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 },
1231      particles: { enabled: true, type: "sparkles", count: 60, speed: 1, color: "#ffffff", size: 2 },
1232      overlay: { enabled: true, color: "#000000", opacity: 0.1 }
1233    }
1234  },
1235  {
1236    id: "national",
1237    name: "National Anthem",
1238    tags: ["national anthem"],
1239    config: {
1240      type: "image",
1241      color: "#000000",
1242      gradient: { type: "linear", angle: 0, stops: [] },
1243      image: { url: "/assets/visualizers/orchestra/bkg/national.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 },
1244      particles: { enabled: true, type: "dust", count: 30, speed: 1, color: "#ffffff", size: 2 },
1245      overlay: { enabled: true, color: "#000000", opacity: 0.1 }
1246    }
1247  },
1248  {
1249    id: "occasions",
1250    name: "Occasions & Broadway",
1251    tags: ["occasions", "broadway musical"],
1252    config: {
1253      type: "image",
1254      color: "#000000",
1255      gradient: { type: "linear", angle: 0, stops: [] },
1256      image: { url: "/assets/visualizers/orchestra/bkg/occasions.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 },
1257      particles: { enabled: true, type: "sparkles", count: 80, speed: 1, color: "#ffffff", size: 2 },
1258      overlay: { enabled: true, color: "#000000", opacity: 0.1 }
1259    }
1260  },
1261  {
1262    id: "original",
1263    name: "Original Composition",
1264    tags: ["original composition"],
1265    config: {
1266      type: "image",
1267      color: "#000000",
1268      gradient: { type: "linear", angle: 0, stops: [] },
1269      image: { url: "/assets/visualizers/orchestra/bkg/original.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 },
1270      particles: { enabled: true, type: "dust", count: 40, speed: 1, color: "#ffffff", size: 2 },
1271      overlay: { enabled: true, color: "#000000", opacity: 0.1 }
1272    }
1273  },
1274  {
1275    id: "pop",
1276    name: "Pop",
1277    tags: ["pop"],
1278    config: {
1279      type: "image",
1280      color: "#000000",
1281      gradient: { type: "linear", angle: 0, stops: [] },
1282      image: { url: "/assets/visualizers/orchestra/bkg/pop.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 },
1283      particles: { enabled: true, type: "confetti", count: 80, speed: 1, color: "#ffffff", size: 2 },
1284      overlay: { enabled: true, color: "#000000", opacity: 0.1 }
1285    }
1286  },
1287  {
1288    id: "reggae",
1289    name: "Reggae",
1290    tags: ["reggae"],
1291    config: {
1292      type: "image",
1293      color: "#000000",
1294      gradient: { type: "linear", angle: 0, stops: [] },
1295      image: { url: "/assets/visualizers/orchestra/bkg/reggae.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 },
1296      particles: { enabled: true, type: "fireflies", count: 10, speed: 1, color: "#ffffff", size: 2 },
1297      overlay: { enabled: true, color: "#000000", opacity: 0.1 }
1298    }
1299  },
1300  {
1301    id: "reggaeton",
1302    name: "Reggaeton",
1303    tags: ["reggaeton"],
1304    config: {
1305      type: "image",
1306      color: "#000000",
1307      gradient: { type: "linear", angle: 0, stops: [] },
1308      image: { url: "/assets/visualizers/orchestra/bkg/reggaeton.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 },
1309      particles: { enabled: true, type: "confetti", count: 80, speed: 1, color: "#ffffff", size: 2 },
1310      overlay: { enabled: true, color: "#000000", opacity: 0.1 }
1311    }
1312  },
1313  {
1314    id: "religious",
1315    name: "Religious",
1316    tags: ["religious"],
1317    config: {
1318      type: "image",
1319      color: "#000000",
1320      gradient: { type: "linear", angle: 0, stops: [] },
1321      image: { url: "/assets/visualizers/orchestra/bkg/religious.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 },
1322      particles: { enabled: true, type: "dust", count: 50, speed: 1, color: "#ffffff", size: 2 },
1323      overlay: { enabled: true, color: "#000000", opacity: 0.1 }
1324    }
1325  },
1326  {
1327    id: "rnb",
1328    name: "R&B",
1329    tags: ["r&b", "rnb"],
1330    config: {
1331      type: "image",
1332      color: "#000000",
1333      gradient: { type: "linear", angle: 0, stops: [] },
1334      image: { url: "/assets/visualizers/orchestra/bkg/rnb.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 },
1335      particles: { enabled: true, type: "hearts", count: 40, speed: 1, color: "#ffffff", size: 2 },
1336      overlay: { enabled: true, color: "#000000", opacity: 0.1 }
1337    }
1338  },
1339  {
1340    id: "rock",
1341    name: "Rock",
1342    tags: ["rock"],
1343    config: {
1344      type: "image",
1345      color: "#000000",
1346      gradient: { type: "linear", angle: 0, stops: [] },
1347      image: { url: "/assets/visualizers/orchestra/bkg/rock.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 },
1348      particles: { enabled: true, type: "laser_lines", count: 30, speed: 1, color: "#ffffff", size: 2 },
1349      overlay: { enabled: true, color: "#000000", opacity: 0.1 }
1350    }
1351  },
1352  {
1353    id: "scifi",
1354    name: "Electronic & Ambient",
1355    tags: ["electronic", "ambient"],
1356    config: {
1357      type: "image",
1358      color: "#000000",
1359      gradient: { type: "linear", angle: 0, stops: [] },
1360      image: { url: "/assets/visualizers/orchestra/bkg/sci-fi.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 },
1361      particles: { enabled: true, type: "matrix", count: 60, speed: 1, color: "#ffffff", size: 2 },
1362      overlay: { enabled: true, color: "#000000", opacity: 0.1 }
1363    }
1364  },
1365  {
1366    id: "techno",
1367    name: "Techno & EDM",
1368    tags: ["techno", "trance", "dance & edm", "house", "disco", "edm"],
1369    config: {
1370      type: "image",
1371      color: "#000000",
1372      gradient: { type: "linear", angle: 0, stops: [] },
1373      image: { url: "/assets/visualizers/orchestra/bkg/techno.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 },
1374      particles: { enabled: true, type: "laser_lines", count: 40, speed: 1, color: "#ffffff", size: 2 },
1375      overlay: { enabled: true, color: "#000000", opacity: 0.1 }
1376    }
1377  },
1378  {
1379    id: "trap",
1380    name: "Trap",
1381    tags: ["trap"],
1382    config: {
1383      type: "image",
1384      color: "#000000",
1385      gradient: { type: "linear", angle: 0, stops: [] },
1386      image: { url: "/assets/visualizers/orchestra/bkg/trap.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 },
1387      particles: { enabled: true, type: "rain", count: 120, speed: 1, color: "#ffffff", size: 2 },
1388      overlay: { enabled: true, color: "#000000", opacity: 0.1 }
1389    }
1390  },
1391  {
1392    id: "world",
1393    name: "World",
1394    tags: ["world"],
1395    config: {
1396      type: "image",
1397      color: "#000000",
1398      gradient: { type: "linear", angle: 0, stops: [] },
1399      image: { url: "/assets/visualizers/orchestra/bkg/world.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 },
1400      particles: { enabled: true, type: "fireflies", count: 70, speed: 1, color: "#ffffff", size: 2 },
1401      overlay: { enabled: true, color: "#000000", opacity: 0.1 }
1402    }
1403  }
1404];
1405const defaultClothingGraphics = {
1406  tuxedo: {
1407    id: "tuxedo",
1408    parts: {
1409      // A black tuxedo jacket over a white shirt with a bowtie
1410      body: `
1411                <g class="clothing-tuxedo-body">
1412                    <!-- White Shirt -->
1413                    <path d="M-12,-45 L12,-45 L15,20 L-15,20 Z" fill="#F8FAFC" />
1414                    <!-- Bowtie -->
1415                    <path d="M-8,-38 L0,-35 L8,-38 L8,-32 L0,-35 L-8,-32 Z" fill="#1E293B" />
1416                    <!-- Jacket Left Side -->
1417                    <path d="M-20,-45 L-5,-10 L-15,25 L-25,20 Z" fill="#0F172A" />
1418                    <!-- Jacket Right Side -->
1419                    <path d="M20,-45 L5,-10 L15,25 L25,20 Z" fill="#0F172A" />
1420                    <!-- Lapels -->
1421                    <path d="M-15,-45 L-5,-10 L-10,-45 Z" fill="#1E293B" />
1422                    <path d="M15,-45 L5,-10 L10,-45 Z" fill="#1E293B" />
1423                    <!-- Buttons -->
1424                    <circle cx="0" cy="-10" r="1.5" fill="#0F172A" />
1425                    <circle cx="0" cy="0" r="1.5" fill="#0F172A" />
1426                    <circle cx="0" cy="10" r="1.5" fill="#0F172A" />
1427                </g>
1428            `,
1429      // Tuxedo sleeves (covers horizontal arms)
1430      upper_arm_l: `
1431                <g class="clothing-tuxedo-sleeve">
1432                    <!-- Black sleeve covering upper arm (horizontal) -->
1433                    <rect x="-6" y="-6" width="42" height="12" rx="6" fill="#0F172A" />
1434                </g>
1435            `,
1436      lower_arm_l: `
1437                <g class="clothing-tuxedo-sleeve">
1438                    <!-- Black sleeve covering lower arm (horizontal) -->
1439                    <rect x="-5" y="-5" width="33" height="10" rx="5" fill="#0F172A" />
1440                    <!-- White cuff peeking out -->
1441                    <rect x="28" y="-5" width="8" height="10" rx="2" fill="#F8FAFC" />
1442                </g>
1443            `,
1444      upper_arm_r: `
1445                <g class="clothing-tuxedo-sleeve">
1446                    <!-- Black sleeve covering upper arm (horizontal) -->
1447                    <rect x="-6" y="-6" width="42" height="12" rx="6" fill="#0F172A" />
1448                </g>
1449            `,
1450      lower_arm_r: `
1451                <g class="clothing-tuxedo-sleeve">
1452                    <!-- Black sleeve covering lower arm (horizontal) -->
1453                    <rect x="-5" y="-5" width="33" height="10" rx="5" fill="#0F172A" />
1454                    <!-- White cuff peeking out -->
1455                    <rect x="28" y="-5" width="8" height="10" rx="2" fill="#F8FAFC" />
1456                </g>
1457            `,
1458      // Tuxedo pants (covers vertical legs of height 30)
1459      leg_l: `
1460                <g class="clothing-tuxedo-pants">
1461                    <rect x="-7" y="-2" width="14" height="34" rx="4" fill="#0F172A" />
1462                    <!-- Satin stripe -->
1463                    <rect x="-7" y="-2" width="3" height="34" fill="#1E293B" />
1464                </g>
1465            `,
1466      leg_r: `
1467                <g class="clothing-tuxedo-pants">
1468                    <rect x="-7" y="-2" width="14" height="34" rx="4" fill="#0F172A" />
1469                    <!-- Satin stripe -->
1470                    <rect x="4" y="-2" width="3" height="34" fill="#1E293B" />
1471                </g>
1472            `
1473    },
1474    gluing: {
1475      body: { type: "two-point", p1: { x: 0, y: -60 }, p2: { x: 0, y: 0 }, t1: "head", t2: "body" },
1476      upper_arm_l: { type: "two-point", p1: { x: 0, y: 0 }, p2: { x: 36, y: 0 }, t1: "shoulder_l", t2: "elbow_l" },
1477      upper_arm_r: { type: "two-point", p1: { x: 0, y: 0 }, p2: { x: 36, y: 0 }, t1: "shoulder_r", t2: "elbow_r" },
1478      lower_arm_l: { type: "two-point", p1: { x: 0, y: 0 }, p2: { x: 32, y: 0 }, t1: "elbow_l", t2: "hand_l" },
1479      lower_arm_r: { type: "two-point", p1: { x: 0, y: 0 }, p2: { x: 32, y: 0 }, t1: "elbow_r", t2: "hand_r" },
1480      leg_l: { type: "two-point", p1: { x: 0, y: 0 }, p2: { x: 0, y: 30 }, t1: "leg_l", t2: "foot_l" },
1481      leg_r: { type: "two-point", p1: { x: 0, y: 0 }, p2: { x: 0, y: 30 }, t1: "leg_r", t2: "foot_r" }
1482    }
1483  },
1484  dress: {
1485    id: "dress",
1486    parts: {
1487      // Elegant flowing red evening gown
1488      body: `
1489                <g class="clothing-dress-body">
1490                    <!-- Dress Bodice -->
1491                    <path d="M-15,-45 L15,-45 L12,0 L-12,0 Z" fill="#9F1239" />
1492                    <path d="M-15,-45 Q0,-35 15,-45 Z" fill="#BE123C" />
1493                    <!-- Dress Skirt (Flows down to cover top of legs) -->
1494                    <path d="M-12,0 L12,0 L25,40 L-25,40 Z" fill="#9F1239" />
1495                    <path d="M-12,0 Q0,40 12,0 Z" fill="#7F1D1D" opacity="0.3" />
1496                    <!-- Belt/Sash -->
1497                    <rect x="-12" y="-5" width="24" height="8" rx="2" fill="#FDA4AF" />
1498                </g>
1499            `,
1500      // Short sleeves covering horizontal upper arms
1501      upper_arm_l: `
1502                <g class="clothing-dress-sleeve">
1503                    <rect x="-6" y="-6" width="20" height="12" rx="6" fill="#9F1239" />
1504                </g>
1505            `,
1506      upper_arm_r: `
1507                <g class="clothing-dress-sleeve">
1508                    <rect x="-6" y="-6" width="20" height="12" rx="6" fill="#9F1239" />
1509                </g>
1510            `
1511    },
1512    gluing: {
1513      body: { type: "two-point", p1: { x: 0, y: -60 }, p2: { x: 0, y: 0 }, t1: "head", t2: "body" },
1514      upper_arm_l: { type: "one-point", p1: { x: 0, y: 0 }, t1: "upper_arm_l" },
1515      upper_arm_r: { type: "one-point", p1: { x: 0, y: 0 }, t1: "upper_arm_r" }
1516    }
1517  }
1518};
1519const grooveSway = {
1520  id: "groove_sway",
1521  name: "Groove Sway",
1522  category: "body",
1523  trigger: "playing",
1524  priority: 10,
1525  duration: 0,
1526  // continuous — driven by wave, not a fixed duration
1527  tempoSync: true,
1528  loop: true,
1529  blendInMs: 0,
1530  blendOutMs: 0,
1531  channels: [
1532    {
1533      joint: "body",
1534      property: "y",
1535      type: "wave",
1536      waveform: "sine",
1537      frequency: 0.5,
1538      // cycles per beat (bps * PI → half-cycle per beat)
1539      amplitude: 10,
1540      amplitudeBase: 2,
1541      intensityScale: 1,
1542      phase: 0,
1543      offset: 0
1544    }
1545  ],
1546  affectedJoints: ["body"]
1547};
1548const headRotation = {
1549  id: "head_rotation",
1550  name: "Head Rotation",
1551  category: "body",
1552  trigger: "playing",
1553  priority: 10,
1554  duration: 0,
1555  tempoSync: true,
1556  loop: true,
1557  blendInMs: 0,
1558  blendOutMs: 0,
1559  channels: [
1560    {
1561      joint: "head",
1562      property: "rotation",
1563      type: "wave",
1564      waveform: "sine",
1565      frequency: 0.25,
1566      // cycles per beat
1567      amplitude: 15,
1568      amplitudeBase: 5,
1569      intensityScale: 1,
1570      phase: 0,
1571      offset: 0
1572    }
1573  ],
1574  affectedJoints: ["head"]
1575};
1576const footTap = {
1577  id: "foot_tap",
1578  name: "Foot Tap",
1579  category: "body",
1580  trigger: "playing",
1581  priority: 10,
1582  duration: 0,
1583  tempoSync: true,
1584  loop: true,
1585  blendInMs: 0,
1586  blendOutMs: 0,
1587  channels: [
1588    {
1589      joint: "foot_r",
1590      property: "y",
1591      type: "wave",
1592      waveform: "abs_sine",
1593      frequency: 1,
1594      // 1 cycle per beat
1595      amplitude: -10,
1596      // negative: foot lifts up (y = -footTap)
1597      amplitudeBase: 0,
1598      intensityScale: 1,
1599      phase: 0,
1600      offset: 0
1601    },
1602    {
1603      joint: "foot_r",
1604      property: "rotation",
1605      type: "wave",
1606      waveform: "abs_sine",
1607      frequency: 1,
1608      amplitude: 20,
1609      // footTap * 2
1610      amplitudeBase: 0,
1611      intensityScale: 1,
1612      phase: 0,
1613      offset: 0
1614    }
1615  ],
1616  affectedJoints: ["foot_r"]
1617};
1618const idleNod = {
1619  id: "idle_nod",
1620  name: "Idle Nod",
1621  category: "body",
1622  trigger: "idle",
1623  priority: 5,
1624  duration: 2e3,
1625  tempoSync: false,
1626  loop: false,
1627  blendInMs: 500,
1628  blendOutMs: 500,
1629  minInterval: 1e4,
1630  maxInterval: 3e4,
1631  channels: [
1632    // Channel 1: head.rotation = sin(progress * PI * 6) * 8
1633    // 13 keyframes tracing 3 full sine cycles
1634    {
1635      joint: "head",
1636      property: "rotation",
1637      type: "keyframe",
1638      keyframes: [
1639        { time: 0 / 12, value: 0, easing: "sine" },
1640        { time: 1 / 12, value: 8, easing: "sine" },
1641        // peak 1
1642        { time: 2 / 12, value: 0, easing: "sine" },
1643        { time: 3 / 12, value: -8, easing: "sine" },
1644        // trough 1
1645        { time: 4 / 12, value: 0, easing: "sine" },
1646        { time: 5 / 12, value: 8, easing: "sine" },
1647        // peak 2
1648        { time: 6 / 12, value: 0, easing: "sine" },
1649        { time: 7 / 12, value: -8, easing: "sine" },
1650        // trough 2
1651        { time: 8 / 12, value: 0, easing: "sine" },
1652        { time: 9 / 12, value: 8, easing: "sine" },
1653        // peak 3
1654        { time: 10 / 12, value: 0, easing: "sine" },
1655        { time: 11 / 12, value: -8, easing: "sine" },
1656        // trough 3
1657        { time: 1, value: 0, easing: "sine" }
1658      ]
1659    },
1660    // Channel 2: head.y = max(0, nodCycle) * 3
1661    // Only positive half-cycles contribute (peaks → 3, everything else → 0)
1662    {
1663      joint: "head",
1664      property: "y",
1665      type: "keyframe",
1666      keyframes: [
1667        { time: 0 / 12, value: 0, easing: "sine" },
1668        { time: 1 / 12, value: 3, easing: "sine" },
1669        // positive peak 1
1670        { time: 2 / 12, value: 0, easing: "sine" },
1671        { time: 3 / 12, value: 0, easing: "linear" },
1672        // trough (clamped to 0)
1673        { time: 4 / 12, value: 0, easing: "sine" },
1674        { time: 5 / 12, value: 3, easing: "sine" },
1675        // positive peak 2
1676        { time: 6 / 12, value: 0, easing: "sine" },
1677        { time: 7 / 12, value: 0, easing: "linear" },
1678        // trough (clamped to 0)
1679        { time: 8 / 12, value: 0, easing: "sine" },
1680        { time: 9 / 12, value: 3, easing: "sine" },
1681        // positive peak 3
1682        { time: 10 / 12, value: 0, easing: "sine" },
1683        { time: 11 / 12, value: 0, easing: "linear" },
1684        // trough (clamped to 0)
1685        { time: 1, value: 0, easing: "sine" }
1686      ]
1687    }
1688  ],
1689  affectedJoints: ["head"]
1690};
1691const idleWave = {
1692  id: "idle_wave",
1693  name: "Idle Wave",
1694  category: "body",
1695  trigger: "idle",
1696  priority: 5,
1697  duration: 3e3,
1698  tempoSync: false,
1699  loop: false,
1700  blendInMs: 500,
1701  blendOutMs: 500,
1702  minInterval: 1e4,
1703  maxInterval: 3e4,
1704  controlsIk: true,
1705  // ikSide is determined at runtime based on instrument mount point
1706  channels: [
1707    // Single wave channel encoding the forearm oscillation.
1708    // The action player interprets '_ik_wave' as a virtual joint that
1709    // drives the IK wave solver rather than a physical joint directly.
1710    //   offset  = 45°  (base wave angle, midpoint of 30°–90° random range)
1711    //   amplitude = 25° (oscillation ±25° around the base angle)
1712    //   frequency = 2.39 Hz (absolute, not tempo-synced)
1713    {
1714      joint: "_ik_wave",
1715      property: "rotation",
1716      type: "wave",
1717      waveform: "sine",
1718      frequency: 2.39,
1719      // ~15/(2*PI) Hz — absolute (tempoSync=false)
1720      amplitude: 25,
1721      amplitudeBase: 0,
1722      intensityScale: 0,
1723      // wave oscillation is independent of intensity
1724      phase: 0,
1725      offset: 45
1726      // base angle in degrees
1727    }
1728  ],
1729  affectedJoints: [
1730    "upper_arm_l",
1731    "elbow_l",
1732    "lower_arm_l",
1733    "hand_l",
1734    "upper_arm_r",
1735    "elbow_r",
1736    "lower_arm_r",
1737    "hand_r"
1738  ]
1739};
1740const defaultBodyActions = [
1741  grooveSway,
1742  headRotation,
1743  footTap,
1744  idleNod,
1745  idleWave
1746];
1747function getDefaultBodyActions() {
1748  return JSON.parse(JSON.stringify(defaultBodyActions));
1749}
1750const defaultActionRules = [
1751  {
1752    id: "default_body_all",
1753    name: "Default Body Actions",
1754    actionIds: ["groove_sway", "head_rotation", "foot_tap", "idle_nod", "idle_wave"],
1755    enabled: true,
1756    condition: { scope: "all" }
1757  }
1758];
1759function multiply(A, B) {
1760  return [
1761    A[0] * B[0] + A[2] * B[1],
1762    A[1] * B[0] + A[3] * B[1],
1763    A[0] * B[2] + A[2] * B[3],
1764    A[1] * B[2] + A[3] * B[3],
1765    A[0] * B[4] + A[2] * B[5] + A[4],
1766    A[1] * B[4] + A[3] * B[5] + A[5]
1767  ];
1768}
1769function transformPoint(pt, M) {
1770  return {
1771    x: M[0] * pt.x + M[2] * pt.y + M[4],
1772    y: M[1] * pt.x + M[3] * pt.y + M[5]
1773  };
1774}
1775function transformPointWithStatic(pt, transform) {
1776  if (!transform) return pt;
1777  const sx = transform.scaleX ?? transform.scale ?? 1;
1778  const sy = transform.scaleY ?? transform.scale ?? 1;
1779  const rot = transform.rotation ?? 0;
1780  const tx = transform.x ?? 0;
1781  const ty = transform.y ?? 0;
1782  const rad = rot * Math.PI / 180;
1783  const cos = Math.cos(rad);
1784  const sin = Math.sin(rad);
1785  const rx = pt.x * cos - pt.y * sin;
1786  const ry = pt.x * sin + pt.y * cos;
1787  return {
1788    x: tx + rx * sx,
1789    y: ty + ry * sy
1790  };
1791}
1792function parseTransformAttribute(transformStr) {
1793  let mat = [1, 0, 0, 1, 0, 0];
1794  if (!transformStr) return mat;
1795  const regex = /([a-zA-Z]+)\s*\(([^)]+)\)/g;
1796  let match;
1797  while ((match = regex.exec(transformStr)) !== null) {
1798    const type = match[1].toLowerCase();
1799    const args = match[2].split(/[\s,]+/).map(parseFloat).filter((v) => !isNaN(v));
1800    let commandMat = [1, 0, 0, 1, 0, 0];
1801    if (type === "translate") {
1802      const tx = args[0] || 0;
1803      const ty = args[1] || 0;
1804      commandMat = [1, 0, 0, 1, tx, ty];
1805    } else if (type === "scale") {
1806      const sx = args[0] !== void 0 ? args[0] : 1;
1807      const sy = args[1] !== void 0 ? args[1] : sx;
1808      commandMat = [sx, 0, 0, sy, 0, 0];
1809    } else if (type === "rotate") {
1810      const deg = args[0] || 0;
1811      const rad = deg * Math.PI / 180;
1812      const cos = Math.cos(rad);
1813      const sin = Math.sin(rad);
1814      if (args.length >= 3) {
1815        const cx = args[1];
1816        const cy = args[2];
1817        const m1 = [1, 0, 0, 1, cx, cy];
1818        const m2 = [cos, sin, -sin, cos, 0, 0];
1819        const m3 = [1, 0, 0, 1, -cx, -cy];
1820        commandMat = multiply(m1, multiply(m2, m3));
1821      } else {
1822        commandMat = [cos, sin, -sin, cos, 0, 0];
1823      }
1824    } else if (type === "matrix") {
1825      if (args.length === 6) {
1826        commandMat = args;
1827      }
1828    }
1829    mat = multiply(mat, commandMat);
1830  }
1831  return mat;
1832}
1833function getSvgJointBasePositions(svgContent) {
1834  const bases = {};
1835  if (!svgContent) return bases;
1836  try {
1837    const parser = new DOMParser();
1838    const doc = parser.parseFromString(`<svg>${svgContent}</svg>`, "image/svg+xml");
1839    const jointIds = ["head", "body", "shoulder_l", "elbow_l", "hand_l", "shoulder_r", "elbow_r", "hand_r"];
1840    jointIds.forEach((id) => {
1841      const el = doc.querySelector(`#${id}`);
1842      if (el) {
1843        const transformMats = [];
1844        let current = el;
1845        while (current && current.tagName !== "svg") {
1846          const transformAttr = current.getAttribute("transform");
1847          if (transformAttr) {
1848            transformMats.push(parseTransformAttribute(transformAttr));
1849          }
1850          current = current.parentElement;
1851        }
1852        let M = [1, 0, 0, 1, 0, 0];
1853        for (let i = transformMats.length - 1; i >= 0; i--) {
1854          M = multiply(M, transformMats[i]);
1855        }
1856        bases[id] = transformPoint({ x: 0, y: 0 }, M);
1857      }
1858    });
1859  } catch (e) {
1860    console.warn("Failed to parse SVG joint base positions:", e);
1861  }
1862  return bases;
1863}
1864function parseSvgSocketsAndPlugs(svgContent) {
1865  const coords = {};
1866  if (!svgContent) return coords;
1867  try {
1868    const parser = new DOMParser();
1869    const doc = parser.parseFromString(`<svg>${svgContent}</svg>`, "image/svg+xml");
1870    const elements = doc.querySelectorAll("*");
1871    elements.forEach((el) => {
1872      const id = el.getAttribute("id");
1873      if (id && (id.startsWith("socket_") || id.startsWith("plug_"))) {
1874        let localPt = null;
1875        const cxAttr = el.getAttribute("cx");
1876        const cyAttr = el.getAttribute("cy");
1877        if (cxAttr !== null && cyAttr !== null) {
1878          localPt = { x: parseFloat(cxAttr), y: parseFloat(cyAttr) };
1879        } else {
1880          const xAttr = el.getAttribute("x");
1881          const yAttr = el.getAttribute("y");
1882          if (xAttr !== null && yAttr !== null) {
1883            localPt = { x: parseFloat(xAttr), y: parseFloat(yAttr) };
1884          } else {
1885            localPt = { x: 0, y: 0 };
1886          }
1887        }
1888        const transformMats = [];
1889        let current = el;
1890        while (current && current.tagName !== "svg") {
1891          const transformAttr = current.getAttribute("transform");
1892          if (transformAttr) {
1893            transformMats.push(parseTransformAttribute(transformAttr));
1894          }
1895          current = current.parentElement;
1896        }
1897        let M = [1, 0, 0, 1, 0, 0];
1898        for (let i = transformMats.length - 1; i >= 0; i--) {
1899          M = multiply(M, transformMats[i]);
1900        }
1901        coords[id] = transformPoint(localPt, M);
1902      }
1903    });
1904  } catch (e) {
1905    console.warn("Failed to parse SVG sockets/plugs:", e);
1906  }
1907  return coords;
1908}
1909class ActionEngine {
1910  constructor() {
1911    this.actions = [];
1912    this.activeActions = /* @__PURE__ */ new Map();
1913    this.idleSchedules = /* @__PURE__ */ new Map();
1914    this.initialized = false;
1915    this.forceActionId = null;
1916    this.disableRandomIdle = false;
1917  }
1918  /** Replace the current action set */
1919  setActions(actions) {
1920    this.actions = actions;
1921    for (const [id] of this.activeActions) {
1922      if (!actions.find((a) => a.id === id)) {
1923        this.activeActions.delete(id);
1924      }
1925    }
1926    for (const action of actions) {
1927      if (action.trigger === "idle" && !this.idleSchedules.has(action.id)) {
1928        this.idleSchedules.set(action.id, {
1929          actionId: action.id,
1930          nextFireTime: 0
1931          // will be set on first tick
1932        });
1933      }
1934    }
1935  }
1936  getActions() {
1937    return this.actions;
1938  }
1939  getActiveActions() {
1940    return this.activeActions;
1941  }
1942  /**
1943   * Evaluate all actions for the current frame.
1944   * Returns a map of joint.property → delta value to apply.
1945   */
1946  tick(time, bps, intensity, isSongPlaying, hasActiveNotes, bodyBehaviorMults, idleBehaviorMults) {
1947    const deltas = {};
1948    if (!this.initialized && this.actions.length > 0) {
1949      this.initialized = true;
1950      for (const action of this.actions) {
1951        if (action.trigger === "idle") {
1952          const schedule = this.idleSchedules.get(action.id);
1953          if (schedule && schedule.nextFireTime === 0) {
1954            schedule.nextFireTime = time + 2e3 + Math.random() * 8e3;
1955          }
1956        }
1957      }
1958    }
1959    if (this.forceActionId) {
1960      for (const [id] of this.activeActions) {
1961        if (id !== this.forceActionId) {
1962          this.activeActions.delete(id);
1963        }
1964      }
1965      const action = this.actions.find((a) => a.id === this.forceActionId);
1966      if (action) {
1967        let existing = this.activeActions.get(this.forceActionId);
1968        if (!existing) {
1969          const durMs = action.tempoSync ? action.duration / bps * 1e3 : action.duration;
1970          existing = {
1971            def: action,
1972            startTime: time,
1973            duration: durMs,
1974            isActive: true,
1975            blend: 1
1976          };
1977          this.activeActions.set(this.forceActionId, existing);
1978        } else {
1979          existing.isActive = true;
1980          existing.def = action;
1981          existing.duration = action.tempoSync ? action.duration / bps * 1e3 : action.duration;
1982        }
1983      }
1984    } else {
1985      for (const action of this.actions) {
1986        const shouldBeActive = this.shouldActivate(action, time, isSongPlaying, hasActiveNotes);
1987        const existing = this.activeActions.get(action.id);
1988        if (shouldBeActive && !existing) {
1989          const durMs = action.tempoSync ? action.duration / bps * 1e3 : action.duration;
1990          this.activeActions.set(action.id, {
1991            def: action,
1992            startTime: time,
1993            duration: durMs,
1994            isActive: true,
1995            blend: 0
1996          });
1997        } else if (!shouldBeActive && existing && existing.isActive) {
1998          existing.isActive = false;
1999        }
2000      }
2001    }
2002    const sorted = [...this.activeActions.entries()].sort((a, b) => a[1].def.priority - b[1].def.priority);
2003    for (const [id, active] of sorted) {
2004      const elapsed = time - active.startTime;
2005      const { def } = active;
2006      if (!def.loop && elapsed >= active.duration + def.blendOutMs) {
2007        this.activeActions.delete(id);
2008        if (def.trigger === "idle") {
2009          const schedule = this.idleSchedules.get(id);
2010          if (schedule) {
2011            const minInt = def.minInterval ?? 1e4;
2012            const maxInt = def.maxInterval ?? 3e4;
2013            schedule.nextFireTime = time + minInt + Math.random() * (maxInt - minInt);
2014          }
2015        }
2016        continue;
2017      }
2018      let blend = 1;
2019      if (active.isActive) {
2020        if (elapsed < def.blendInMs && def.blendInMs > 0) {
2021          blend = elapsed / def.blendInMs;
2022        }
2023      } else {
2024        blend = Math.max(0, active.blend - 1 / Math.max(1, def.blendOutMs) * 16.67);
2025        if (blend <= 0) {
2026          this.activeActions.delete(id);
2027          continue;
2028        }
2029      }
2030      active.blend = blend;
2031      let progress;
2032      if (def.loop) {
2033        progress = elapsed % active.duration / active.duration;
2034      } else {
2035        progress = Math.min(1, elapsed / active.duration);
2036      }
2037      let behaviorMult = 1;
2038      if (bodyBehaviorMults) {
2039        if (def.id === "groove_sway") behaviorMult = bodyBehaviorMults.grooveSway ?? 1;
2040        else if (def.id === "head_rotation") behaviorMult = bodyBehaviorMults.headRotation ?? 1;
2041        else if (def.id === "foot_tap") behaviorMult = bodyBehaviorMults.footTap ?? 1;
2042      }
2043      for (const channel of def.channels) {
2044        const key = `${channel.joint}.${channel.property}`;
2045        let value = 0;
2046        if (channel.type === "wave") {
2047          value = this.evaluateWaveChannel(channel, time, bps, intensity, def.tempoSync);
2048        } else if (channel.type === "keyframe") {
2049          value = this.evaluateKeyframeChannel(channel, progress);
2050        }
2051        let channelMult = behaviorMult;
2052        if (def.trigger === "idle" && idleBehaviorMults) {
2053          if (channel.joint === "head") channelMult *= idleBehaviorMults.headSway ?? 1;
2054          else if (channel.joint === "body") channelMult *= idleBehaviorMults.bodySway ?? 1;
2055          else if (channel.joint.includes("arm") || channel.joint.includes("hand")) channelMult *= idleBehaviorMults.armSway ?? 1;
2056        }
2057        value *= blend * channelMult;
2058        deltas[key] = (deltas[key] || 0) + value;
2059      }
2060    }
2061    return deltas;
2062  }
2063  /** Check whether an idle_wave action is currently active */
2064  isIdleWaveActive() {
2065    for (const [, active] of this.activeActions) {
2066      if (active.def.controlsIk && active.isActive && active.blend > 0.01) {
2067        return true;
2068      }
2069    }
2070    return false;
2071  }
2072  /** Get the current wave oscillation data for IK wave actions */
2073  getIdleWaveData(time) {
2074    for (const [, active] of this.activeActions) {
2075      if (!active.def.controlsIk || active.blend < 0.01) continue;
2076      const waveChannel = active.def.channels.find((c) => c.joint === "_ik_wave");
2077      if (!waveChannel) continue;
2078      const elapsed = time - active.startTime;
2079      const baseAngle = (waveChannel.offset ?? 45) * Math.PI / 180;
2080      const amplitude = (waveChannel.amplitude ?? 25) * Math.PI / 180;
2081      const freq = waveChannel.frequency ?? 2.39;
2082      const waveTheta = baseAngle + Math.sin(elapsed * 1e-3 * freq * Math.PI * 2) * amplitude;
2083      return {
2084        blend: active.blend,
2085        waveTheta,
2086        side: active.def.ikSide || "r"
2087      };
2088    }
2089    return null;
2090  }
2091  /** Force-start an action (used by forceWave in PuppetStudio) */
2092  forceAction(actionId, time, bps) {
2093    const action = this.actions.find((a) => a.id === actionId);
2094    if (!action) return;
2095    const existing = this.activeActions.get(actionId);
2096    if (existing) return;
2097    const durMs = action.tempoSync ? action.duration / bps * 1e3 : action.duration;
2098    this.activeActions.set(actionId, {
2099      def: action,
2100      startTime: time,
2101      duration: durMs,
2102      isActive: true,
2103      blend: 0
2104    });
2105  }
2106  /** Stop all actions (used when song state changes) */
2107  clearActive() {
2108    for (const [, active] of this.activeActions) {
2109      active.isActive = false;
2110    }
2111  }
2112  shouldActivate(action, time, isSongPlaying, hasActiveNotes) {
2113    switch (action.trigger) {
2114      case "playing":
2115        return isSongPlaying;
2116      case "idle":
2117        if (isSongPlaying || this.disableRandomIdle) return false;
2118        const schedule = this.idleSchedules.get(action.id);
2119        if (!schedule) return false;
2120        if (time >= schedule.nextFireTime && !this.activeActions.has(action.id)) {
2121          return true;
2122        }
2123        return this.activeActions.has(action.id);
2124      case "note_on":
2125        return hasActiveNotes;
2126      case "always":
2127        return true;
2128      default:
2129        return false;
2130    }
2131  }
2132  evaluateWaveChannel(channel, time, bps, intensity, tempoSync) {
2133    const freq = channel.frequency ?? 1;
2134    const amplitude = channel.amplitude ?? 1;
2135    const amplitudeBase = channel.amplitudeBase ?? 0;
2136    const phase = channel.phase ?? 0;
2137    const offset = channel.offset ?? 0;
2138    const intensityScale = channel.intensityScale ?? 0;
2139    let t;
2140    if (tempoSync) {
2141      t = time / 1e3 * bps * Math.PI * 2 * freq + phase;
2142    } else {
2143      t = time / 1e3 * Math.PI * 2 * freq + phase;
2144    }
2145    let wave;
2146    switch (channel.waveform ?? "sine") {
2147      case "sine":
2148        wave = Math.sin(t);
2149        break;
2150      case "cosine":
2151        wave = Math.cos(t);
2152        break;
2153      case "triangle":
2154        wave = 2 / Math.PI * Math.asin(Math.sin(t));
2155        break;
2156      case "abs_sine":
2157        wave = Math.max(0, Math.sin(t));
2158        break;
2159      default:
2160        wave = Math.sin(t);
2161    }
2162    let effectiveAmplitude;
2163    if (intensityScale > 0) {
2164      effectiveAmplitude = amplitudeBase + amplitude * intensity * intensityScale;
2165    } else {
2166      effectiveAmplitude = amplitude;
2167    }
2168    return wave * effectiveAmplitude + offset;
2169  }
2170  evaluateKeyframeChannel(channel, progress) {
2171    const kfs = channel.keyframes;
2172    if (!kfs || kfs.length === 0) return 0;
2173    if (kfs.length === 1) return kfs[0].value;
2174    let prev = kfs[0];
2175    let next = kfs[kfs.length - 1];
2176    for (let i = 0; i < kfs.length - 1; i++) {
2177      if (progress >= kfs[i].time && progress <= kfs[i + 1].time) {
2178        prev = kfs[i];
2179        next = kfs[i + 1];
2180        break;
2181      }
2182    }
2183    if (prev === next) return prev.value;
2184    const segmentDuration = next.time - prev.time;
2185    if (segmentDuration <= 0) return prev.value;
2186    let t = (progress - prev.time) / segmentDuration;
2187    const easing = next.easing ?? "sine";
2188    switch (easing) {
2189      case "linear":
2190        break;
2191      case "sine":
2192        t = 0.5 - 0.5 * Math.cos(t * Math.PI);
2193        break;
2194      case "ease-in-out":
2195        t = t < 0.5 ? 2 * t * t : 1 - 2 * (1 - t) * (1 - t);
2196        break;
2197    }
2198    return prev.value + (next.value - prev.value) * t;
2199  }
2200}
2201class BaseAnimator {
2202  // true when custom or default actions are loaded
2203  constructor(config) {
2204    var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t;
2205    this.baseSwayOffset = 0;
2206    this.sockets = {};
2207    this.jointBases = {};
2208    this.idleAction = "none";
2209    this.idleActionStartTime = 0;
2210    this.idleActionDuration = 0;
2211    this.nextIdleActionTime = 0;
2212    this.idleWaveAngle = 0;
2213    this.lastInstrumentDef = null;
2214    this.resolvedTargetL = { x: -30, y: 50 };
2215    this.resolvedTargetR = { x: 30, y: 50 };
2216    this.actionEngine = new ActionEngine();
2217    this.useActionEngine = false;
2218    this.config = config;
2219    this.jointBases = getSvgJointBasePositions(config.svgContent);
2220    const svgCoords = parseSvgSocketsAndPlugs(config.svgContent);
2221    const sockets = {};
2222    Object.entries(svgCoords).forEach(([key, pt]) => {
2223      if (key.startsWith("socket_")) {
2224        sockets[key.replace("socket_", "")] = pt;
2225      }
2226    });
2227    if (config.sockets) {
2228      Object.entries(config.sockets).forEach(([key, pt]) => {
2229        sockets[key] = pt;
2230      });
2231    }
2232    this.sockets = sockets;
2233    const shoulderL = sockets.shoulder_l || { x: ((_a = config.ikLeftArm) == null ? void 0 : _a.originX) ?? -30, y: ((_b = config.ikLeftArm) == null ? void 0 : _b.originY) ?? -20 };
2234    const shoulderR = sockets.shoulder_r || { x: ((_c = config.ikRightArm) == null ? void 0 : _c.originX) ?? 30, y: ((_d = config.ikRightArm) == null ? void 0 : _d.originY) ?? -20 };
2235    let upperArmLengthL = ((_e = config.ikLeftArm) == null ? void 0 : _e.upperArmLength) || 40;
2236    let lowerArmLengthL = ((_f = config.ikLeftArm) == null ? void 0 : _f.lowerArmLength) || 40;
2237    if (sockets.shoulder_l && sockets.elbow_l) {
2238      const dx = sockets.elbow_l.x - sockets.shoulder_l.x;
2239      const dy = sockets.elbow_l.y - sockets.shoulder_l.y;
2240      upperArmLengthL = Math.sqrt(dx * dx + dy * dy);
2241    }
2242    if (sockets.elbow_l && sockets.hand_l) {
2243      const dx = sockets.hand_l.x - sockets.elbow_l.x;
2244      const dy = sockets.hand_l.y - sockets.elbow_l.y;
2245      lowerArmLengthL = Math.sqrt(dx * dx + dy * dy);
2246    }
2247    let upperArmLengthR = ((_g = config.ikRightArm) == null ? void 0 : _g.upperArmLength) || 40;
2248    let lowerArmLengthR = ((_h = config.ikRightArm) == null ? void 0 : _h.lowerArmLength) || 40;
2249    if (sockets.shoulder_r && sockets.elbow_r) {
2250      const dx = sockets.elbow_r.x - sockets.shoulder_r.x;
2251      const dy = sockets.elbow_r.y - sockets.shoulder_r.y;
2252      upperArmLengthR = Math.sqrt(dx * dx + dy * dy);
2253    }
2254    if (sockets.elbow_r && sockets.hand_r) {
2255      const dx = sockets.hand_r.x - sockets.elbow_r.x;
2256      const dy = sockets.hand_r.y - sockets.elbow_r.y;
2257      lowerArmLengthR = Math.sqrt(dx * dx + dy * dy);
2258    }
2259    this.config.ikLeftArm = {
2260      ...config.ikLeftArm,
2261      originX: shoulderL.x,
2262      originY: shoulderL.y,
2263      upperArmLength: upperArmLengthL,
2264      lowerArmLength: lowerArmLengthL
2265    };
2266    this.config.ikRightArm = {
2267      ...config.ikRightArm,
2268      originX: shoulderR.x,
2269      originY: shoulderR.y,
2270      upperArmLength: upperArmLengthR,
2271      lowerArmLength: lowerArmLengthR
2272    };
2273    this.state = {
2274      intensity: 0,
2275      isPlaying: false,
2276      joints: {
2277        head: { x: 0, y: 0, rotation: 0 },
2278        body: { x: 0, y: 0, rotation: 0 },
2279        shoulder_l: { x: shoulderL.x, y: shoulderL.y, rotation: 0 },
2280        upper_arm_l: { x: shoulderL.x, y: shoulderL.y, rotation: 0 },
2281        elbow_l: { x: ((_i = sockets.elbow_l) == null ? void 0 : _i.x) ?? -30, y: ((_j = sockets.elbow_l) == null ? void 0 : _j.y) ?? 20, rotation: 0 },
2282        lower_arm_l: { x: ((_k = sockets.elbow_l) == null ? void 0 : _k.x) ?? -30, y: ((_l = sockets.elbow_l) == null ? void 0 : _l.y) ?? 20, rotation: 0 },
2283        hand_l: { x: ((_m = sockets.hand_l) == null ? void 0 : _m.x) ?? -30, y: ((_n = sockets.hand_l) == null ? void 0 : _n.y) ?? 50, rotation: 0 },
2284        shoulder_r: { x: shoulderR.x, y: shoulderR.y, rotation: 0 },
2285        upper_arm_r: { x: shoulderR.x, y: shoulderR.y, rotation: 0 },
2286        elbow_r: { x: ((_o = sockets.elbow_r) == null ? void 0 : _o.x) ?? 30, y: ((_p = sockets.elbow_r) == null ? void 0 : _p.y) ?? 20, rotation: 0 },
2287        lower_arm_r: { x: ((_q = sockets.elbow_r) == null ? void 0 : _q.x) ?? 30, y: ((_r = sockets.elbow_r) == null ? void 0 : _r.y) ?? 20, rotation: 0 },
2288        hand_r: { x: ((_s = sockets.hand_r) == null ? void 0 : _s.x) ?? 30, y: ((_t = sockets.hand_r) == null ? void 0 : _t.y) ?? 50, rotation: 0 },
2289        leg_l: { x: 0, y: 0, rotation: 0 },
2290        foot_l: { x: 0, y: 0, rotation: 0 },
2291        leg_r: { x: 0, y: 0, rotation: 0 },
2292        foot_r: { x: 0, y: 0, rotation: 0 }
2293      }
2294    };
2295    const customActions = config.actions;
2296    if (customActions && customActions.length > 0) {
2297      this.actionEngine.setActions(customActions);
2298      this.useActionEngine = true;
2299    } else {
2300      this.actionEngine.setActions(getDefaultBodyActions());
2301      this.useActionEngine = true;
2302    }
2303  }
2304  getState() {
2305    return this.state;
2306  }
2307  syncShoulders() {
2308    var _a, _b, _c, _d, _e, _f, _g, _h;
2309    this.state.joints.shoulder_l.x = ((_a = this.config.ikLeftArm) == null ? void 0 : _a.originX) ?? -30;
2310    this.state.joints.shoulder_l.y = ((_b = this.config.ikLeftArm) == null ? void 0 : _b.originY) ?? -20;
2311    this.state.joints.upper_arm_l.x = ((_c = this.config.ikLeftArm) == null ? void 0 : _c.originX) ?? -30;
2312    this.state.joints.upper_arm_l.y = ((_d = this.config.ikLeftArm) == null ? void 0 : _d.originY) ?? -20;
2313    this.state.joints.shoulder_r.x = ((_e = this.config.ikRightArm) == null ? void 0 : _e.originX) ?? 30;
2314    this.state.joints.shoulder_r.y = ((_f = this.config.ikRightArm) == null ? void 0 : _f.originY) ?? -20;
2315    this.state.joints.upper_arm_r.x = ((_g = this.config.ikRightArm) == null ? void 0 : _g.originX) ?? 30;
2316    this.state.joints.upper_arm_r.y = ((_h = this.config.ikRightArm) == null ? void 0 : _h.originY) ?? -20;
2317  }
2318  updateBaseState(time, activeNotes, bps, isSongPlaying, instrumentDef) {
2319    var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
2320    this.syncShoulders();
2321    this.state.isPlaying = activeNotes.length > 0;
2322    if (instrumentDef !== this.lastInstrumentDef) {
2323      this.prevOffsetL = void 0;
2324      this.prevOffsetR = void 0;
2325    }
2326    this.lastInstrumentDef = instrumentDef || null;
2327    if (this.state.intensity > 0) {
2328      this.state.intensity -= 0.05;
2329      if (this.state.intensity < 0) this.state.intensity = 0;
2330    }
2331    activeNotes.forEach((note) => {
2332      const vel = note.velocity / 127;
2333      if (vel > this.state.intensity) {
2334        this.state.intensity = vel;
2335      }
2336    });
2337    this.state.joints.head.x = 0;
2338    this.state.joints.head.y = 0;
2339    this.state.joints.body.y = 0;
2340    this.state.joints.leg_l.x = 0;
2341    this.state.joints.leg_l.y = 0;
2342    this.state.joints.leg_r.x = 0;
2343    this.state.joints.leg_r.y = 0;
2344    this.state.joints.foot_l.x = 0;
2345    this.state.joints.foot_l.y = 0;
2346    this.state.joints.foot_r.x = 0;
2347    this.state.joints.foot_r.y = 0;
2348    this.state.joints.foot_r.rotation = 0;
2349    this.state.joints.head.rotation = 0;
2350    if (this.useActionEngine) {
2351      this.actionEngine.disableRandomIdle = !!this.disableRandomIdle;
2352      const bodyBehaviorMults = {
2353        grooveSway: ((_a = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _a.grooveSway) ?? 1,
2354        headRotation: ((_b = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _b.headRotation) ?? 1,
2355        footTap: ((_c = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _c.footTap) ?? 1
2356      };
2357      const idleBehaviorMults = {
2358        bodySway: ((_d = instrumentDef == null ? void 0 : instrumentDef.idleBehavior) == null ? void 0 : _d.bodySway) ?? 1,
2359        headSway: ((_e = instrumentDef == null ? void 0 : instrumentDef.idleBehavior) == null ? void 0 : _e.headSway) ?? 1,
2360        armSway: ((_f = instrumentDef == null ? void 0 : instrumentDef.idleBehavior) == null ? void 0 : _f.armSway) ?? 1
2361      };
2362      const previewIntensity = this.actionEngine.forceActionId ? 1 : isSongPlaying ? Math.max(0.5, this.state.intensity) : this.state.intensity;
2363      const deltas = this.actionEngine.tick(
2364        time,
2365        bps,
2366        previewIntensity,
2367        isSongPlaying,
2368        activeNotes.length > 0,
2369        bodyBehaviorMults,
2370        idleBehaviorMults
2371      );
2372      for (const [key, value] of Object.entries(deltas)) {
2373        const [jointName, prop] = key.split(".");
2374        if (jointName.startsWith("_")) continue;
2375        const joint = this.state.joints[jointName];
2376        if (joint && (prop === "x" || prop === "y" || prop === "rotation")) {
2377          joint[prop] += value;
2378        }
2379      }
2380    } else {
2381      const grooveSwayMult = ((_g = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _g.grooveSway) ?? 1;
2382      const headRotationMult = ((_h = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _h.headRotation) ?? 1;
2383      const footTapMult = ((_i = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _i.footTap) ?? 1;
2384      if (isSongPlaying) {
2385        const grooveSway2 = Math.sin(time / 1e3 * bps * Math.PI) * (2 + this.state.intensity * 10) * grooveSwayMult;
2386        const headRotation2 = Math.sin(time / 1e3 * bps * Math.PI * 0.5) * (5 + this.state.intensity * 15) * headRotationMult;
2387        const footTap2 = Math.max(0, Math.sin(time / 1e3 * bps * Math.PI * 2)) * 10 * this.state.intensity * footTapMult;
2388        this.state.joints.body.y = grooveSway2;
2389        this.state.joints.head.rotation = headRotation2;
2390        this.state.joints.foot_r.y = -footTap2;
2391        this.state.joints.foot_r.rotation = footTap2 * 2;
2392      }
2393    }
2394    this.state.joints.shoulder_l.x += this.state.joints.body.x;
2395    this.state.joints.shoulder_l.y += this.state.joints.body.y;
2396    this.state.joints.shoulder_r.x += this.state.joints.body.x;
2397    this.state.joints.shoulder_r.y += this.state.joints.body.y;
2398    this.state.joints.head.x += this.state.joints.body.x;
2399    this.state.joints.head.y += this.state.joints.body.y;
2400    this.state.joints.leg_l.x += this.state.joints.body.x;
2401    this.state.joints.leg_l.y += this.state.joints.body.y;
2402    this.state.joints.leg_r.x += this.state.joints.body.x;
2403    this.state.joints.leg_r.y += this.state.joints.body.y;
2404    this.state.joints.foot_l.x += this.state.joints.body.x;
2405    this.state.joints.foot_l.y += this.state.joints.body.y;
2406    this.state.joints.foot_r.x += this.state.joints.body.x;
2407    this.state.joints.foot_r.y += this.state.joints.body.y;
2408    if (activeNotes.length > 0) {
2409      const now = time;
2410      const bowGapMs = ((_j = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _j.bowGapMs) ?? 500;
2411      const bowPhaseStep = ((_k = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _k.bowPhaseStep) ?? 0.05;
2412      if (!this.state.lastNoteTime || now - this.state.lastNoteTime > bowGapMs) {
2413        this.state.bowDirection = (this.state.bowDirection || 1) === 1 ? -1 : 1;
2414        this.state.bowPhase = this.state.bowDirection === 1 ? 0 : 1;
2415      } else {
2416        this.state.bowPhase = (this.state.bowPhase || 0) + (this.state.bowDirection === 1 ? bowPhaseStep : -bowPhaseStep);
2417        if (this.state.bowPhase > 1) {
2418          this.state.bowPhase = 1;
2419          this.state.bowDirection = -1;
2420        }
2421        if (this.state.bowPhase < 0) {
2422          this.state.bowPhase = 0;
2423          this.state.bowDirection = 1;
2424        }
2425      }
2426      this.state.lastNoteTime = now;
2427    }
2428  }
2429  getTargetFromMotion(motion2, hand, lowest, highest, bps, time, instrumentDef) {
2430    var _a, _b;
2431    if (motion2.logic === "none") return { ...motion2.start };
2432    let t = 0;
2433    if (this.state.isPlaying) {
2434      if (motion2.logic === "oscillate") {
2435        t = (Math.sin(time / 1e3 * 0.01 * bps) + 1) / 2;
2436      } else if (motion2.logic === "strike") {
2437        t = this.state.intensity;
2438      } else if (motion2.logic === "pitch_map") {
2439        const minNote = ((_a = instrumentDef == null ? void 0 : instrumentDef.noteRange) == null ? void 0 : _a.minNote) ?? 40;
2440        const maxNote = ((_b = instrumentDef == null ? void 0 : instrumentDef.noteRange) == null ? void 0 : _b.maxNote) ?? 88;
2441        const noteVal = hand === "l" ? lowest : highest;
2442        t = Math.max(0, Math.min(1, (noteVal - minNote) / (maxNote - minNote)));
2443      } else if (motion2.logic === "phrase_bow") {
2444        t = this.state.bowPhase || 0;
2445      }
2446    } else {
2447      if (motion2.logic === "phrase_bow") t = this.state.bowPhase || 0;
2448      else t = 0;
2449    }
2450    return {
2451      x: this.lerp(motion2.start.x, motion2.end.x, t),
2452      y: this.lerp(motion2.start.y, motion2.end.y, t)
2453    };
2454  }
2455  resolveAndApplyIk(targetL, targetR, instrumentDef, time, isSongPlaying, defaultLerpL = 0.3, defaultLerpR = 0.3) {
2456    var _a, _b, _c, _d, _e, _f, _g, _h, _i;
2457    targetL = { ...targetL };
2458    targetR = { ...targetR };
2459    const lerpL = ((_a = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _a.ikLerpL) ?? defaultLerpL;
2460    const lerpR = ((_b = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _b.ikLerpR) ?? defaultLerpR;
2461    let anchorWorldL = { x: this.state.joints.shoulder_l.x, y: this.state.joints.shoulder_l.y };
2462    let anchorWorldR = { x: this.state.joints.shoulder_r.x, y: this.state.joints.shoulder_r.y };
2463    if (instrumentDef) {
2464      const instPlugs = {};
2465      if (instrumentDef.svgContent) {
2466        const svgCoords = parseSvgSocketsAndPlugs(instrumentDef.svgContent);
2467        Object.entries(svgCoords).forEach(([key, pt]) => {
2468          if (key.startsWith("plug_")) {
2469            instPlugs[key.replace("plug_", "")] = pt;
2470          }
2471        });
2472      }
2473      if (instrumentDef.plugs) {
2474        Object.entries(instrumentDef.plugs).forEach(([key, pt]) => {
2475          instPlugs[key] = pt;
2476        });
2477      }
2478      let gluingArray = Array.isArray(instrumentDef.gluing) ? instrumentDef.gluing : instrumentDef.gluing ? [instrumentDef.gluing] : [];
2479      const mouthpiecePlug = instPlugs.mouthpiece || instrumentDef.headAnchor;
2480      const mouthpieceSocketName = (mouthpiecePlug == null ? void 0 : mouthpiecePlug.socket) || "mouth";
2481      const mouthSocket = this.sockets[mouthpieceSocketName] || this.sockets.mouth;
2482      if (gluingArray.length === 0 && (instrumentDef.headAnchor || instPlugs.mouthpiece)) {
2483        gluingArray = [{ type: "one-point", ip: instrumentDef.headAnchor || instPlugs.mouthpiece, t1: "head", tp: mouthSocket || { x: 0, y: -45 } }];
2484      }
2485      const firstGlue = gluingArray.length > 0 ? gluingArray[0] : null;
2486      const hasOnePoint = (firstGlue == null ? void 0 : firstGlue.type) === "one-point";
2487      if (hasOnePoint && firstGlue) {
2488        let targetWorld = { x: 0, y: 0 };
2489        let targetRot = 0;
2490        const t1Key = firstGlue.t1 || "head";
2491        const defaultTp = t1Key === "head" ? mouthSocket || { x: 0, y: -45 } : { x: 0, y: 0 };
2492        const tpVal = ((_c = firstGlue.tp) == null ? void 0 : _c.x) !== void 0 ? firstGlue.tp : defaultTp;
2493        const targetState = this.state.joints[t1Key];
2494        if (targetState) {
2495          targetRot = targetState.rotation || 0;
2496          const rad2 = targetRot * Math.PI / 180;
2497          const cos2 = Math.cos(rad2);
2498          const sin2 = Math.sin(rad2);
2499          const targetBase = this.jointBases[t1Key] || { x: 0, y: 0 };
2500          targetWorld = {
2501            x: targetBase.x + targetState.x + (tpVal.x * cos2 - tpVal.y * sin2),
2502            y: targetBase.y + targetState.y + (tpVal.x * sin2 + tpVal.y * cos2)
2503          };
2504        }
2505        anchorWorldL = targetWorld;
2506        anchorWorldR = targetWorld;
2507        const instRot = targetRot + (this.config.instrumentRotation || 0);
2508        const instScaleX = this.config.instrumentScale ?? 1;
2509        const instScaleY = this.config.instrumentScale ?? 1;
2510        let ipVal = { x: 0, y: 0 };
2511        if ((firstGlue == null ? void 0 : firstGlue.plugName) && instPlugs[firstGlue.plugName]) {
2512          ipVal = instPlugs[firstGlue.plugName];
2513        } else if (firstGlue && !firstGlue.plugName && firstGlue.t1 === "head" && (instPlugs.mouthpiece || instrumentDef.headAnchor)) {
2514          ipVal = instPlugs.mouthpiece || instrumentDef.headAnchor;
2515        } else {
2516          ipVal = (firstGlue == null ? void 0 : firstGlue.ip) || instrumentDef.headAnchor || instPlugs.mouthpiece || { x: 0, y: 0 };
2517        }
2518        const ipValTransformed = transformPointWithStatic(ipVal, instrumentDef.transform);
2519        const rad = instRot * Math.PI / 180;
2520        const cos = Math.cos(rad);
2521        const sin = Math.sin(rad);
2522        const leftGlue = gluingArray.find((g) => g.t1 === "hand_l");
2523        const rightGlue = gluingArray.find((g) => g.t1 === "hand_r");
2524        const leftHandPlug = ((leftGlue == null ? void 0 : leftGlue.plugName) ? instPlugs[leftGlue.plugName] : null) || ((leftGlue == null ? void 0 : leftGlue.t1) ? instPlugs[leftGlue.t1] : null) || ((_d = Object.entries(instPlugs).find(([_, pt]) => pt.socket === "hand_l")) == null ? void 0 : _d[1]) || instPlugs.hand_l || (leftGlue == null ? void 0 : leftGlue.ip);
2525        const rightHandPlug = ((rightGlue == null ? void 0 : rightGlue.plugName) ? instPlugs[rightGlue.plugName] : null) || ((rightGlue == null ? void 0 : rightGlue.t1) ? instPlugs[rightGlue.t1] : null) || ((_e = Object.entries(instPlugs).find(([_, pt]) => pt.socket === "hand_r")) == null ? void 0 : _e[1]) || instPlugs.hand_r || (rightGlue == null ? void 0 : rightGlue.ip);
2526        const leftHandPlugTransformed = leftHandPlug ? transformPointWithStatic(leftHandPlug, instrumentDef.transform) : null;
2527        const rightHandPlugTransformed = rightHandPlug ? transformPointWithStatic(rightHandPlug, instrumentDef.transform) : null;
2528        const armReachL = (((_f = this.config.ikLeftArm) == null ? void 0 : _f.upperArmLength) ?? 40) + (((_g = this.config.ikLeftArm) == null ? void 0 : _g.lowerArmLength) ?? 40);
2529        const armReachR = (((_h = this.config.ikRightArm) == null ? void 0 : _h.upperArmLength) ?? 40) + (((_i = this.config.ikRightArm) == null ? void 0 : _i.lowerArmLength) ?? 40);
2530        const shoulderL = { x: this.state.joints.shoulder_l.x, y: this.state.joints.shoulder_l.y };
2531        const shoulderR = { x: this.state.joints.shoulder_r.x, y: this.state.joints.shoulder_r.y };
2532        if (instrumentDef.mountPoint !== "hand_l") {
2533          if (leftHandPlugTransformed) {
2534            const txL = (leftHandPlugTransformed.x - ipValTransformed.x) * instScaleX;
2535            const tyL = (leftHandPlugTransformed.y - ipValTransformed.y) * instScaleY;
2536            const worldLx = targetWorld.x + (txL * cos - tyL * sin);
2537            const worldLy = targetWorld.y + (txL * sin + tyL * cos);
2538            const dxL = worldLx - shoulderL.x;
2539            const dyL = worldLy - shoulderL.y;
2540            if (Math.sqrt(dxL * dxL + dyL * dyL) <= armReachL * 1.2) {
2541              targetL = { x: worldLx, y: worldLy };
2542            } else if ((leftGlue == null ? void 0 : leftGlue.tp) !== void 0) {
2543              const rx = leftGlue.tp.x * cos - leftGlue.tp.y * sin;
2544              const ry = leftGlue.tp.x * sin + leftGlue.tp.y * cos;
2545              targetL = { x: targetWorld.x + rx, y: targetWorld.y + ry };
2546            }
2547          } else if ((leftGlue == null ? void 0 : leftGlue.tp) !== void 0) {
2548            const rx = leftGlue.tp.x * cos - leftGlue.tp.y * sin;
2549            const ry = leftGlue.tp.x * sin + leftGlue.tp.y * cos;
2550            targetL = { x: targetWorld.x + rx, y: targetWorld.y + ry };
2551          }
2552        }
2553        if (instrumentDef.mountPoint !== "hand_r") {
2554          if (rightHandPlugTransformed) {
2555            const txR = (rightHandPlugTransformed.x - ipValTransformed.x) * instScaleX;
2556            const tyR = (rightHandPlugTransformed.y - ipValTransformed.y) * instScaleY;
2557            const worldRx = targetWorld.x + (txR * cos - tyR * sin);
2558            const worldRy = targetWorld.y + (txR * sin + tyR * cos);
2559            const dxR = worldRx - shoulderR.x;
2560            const dyR = worldRy - shoulderR.y;
2561            if (Math.sqrt(dxR * dxR + dyR * dyR) <= armReachR * 1.2) {
2562              targetR = { x: worldRx, y: worldRy };
2563            } else if ((rightGlue == null ? void 0 : rightGlue.tp) !== void 0) {
2564              const rx = rightGlue.tp.x * cos - rightGlue.tp.y * sin;
2565              const ry = rightGlue.tp.x * sin + rightGlue.tp.y * cos;
2566              targetR = { x: targetWorld.x + rx, y: targetWorld.y + ry };
2567            }
2568          } else if ((rightGlue == null ? void 0 : rightGlue.tp) !== void 0) {
2569            const rx = rightGlue.tp.x * cos - rightGlue.tp.y * sin;
2570            const ry = rightGlue.tp.x * sin + rightGlue.tp.y * cos;
2571            targetR = { x: targetWorld.x + rx, y: targetWorld.y + ry };
2572          }
2573        }
2574      } else if (instrumentDef.mountPoint && instrumentDef.mountPoint !== "root" && instrumentDef.mountPoint !== "front") {
2575        const mountState = this.state.joints[instrumentDef.mountPoint];
2576        if (mountState) {
2577          let dx = mountState.x || 0;
2578          let dy = mountState.y || 0;
2579          let rot = mountState.rotation || 0;
2580          const pivotX = 0;
2581          const pivotY = 40;
2582          const rad = rot * Math.PI / 180;
2583          const cos = Math.cos(rad);
2584          const sin = Math.sin(rad);
2585          if (instrumentDef.mountPoint !== "hand_l") {
2586            const tx = targetL.x - pivotX;
2587            const ty = targetL.y - pivotY;
2588            targetL.x = pivotX + (tx * cos - ty * sin) + dx;
2589            targetL.y = pivotY + (tx * sin + ty * cos) + dy;
2590          }
2591          if (instrumentDef.mountPoint !== "hand_r") {
2592            const tx = targetR.x - pivotX;
2593            const ty = targetR.y - pivotY;
2594            targetR.x = pivotX + (tx * cos - ty * sin) + dx;
2595            targetR.y = pivotY + (tx * sin + ty * cos) + dy;
2596          }
2597          const mountBase = this.jointBases[instrumentDef.mountPoint] || { x: 0, y: 0 };
2598          const mountWorld = {
2599            x: mountBase.x + dx,
2600            y: mountBase.y + dy
2601          };
2602          anchorWorldL = mountWorld;
2603          anchorWorldR = mountWorld;
2604        }
2605      }
2606    }
2607    this.resolvedTargetL = { ...targetL };
2608    this.resolvedTargetR = { ...targetR };
2609    const offsetL = {
2610      x: targetL.x - anchorWorldL.x,
2611      y: targetL.y - anchorWorldL.y
2612    };
2613    const offsetR = {
2614      x: targetR.x - anchorWorldR.x,
2615      y: targetR.y - anchorWorldR.y
2616    };
2617    if (!this.prevOffsetL) {
2618      this.prevOffsetL = { ...offsetL };
2619    }
2620    if (!this.prevOffsetR) {
2621      this.prevOffsetR = { ...offsetR };
2622    }
2623    const smoothOffsetL = {
2624      x: this.lerp(this.prevOffsetL.x, offsetL.x, lerpL),
2625      y: this.lerp(this.prevOffsetL.y, offsetL.y, lerpL)
2626    };
2627    const smoothOffsetR = {
2628      x: this.lerp(this.prevOffsetR.x, offsetR.x, lerpR),
2629      y: this.lerp(this.prevOffsetR.y, offsetR.y, lerpR)
2630    };
2631    this.prevOffsetL = { ...smoothOffsetL };
2632    this.prevOffsetR = { ...smoothOffsetR };
2633    const smoothL = {
2634      x: anchorWorldL.x + smoothOffsetL.x,
2635      y: anchorWorldL.y + smoothOffsetL.y
2636    };
2637    const smoothR = {
2638      x: anchorWorldR.x + smoothOffsetR.x,
2639      y: anchorWorldR.y + smoothOffsetR.y
2640    };
2641    this.applyIkToArm("l", smoothL);
2642    this.applyIkToArm("r", smoothR);
2643    this.applyIdleAction(time, isSongPlaying);
2644  }
2645  getFamilyDefaultRestPose(family) {
2646    switch (family) {
2647      case "percussion":
2648        return { leftHand: { x: -40, y: 50 }, rightHand: { x: 40, y: 50 } };
2649      case "keys":
2650        return { leftHand: { x: -40, y: 40 }, rightHand: { x: 40, y: 40 } };
2651      case "strings":
2652        return { leftHand: { x: -50, y: 10 }, rightHand: { x: 10, y: 40 } };
2653      case "winds":
2654        return { leftHand: { x: -10, y: 30 }, rightHand: { x: 10, y: 35 } };
2655      default:
2656        return { leftHand: { x: -30, y: 50 }, rightHand: { x: 30, y: 50 } };
2657    }
2658  }
2659  tick(time, activeNotes, bps, isSongPlaying, instrumentDef) {
2660    var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
2661    this.updateBaseState(time, activeNotes, bps, isSongPlaying, instrumentDef);
2662    let lowest = 128;
2663    let highest = -1;
2664    activeNotes.forEach((n) => {
2665      if (n.noteNumber < lowest) lowest = n.noteNumber;
2666      if (n.noteNumber > highest) highest = n.noteNumber;
2667    });
2668    const familyDefault = this.getFamilyDefaultRestPose(this.config.family);
2669    let targetL = (instrumentDef == null ? void 0 : instrumentDef.leftHandMotion) ? this.getTargetFromMotion(instrumentDef.leftHandMotion, "l", lowest, highest, bps, time, instrumentDef) : { ...((_a = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _a.leftHand) || familyDefault.leftHand };
2670    let targetR = (instrumentDef == null ? void 0 : instrumentDef.rightHandMotion) ? this.getTargetFromMotion(instrumentDef.rightHandMotion, "r", lowest, highest, bps, time, instrumentDef) : { ...((_b = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _b.rightHand) || familyDefault.rightHand };
2671    if (!(instrumentDef == null ? void 0 : instrumentDef.leftHandMotion) && activeNotes.length > 0 && ((_c = instrumentDef == null ? void 0 : instrumentDef.playAnimation) == null ? void 0 : _c.leftHandMode)) {
2672      const mode = instrumentDef.playAnimation.leftHandMode;
2673      let avgNote = 0;
2674      activeNotes.forEach((n) => avgNote += n.noteNumber);
2675      avgNote /= activeNotes.length;
2676      if (mode === "fret_neck") {
2677        const minNote = ((_d = instrumentDef == null ? void 0 : instrumentDef.noteRange) == null ? void 0 : _d.minNote) ?? 40;
2678        const maxNote = ((_e = instrumentDef == null ? void 0 : instrumentDef.noteRange) == null ? void 0 : _e.maxNote) ?? 88;
2679        const neckLength = ((_f = instrumentDef == null ? void 0 : instrumentDef.noteRange) == null ? void 0 : _f.physicalLength) ?? 80;
2680        targetL.y += (avgNote - minNote) / (maxNote - minNote) * neckLength;
2681      } else if (mode === "strike") {
2682        const strikeFrequency = ((_g = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _g.strikeFrequency) ?? 0.02;
2683        const strikeAmplitude = ((_h = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _h.strikeAmplitude) ?? 10;
2684        targetL.y += Math.sin(time / 1e3 * strikeFrequency) * strikeAmplitude * this.state.intensity;
2685      }
2686    }
2687    if (!(instrumentDef == null ? void 0 : instrumentDef.rightHandMotion) && activeNotes.length > 0 && ((_i = instrumentDef == null ? void 0 : instrumentDef.playAnimation) == null ? void 0 : _i.rightHandMode)) {
2688      const mode = instrumentDef.playAnimation.rightHandMode;
2689      if (mode === "bow") targetR.x += Math.sin(time / 1e3 * 0.01 * (bps * 0.5)) * 30;
2690      else if (mode === "strum") {
2691        const strumSpeedMult = ((_j = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _j.strumSpeedMult) ?? 0.03;
2692        const strumVerticalAmp = ((_k = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _k.strumVerticalAmp) ?? 15;
2693        const strumHorizontalAmp = ((_l = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _l.strumHorizontalAmp) ?? 5;
2694        const strumSpd = time / 1e3 * strumSpeedMult * (bps * 0.5);
2695        targetR.y += Math.sin(strumSpd) * strumVerticalAmp * this.state.intensity;
2696        targetR.x += Math.cos(strumSpd) * strumHorizontalAmp;
2697      } else if (mode === "strike") {
2698        const strikeFrequency = ((_m = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _m.strikeFrequency) ?? 0.02;
2699        const strikeAmplitude = ((_n = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _n.strikeAmplitude) ?? 10;
2700        targetR.y += Math.sin(time / 1e3 * strikeFrequency) * strikeAmplitude * this.state.intensity;
2701      }
2702    }
2703    this.resolveAndApplyIk(targetL, targetR, instrumentDef, time, isSongPlaying);
2704  }
2705  lerp(start, end, t) {
2706    return start * (1 - t) + end * t;
2707  }
2708  lerpAngle(start, end, t) {
2709    const delta = (end - start + 540) % 360 - 180;
2710    return start + delta * t;
2711  }
2712  getBlendFactor(elapsed, duration) {
2713    const transitionTime = 500;
2714    if (elapsed < transitionTime) {
2715      return elapsed / transitionTime;
2716    } else if (elapsed > duration - transitionTime) {
2717      return Math.max(0, (duration - elapsed) / transitionTime);
2718    } else {
2719      return 1;
2720    }
2721  }
2722  updateIdleState(time) {
2723    if (this.nextIdleActionTime === 0) {
2724      this.nextIdleActionTime = time + 2e3 + Math.random() * 8e3;
2725    }
2726    if (this.idleAction === "none") {
2727      if (time >= this.nextIdleActionTime) {
2728        const rand = Math.random();
2729        if (rand < 0.4) {
2730          this.idleAction = "nod";
2731          this.idleActionDuration = 1500 + Math.random() * 1e3;
2732        } else {
2733          let waveChoice = Math.random() < 0.5 ? "wave_l" : "wave_r";
2734          if (this.lastInstrumentDef) {
2735            if (this.lastInstrumentDef.mountPoint === "hand_l") {
2736              waveChoice = "wave_r";
2737            } else if (this.lastInstrumentDef.mountPoint === "hand_r") {
2738              waveChoice = "wave_l";
2739            }
2740          }
2741          this.idleAction = waveChoice;
2742          this.idleActionDuration = 1e3 + Math.random() * 5e3;
2743          this.idleWaveAngle = (30 + Math.random() * 60) * (Math.PI / 180);
2744        }
2745        this.idleActionStartTime = time;
2746      }
2747    } else {
2748      if (time >= this.idleActionStartTime + this.idleActionDuration) {
2749        this.idleAction = "none";
2750        const minInterval = 1e4;
2751        const maxInterval = 3e4;
2752        this.nextIdleActionTime = time + minInterval + Math.random() * (maxInterval - minInterval);
2753      }
2754    }
2755  }
2756  applyIdleAction(time, isSongPlaying) {
2757    var _a;
2758    if (this.forceWave) {
2759      if (this.useActionEngine) {
2760        let waveSide = "r";
2761        if (((_a = this.lastInstrumentDef) == null ? void 0 : _a.mountPoint) === "hand_r") {
2762          waveSide = "l";
2763        }
2764        const waveAction = this.actionEngine.getActions().find((a) => a.id === "idle_wave");
2765        if (waveAction) {
2766          waveAction.ikSide = waveSide;
2767          this.actionEngine.forceAction("idle_wave", time, 1);
2768        }
2769        const waveData = this.actionEngine.getIdleWaveData(time);
2770        if (waveData) {
2771          this.applyWaveIk(waveData.side, waveData.waveTheta, waveData.blend, time);
2772        }
2773        return;
2774      }
2775      if (this.idleAction !== "wave_r" && this.idleAction !== "wave_l") {
2776        let waveChoice = "wave_r";
2777        if (this.lastInstrumentDef) {
2778          if (this.lastInstrumentDef.mountPoint === "hand_r") {
2779            waveChoice = "wave_l";
2780          }
2781        }
2782        this.idleAction = waveChoice;
2783        this.idleActionStartTime = time;
2784        this.idleActionDuration = 99999999;
2785        this.idleWaveAngle = 45 * Math.PI / 180;
2786      }
2787    } else if (!this.forceWave && this.idleActionDuration === 99999999) {
2788      this.idleAction = "none";
2789      this.nextIdleActionTime = time + 2e3;
2790    }
2791    if (this.useActionEngine && !this.forceWave) {
2792      const waveData = this.actionEngine.getIdleWaveData(time);
2793      if (waveData) {
2794        let waveSide = waveData.side;
2795        if (this.lastInstrumentDef) {
2796          if (this.lastInstrumentDef.mountPoint === "hand_l") waveSide = "r";
2797          else if (this.lastInstrumentDef.mountPoint === "hand_r") waveSide = "l";
2798        }
2799        this.applyWaveIk(waveSide, waveData.waveTheta, waveData.blend, time);
2800      }
2801      return;
2802    }
2803    if (!this.forceWave) {
2804      if (this.disableRandomIdle) {
2805        this.idleAction = "none";
2806        return;
2807      } else if (isSongPlaying) {
2808        this.idleAction = "none";
2809        return;
2810      }
2811    }
2812    if (!this.forceWave) {
2813      this.updateIdleState(time);
2814    }
2815    if (this.idleAction === "nod") {
2816      const elapsed = time - this.idleActionStartTime;
2817      const progress = elapsed / this.idleActionDuration;
2818      const nodCycle = Math.sin(progress * Math.PI * 6);
2819      this.state.joints.head.rotation = nodCycle * 8;
2820      this.state.joints.head.y += Math.max(0, nodCycle) * 3;
2821    } else if (this.idleAction === "wave_l") {
2822      const elapsed = time - this.idleActionStartTime;
2823      const blend = this.getBlendFactor(elapsed, this.idleActionDuration);
2824      const waveTheta = this.idleWaveAngle + Math.sin(elapsed * 0.015) * (25 * Math.PI / 180);
2825      this.applyWaveIk("l", waveTheta, blend, time);
2826    } else if (this.idleAction === "wave_r") {
2827      const elapsed = time - this.idleActionStartTime;
2828      const blend = this.getBlendFactor(elapsed, this.idleActionDuration);
2829      const waveTheta = this.idleWaveAngle + Math.sin(elapsed * 0.015) * (25 * Math.PI / 180);
2830      this.applyWaveIk("r", waveTheta, blend, time);
2831    }
2832  }
2833  /** Shared IK geometry for the waving arm animation */
2834  applyWaveIk(side, waveTheta, blend, _time) {
2835    const ikConfig = side === "l" ? this.config.ikLeftArm : this.config.ikRightArm;
2836    const L1 = (ikConfig == null ? void 0 : ikConfig.upperArmLength) || 40;
2837    const L2 = (ikConfig == null ? void 0 : ikConfig.lowerArmLength) || 40;
2838    const shoulderX = this.state.joints[`shoulder_${side}`].x;
2839    const shoulderY = this.state.joints[`shoulder_${side}`].y;
2840    if (side === "l") {
2841      const waveShoulderAngle = 180;
2842      const waveElbowX = shoulderX - L1;
2843      const waveElbowY = shoulderY;
2844      const waveElbowAngle = -90 - waveTheta * 180 / Math.PI;
2845      const waveHandX = waveElbowX + Math.cos(waveElbowAngle * Math.PI / 180) * L2;
2846      const waveHandY = waveElbowY + Math.sin(waveElbowAngle * Math.PI / 180) * L2;
2847      this.state.joints.upper_arm_l.rotation = this.lerpAngle(this.state.joints.upper_arm_l.rotation, waveShoulderAngle, blend);
2848      this.state.joints.elbow_l.x = this.lerp(this.state.joints.elbow_l.x, waveElbowX, blend);
2849      this.state.joints.elbow_l.y = this.lerp(this.state.joints.elbow_l.y, waveElbowY, blend);
2850      this.state.joints.lower_arm_l.rotation = this.lerpAngle(this.state.joints.lower_arm_l.rotation, waveElbowAngle, blend);
2851      this.state.joints.lower_arm_l.x = this.state.joints.elbow_l.x;
2852      this.state.joints.lower_arm_l.y = this.state.joints.elbow_l.y;
2853      this.state.joints.hand_l.x = this.lerp(this.state.joints.hand_l.x, waveHandX, blend);
2854      this.state.joints.hand_l.y = this.lerp(this.state.joints.hand_l.y, waveHandY, blend);
2855      this.state.joints.hand_l.rotation = this.lerpAngle(this.state.joints.hand_l.rotation || 0, waveElbowAngle, blend);
2856    } else {
2857      const waveShoulderAngle = 0;
2858      const waveElbowX = shoulderX + L1;
2859      const waveElbowY = shoulderY;
2860      const waveElbowAngle = -90 + waveTheta * 180 / Math.PI;
2861      const waveHandX = waveElbowX + Math.cos(waveElbowAngle * Math.PI / 180) * L2;
2862      const waveHandY = waveElbowY + Math.sin(waveElbowAngle * Math.PI / 180) * L2;
2863      this.state.joints.upper_arm_r.rotation = this.lerpAngle(this.state.joints.upper_arm_r.rotation, waveShoulderAngle, blend);
2864      this.state.joints.elbow_r.x = this.lerp(this.state.joints.elbow_r.x, waveElbowX, blend);
2865      this.state.joints.elbow_r.y = this.lerp(this.state.joints.elbow_r.y, waveElbowY, blend);
2866      this.state.joints.lower_arm_r.rotation = this.lerpAngle(this.state.joints.lower_arm_r.rotation, waveElbowAngle, blend);
2867      this.state.joints.lower_arm_r.x = this.state.joints.elbow_r.x;
2868      this.state.joints.lower_arm_r.y = this.state.joints.elbow_r.y;
2869      this.state.joints.hand_r.x = this.lerp(this.state.joints.hand_r.x, waveHandX, blend);
2870      this.state.joints.hand_r.y = this.lerp(this.state.joints.hand_r.y, waveHandY, blend);
2871      this.state.joints.hand_r.rotation = this.lerpAngle(this.state.joints.hand_r.rotation || 0, waveElbowAngle, blend);
2872    }
2873  }
2874  applyIkToArm(side, target) {
2875    const shoulder = this.state.joints[`shoulder_${side}`];
2876    const ikConfig = side === "l" ? this.config.ikLeftArm : this.config.ikRightArm;
2877    const L1 = (ikConfig == null ? void 0 : ikConfig.upperArmLength) || 40;
2878    const L2 = (ikConfig == null ? void 0 : ikConfig.lowerArmLength) || 40;
2879    const bendDir = (ikConfig == null ? void 0 : ikConfig.bendDirection) || (side === "l" ? 1 : -1);
2880    const result = solve2BoneIK(shoulder, target, L1, L2, bendDir);
2881    this.state.joints[`upper_arm_${side}`].rotation = result.shoulderAngle;
2882    this.state.joints[`elbow_${side}`].x = result.elbow.x;
2883    this.state.joints[`elbow_${side}`].y = result.elbow.y;
2884    this.state.joints[`lower_arm_${side}`].rotation = result.elbowAngle;
2885    this.state.joints[`lower_arm_${side}`].x = result.elbow.x;
2886    this.state.joints[`lower_arm_${side}`].y = result.elbow.y;
2887    this.state.joints[`hand_${side}`].x = result.hand.x;
2888    this.state.joints[`hand_${side}`].y = result.hand.y;
2889    this.state.joints[`hand_${side}`].rotation = result.elbowAngle;
2890  }
2891}
2892class PercussionAnimator extends BaseAnimator {
2893  tick(time, activeNotes, bps, isSongPlaying, instrumentDef) {
2894    var _a, _b, _c, _d, _e, _f, _g;
2895    this.updateBaseState(time, activeNotes, bps, isSongPlaying, instrumentDef);
2896    const bodySway = ((_a = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _a.bodySway) ?? 0;
2897    const headSway = ((_b = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _b.headSway) ?? 0;
2898    if (bodySway > 0 && isSongPlaying) {
2899      const swayFrequency = ((_c = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _c.swayFrequency) ?? 5e-3;
2900      const sway = Math.sin(time / 1e3 * swayFrequency * bps) * bodySway * Math.max(0.5, this.state.intensity);
2901      this.state.joints.body.x = this.lerp(this.state.joints.body.x, sway, 0.1);
2902      this.state.joints.head.rotation = this.lerp(this.state.joints.head.rotation, sway * headSway, 0.1);
2903    } else {
2904      this.state.joints.body.x = this.lerp(this.state.joints.body.x, 0, 0.05);
2905      this.state.joints.head.rotation = this.lerp(this.state.joints.head.rotation, 0, 0.05);
2906    }
2907    let lowest = 128;
2908    let highest = -1;
2909    activeNotes.forEach((n) => {
2910      if (n.noteNumber < lowest) lowest = n.noteNumber;
2911      if (n.noteNumber > highest) highest = n.noteNumber;
2912    });
2913    const familyDefault = this.getFamilyDefaultRestPose("percussion");
2914    let targetL = (instrumentDef == null ? void 0 : instrumentDef.leftHandMotion) ? this.getTargetFromMotion(instrumentDef.leftHandMotion, "l", lowest, highest, bps, time, instrumentDef) : { ...((_d = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _d.leftHand) || familyDefault.leftHand };
2915    let targetR = (instrumentDef == null ? void 0 : instrumentDef.rightHandMotion) ? this.getTargetFromMotion(instrumentDef.rightHandMotion, "r", lowest, highest, bps, time, instrumentDef) : { ...((_e = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _e.rightHand) || familyDefault.rightHand };
2916    if (!(instrumentDef == null ? void 0 : instrumentDef.leftHandMotion) || !(instrumentDef == null ? void 0 : instrumentDef.rightHandMotion)) {
2917      if (activeNotes.length > 0) {
2918        const strikeFrequency = ((_f = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _f.strikeFrequency) ?? 0.02;
2919        const strikeAmplitude = ((_g = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _g.strikeAmplitude) ?? 10;
2920        activeNotes.forEach((note, idx) => {
2921          const map = this.config.noteMap[note.noteNumber];
2922          const hitPos = map ? { x: map.x, y: map.y } : { x: (Math.random() - 0.5) * 60, y: 50 };
2923          const strikeBounce = Math.sin(time / 1e3 * strikeFrequency) * strikeAmplitude * this.state.intensity;
2924          if ((map == null ? void 0 : map.hand) === "l") {
2925            if (!(instrumentDef == null ? void 0 : instrumentDef.leftHandMotion)) targetL = { x: hitPos.x, y: hitPos.y + strikeBounce };
2926          } else if ((map == null ? void 0 : map.hand) === "r") {
2927            if (!(instrumentDef == null ? void 0 : instrumentDef.rightHandMotion)) targetR = { x: hitPos.x, y: hitPos.y + strikeBounce };
2928          } else if (idx % 2 === 0) {
2929            if (!(instrumentDef == null ? void 0 : instrumentDef.rightHandMotion)) targetR = { x: hitPos.x, y: hitPos.y + strikeBounce };
2930          } else {
2931            if (!(instrumentDef == null ? void 0 : instrumentDef.leftHandMotion)) targetL = { x: hitPos.x, y: hitPos.y + strikeBounce };
2932          }
2933        });
2934      }
2935    }
2936    this.resolveAndApplyIk(targetL, targetR, instrumentDef, time, isSongPlaying, 0.3, 0.3);
2937  }
2938}
2939class KeysAnimator extends BaseAnimator {
2940  tick(time, activeNotes, bps, isSongPlaying, instrumentDef) {
2941    var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
2942    this.updateBaseState(time, activeNotes, bps, isSongPlaying, instrumentDef);
2943    const bodySway = ((_a = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _a.bodySway) ?? 0;
2944    const headSway = ((_b = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _b.headSway) ?? 0;
2945    if (bodySway > 0 && isSongPlaying) {
2946      const swayFrequency = ((_c = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _c.swayFrequency) ?? 5e-3;
2947      const sway = Math.sin(time / 1e3 * swayFrequency * bps) * bodySway * Math.max(0.5, this.state.intensity);
2948      this.state.joints.body.x = this.lerp(this.state.joints.body.x, sway, 0.1);
2949      this.state.joints.head.rotation = this.lerp(this.state.joints.head.rotation, sway * headSway, 0.1);
2950    } else {
2951      this.state.joints.body.x = this.lerp(this.state.joints.body.x, 0, 0.05);
2952      this.state.joints.head.rotation = this.lerp(this.state.joints.head.rotation, 0, 0.05);
2953    }
2954    let lowest = 128;
2955    let highest = -1;
2956    activeNotes.forEach((n) => {
2957      if (n.noteNumber < lowest) lowest = n.noteNumber;
2958      if (n.noteNumber > highest) highest = n.noteNumber;
2959    });
2960    const familyDefault = this.getFamilyDefaultRestPose("keys");
2961    let targetL = (instrumentDef == null ? void 0 : instrumentDef.leftHandMotion) ? this.getTargetFromMotion(instrumentDef.leftHandMotion, "l", lowest, highest, bps, time, instrumentDef) : { ...((_d = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _d.leftHand) || familyDefault.leftHand };
2962    let targetR = (instrumentDef == null ? void 0 : instrumentDef.rightHandMotion) ? this.getTargetFromMotion(instrumentDef.rightHandMotion, "r", lowest, highest, bps, time, instrumentDef) : { ...((_e = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _e.rightHand) || familyDefault.rightHand };
2963    if ((!(instrumentDef == null ? void 0 : instrumentDef.leftHandMotion) || !(instrumentDef == null ? void 0 : instrumentDef.rightHandMotion)) && activeNotes.length > 0) {
2964      const keyboardWidth = ((_f = instrumentDef == null ? void 0 : instrumentDef.noteRange) == null ? void 0 : _f.physicalLength) ?? 140;
2965      const minNote = ((_g = instrumentDef == null ? void 0 : instrumentDef.noteRange) == null ? void 0 : _g.minNote) ?? 21;
2966      const maxNote = ((_h = instrumentDef == null ? void 0 : instrumentDef.noteRange) == null ? void 0 : _h.maxNote) ?? 108;
2967      const lx = (lowest - minNote) / (maxNote - minNote) * keyboardWidth - keyboardWidth / 2;
2968      const hx = (highest - minNote) / (maxNote - minNote) * keyboardWidth - keyboardWidth / 2;
2969      const pressBounce = (((_i = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _i.strikeAmplitude) ?? 5) * this.state.intensity;
2970      const restY_L = ((_k = (_j = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _j.leftHand) == null ? void 0 : _k.y) ?? familyDefault.leftHand.y;
2971      const restY_R = ((_m = (_l = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _l.rightHand) == null ? void 0 : _m.y) ?? familyDefault.rightHand.y;
2972      let useL = { x: lx, y: restY_L + pressBounce };
2973      let useR = { x: hx, y: restY_R + pressBounce };
2974      if (activeNotes.length === 1) {
2975        const noteMap = this.config.noteMap[activeNotes[0].noteNumber];
2976        if (noteMap) {
2977          if (noteMap.hand === "l") useL = { x: noteMap.x, y: noteMap.y + pressBounce };
2978          if (noteMap.hand === "r") useR = { x: noteMap.x, y: noteMap.y + pressBounce };
2979        }
2980      }
2981      if (!(instrumentDef == null ? void 0 : instrumentDef.leftHandMotion)) targetL = useL;
2982      if (!(instrumentDef == null ? void 0 : instrumentDef.rightHandMotion)) targetR = useR;
2983    }
2984    this.resolveAndApplyIk(targetL, targetR, instrumentDef, time, isSongPlaying, 0.2, 
29840.2);
2985  }
2986}
2987class StringsAnimator extends BaseAnimator {
2988  tick(time, activeNotes, bps, isSongPlaying, instrumentDef) {
2989    var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
2990    this.updateBaseState(time, activeNotes, bps, isSongPlaying, instrumentDef);
2991    const bodySway = ((_a = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _a.bodySway) ?? 0;
2992    const headSway = ((_b = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _b.headSway) ?? 0;
2993    if (isSongPlaying) {
2994      const swayFrequency = ((_c = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _c.swayFrequency) ?? 5e-3;
2995      const sway = Math.sin(time / 1e3 * swayFrequency * bps) * bodySway * Math.max(0.5, this.state.intensity);
2996      this.state.joints.body.x = this.lerp(this.state.joints.body.x, sway, 0.1);
2997      this.state.joints.head.rotation = this.lerp(this.state.joints.head.rotation, sway * headSway, 0.1);
2998    } else {
2999      this.state.joints.body.x = this.lerp(this.state.joints.body.x, 0, 0.05);
3000      this.state.joints.head.rotation = this.lerp(this.state.joints.head.rotation, 0, 0.05);
3001    }
3002    let lowest = 128;
3003    let highest = -1;
3004    activeNotes.forEach((n) => {
3005      if (n.noteNumber < lowest) lowest = n.noteNumber;
3006      if (n.noteNumber > highest) highest = n.noteNumber;
3007    });
3008    const familyDefault = this.getFamilyDefaultRestPose("strings");
3009    let targetL = (instrumentDef == null ? void 0 : instrumentDef.leftHandMotion) ? this.getTargetFromMotion(instrumentDef.leftHandMotion, "l", lowest, highest, bps, time, instrumentDef) : { ...((_d = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _d.leftHand) || familyDefault.leftHand };
3010    let targetR = (instrumentDef == null ? void 0 : instrumentDef.rightHandMotion) ? this.getTargetFromMotion(instrumentDef.rightHandMotion, "r", lowest, highest, bps, time, instrumentDef) : { ...((_e = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _e.rightHand) || familyDefault.rightHand };
3011    if ((!(instrumentDef == null ? void 0 : instrumentDef.leftHandMotion) || !(instrumentDef == null ? void 0 : instrumentDef.rightHandMotion)) && activeNotes.length > 0) {
3012      const minNote = ((_f = instrumentDef == null ? void 0 : instrumentDef.noteRange) == null ? void 0 : _f.minNote) ?? 40;
3013      const maxNote = ((_g = instrumentDef == null ? void 0 : instrumentDef.noteRange) == null ? void 0 : _g.maxNote) ?? 88;
3014      const neckLength = ((_h = instrumentDef == null ? void 0 : instrumentDef.noteRange) == null ? void 0 : _h.physicalLength) ?? 80;
3015      let avgNote = 0;
3016      activeNotes.forEach((n) => avgNote += n.noteNumber);
3017      avgNote /= activeNotes.length;
3018      if (!(instrumentDef == null ? void 0 : instrumentDef.leftHandMotion)) {
3019        const fretPos = (avgNote - minNote) / (maxNote - minNote) * neckLength;
3020        const rest_L = ((_i = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _i.leftHand) || familyDefault.leftHand;
3021        targetL = { x: rest_L.x + fretPos * 0.5, y: rest_L.y - fretPos * 0.2 };
3022      }
3023      if (!(instrumentDef == null ? void 0 : instrumentDef.rightHandMotion)) {
3024        const strumSpeedMult = ((_j = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _j.strumSpeedMult) ?? 0.03;
3025        const strumVerticalAmp = ((_k = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _k.strumVerticalAmp) ?? 15;
3026        const strumHorizontalAmp = ((_l = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _l.strumHorizontalAmp) ?? 5;
3027        const strumSpd = time / 1e3 * strumSpeedMult * (bps * 0.5);
3028        const rest_R = ((_m = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _m.rightHand) || familyDefault.rightHand;
3029        targetR = {
3030          x: rest_R.x + Math.cos(strumSpd) * strumHorizontalAmp,
3031          y: rest_R.y + Math.sin(strumSpd) * strumVerticalAmp * this.state.intensity
3032        };
3033      }
3034    }
3035    this.resolveAndApplyIk(targetL, targetR, instrumentDef, time, isSongPlaying, 0.15, 0.4);
3036  }
3037}
3038class WindsAnimator extends BaseAnimator {
3039  tick(time, activeNotes, bps, isSongPlaying, instrumentDef) {
3040    var _a, _b, _c, _d, _e, _f;
3041    this.updateBaseState(time, activeNotes, bps, isSongPlaying, instrumentDef);
3042    const bodySway = ((_a = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _a.bodySway) ?? 20;
3043    const headSway = ((_b = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _b.headSway) ?? 1;
3044    const fingerFlutter = ((_c = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _c.fingerFlutter) ?? 2;
3045    if (isSongPlaying) {
3046      const swayFrequency = ((_d = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _d.swayFrequency) ?? 5e-3;
3047      const sway = Math.sin(time / 1e3 * swayFrequency * bps) * bodySway * Math.max(0.5, this.state.intensity);
3048      this.state.joints.body.x = this.lerp(this.state.joints.body.x, sway, 0.1);
3049      this.state.joints.head.rotation = this.lerp(this.state.joints.head.rotation, sway * 0.5 * headSway, 0.1);
3050    } else {
3051      this.state.joints.body.x = this.lerp(this.state.joints.body.x, 0, 0.05);
3052      this.state.joints.head.rotation = this.lerp(this.state.joints.head.rotation, 0, 0.05);
3053    }
3054    let lowest = 128;
3055    let highest = -1;
3056    activeNotes.forEach((n) => {
3057      if (n.noteNumber < lowest) lowest = n.noteNumber;
3058      if (n.noteNumber > highest) highest = n.noteNumber;
3059    });
3060    const familyDefault = this.getFamilyDefaultRestPose("winds");
3061    let targetL = (instrumentDef == null ? void 0 : instrumentDef.leftHandMotion) ? this.getTargetFromMotion(instrumentDef.leftHandMotion, "l", lowest, highest, bps, time, instrumentDef) : { ...((_e = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _e.leftHand) || familyDefault.leftHand };
3062    let targetR = (instrumentDef == null ? void 0 : instrumentDef.rightHandMotion) ? this.getTargetFromMotion(instrumentDef.rightHandMotion, "r", lowest, highest, bps, time, instrumentDef) : { ...((_f = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _f.rightHand) || familyDefault.rightHand };
3063    if (activeNotes.length > 0 && fingerFlutter > 0) {
3064      if (!(instrumentDef == null ? void 0 : instrumentDef.leftHandMotion)) {
3065        targetL.y += Math.random() * fingerFlutter * this.state.intensity;
3066      }
3067      if (!(instrumentDef == null ? void 0 : instrumentDef.rightHandMotion)) {
3068        targetR.y += Math.random() * fingerFlutter * this.state.intensity;
3069      }
3070    }
3071    this.resolveAndApplyIk(targetL, targetR, instrumentDef, time, isSongPlaying, 0.3, 0.3);
3072  }
3073}
3074class GenericAnimator extends BaseAnimator {
3075  tick(time, activeNotes, bps, isSongPlaying, instrumentDef) {
3076    var _a, _b, _c, _d;
3077    this.updateBaseState(time, activeNotes, bps, isSongPlaying, instrumentDef);
3078    let lowest = 128;
3079    let highest = -1;
3080    activeNotes.forEach((n) => {
3081      if (n.noteNumber < lowest) lowest = n.noteNumber;
3082      if (n.noteNumber > highest) highest = n.noteNumber;
3083    });
3084    const familyDefault = this.getFamilyDefaultRestPose("other");
3085    let targetL = (instrumentDef == null ? void 0 : instrumentDef.leftHandMotion) ? this.getTargetFromMotion(instrumentDef.leftHandMotion, "l", lowest, highest, bps, time, instrumentDef) : { ...((_a = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _a.leftHand) || familyDefault.leftHand };
3086    let targetR = (instrumentDef == null ? void 0 : instrumentDef.rightHandMotion) ? this.getTargetFromMotion(instrumentDef.rightHandMotion, "r", lowest, highest, bps, time, instrumentDef) : { ...((_b = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _b.rightHand) || familyDefault.rightHand };
3087    if (activeNotes.length > 0) {
3088      const strikeFrequency = ((_c = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _c.strikeFrequency) ?? 0.02;
3089      const strikeAmplitude = ((_d = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _d.strikeAmplitude) ?? 10;
3090      if (!(instrumentDef == null ? void 0 : instrumentDef.leftHandMotion)) {
3091        targetL.y += Math.sin(time / 1e3 * strikeFrequency) * strikeAmplitude * this.state.intensity;
3092      }
3093      if (!(instrumentDef == null ? void 0 : instrumentDef.rightHandMotion)) {
3094        targetR.y += Math.sin(time / 1e3 * strikeFrequency + Math.PI) * strikeAmplitude * this.state.intensity;
3095      }
3096    }
3097    this.resolveAndApplyIk(targetL, targetR, instrumentDef, time, isSongPlaying, 0.2, 0.2);
3098  }
3099}
3100function createAnimator(config) {
3101  switch (config.family) {
3102    case "percussion":
3103      return new PercussionAnimator(config);
3104    case "keys":
3105      return new KeysAnimator(config);
3106    case "strings":
3107      return new StringsAnimator(config);
3108    case "winds":
3109      return new WindsAnimator(config);
3110    default:
3111      return new GenericAnimator(config);
3112  }
3113}
3114function sanitizeSvgContent(svg) {
3115  if (!svg) return svg;
3116  return svg.replace(/<sodipodi:[^>]*\/>/gi, "").replace(/<sodipodi:[\s\S]*?<\/sodipodi:[^>]*>/gi, "").replace(/<inkscape:[^>]*\/>/gi, "").replace(/<inkscape:[\s\S]*?<\/inkscape:[^>]*>/gi, "").replace(/<(dc|cc|rdf):[\s\S]*?<\/(?:dc|cc|rdf):[^>]*>/gi, "").replace(/<metadata[\s\S]*?<\/metadata>/gi, "").replace(/\s(sodipodi|inkscape|dc|cc|rdf):[a-z-]+\s*=\s*"[^"]*"/gi, "").replace(/\s(sodipodi|inkscape|dc|cc|rdf):[a-z-]+\s*=\s*'[^']*'/gi, "").replace(/\s xlink:(?!href)[a-z-]+\s*=\s*"[^"]*"/gi, "").replace(/\s xmlns:(sodipodi|inkscape|dc|cc|rdf|xlink)="[^"]*"/gi, "");
3117}
3118function parseSvgTranslate(transformStr) {
3119  if (!transformStr) return { x: 0, y: 0 };
3120  const match = transformStr.match(/translate\(([^,)]+)[,\s]+([^)]+)\)/);
3121  if (match) {
3122    return {
3123      x: parseFloat(match[1]) || 0,
3124      y: parseFloat(match[2]) || 0
3125    };
3126  }
3127  return { x: 0, y: 0 };
3128}
3129function parseSvgRotation(transformStr) {
3130  if (!transformStr) return 0;
3131  const match = transformStr.match(/rotate\(([^)]+)\)/);
3132  if (match) {
3133    const deg = parseFloat(match[1].split(/[\s,]+/)[0]) || 0;
3134    return deg;
3135  }
3136  return 0;
3137}
3138const PuppetCharacter = ({
3139  config,
3140  state: propsState,
3141  x,
3142  y,
3143  isSoloed,
3144  isMuted = false,
3145  isHidden,
3146  instrumentName,
3147  onClick,
3148  onContextMenu,
3149  onHover,
3150  showSkeleton,
3151  visibility,
3152  specificGraphic,
3153  clothingGraphic,
3154  onClothingMouseDown,
3155  onBodyPartMouseDown,
3156  onInstrumentMouseDown,
3157  isCharacterTab,
3158  showActionPaths,
3159  activeTab,
3160  selectedActionId,
3161  globalActions,
3162  onActionOverlayMouseDown,
3163  viewZoom,
3164  isPlaying
3165}) => {
3166  const state = reactExports.useMemo(() => {
3167    var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
3168    if (!isCharacterTab || isPlaying) return propsState;
3169    const svgCoords = parseSvgSocketsAndPlugs(config.svgContent);
3170    const sockets = {};
3171    Object.entries(svgCoords).forEach(([key, pt]) => {
3172      if (key.startsWith("socket_")) {
3173        sockets[key.replace("socket_", "")] = pt;
3174      }
3175    });
3176    if (config.sockets) {
3177      Object.entries(config.sockets).forEach(([key, pt]) => {
3178        sockets[key] = pt;
3179      });
3180    }
3181    const shoulderL = sockets.shoulder_l || { x: ((_a = config.ikLeftArm) == null ? void 0 : _a.originX) ?? -30, y: ((_b = config.ikLeftArm) == null ? void 0 : _b.originY) ?? -20 };
3182    const shoulderR = sockets.shoulder_r || { x: ((_c = config.ikRightArm) == null ? void 0 : _c.originX) ?? 30, y: ((_d = config.ikRightArm) == null ? void 0 : _d.originY) ?? -20 };
3183    const elbowL = sockets.elbow_l || { x: ((_e = config.ikLeftArm) == null ? void 0 : _e.originX) ?? -30, y: (((_f = config.ikLeftArm) == null ? void 0 : _f.originY) ?? -20) + 40 };
3184    const elbowR = sockets.elbow_r || { x: ((_g = config.ikRightArm) == null ? void 0 : _g.originX) ?? 30, y: (((_h = config.ikRightArm) == null ? void 0 : _h.originY) ?? -20) + 40 };
3185    const handL = sockets.hand_l || { x: ((_i = config.ikLeftArm) == null ? void 0 : _i.originX) ?? -30, y: (((_j = config.ikLeftArm) == null ? void 0 : _j.originY) ?? -20) + 70 };
3186    const handR = sockets.hand_r || { x: ((_k = config.ikRightArm) == null ? void 0 : _k.originX) ?? 30, y: (((_l = config.ikRightArm) == null ? void 0 : _l.originY) ?? -20) + 70 };
3187    const rotL1 = Math.atan2(elbowL.y - shoulderL.y, elbowL.x - shoulderL.x) * 180 / Math.PI;
3188    const rotL2 = Math.atan2(handL.y - elbowL.y, handL.x - elbowL.x) * 180 / Math.PI;
3189    const rotR1 = Math.atan2(elbowR.y - shoulderR.y, elbowR.x - shoulderR.x) * 180 / Math.PI;
3190    const rotR2 = Math.atan2(handR.y - elbowR.y, handR.x - elbowR.x) * 180 / Math.PI;
3191    return {
3192      ...propsState,
3193      joints: {
3194        ...propsState.joints,
3195        head: { x: 0, y: 0, rotation: 0 },
3196        body: { x: 0, y: 0, rotation: 0 },
3197        shoulder_l: { x: shoulderL.x, y: shoulderL.y, rotation: 0 },
3198        upper_arm_l: { x: shoulderL.x, y: shoulderL.y, rotation: rotL1 },
3199        elbow_l: { x: elbowL.x, y: elbowL.y, rotation: 0 },
3200        lower_arm_l: { x: elbowL.x, y: elbowL.y, rotation: rotL2 },
3201        hand_l: { x: handL.x, y: handL.y, rotation: rotL2 },
3202        shoulder_r: { x: shoulderR.x, y: shoulderR.y, rotation: 0 },
3203        upper_arm_r: { x: shoulderR.x, y: shoulderR.y, rotation: rotR1 },
3204        elbow_r: { x: elbowR.x, y: elbowR.y, rotation: 0 },
3205        lower_arm_r: { x: elbowR.x, y: elbowR.y, rotation: rotR2 },
3206        hand_r: { x: handR.x, y: handR.y, rotation: rotR2 },
3207        leg_l: { x: 0, y: 0, rotation: 0 },
3208        foot_l: { x: 0, y: 0, rotation: 0 },
3209        leg_r: { x: 0, y: 0, rotation: 0 },
3210        foot_r: { x: 0, y: 0, rotation: 0 }
3211      }
3212    };
3213  }, [propsState, config, isCharacterTab, isPlaying]);
3214  const clothingPartsJson = clothingGraphic && clothingGraphic.parts ? JSON.stringify(clothingGraphic.parts) : "";
3215  const onClothingMouseDownRef = reactExports.useRef(onClothingMouseDown);
3216  onClothingMouseDownRef.current = onClothingMouseDown;
3217  const onBodyPartMouseDownRef = reactExports.useRef(onBodyPartMouseDown);
3218  onBodyPartMouseDownRef.current = onBodyPartMouseDown;
3219  const onInstrumentMouseDownRef = reactExports.useRef(onInstrumentMouseDown);
3220  onInstrumentMouseDownRef.current = onInstrumentMouseDown;
3221  const containerRef = reactExports.useRef(null);
3222  const svgRef = reactExports.useRef(null);
3223  const initialTransforms = reactExports.useRef({});
3224  const mergedVisibility = reactExports.useMemo(() => {
3225    if (!config.visibility && !visibility) return null;
3226    return {
3227      ...config.visibility || {},
3228      ...visibility || {}
3229    };
3230  }, [config.visibility, visibility]);
3231  const visibilityRef = reactExports.useRef(mergedVisibility);
3232  const clothingGraphicRef = reactExports.useRef(clothingGraphic);
3233  clothingGraphicRef.current = clothingGraphic;
3234  const configRef = reactExports.useRef(config);
3235  configRef.current = config;
3236  const stateRef = reactExports.useRef(state);
3237  stateRef.current = state;
3238  const specificGraphicRef = reactExports.useRef(specificGraphic);
3239  specificGraphicRef.current = specificGraphic;
3240  const showSkeletonRef = reactExports.useRef(showSkeleton);
3241  showSkeletonRef.current = showSkeleton;
3242  reactExports.useEffect(() => {
3243    visibilityRef.current = mergedVisibility;
3244  }, [mergedVisibility]);
3245  const getBase = (id) => initialTransforms.current[id] || { x: 0, y: 0, rotation: 0 };
3246  const getJointCoordinate = (jointId, useCenter = false) => {
3247    var _a, _b, _c, _d, _e, _f;
3248    if (jointId === "shoulder_l") return stateRef.current.joints.shoulder_l || { x: 0, y: 0 };
3249    if (jointId === "elbow_l") return stateRef.current.joints.elbow_l || { x: 0, y: 0 };
3250    if (jointId === "hand_l") return stateRef.current.joints.hand_l || { x: 0, y: 0 };
3251    if (jointId === "shoulder_r") return stateRef.current.joints.shoulder_r || { x: 0, y: 0 };
3252    if (jointId === "elbow_r") return stateRef.current.joints.elbow_r || { x: 0, y: 0 };
3253    if (jointId === "hand_r") return stateRef.current.joints.hand_r || { x: 0, y: 0 };
3254    if (svgRef.current) {
3255      const targetNode = svgRef.current.querySelector(`#${jointId}`);
3256      if (targetNode) {
3257        const ctm = targetNode.getCTM();
3258        const parentCtm = svgRef.current.getCTM();
3259        if (ctm && parentCtm) {
3260          try {
3261            const relativeCtm = parentCtm.inverse().multiply(ctm);
3262            const ownerSvg = svgRef.current.ownerSVGElement || svgRef.current.closest("svg");
3263            if (ownerSvg) {
3264              const pt = ownerSvg.createSVGPoint();
3265              if (useCenter) {
3266                const bbox = targetNode.getBBox();
3267                pt.x = bbox.x + bbox.width / 2;
3268                pt.y = bbox.y + bbox.height / 2;
3269              } else {
3270                pt.x = 0;
3271                pt.y = 0;
3272              }
3273              const localP = pt.matrixTransform(relativeCtm);
3274              return { x: localP.x, y: localP.y };
3275            }
3276          } catch (e) {
3277            console.error("Error calculating relative CTM coordinate:", e);
3278          }
3279        }
3280      }
3281    }
3282    if (jointId === "head") {
3283      const headBase = getBase("head");
3284      const headUt = ((_a = configRef.current.transforms) == null ? void 0 : _a["head"]) || { x: 0, y: 0 };
3285      let targetX = stateRef.current.joints.head.x;
3286      let targetY = stateRef.current.joints.head.y;
3287      if (specificGraphicRef.current && typeof specificGraphicRef.current === "object" && specificGraphicRef.current.headAnchor) {
3288        let dx = 0;
3289        let dy = 0;
3290        if (specificGraphicRef.current.mountPoint && specificGraphicRef.current.mountPoint !== "root" && specificGraphicRef.current.mountPoint !== "front") {
3291          const mountState = stateRef.current.joints[specificGraphicRef.current.mountPoint];
3292          if (mountState) {
3293            dx = mountState.x || 0;
3294            dy = mountState.y || 0;
3295          }
3296        }
3297        targetX = specificGraphicRef.current.headAnchor.x - headBase.x + dx;
3298        targetY = specificGraphicRef.current.headAnchor.y - headBase.y + dy;
3299      }
3300      return {
3301        x: headBase.x + targetX + (headUt.x || 0),
3302        y: headBase.y + targetY + (headUt.y || 0)
3303      };
3304    }
3305    if (jointId === "body") {
3306      const bodyBase = getBase("body");
3307      const bodyUt = ((_b = configRef.current.transforms) == null ? void 0 : _b["body"]) || { x: 0, y: 0 };
3308      return {
3309        x: bodyBase.x + stateRef.current.joints.body.x + (bodyUt.x || 0),
3310        y: bodyBase.y + stateRef.current.joints.body.y + (bodyUt.y || 0)
3311      };
3312    }
3313    if ((jointId == null ? void 0 : jointId.startsWith("leg_")) || (jointId == null ? void 0 : jointId.startsWith("foot_"))) {
3314      const side = jointId.endsWith("_l") ? "l" : "r";
3315      const type = jointId.startsWith("leg_") ? "leg" : "foot";
3316      const baseJoint = getBase(`${type}_${side}`);
3317      const jointState = stateRef.current.joints[`${type}_${side}`];
3318      if (jointState) {
3319        const ut = ((_c = configRef.current.transforms) == null ? void 0 : _c[`${type}_${side}`]) || { x: 0, y: 0 };
3320        return {
3321          x: baseJoint.x + jointState.x + (ut.x || 0),
3322          y: baseJoint.y + jointState.y + (ut.y || 0)
3323        };
3324      }
3325    }
3326    let socketPt = (_d = configRef.current.sockets) == null ? void 0 : _d[jointId];
3327    if (!socketPt && svgRef.current) {
3328      const socketEl = svgRef.current.querySelector(`#socket_${jointId}`);
3329      if (socketEl) {
3330        const cxAttr = socketEl.getAttribute("cx");
3331        const cyAttr = socketEl.getAttribute("cy");
3332        if (cxAttr !== null && cyAttr !== null) {
3333          socketPt = { x: parseFloat(cxAttr), y: parseFloat(cyAttr) };
3334        } else {
3335          const bbox = socketEl.getBBox();
3336          socketPt = { x: bbox.x + bbox.width / 2, y: bbox.y + bbox.height / 2 };
3337        }
3338      }
3339    }
3340    if (socketPt) {
3341      if (jointId === "mouth" || jointId === "head") {
3342        const headBase = getBase("head");
3343        const headUt = ((_e = configRef.current.transforms) == null ? void 0 : _e["head"]) || { x: 0, y: 0 };
3344        const rad2 = (stateRef.current.joints.head.rotation || 0) * Math.PI / 180;
3345        const cos2 = Math.cos(rad2);
3346        const sin2 = Math.sin(rad2);
3347        const dx2 = socketPt.x - headBase.x;
3348        const dy2 = socketPt.y - headBase.y;
3349        return {
3350          x: headBase.x + stateRef.current.joints.head.x + (dx2 * cos2 - dy2 * sin2) + (headUt.x || 0),
3351          y: headBase.y + stateRef.current.joints.head.y + (dx2 * sin2 + dy2 * cos2) + (headUt.y || 0)
3352        };
3353      }
3354      const bodyBase = getBase("body");
3355      const bodyUt = ((_f = configRef.current.transforms) == null ? void 0 : _f["body"]) || { x: 0, y: 0 };
3356      const rad = (stateRef.current.joints.body.rotation || 0) * Math.PI / 180;
3357      const cos = Math.cos(rad);
3358      const sin = Math.sin(rad);
3359      const dx = socketPt.x - bodyBase.x;
3360      const dy = socketPt.y - bodyBase.y;
3361      return {
3362        x: bodyBase.x + stateRef.current.joints.body.x + (dx * cos - dy * sin) + (bodyUt.x || 0),
3363        y: bodyBase.y + stateRef.current.joints.body.y + (dx * sin + dy * cos) + (bodyUt.y || 0)
3364      };
3365    }
3366    return { x: 0, y: 0 };
3367  };
3368  reactExports.useEffect(() => {
3369    if (!svgRef.current) return;
3370    ["l", "r"].forEach((side) => {
3371      var _a, _b, _c, _d;
3372      const leg = (_a = svgRef.current) == null ? void 0 : _a.querySelector(`#leg_${side}`);
3373      const foot = (_b = svgRef.current) == null ? void 0 : _b.querySelector(`#foot_${side}`);
3374      if (leg && foot && leg.contains(foot)) {
3375        const footCtm = foot.getCTM();
3376        const svgCtm = (_c = svgRef.current) == null ? void 0 : _c.getCTM();
3377        if (footCtm && svgCtm) {
3378          try {
3379            const relMatrix = svgCtm.inverse().multiply(footCtm);
3380            foot.setAttribute("transform", `translate(${relMatrix.e}, ${relMatrix.f})`);
3381            (_d = svgRef.current) == null ? void 0 : _d.appendChild(foot);
3382          } catch (e) {
3383            console.error("Error un-nesting foot:", e);
3384          }
3385        }
3386      }
3387    });
3388    const parts = ["head", "body", "leg_l", "leg_r", "foot_l", "foot_r", "upper_arm_l", "lower_arm_l", "hand_l", "upper_arm_r", "lower_arm_r", "hand_r"];
3389    parts.forEach((part) => {
3390      var _a;
3391      const node = (_a = svgRef.current) == null ? void 0 : _a.querySelector(`#${part}`);
3392      if (node) {
3393        node.style.cursor = "grab";
3394        const listener = (e) => {
3395          if (onBodyPartMouseDownRef.current) {
3396            onBodyPartMouseDownRef.current(part, e);
3397          }
3398        };
3399        node.addEventListener("mousedown", listener);
3400        if (["head", "body", "leg_l", "leg_r", "foot_l", "foot_r"].includes(part)) {
3401          const bbox = node.getBBox();
3402          const transformAttr = node.getAttribute("transform");
3403          const translate = parseSvgTranslate(transformAttr);
3404          const rotation = parseSvgRotation(transformAttr);
3405          initialTransforms.current[part] = {
3406            x: translate.x,
3407            y: translate.y,
3408            rotation,
3409            originCenterTop: `${bbox.x + bbox.width / 2}px ${bbox.y}px`,
3410            originCenterCenter: `${bbox.x + bbox.width / 2}px ${bbox.y + bbox.height / 2}px`,
3411            originCenterBottom: `${bbox.x + bbox.width / 2}px ${bbox.y + bbox.height}px`
3412          };
3413        }
3414      }
3415    });
3416  }, [config.svgContent]);
3417  reactExports.useEffect(() => {
3418    if (!svgRef.current) return;
3419    const svgs = svgRef.current.querySelectorAll("svg");
3420    svgs.forEach((nestedSvg) => {
3421      const viewBox = nestedSvg.getAttribute("viewBox");
3422      if (viewBox) {
3423        const parts = viewBox.trim().split(/\s+/);
3424        if (parts.length === 4) {
3425          const w = parseFloat(parts[2]);
3426          const h = parseFloat(parts[3]);
3427          if (!isNaN(w) && !isNaN(h)) {
3428            const widthAttr = nestedSvg.getAttribute("width");
3429            const heightAttr = nestedSvg.getAttribute("height");
3430            if (!widthAttr || widthAttr.includes("%") || parseFloat(widthAttr) !== w) {
3431              nestedSvg.setAttribute("width", w.toString());
3432            }
3433            if (!heightAttr || heightAttr.includes("%") || parseFloat(heightAttr) !== h) {
3434              nestedSvg.setAttribute("height", h.toString());
3435            }
3436          }
3437        }
3438      }
3439      nestedSvg.setAttribute("overflow", "visible");
3440    });
3441  }, [config.svgContent, specificGraphic, clothingPartsJson]);
3442  reactExports.useEffect(() => {
3443    if (!svgRef.current) return;
3444    svgRef.current.querySelectorAll(".custom-instrument-mount").forEach((el) => el.remove());
3445    const legacyInstrumentGroup = svgRef.current.querySelector("#instrument");
3446    if (legacyInstrumentGroup) {
3447      legacyInstrumentGroup.innerHTML = "";
3448    }
3449    if (!specificGraphic) return;
3450    if (typeof specificGraphic === "string") {
3451      if (legacyInstrumentGroup) {
3452        legacyInstrumentGroup.innerHTML = specificGraphic;
3453        const scale = config.instrumentScale ?? 1;
3454        const rotation = config.instrumentRotation ?? 0;
3455        if (scale !== 1 || rotation !== 0) {
3456          legacyInstrumentGroup.setAttribute("transform", `scale(${scale}) rotate(${rotation})`);
3457        } else {
3458          legacyInstrumentGroup.removeAttribute("transform");
3459        }
3460      }
3461    } else {
3462      const mountInstrument = (svgContent, mountPoint, partType, transformObj) => {
3463        if (svgRef.current) {
3464          const wrapper = document.createElementNS("http://www.w3.org/2000/svg", "g");
3465          wrapper.classList.add("custom-instrument-mount");
3466          wrapper.setAttribute("data-target-mount", mountPoint);
3467          wrapper.setAttribute("data-instrument-part", partType);
3468          const inner = document.createElementNS("http://www.w3.org/2000/svg", "g");
3469          inner.innerHTML = sanitizeSvgContent(svgContent);
3470          inner.classList.add("custom-instrument-svg");
3471          const baseScaleX = (transformObj == null ? void 0 : transformObj.scaleX) ?? (transformObj == null ? void 0 : transformObj.scale) ?? 1;
3472          const baseScaleY = (transformObj == null ? void 0 : transformObj.scaleY) ?? (transformObj == null ? void 0 : transformObj.scale) ?? 1;
3473          const baseRot = (transformObj == null ? void 0 : transformObj.rotation) ?? 0;
3474          const baseX = (transformObj == null ? void 0 : transformObj.x) ?? 0;
3475          const baseY = (transformObj == null ? void 0 : transformObj.y) ?? 0;
3476          inner.setAttribute("transform", `translate(${baseX}, ${baseY}) scale(${baseScaleX}, ${baseScaleY}) rotate(${baseRot})`);
3477          wrapper.appendChild(inner);
3478          if (onInstrumentMouseDownRef.current) {
3479            wrapper.style.cursor = "grab";
3480            wrapper.addEventListener("mousedown", (e) => {
3481              if (onInstrumentMouseDownRef.current) {
3482                onInstrumentMouseDownRef.current(partType, e);
3483              }
3484            });
3485          }
3486          svgRef.current.appendChild(wrapper);
3487        }
3488      };
3489      mountInstrument(specificGraphic.svgContent, specificGraphic.mountPoint, "main", specificGraphic.transform);
3490      if (specificGraphic.accessorySvg && specificGraphic.accessoryMountPoint) {
3491        mountInstrument(specificGraphic.accessorySvg, specificGraphic.accessoryMountPoint, "accessory", specificGraphic.accessoryTransform);
3492      }
3493    }
3494  }, [config.svgContent, specificGraphic, config.instrumentScale, config.instrumentRotation]);
3495  reactExports.useEffect(() => {
3496    if (!svgRef.current) return;
3497    svgRef.current.querySelectorAll(".custom-clothing-mount").forEach((el) => el.remove());
3498    if (!clothingGraphic || !clothingGraphic.parts) return;
3499    const parts = Object.entries(clothingGraphic.parts);
3500    for (const [jointId, svgString] of parts) {
3501      if (!svgString) continue;
3502      const wrapper = document.createElementNS("http://www.w3.org/2000/svg", "g");
3503      wrapper.classList.add("custom-clothing-mount");
3504      wrapper.setAttribute("data-target-joint", jointId);
3505      const offsetGroup = document.createElementNS("http://www.w3.org/2000/svg", "g");
3506      offsetGroup.classList.add("custom-clothing-offset");
3507      offsetGroup.innerHTML = sanitizeSvgContent(svgString);
3508      wrapper.appendChild(offsetGroup);
3509      if (onClothingMouseDownRef.current) {
3510        wrapper.style.cursor = "grab";
3511        wrapper.addEventListener("mousedown", (e) => {
3512          if (onClothingMouseDownRef.current) {
3513            onClothingMouseDownRef.current(jointId, e);
3514          }
3515        });
3516      }
3517      svgRef.current.appendChild(wrapper);
3518    }
3519  }, [config.svgContent, clothingPartsJson]);
3520  reactExports.useEffect(() => {
3521    if (!svgRef.current) return;
3522    svgRef.current.querySelectorAll(".custom-bodypart-mount").forEach((el) => el.remove());
3523    const allParts = ["head", "body", "upper_arm_l", "lower_arm_l", "hand_l", "upper_arm_r", "lower_arm_r", "hand_r", "leg_l", "foot_l", "leg_r", "foot_r"];
3524    allParts.forEach((partId) => {
3525      var _a;
3526      const baseNode = (_a = svgRef.current) == null ? void 0 : _a.querySelector(`#${partId}`);
3527      if (baseNode) {
3528        baseNode.style.display = "";
3529      }
3530    });
3531    if (!config.parts) return;
3532    const parts = Object.entries(config.parts);
3533    for (const [jointId, svgString] of parts) {
3534      if (!svgString) continue;
3535      const wrapper = document.createElementNS("http://www.w3.org/2000/svg", "g");
3536      wrapper.classList.add("custom-bodypart-mount");
3537      wrapper.setAttribute("data-target-joint", jointId);
3538      const offsetGroup = document.createElementNS("http://www.w3.org/2000/svg", "g");
3539      offsetGroup.classList.add("custom-bodypart-offset");
3540      offsetGroup.innerHTML = sanitizeSvgContent(svgString);
3541      wrapper.appendChild(offsetGroup);
3542      if (onBodyPartMouseDownRef.current) {
3543        wrapper.style.cursor = "grab";
3544        wrapper.addEventListener("mousedown", (e) => {
3545          if (onBodyPartMouseDownRef.current) {
3546            onBodyPartMouseDownRef.current(jointId, e);
3547          }
3548        });
3549      }
3550      const baseNode = svgRef.current.querySelector(`#${jointId}`);
3551      if (baseNode) {
3552        baseNode.style.display = "none";
3553      }
3554      svgRef.current.appendChild(wrapper);
3555    }
3556  }, [config.svgContent, config.parts ? JSON.stringify(config.parts) : ""]);
3557  reactExports.useEffect(() => {
3558    if (!svgRef.current) return;
3559    const defaults = {
3560      leg_l: 1,
3561      clothing_leg_l: 1.5,
3562      leg_r: 2,
3563      clothing_leg_r: 2.5,
3564      foot_l: 3,
3565      foot_r: 4,
3566      body: 5,
3567      clothing_body: 5.5,
3568      head: 6,
3569      clothing_head: 6.5,
3570      upper_arm_r: 7,
3571      clothing_upper_arm_r: 7.2,
3572      lower_arm_r: 8,
3573      clothing_lower_arm_r: 8.2,
3574      hand_r: 9,
3575      clothing_hand_r: 9.2,
3576      instrument: 10,
3577      upper_arm_l: 11,
3578      clothing_upper_arm_l: 11.2,
3579      lower_arm_l: 12,
3580      clothing_lower_arm_l: 12.2,
3581      hand_l: 13,
3582      clothing_hand_l: 13.2
3583    };
3584    const customZOrder = specificGraphic && typeof specificGraphic === "object" ? specificGraphic.zOrder : void 0;
3585    const getZIndex = (el) => {
3586      let id = el.id;
3587      if (el.classList.contains("custom-instrument-mount")) {
3588        id = "instrument";
3589      }
3590      if (el.classList.contains("custom-clothing-mount")) {
3591        const jointId = el.getAttribute("data-target-joint");
3592        id = `clothing_${jointId}`;
3593      }
3594      if (el.classList.contains("custom-bodypart-mount")) {
3595        id = el.getAttribute("data-target-joint") || id;
3596      }
3597      if (id && customZOrder && customZOrder[id] !== void 0) {
3598        return customZOrder[id];
3599      }
3600      if (id && config.baseZOrder && config.baseZOrder[id] !== void 0) {
3601        return config.baseZOrder[id];
3602      }
3603      return defaults[id] ?? 0;
3604    };
3605    const children = Array.from(svgRef.current.childNodes).filter((el) => el instanceof SVGElement);
3606    const sorted = [...children].sort((a, b) => getZIndex(a) - getZIndex(b));
3607    sorted.forEach((el) => {
3608      var _a;
3609      (_a = svgRef.current) == null ? void 0 : _a.appendChild(el);
3610    });
3611  }, [config.svgContent, specificGraphic, clothingGraphic == null ? void 0 : clothingGraphic.id, clothingPartsJson, config.baseZOrder, config.parts ? JSON.stringify(config.parts) : ""]);
3612  reactExports.useEffect(() => {
3613    var _a;
3614    if (!svgRef.current || !clothingGraphic) return;
3615    const parts = Object.keys(clothingGraphic.parts || {});
3616    for (const jointId of parts) {
3617      const wrapper = svgRef.current.querySelector(`.custom-clothing-mount[data-target-joint="${jointId}"]`);
3618      if (!wrapper) continue;
3619      const offsetGroup = wrapper.querySelector(".custom-clothing-offset");
3620      if (!offsetGroup) continue;
3621      const transform = (_a = clothingGraphic.transforms) == null ? void 0 : _a[jointId];
3622      const x2 = (transform == null ? void 0 : transform.x) || 0;
3623      const y2 = (transform == null ? void 0 : transform.y) || 0;
3624      const scaleX = (transform == null ? void 0 : transform.scaleX) ?? 1;
3625      const scaleY = (transform == null ? void 0 : transform.scaleY) ?? 1;
3626      const rotation = (transform == null ? void 0 : transform.rotation) || 0;
3627      offsetGroup.setAttribute("transform", `translate(${x2}, ${y2}) scale(${scaleX}, ${scaleY}) rotate(${rotation})`);
3628    }
3629  }, [clothingGraphic == null ? void 0 : clothingGraphic.transforms]);
3630  useAnimationFrame(() => {
3631    var _a, _b, _c, _d, _e;
3632    if (!svgRef.current) return;
3633    const clothingGraphic2 = clothingGraphicRef.current;
3634    const config2 = configRef.current;
3635    const state2 = stateRef.current;
3636    const specificGraphic2 = specificGraphicRef.current;
3637    if (visibilityRef.current) {
3638      const parts = [
3639        "head",
3640        "body",
3641        "upper_arm_l",
3642        "lower_arm_l",
3643        "hand_l",
3644        "upper_arm_r",
3645        "lower_arm_r",
3646        "hand_r",
3647        "leg_l",
3648        "leg_r",
3649        "foot_l",
3650        "foot_r",
3651        "instrument",
3652        "clothing_body",
3653        "clothing_head",
3654        "clothing_upper_arm_l",
3655        "clothing_lower_arm_l",
3656        "clothing_hand_l",
3657        "clothing_upper_arm_r",
3658        "clothing_lower_arm_r",
3659        "clothing_hand_r",
3660        "clothing_leg_l",
3661        "clothing_leg_r",
3662        "clothing_foot_l",
3663        "clothing_foot_r"
3664      ];
3665      parts.forEach((partId) => {
3666        var _a2, _b2, _c2, _d2;
3667        const node = ((_a2 = svgRef.current) == null ? void 0 : _a2.querySelector(`.custom-bodypart-mount[data-target-joint="${partId}"]`)) || ((_b2 = svgRef.current) == null ? void 0 : _b2.querySelector(`#${partId}`));
3668        if (node) {
3669          gsapWithCSS.set(node, { autoAlpha: visibilityRef.current[partId] === false ? 0 : 1 });
3670        }
3671        if (partId === "instrument") {
3672          (_c2 = svgRef.current) == null ? void 0 : _c2.querySelectorAll(".custom-instrument-mount").forEach((instNode) => {
3673            gsapWithCSS.set(instNode, { autoAlpha: visibilityRef.current[partId] === false ? 0 : 1 });
3674          });
3675        }
3676        if (partId.startsWith("clothing_")) {
3677          const jointId = partId.replace("clothing_", "");
3678          (_d2 = svgRef.current) == null ? void 0 : _d2.querySelectorAll(`.custom-clothing-mount[data-target-joint="${jointId}"]`).forEach((clothNode) => {
3679            gsapWithCSS.set(clothNode, { autoAlpha: visibilityRef.current[partId] === false ? 0 : 1 });
3680          });
3681        }
3682      });
3683    }
3684    const head = svgRef.current.querySelector('.custom-bodypart-mount[data-target-joint="head"]') || svgRef.current.querySelector("#head");
3685    const body = svgRef.current.querySelector('.custom-bodypart-mount[data-target-joint="body"]') || svgRef.current.querySelector("#body");
3686    const getJointRotation = (jointId) => {
3687      var _a2;
3688      if (jointId === "shoulder_l" || jointId === "elbow_l" || jointId === "hand_l" || jointId === "shoulder_r" || jointId === "elbow_r" || jointId === "hand_r") {
3689        const jointState2 = state2.joints[jointId];
3690        return (jointState2 == null ? void 0 : jointState2.rotation) || 0;
3691      }
3692      if (svgRef.current) {
3693        const targetNode = svgRef.current.querySelector(`#${jointId}`);
3694        if (targetNode) {
3695          const ctm = targetNode.getCTM();
3696          const parentCtm = svgRef.current.getCTM();
3697          if (ctm && parentCtm) {
3698            try {
3699              const relativeCtm = parentCtm.inverse().multiply(ctm);
3700              return Math.atan2(relativeCtm.b, relativeCtm.a) * 180 / Math.PI;
3701            } catch (e) {
3702              console.error("Error calculating relative CTM rotation:", e);
3703            }
3704          }
3705        }
3706      }
3707      if (jointId === "head") return getBase("head").rotation + state2.joints.head.rotation;
3708      if (jointId === "body") return getBase("body").rotation + state2.joints.body.rotation;
3709      if ((jointId == null ? void 0 : jointId.startsWith("leg_")) || (jointId == null ? void 0 : jointId.startsWith("foot_"))) {
3710        const side = jointId.endsWith("_l") ? "l" : "r";
3711        const type = jointId.startsWith("leg_") ? "leg" : "foot";
3712        const baseJoint = getBase(`${type}_${side}`);
3713        return baseJoint.rotation + (((_a2 = state2.joints[`${type}_${side}`]) == null ? void 0 : _a2.rotation) || 0);
3714      }
3715      const jointState = state2.joints[jointId];
3716      if (jointState) return jointState.rotation || 0;
3717      return 0;
3718    };
3719    if (head) {
3720      const base = getBase("head");
3721      let targetX = state2.joints.head.x;
3722      let targetY = state2.joints.head.y;
3723      if (specificGraphic2 && typeof specificGraphic2 === "object" && specificGraphic2.headAnchor) {
3724        let dx = 0;
3725        let dy = 0;
3726        if (specificGraphic2.mountPoint && specificGraphic2.mountPoint !== "root" && specificGraphic2.mountPoint !== "front") {
3727          const mountState = state2.joints[specificGraphic2.mountPoint];
3728          if (mountState) {
3729            dx = mountState.x || 0;
3730            dy = mountState.y || 0;
3731          }
3732        }
3733        targetX = specificGraphic2.headAnchor.x - base.x + dx;
3734        targetY = specificGraphic2.headAnchor.y - base.y + dy;
3735      }
3736      const ut = ((_a = config2.transforms) == null ? void 0 : _a["head"]) || { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0 };
3737      gsapWithCSS.set(head, {
3738        rotation: base.rotation + state2.joints.head.rotation + (ut.rotation || 0),
3739        x: base.x + targetX + (ut.x || 0),
3740        y: base.y + targetY + (ut.y || 0),
3741        scaleX: ut.scaleX ?? 1,
3742        scaleY: ut.scaleY ?? 1,
3743        transformOrigin: base.originCenterBottom || "center bottom"
3744      });
3745    }
3746    if (body) {
3747      const base = getBase("body");
3748      const ut = ((_b = config2.transforms) == null ? void 0 : _b["body"]) || { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0 };
3749      gsapWithCSS.set(body, {
3750        rotation: base.rotation + state2.joints.body.rotation + (ut.rotation || 0),
3751        x: base.x + state2.joints.body.x + (ut.x || 0),
3752        y: base.y + state2.joints.body.y + (ut.y || 0),
3753        scaleX: ut.scaleX ?? 1,
3754        scaleY: ut.scaleY ?? 1,
3755        transformOrigin: base.originCenterCenter || "center center"
3756      });
3757    }
3758    const updateArm = (side) => {
3759      var _a2, _b2, _c2, _d2, _e2, _f, _g, _h, _i, _j, _k;
3760      const shoulderNode = (_a2 = svgRef.current) == null ? void 0 : _a2.querySelector(`#shoulder_${side}`);
3761      const upperArmNode = ((_b2 = svgRef.current) == null ? void 0 : _b2.querySelector(`.custom-bodypart-mount[data-target-joint="upper_arm_${side}"]`)) || ((_c2 = svgRef.current) == null ? void 0 : _c2.querySelector(`#upper_arm_${side}`));
3762      const elbowNode = (_d2 = svgRef.current) == null ? void 0 : _d2.querySelector(`#elbow_${side}`);
3763      const lowerArmNode = ((_e2 = svgRef.current) == null ? void 0 : _e2.querySelector(`.custom-bodypart-mount[data-target-joint="lower_arm_${side}"]`)) || ((_f = svgRef.current) == null ? void 0 : _f.querySelector(`#lower_arm_${side}`));
3764      const handNode = ((_g = svgRef.current) == null ? void 0 : _g.querySelector(`.custom-bodypart-mount[data-target-joint="hand_${side}"]`)) || ((_h = svgRef.current) == null ? void 0 : _h.querySelector(`#hand_${side}`));
3765      const shoulderState = state2.joints[`shoulder_${side}`];
3766      const upperArmState = state2.joints[`upper_arm_${side}`];
3767      const elbowState = state2.joints[`elbow_${side}`];
3768      const lowerArmState = state2.joints[`lower_arm_${side}`];
3769      const handState = state2.joints[`hand_${side}`];
3770      if (shoulderNode && shoulderState) {
3771        shoulderNode.setAttribute("transform", `translate(${shoulderState.x}, ${shoulderState.y})`);
3772      }
3773      if (upperArmNode && upperArmState && shoulderState) {
3774        const ut = ((_i = config2.transforms) == null ? void 0 : _i[`upper_arm_${side}`]) || { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0 };
3775        upperArmNode.setAttribute("transform", `translate(${shoulderState.x + (ut.x || 0)}, ${shoulderState.y + (ut.y || 0)}) scale(${ut.scaleX ?? 1}, ${ut.scaleY ?? 1}) rotate(${(upperArmState.rotation || 0) + (ut.rotation || 0)})`);
3776      }
3777      if (elbowNode && elbowState) {
3778        elbowNode.setAttribute("transform", `translate(${elbowState.x}, ${elbowState.y})`);
3779      }
3780      if (lowerArmNode && lowerArmState && elbowState) {
3781        const ut = ((_j = config2.transforms) == null ? void 0 : _j[`lower_arm_${side}`]) || { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0 };
3782        lowerArmNode.setAttribute("transform", `translate(${elbowState.x + (ut.x || 0)}, ${elbowState.y + (ut.y || 0)}) scale(${ut.scaleX ?? 1}, ${ut.scaleY ?? 1}) rotate(${(lowerArmState.rotation || 0) + (ut.rotation || 0)})`);
3783      }
3784      if (handNode && handState) {
3785        const ut = ((_k = config2.transforms) == null ? void 0 : _k[`hand_${side}`]) || { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0 };
3786        handNode.setAttribute("transform", `translate(${handState.x + (ut.x || 0)}, ${handState.y + (ut.y || 0)}) scale(${ut.scaleX ?? 1}, ${ut.scaleY ?? 1}) rotate(${(handState.rotation || 0) + (ut.rotation || 0)})`);
3787      }
3788    };
3789    const updateLeg = (side) => {
3790      var _a2, _b2, _c2, _d2, _e2, _f;
3791      const legNode = ((_a2 = svgRef.current) == null ? void 0 : _a2.querySelector(`.custom-bodypart-mount[data-target-joint="leg_${side}"]`)) || ((_b2 = svgRef.current) == null ? void 0 : _b2.querySelector(`#leg_${side}`));
3792      const footNode = ((_c2 = svgRef.current) == null ? void 0 : _c2.querySelector(`.custom-bodypart-mount[data-target-joint="foot_${side}"]`)) || ((_d2 = svgRef.current) == null ? void 0 : _d2.querySelector(`#foot_${side}`));
3793      if (legNode) {
3794        const baseLeg = getBase(`leg_${side}`);
3795        const legState = state2.joints[`leg_${side}`];
3796        if (legState) {
3797          const ut = ((_e2 = config2.transforms) == null ? void 0 : _e2[`leg_${side}`]) || { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0 };
3798          gsapWithCSS.set(legNode, {
3799            x: baseLeg.x + legState.x + (ut.x || 0),
3800            y: baseLeg.y + legState.y + (ut.y || 0),
3801            rotation: baseLeg.rotation + legState.rotation + (ut.rotation || 0),
3802            scaleX: ut.scaleX ?? 1,
3803            scaleY: ut.scaleY ?? 1,
3804            transformOrigin: baseLeg.originCenterTop || "center top"
3805          });
3806        }
3807      }
3808      if (footNode) {
3809        const baseFoot = getBase(`foot_${side}`);
3810        const footState = state2.joints[`foot_${side}`];
3811        if (footState) {
3812          const ut = ((_f = config2.transforms) == null ? void 0 : _f[`foot_${side}`]) || { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0 };
3813          gsapWithCSS.set(footNode, {
3814            x: baseFoot.x + footState.x + (ut.x || 0),
3815            y: baseFoot.y + footState.y + (ut.y || 0),
3816            rotation: baseFoot.rotation + footState.rotation + (ut.rotation || 0),
3817            scaleX: ut.scaleX ?? 1,
3818            scaleY: ut.scaleY ?? 1,
3819            transformOrigin: baseFoot.originCenterCenter || "center center"
3820          });
3821        }
3822      }
3823    };
3824    updateArm("l");
3825    updateArm("r");
3826    updateLeg("l");
3827    updateLeg("r");
3828    (_c = svgRef.current) == null ? void 0 : _c.querySelectorAll(".custom-instrument-mount").forEach((instNode) => {
3829      var _a2, _b2, _c2, _d2;
3830      const mountPoint = instNode.getAttribute("data-target-mount") || "root";
3831      if (specificGraphic2 && typeof specificGraphic2 === "object") {
3832        const mouthpiecePlug = (_a2 = specificGraphic2.plugs) == null ? void 0 : _a2.mouthpiece;
3833        let gluingArray = Array.isArray(specificGraphic2.gluing) ? specificGraphic2.gluing : specificGraphic2.gluing ? [specificGraphic2.gluing] : [];
3834        if (gluingArray.length === 0 && (specificGraphic2.headAnchor || mouthpiecePlug)) {
3835          gluingArray = [{ type: "one-point", ip: specificGraphic2.headAnchor || mouthpiecePlug, t1: "head", tp: void 0 }];
3836        }
3837        const firstGlue = gluingArray.length > 0 ? gluingArray[0] : null;
3838        const gluingType = (firstGlue == null ? void 0 : firstGlue.type) || "none";
3839        const instScaleX = config2.instrumentScale ?? 1;
3840        const instScaleY = config2.instrumentScale ?? 1;
3841        if (gluingType === "one-point" && firstGlue) {
3842          const t1Key = firstGlue.t1 || "head";
3843          let tpVal = firstGlue.tp;
3844          if (!tpVal) {
3845            const mSocketName = (mouthpiecePlug == null ? void 0 : mouthpiecePlug.socket) || "mouth";
3846            tpVal = ((_b2 = config2.sockets) == null ? void 0 : _b2[mSocketName]) || ((_c2 = config2.sockets) == null ? void 0 : _c2.mouth) || { x: 0, y: 0 };
3847          }
3848          const targetBase = getJointCoordinate(t1Key);
3849          const targetRot = getJointRotation(t1Key);
3850          const rad = targetRot * Math.PI / 180;
3851          const cos = Math.cos(rad);
3852          const sin = Math.sin(rad);
3853          const targetWorld = {
3854            x: targetBase.x + (tpVal.x * cos - tpVal.y * sin),
3855            y: targetBase.y + (tpVal.x * sin + tpVal.y * cos)
3856          };
3857          const instRot = targetRot + (config2.instrumentRotation || 0);
3858          let ipVal = { x: 0, y: 0 };
3859          if (firstGlue.plugName && ((_d2 = specificGraphic2.plugs) == null ? void 0 : _d2[firstGlue.plugName])) {
3860            ipVal = specificGraphic2.plugs[firstGlue.plugName];
3861          } else if (!firstGlue.plugName && firstGlue.t1 === "head" && (mouthpiecePlug || specificGraphic2.headAnchor)) {
3862            ipVal = mouthpiecePlug || specificGraphic2.headAnchor;
3863          } else {
3864            ipVal = firstGlue.ip || specificGraphic2.headAnchor || mouthpiecePlug || { x: 0, y: 0 };
3865          }
3866          const ipValTransformed = transformPointWithStatic(ipVal, specificGraphic2.transform);
3867          gsapWithCSS.set(instNode, {
3868            x: targetWorld.x - ipValTransformed.x,
3869            y: targetWorld.y - ipValTransformed.y,
3870            rotation: instRot,
3871            scaleX: instScaleX,
3872            scaleY: instScaleY,
3873            svgOrigin: `${ipValTransformed.x} ${ipValTransformed.y}`
3874          });
3875        } else {
3876          if (mountPoint === "body") {
3877            const bodyBase = getBase("body");
3878            gsapWithCSS.set(instNode, {
3879              x: bodyBase.x + state2.joints.body.x,
3880              y: bodyBase.y + state2.joints.body.y,
3881              rotation: bodyBase.rotation + state2.joints.body.rotation,
3882              scaleX: instScaleX,
3883              scaleY: instScaleY,
3884              transformOrigin: bodyBase.originCenterCenter || "center center"
3885            });
3886          } else if (mountPoint === "head") {
3887            const headBase = getBase("head");
3888            gsapWithCSS.set(instNode, {
3889              x: headBase.x + state2.joints.head.x,
3890              y: headBase.y + state2.joints.head.y,
3891              rotation: headBase.rotation + state2.joints.head.rotation,
3892              scaleX: instScaleX,
3893              scaleY: instScaleY,
3894              transformOrigin: headBase.originCenterBottom || "center bottom"
3895            });
3896          } else if (mountPoint === "hand_l" || mountPoint === "hand_r") {
3897            const handState = state2.joints[mountPoint];
3898            if (handState) {
3899              gsapWithCSS.set(instNode, {
3900                x: handState.x,
3901                y: handState.y,
3902                rotation: handState.rotation || 0,
3903                scaleX: instScaleX,
3904                scaleY: instScaleY,
3905                transformOrigin: "center center"
3906              });
3907            }
3908          } else {
3909            gsapWithCSS.set(instNode, {
3910              x: 0,
3911              y: 0,
3912              rotation: 0,
3913              scaleX: instScaleX,
3914              scaleY: instScaleY,
3915              transformOrigin: "center center"
3916            });
3917          }
3918        }
3919      } else {
3920        const instScaleXLegacy = config2.instrumentScale ?? 1;
3921        const instScaleYLegacy = config2.instrumentScale ?? 1;
3922        gsapWithCSS.set(instNode, {
3923          x: 0,
3924          y: 0,
3925          rotation: 0,
3926          scaleX: instScaleXLegacy,
3927          scaleY: instScaleYLegacy,
3928          transformOrigin: "center center"
3929        });
3930      }
3931    });
3932    (_d = svgRef.current) == null ? void 0 : _d.querySelectorAll(".custom-clothing-mount").forEach((clothNode) => {
3933      var _a2, _b2, _c2, _d2, _e2, _f;
3934      const jointId = clothNode.getAttribute("data-target-joint");
3935      if (!jointId) return;
3936      const glueCfg = (_a2 = clothingGraphic2 == null ? void 0 : clothingGraphic2.gluing) == null ? void 0 : _a2[jointId];
3937      let p1 = null;
3938      let p2 = null;
3939      const p1El = clothNode.querySelector("#plug_p1");
3940      if (p1El) {
3941        const cxAttr = p1El.getAttribute("cx");
3942        const cyAttr = p1El.getAttribute("cy");
3943        if (cxAttr !== null && cyAttr !== null) {
3944          p1 = { x: parseFloat(cxAttr), y: parseFloat(cyAttr) };
3945        } else {
3946          const bbox = p1El.getBBox();
3947          p1 = { x: bbox.x + bbox.width / 2, y: bbox.y + bbox.height / 2 };
3948        }
3949      }
3950      const p2El = clothNode.querySelector("#plug_p2");
3951      if (p2El) {
3952        const cxAttr = p2El.getAttribute("cx");
3953        const cyAttr = p2El.getAttribute("cy");
3954        if (cxAttr !== null && cyAttr !== null) {
3955          p2 = { x: parseFloat(cxAttr), y: parseFloat(cyAttr) };
3956        } else {
3957          const bbox = p2El.getBBox();
3958          p2 = { x: bbox.x + bbox.width / 2, y: bbox.y + bbox.height / 2 };
3959        }
3960      }
3961      const dbPlugs = (_b2 = clothingGraphic2 == null ? void 0 : clothingGraphic2.plugs) == null ? void 0 : _b2[jointId];
3962      if (dbPlugs) {
3963        if (dbPlugs.p1) p1 = dbPlugs.p1;
3964        if (dbPlugs.p2) p2 = dbPlugs.p2;
3965      }
3966      if (glueCfg) {
3967        if (!p1 && glueCfg.p1) p1 = glueCfg.p1;
3968        if (!p2 && glueCfg.p2) p2 = glueCfg.p2;
3969      }
3970      if (glueCfg && glueCfg.type === "two-point") {
3971        const side = jointId.endsWith("_l") ? "l" : "r";
3972        const t1Key = glueCfg.t1 || (jointId.startsWith("upper_arm_") ? "shoulder_" + side : jointId.startsWith("lower_arm_") ? "elbow_" + side : "shoulder_l");
3973        const t2Key = glueCfg.t2 || (jointId.startsWith("upper_arm_") ? "elbow_" + side : jointId.startsWith("lower_arm_") ? "hand_" + side : "elbow_l");
3974        const resolvedP1 = p1 || { x: 0, y: 0 };
3975        const resolvedP2 = p2 || { x: 0, y: 40 };
3976        const T1 = getJointCoordinate(t1Key);
3977        const T2 = getJointCoordinate(t2Key);
3978        const dx_s = resolvedP2.x - resolvedP1.x;
3979        const dy_s = resolvedP2.y - resolvedP1.y;
3980        const L_s = Math.sqrt(dx_s * dx_s + dy_s * dy_s);
3981        const theta_s = Math.atan2(dy_s, dx_s) * 180 / Math.PI;
3982        const dx_t = T2.x - T1.x;
3983        const dy_t = T2.y - T1.y;
3984        const L_t = Math.sqrt(dx_t * dx_t + dy_t * dy_t);
3985        const theta_t = Math.atan2(dy_t, dx_t) * 180 / Math.PI;
3986        const s = L_s > 1e-3 ? L_t / L_s : 1;
3987        const rot = theta_t - theta_s;
3988        const rotRad = rot * Math.PI / 180;
3989        const cosR = Math.cos(rotRad);
3990        const sinR = Math.sin(rotRad);
3991        const tx = T1.x - s * (resolvedP1.x * cosR - resolvedP1.y * sinR);
3992        const ty = T1.y - s * (resolvedP1.x * sinR + resolvedP1.y * cosR);
3993        clothNode.style.transform = "";
3994        clothNode.setAttribute(
3995          "transform",
3996          `matrix(${s * cosR},${s * sinR},${-s * sinR},${s * cosR},${tx},${ty})`
3997        );
3998      } else if (glueCfg && glueCfg.type === "one-point") {
3999        const t1Key = glueCfg.t1 || "body";
4000        const resolvedP1 = p1 || { x: 0, y: 0 };
4001        const T1 = getJointCoordinate(t1Key);
4002        const rot = getJointRotation(t1Key);
4003        const rotRad1 = rot * Math.PI / 180;
4004        const cosR1 = Math.cos(rotRad1);
4005        const sinR1 = Math.sin(rotRad1);
4006        const tx1 = T1.x - (resolvedP1.x * cosR1 - resolvedP1.y * sinR1);
4007        const ty1 = T1.y - (resolvedP1.x * sinR1 + resolvedP1.y * cosR1);
4008        clothNode.style.transform = "";
4009        clothNode.setAttribute(
4010          "transform",
4011          `matrix(${cosR1},${sinR1},${-sinR1},${cosR1},${tx1},${ty1})`
4012        );
4013      } else {
4014        if (jointId === "body") {
4015          const bodyBase = getBase("body");
4016          const ut = ((_c2 = config2.transforms) == null ? void 0 : _c2["body"]) || { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0 };
4017          gsapWithCSS.set(clothNode, {
4018            x: bodyBase.x + state2.joints.body.x + (ut.x || 0),
4019            y: bodyBase.y + state2.joints.body.y + (ut.y || 0),
4020            rotation: bodyBase.rotation + state2.joints.body.rotation + (ut.rotation || 0),
4021            scaleX: ut.scaleX ?? 1,
4022            scaleY: ut.scaleY ?? 1,
4023            transformOrigin: bodyBase.originCenterCenter || "center center"
4024          });
4025        } else if (jointId === "head") {
4026          const headBase = getBase("head");
4027          const ut = ((_d2 = config2.transforms) == null ? void 0 : _d2["head"]) || { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0 };
4028          let targetX = state2.joints.head.x;
4029          let targetY = state2.joints.head.y;
4030          if (specificGraphic2 && typeof specificGraphic2 === "object" && specificGraphic2.headAnchor) {
4031            let dx = 0;
4032            let dy = 0;
4033            if (specificGraphic2.mountPoint && specificGraphic2.mountPoint !== "root" && specificGraphic2.mountPoint !== "front") {
4034              const mountState = state2.joints[specificGraphic2.mountPoint];
4035              if (mountState) {
4036                dx = mountState.x || 0;
4037                dy = mountState.y || 0;
4038              }
4039            }
4040            targetX = specificGraphic2.headAnchor.x - headBase.x + dx;
4041            targetY = specificGraphic2.headAnchor.y - headBase.y + dy;
4042          }
4043          gsapWithCSS.set(clothNode, {
4044            x: headBase.x + targetX + (ut.x || 0),
4045            y: headBase.y + targetY + (ut.y || 0),
4046            rotation: headBase.rotation + state2.joints.head.rotation + (ut.rotation || 0),
4047            scaleX: ut.scaleX ?? 1,
4048            scaleY: ut.scaleY ?? 1,
4049            transformOrigin: headBase.originCenterBottom || "center bottom"
4050          });
4051        } else if ((jointId == null ? void 0 : jointId.startsWith("upper_arm_")) || (jointId == null ? void 0 : jointId.startsWith("lower_arm_")) || (jointId == null ? void 0 : jointId.startsWith("hand_"))) {
4052          const side = jointId.endsWith("_l") ? "l" : "r";
4053          const type = jointId.startsWith("upper_arm_") ? "upper_arm" : jointId.startsWith("lower_arm_") ? "lower_arm" : "hand";
4054          const jointState = state2.joints[`${type}_${side}`];
4055          if (jointState) {
4056            const ut = ((_e2 = config2.transforms) == null ? void 0 : _e2[`${type}_${side}`]) || { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0 };
4057            if (type === "upper_arm") {
4058              const shoulderState = state2.joints[`shoulder_${side}`];
4059              if (shoulderState) {
4060                gsapWithCSS.set(clothNode, {
4061                  x: shoulderState.x + (ut.x || 0),
4062                  y: shoulderState.y + (ut.y || 0),
4063                  rotation: (jointState.rotation || 0) + (ut.rotation || 0),
4064                  scaleX: ut.scaleX ?? 1,
4065                  scaleY: ut.scaleY ?? 1,
4066                  transformOrigin: "0px 0px"
4067                });
4068              }
4069            } else if (type === "lower_arm") {
4070              const elbowState = state2.joints[`elbow_${side}`];
4071              if (elbowState) {
4072                gsapWithCSS.set(clothNode, {
4073                  x: elbowState.x + (ut.x || 0),
4074                  y: elbowState.y + (ut.y || 0),
4075                  rotation: (jointState.rotation || 0) + (ut.rotation || 0),
4076                  scaleX: ut.scaleX ?? 1,
4077                  scaleY: ut.scaleY ?? 1,
4078                  transformOrigin: "0px 0px"
4079                });
4080              }
4081            } else {
4082              gsapWithCSS.set(clothNode, {
4083                x: jointState.x + (ut.x || 0),
4084                y: jointState.y + (ut.y || 0),
4085                rotation: (jointState.rotation || 0) + (ut.rotation || 0),
4086                scaleX: ut.scaleX ?? 1,
4087                scaleY: ut.scaleY ?? 1,
4088                transformOrigin: "center center"
4089              });
4090            }
4091          }
4092        } else if ((jointId == null ? void 0 : jointId.startsWith("leg_")) || (jointId == null ? void 0 : jointId.startsWith("foot_"))) {
4093          const side = jointId.endsWith("_l") ? "l" : "r";
4094          const type = jointId.startsWith("leg_") ? "leg" : "foot";
4095          const baseJoint = getBase(`${type}_${side}`);
4096          const jointState = state2.joints[`${type}_${side}`];
4097          if (jointState) {
4098            const ut = ((_f = config2.transforms) == null ? void 0 : _f[`${type}_${side}`]) || { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0 };
4099            gsapWithCSS.set(clothNode, {
4100              x: baseJoint.x + jointState.x + (ut.x || 0),
4101              y: baseJoint.y + jointState.y + (ut.y || 0),
4102              rotation: baseJoint.rotation + jointState.rotation + (ut.rotation || 0),
4103              scaleX: ut.scaleX ?? 1,
4104              scaleY: ut.scaleY ?? 1,
4105              transformOrigin: type === "leg" ? baseJoint.originCenterTop || "center top" : baseJoint.originCenterCenter || "center center"
4106            });
4107          }
4108        }
4109      }
4110    });
4111    const innerSvg = (_e = svgRef.current) == null ? void 0 : _e.querySelector("svg");
4112    if (innerSvg) {
4113      let diagG = innerSvg.querySelector("#diagnostic-skeleton-inner");
4114      if (!showSkeletonRef.current) {
4115        if (diagG) diagG.remove();
4116      } else {
4117        if (!diagG) {
4118          diagG = document.createElementNS("http://www.w3.org/2000/svg", "g");
4119          diagG.setAttribute("id", "diagnostic-skeleton-inner");
4120          diagG.setAttribute("pointer-events", "none");
4121          diagG.setAttribute("opacity", "0.8");
4122          innerSvg.appendChild(diagG);
4123        }
4124        const sL = state2.joints.shoulder_l || { x: 0, y: 0 };
4125        const eL = state2.joints.elbow_l || { x: 0, y: 0 };
4126        const hL = state2.joints.hand_l || { x: 0, y: 0 };
4127        const sR = state2.joints.shoulder_r || { x: 0, y: 0 };
4128        const eR = state2.joints.elbow_r || { x: 0, y: 0 };
4129        const hR = state2.joints.hand_r || { x: 0, y: 0 };
4130        diagG.innerHTML = `
4131                    <!-- Left Arm Skeleton -->
4132                    <line x1="${sL.x}" y1="${sL.y}" x2="${eL.x}" y2="${eL.y}" stroke="red" stroke-width="2" stroke-dasharray="4" />
4133                    <line x1="${eL.x}" y1="${eL.y}" x2="${hL.x}" y2="${hL.y}" stroke="red" stroke-width="2" stroke-dasharray="4" />
4134                    <circle cx="${sL.x}" cy="${sL.y}" r="4" fill="yellow" />
4135                    <circle cx="${eL.x}" cy="${eL.y}" r="4" fill="yellow" />
4136                    <circle cx="${hL.x}" cy="${hL.y}" r="4" fill="yellow" />
4137
4138                    <!-- Right Arm Skeleton -->
4139                    <line x1="${sR.x}" y1="${sR.y}" x2="${eR.x}" y2="${eR.y}" stroke="red" stroke-width="2" stroke-dasharray="4" />
4140                    <line x1="${eR.x}" y1="${eR.y}" x2="${hR.x}" y2="${hR.y}" stroke="red" stroke-width="2" stroke-dasharray="4" />
4141                    <circle cx="${sR.x}" cy="${sR.y}" r="4" fill="yellow" />
4142                    <circle cx="${eR.x}" cy="${eR.y}" r="4" fill="yellow" />
4143                    <circle cx="${hR.x}" cy="${hR.y}" r="4" fill="yellow" />
4144                `;
4145      }
4146    }
4147  });
4148  const renderSkeleton = () => {
4149    return null;
4150  };
4151  const renderActionPaths = () => {
4152    if (!showActionPaths || !globalActions) return null;
4153    const actionsToShow = activeTab === "actions" && selectedActionId ? globalActions.filter((a) => a.id === selectedActionId) : globalActions.filter((a) => a.trigger === "playing");
4154    const CHANNEL_COLORS = {
4155      "body": "#818cf8",
4156      "head": "#f472b6",
4157      "foot_r": "#34d399",
4158      "foot_l": "#34d399",
4159      "hand_l": "#f59e0b",
4160      "hand_r": "#f59e0b",
4161      "upper_arm_l": "#60a5fa",
4162      "upper_arm_r": "#60a5fa",
4163      "lower_arm_l": "#a78bfa",
4164      "lower_arm_r": "#a78bfa",
4165      "leg_l": "#fb923c",
4166      "leg_r": "#fb923c"
4167    };
4168    const zoom = viewZoom || 1;
4169    return /* @__PURE__ */ jsxRuntimeExports.jsx("g", { id: "action-paths-overlay", transform: `translate(${config.anchorX}, ${config.anchorY})`, pointerEvents: "auto", opacity: "0.8", children: actionsToShow.flatMap(
4170      (action) => action.channels.map((ch, ci) => {
4171        const jointPt = getJointCoordinate(ch.joint, true);
4172        const delta = state.joints[ch.joint] || { x: 0, y: 0 };
4173        const jx = jointPt.x - (delta.x || 0);
4174        const jy = jointPt.y - (delta.y || 0);
4175        const color = CHANNEL_COLORS[ch.joint] || "#818cf8";
4176        if (ch.type === "wave") {
4177          const amp = Math.min(20, Math.abs(ch.amplitude || 10));
4178          const isVertical = ch.property === "y";
4179          const pts = [];
4180          for (let i = 0; i <= 20; i++) {
4181            const t = i / 20;
4182            const wave = Math.sin(t * Math.PI * 2 * (ch.frequency || 1)) * amp * 0.3;
4183            if (isVertical) {
4184              pts.push(`${jx + (t - 0.5) * 30},${jy + wave}`);
4185            } else if (ch.property === "x") {
4186              pts.push(`${jx + wave},${jy + (t - 0.5) * 30}`);
4187            } else {
4188              pts.push(`${jx + (t - 0.5) * 30},${jy - 15 + wave}`);
4189            }
4190          }
4191          const handleX = isVertical ? jx : ch.property === "x" ? j
4191x - amp * 0.3 : jx;
4192          const handleY = isVertical ? jy - amp * 0.3 : ch.property === "x" ? jy : jy - 15 - amp * 0.3;
4193          return /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { children: [
4194            /* @__PURE__ */ jsxRuntimeExports.jsx("polyline", { points: pts.join(" "), fill: "none", stroke: color, strokeWidth: "1.5", strokeLinecap: "round", pointerEvents: "none" }),
4195            /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: jx, cy: jy, r: "3", fill: color, opacity: "0.3", pointerEvents: "none" }),
4196            /* @__PURE__ */ jsxRuntimeExports.jsx(
4197              "circle",
4198              {
4199                cx: handleX,
4200                cy: handleY,
4201                r: 3 / zoom,
4202                fill: "white",
4203                stroke: color,
4204                strokeWidth: 1 / zoom,
4205                className: "cursor-grab active:cursor-grabbing",
4206                pointerEvents: "auto",
4207                onMouseDown: (e) => {
4208                  e.stopPropagation();
4209                  e.preventDefault();
4210                  onActionOverlayMouseDown == null ? void 0 : onActionOverlayMouseDown({
4211                    target: "action_wave_amp",
4212                    actionId: action.id,
4213                    channelIdx: ci,
4214                    isVertical,
4215                    isX: ch.property === "x",
4216                    jx,
4217                    jy
4218                  });
4219                }
4220              }
4221            ),
4222            /* @__PURE__ */ jsxRuntimeExports.jsxs("text", { x: jx + 5, y: jy - 5, fill: color, fontSize: "4", fontFamily: "monospace", pointerEvents: "none", children: [
4223              ch.joint,
4224              ".",
4225              ch.property
4226            ] })
4227          ] }, `${action.id}-${ci}`);
4228        } else if (ch.type === "keyframe" && ch.keyframes) {
4229          const maxV = Math.max(20, ...(ch.keyframes || []).map((k) => Math.abs(k.value)));
4230          return /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { children: [
4231            /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: jx - 15, y1: jy - 12, x2: jx + 15, y2: jy - 12, stroke: color, strokeWidth: "0.5", opacity: "0.4", pointerEvents: "none" }),
4232            ch.keyframes.map((kf, ki) => /* @__PURE__ */ jsxRuntimeExports.jsx(
4233              "circle",
4234              {
4235                cx: jx - 15 + kf.time * 30,
4236                cy: jy - 12 - kf.value / maxV * 8,
4237                r: 2.5 / zoom,
4238                fill: color,
4239                stroke: "white",
4240                strokeWidth: 0.5 / zoom,
4241                className: "cursor-grab active:cursor-grabbing",
4242                pointerEvents: "auto",
4243                onMouseDown: (e) => {
4244                  e.stopPropagation();
4245                  e.preventDefault();
4246                  onActionOverlayMouseDown == null ? void 0 : onActionOverlayMouseDown({
4247                    target: "action_keyframe",
4248                    actionId: action.id,
4249                    channelIdx: ci,
4250                    keyframeIdx: ki,
4251                    startX: kf.time,
4252                    startY: kf.value,
4253                    maxV,
4254                    jx,
4255                    jy
4256                  });
4257                }
4258              },
4259              ki
4260            )),
4261            /* @__PURE__ */ jsxRuntimeExports.jsxs("text", { x: jx + 5, y: jy - 18, fill: color, fontSize: "4", fontFamily: "monospace", pointerEvents: "none", children: [
4262              ch.joint,
4263              ".",
4264              ch.property
4265            ] })
4266          ] }, `${action.id}-${ci}`);
4267        }
4268        return null;
4269      })
4270    ) });
4271  };
4272  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
4273    "g",
4274    {
4275      ref: containerRef,
4276      transform: `translate(${x}, ${y}) scale(${config.scale})`,
4277      opacity: isHidden ? 0.3 : 1,
4278      onClick,
4279      onContextMenu,
4280      onMouseEnter: () => onHover && onHover(true),
4281      onMouseLeave: () => onHover && onHover(false),
4282      onDragStart: (e) => e.preventDefault(),
4283      className: onClick ? "cursor-pointer group" : "group",
4284      children: [
4285        isSoloed && /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: config.anchorX, cy: config.anchorY - 100, r: 100, fill: "url(#soloGradient)", className: "pointer-events-none" }),
4286        /* @__PURE__ */ jsxRuntimeExports.jsx(
4287          "g",
4288          {
4289            ref: svgRef,
4290            className: "custom-character-svg",
4291            dangerouslySetInnerHTML: { __html: sanitizeSvgContent(config.svgContent) },
4292            transform: `translate(${config.anchorX}, ${config.anchorY})`
4293          }
4294        ),
4295        renderSkeleton(),
4296        renderActionPaths(),
4297        isSoloed && /* @__PURE__ */ jsxRuntimeExports.jsx(
4298          "text",
4299          {
4300            x: config.anchorX,
4301            y: config.anchorY - 220,
4302            textAnchor: "middle",
4303            fill: "#F59E0B",
4304            fontSize: "24",
4305            fontWeight: "bold",
4306            className: "pointer-events-none",
4307            children: "SOLO"
4308          }
4309        ),
4310        isMuted && !isSoloed && /* @__PURE__ */ jsxRuntimeExports.jsx(
4311          "text",
4312          {
4313            x: config.anchorX,
4314            y: config.anchorY - 220,
4315            textAnchor: "middle",
4316            fill: "#EF4444",
4317            fontSize: "24",
4318            fontWeight: "bold",
4319            className: "pointer-events-none",
4320            children: "MUTE"
4321          }
4322        ),
4323        instrumentName && /* @__PURE__ */ jsxRuntimeExports.jsx("foreignObject", { x: config.anchorX - 100, y: config.anchorY + 50, width: "200", height: "40", className: "overflow-visible pointer-events-none", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `flex justify-center w-full transition-opacity duration-300 ${state.isPlaying || isSoloed ? "opacity-100" : "opacity-40"}`, children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "bg-black/70 rounded-full px-3 py-1 text-white text-sm font-bold text-center whitespace-nowrap", children: instrumentName }) }) })
4324      ]
4325    }
4326  );
4327};
4328const BackgroundParticlesCanvas = ({ config, isPlaying }) => {
4329  const canvasRef = reactExports.useRef(null);
4330  reactExports.useEffect(() => {
4331    const canvas = canvasRef.current;
4332    if (!canvas) return;
4333    const ctx = canvas.getContext("2d");
4334    if (!ctx) return;
4335    let animationFrameId;
4336    let width = canvas.width = canvas.offsetWidth || window.innerWidth;
4337    let height = canvas.height = canvas.offsetHeight || window.innerHeight;
4338    const handleResize = () => {
4339      if (!canvas) return;
4340      width = canvas.width = canvas.offsetWidth || window.innerWidth;
4341      height = canvas.height = canvas.offsetHeight || window.innerHeight;
4342    };
4343    window.addEventListener("resize", handleResize);
4344    class Particle {
4345      constructor() {
4346        this.x = Math.random() * width;
4347        this.y = Math.random() * height;
4348        let speed = (0.5 + Math.random() * 1.5) * config.speed;
4349        let angle = -Math.PI / 2;
4350        let drift = (Math.random() - 0.5) * 0.5;
4351        if (config.type === "snow" || config.type === "rain" || config.type === "confetti" || config.type === "autumn_leaves" || config.type === "matrix") {
4352          angle = Math.PI / 2;
4353        }
4354        if (config.type === "rain") {
4355          speed *= 3;
4356          drift = 0.1;
4357          this.length = 10 + Math.random() * 10;
4358        } else if (config.type === "matrix") {
4359          speed *= 1.5;
4360          drift = 0;
4361        } else if (config.type === "laser_lines") {
4362          angle = Math.random() > 0.5 ? 0 : Math.PI;
4363          speed *= 4;
4364          this.length = 50 + Math.random() * 100;
4365          drift = 0;
4366        } else if (config.type === "fireflies" || config.type === "dust" || config.type === "floating_orbs") {
4367          angle = Math.random() * Math.PI * 2;
4368          speed *= 0.3;
4369        }
4370        this.vx = Math.sin(angle + drift) * speed;
4371        this.vy = Math.cos(angle + drift) * speed;
4372        this.size = Math.random() * config.size + 1;
4373        this.alpha = Math.random() * 0.5 + 0.2;
4374        this.fadeSpeed = 2e-3 + Math.random() * 5e-3;
4375        this.angleMult = Math.random() * Math.PI * 2;
4376        if (config.type === "notes") {
4377          const notes = ["♩", "♪", "♫", "♬", "♭", "♮", "♯"];
4378          this.noteChar = notes[Math.floor(Math.random() * notes.length)];
4379        } else if (config.type === "matrix") {
4380          this.noteChar = Math.random() > 0.5 ? "0" : "1";
4381        } else if (config.type === "confetti") {
4382          const colors = ["#f44336", "#e91e63", "#9c27b0", "#673ab7", "#3f51b5", "#2196f3", "#03a9f4", "#00bcd4", "#009688", "#4caf50", "#8bc34a", "#cddc39", "#ffeb3b", "#ffc107", "#ff9800", "#ff5722"];
4383          this.color = colors[Math.floor(Math.random() * colors.length)];
4384        } else if (config.type === "autumn_leaves") {
4385          const colors = ["#d84315", "#e65100", "#ef6c00", "#f57c00", "#ff9800", "#ffb300", "#fbc02d", "#8d6e63"];
4386          this.color = colors[Math.floor(Math.random() * colors.length)];
4387        }
4388      }
4389      update() {
4390        if (config.type === "autumn_leaves" || config.type === "confetti") {
4391          this.vx = Math.sin(this.angleMult) * 2;
4392          this.angleMult += 0.05;
4393        } else if (config.type === "fireflies") {
4394          this.vx += (Math.random() - 0.5) * 0.2;
4395          this.vy += (Math.random() - 0.5) * 0.2;
4396          const currentSpeed = Math.sqrt(this.vx * this.vx + this.vy * this.vy);
4397          if (currentSpeed > 2) {
4398            this.vx *= 0.9;
4399            this.vy *= 0.9;
4400          }
4401        }
4402        this.x += this.vx;
4403        this.y += this.vy;
4404        const fallingTypes = ["snow", "rain", "confetti", "autumn_leaves", "matrix"];
4405        if (fallingTypes.includes(config.type)) {
4406          if (this.y > height + 20) {
4407            this.y = -20;
4408            this.x = Math.random() * width;
4409            if (config.type === "matrix") this.noteChar = Math.random() > 0.5 ? "0" : "1";
4410          }
4411        } else {
4412          if (this.y < -20) {
4413            this.y = height + 20;
4414            this.x = Math.random() * width;
4415          } else if (this.y > height + 20) {
4416            this.y = -20;
4417          }
4418        }
4419        if (this.x < -20) this.x = width + 20;
4420        if (this.x > width + 20) this.x = -20;
4421        if (["stars", "fireflies", "sparkles"].includes(config.type)) {
4422          this.alpha += this.fadeSpeed;
4423          if (this.alpha > (config.type === "fireflies" ? 1 : 0.8) || this.alpha < 0.1) {
4424            this.fadeSpeed = -this.fadeSpeed;
4425          }
4426        } else if (config.type === "matrix") {
4427          if (Math.random() < 0.05) this.noteChar = Math.random() > 0.5 ? "0" : "1";
4428        }
4429      }
4430      draw(c) {
4431        c.save();
4432        c.globalAlpha = Math.max(0, Math.min(1, this.alpha));
4433        c.fillStyle = this.color || config.color;
4434        c.strokeStyle = this.color || config.color;
4435        if (config.type === "notes" && this.noteChar) {
4436          c.font = `${this.size * 5 + 10}px sans-serif`;
4437          c.fillText(this.noteChar, this.x, this.y);
4438        } else if (config.type === "matrix" && this.noteChar) {
4439          c.font = `${this.size * 5 + 10}px monospace`;
4440          c.fillStyle = "#0f0";
4441          c.fillText(this.noteChar, this.x, this.y);
4442        } else if (config.type === "bubbles") {
4443          c.beginPath();
4444          c.arc(this.x, this.y, this.size, 0, Math.PI * 2);
4445          c.lineWidth = 1.5;
4446          c.stroke();
4447        } else if (config.type === "rain" || config.type === "laser_lines") {
4448          c.beginPath();
4449          c.moveTo(this.x, this.y);
4450          c.lineTo(this.x - this.vx * (this.length || 10) * 0.1, this.y - this.vy * (this.length || 10) * 0.1);
4451          c.lineWidth = this.size;
4452          c.stroke();
4453        } else if (config.type === "hearts") {
4454          c.beginPath();
4455          const s = this.size * 2;
4456          c.moveTo(this.x, this.y + s / 4);
4457          c.quadraticCurveTo(this.x, this.y, this.x - s / 2, this.y);
4458          c.quadraticCurveTo(this.x - s, this.y, this.x - s, this.y + s / 2.5);
4459          c.quadraticCurveTo(this.x - s, this.y + s, this.x, this.y + s * 1.5);
4460          c.quadraticCurveTo(this.x + s, this.y + s, this.x + s, this.y + s / 2.5);
4461          c.quadraticCurveTo(this.x + s, this.y, this.x + s / 2, this.y);
4462          c.quadraticCurveTo(this.x, this.y, this.x, this.y + s / 4);
4463          c.fill();
4464        } else if (config.type === "confetti" || config.type === "autumn_leaves") {
4465          c.translate(this.x, this.y);
4466          c.rotate(this.angleMult);
4467          c.fillRect(-this.size, -this.size / 2, this.size * 2, this.size);
4468        } else if (config.type === "sparkles") {
4469          c.beginPath();
4470          c.moveTo(this.x, this.y - this.size * 2);
4471          c.lineTo(this.x + this.size / 2, this.y - this.size / 2);
4472          c.lineTo(this.x + this.size * 2, this.y);
4473          c.lineTo(this.x + this.size / 2, this.y + this.size / 2);
4474          c.lineTo(this.x, this.y + this.size * 2);
4475          c.lineTo(this.x - this.size / 2, this.y + this.size / 2);
4476          c.lineTo(this.x - this.size * 2, this.y);
4477          c.lineTo(this.x - this.size / 2, this.y - this.size / 2);
4478          c.closePath();
4479          c.fill();
4480        } else if (config.type === "floating_orbs") {
4481          c.beginPath();
4482          c.arc(this.x, this.y, this.size * 3, 0, Math.PI * 2);
4483          c.fill();
4484        } else {
4485          c.beginPath();
4486          c.arc(this.x, this.y, this.size, 0, Math.PI * 2);
4487          c.fill();
4488        }
4489        c.restore();
4490      }
4491    }
4492    const particles = [];
4493    for (let i = 0; i < config.count; i++) {
4494      particles.push(new Particle());
4495    }
4496    const render = () => {
4497      ctx.clearRect(0, 0, width, height);
4498      particles.forEach((p) => {
4499        p.update();
4500        p.draw(ctx);
4501      });
4502      animationFrameId = requestAnimationFrame(render);
4503    };
4504    render();
4505    return () => {
4506      cancelAnimationFrame(animationFrameId);
4507      window.removeEventListener("resize", handleResize);
4508    };
4509  }, [config]);
4510  return /* @__PURE__ */ jsxRuntimeExports.jsx(
4511    "canvas",
4512    {
4513      ref: canvasRef,
4514      className: "absolute inset-0 w-full h-full pointer-events-none",
4515      style: { zIndex: 3 }
4516    }
4517  );
4518};
4519const StagePuppetEngine = ({
4520  track,
4521  currentTime,
4522  isPlaying,
4523  pianoRollData,
4524  processedInstruments,
4525  uniqueNames,
4526  soloedIndex,
4527  onSolo,
4528  mutedIndices = /* @__PURE__ */ new Set(),
4529  onMute = () => {
4530  },
4531  onPanChange,
4532  puppetConfigs,
4533  instrumentConfigs,
4534  clothingConfigs,
4535  backgroundConfig,
4536  hiddenTracks = /* @__PURE__ */ new Set()
4537}) => {
4538  var _a, _b, _c, _d;
4539  const musicians = reactExports.useMemo(() => {
4540    if (!processedInstruments) return [];
4541    const activeTrackIndicesWithNotes = /* @__PURE__ */ new Set();
4542    const activeTrackNamesWithNotes = /* @__PURE__ */ new Set();
4543    if (Array.isArray(pianoRollData) && pianoRollData.length > 0) {
4544      for (let i = 0; i < pianoRollData.length; i++) {
4545        const n = pianoRollData[i];
4546        if (n) {
4547          if (typeof n.trackIdx === "number" && n.trackIdx >= 0) {
4548            activeTrackIndicesWithNotes.add(n.trackIdx);
4549          }
4550          if (typeof n.trackId === "number" && n.trackId >= 0) {
4551            activeTrackIndicesWithNotes.add(n.trackId);
4552          }
4553          if (typeof n.i === "
4553number") {
4554            activeTrackIndicesWithNotes.add(n.i);
4555          } else if (typeof n.i === "string" && n.i) {
4556            activeTrackNamesWithNotes.add(n.i);
4557          }
4558        }
4559      }
4560    }
4561    return processedInstruments.map((inst, idx) => ({ ...inst, index: idx })).filter((inst) => {
4562      if (inst.midiChannel === -1 || inst.channel === -1) {
4563        return false;
4564      }
4565      if (inst.notesCount === 0 || inst.notes_count === 0) {
4566        return false;
4567      }
4568      const name = ((inst == null ? void 0 : inst.displayName) || (inst == null ? void 0 : inst.name) || (inst == null ? void 0 : inst.originalName) || "").toLowerCase();
4569      if (name.includes("informational") || name.includes("marker") || name.includes("conductor") || name.includes("tempo") || name === "informational track") {
4570        return false;
4571      }
4572      const uniqueName = uniqueNames[inst.index] || inst.displayName || inst.name;
4573      if (hiddenTracks && hiddenTracks.has(uniqueName)) {
4574        return false;
4575      }
4576      if (Array.isArray(pianoRollData) && pianoRollData.length > 0) {
4577        const hasNoteIndex = activeTrackIndicesWithNotes.has(inst.index) || inst.trackId !== void 0 && activeTrackIndicesWithNotes.has(inst.trackId);
4578        const hasNoteName = activeTrackNamesWithNotes.has(uniqueName) || activeTrackNamesWithNotes.has(inst.displayName) || activeTrackNamesWithNotes.has(inst.name) || activeTrackNamesWithNotes.has(inst.originalName);
4579        if (!hasNoteIndex && !hasNoteName) {
4580          return false;
4581        }
4582      }
4583      return true;
4584    }).map((inst) => {
4585      const program = inst.program;
4586      const channel = inst.midiChannel;
4587      let specificGraphic = null;
4588      if (instrumentConfigs) {
4589        if (channel === 9) {
4590          specificGraphic = instrumentConfigs.percussion[35] || null;
4591        } else {
4592          specificGraphic = instrumentConfigs.melodic[program] || null;
4593        }
4594      }
4595      let familyId = "family_0_pianos";
4596      if (specificGraphic && typeof specificGraphic !== "string" && specificGraphic.characterId) {
4597        familyId = specificGraphic.characterId;
4598      } else if (channel === 9) {
4599        familyId = "percussion";
4600      } else {
4601        const familyIndex = Math.floor(program / 8);
4602        const familyKeys = [
4603          "family_0_pianos",
4604          "family_1_chromatic_percussion",
4605          "family_2_organs",
4606          "family_3_guitars",
4607          "family_4_basses",
4608          "family_5_strings",
4609          "family_6_ensemble",
4610          "family_7_brass",
4611          "family_8_reed",
4612          "family_9_pipe",
4613          "family_10_synth_lead",
4614          "family_11_synth_pad",
4615          "family_12_synth_effects",
4616          "family_13_ethnic",
4617          "family_14_percussive",
4618          "family_15_sound_effects"
4619        ];
4620        familyId = familyKeys[familyIndex] || "family_0_pianos";
4621      }
4622      const rawConfig = puppetConfigs ? puppetConfigs[familyId] || puppetConfigs["family_0_pianos"] || Object.values(puppetConfigs)[0] : null;
4623      if (!rawConfig) return null;
4624      const config = mergeWithGorillaConfig(rawConfig, puppetConfigs);
4625      const animator = createAnimator(config);
4626      const allActions = config.actions && config.actions.length > 0 ? config.actions : getDefaultBodyActions();
4627      const allRules = config.actionRules && config.actionRules.length > 0 ? config.actionRules : [...defaultActionRules];
4628      const musicianFamily = config.family || "other";
4629      const matchedActionIds = /* @__PURE__ */ new Set();
4630      for (const rule of allRules) {
4631        if (!rule.enabled) continue;
4632        let matches = false;
4633        switch (rule.condition.scope) {
4634          case "all":
4635            matches = true;
4636            break;
4637          case "family":
4638            matches = (rule.condition.families || []).includes(musicianFamily);
4639            break;
4640          case "selected":
4641            matches = (rule.condition.characterIds || []).includes(familyId);
4642            break;
4643        }
4644        if (matches) {
4645          rule.actionIds.forEach((id) => matchedActionIds.add(id));
4646        }
4647      }
4648      if (matchedActionIds.size > 0) {
4649        const filteredActions = allActions.filter((a) => matchedActionIds.has(a.id));
4650        animator.actionEngine.setActions(filteredActions);
4651      }
4652      let clothingGraphic = null;
4653      if (specificGraphic && typeof specificGraphic !== "string" && specificGraphic.clothingId === "
4653none") {
4654        clothingGraphic = null;
4655      } else if (specificGraphic && typeof specificGraphic !== "string" && specificGraphic.clothingId && clothingConfigs && clothingConfigs[specificGraphic.clothingId]) {
4656        clothingGraphic = clothingConfigs[specificGraphic.clothingId];
4657      } else if (specificGraphic && typeof specificGraphic !== "string" && specificGraphic.clothingId && defaultClothingGraphics[specificGraphic.clothingId]) {
4658        clothingGraphic = defaultClothingGraphics[specificGraphic.clothingId];
4659      } else {
4660        const availableClothes = clothingConfigs ? Object.values(clothingConfigs) : Object.values(defaultClothingGraphics);
4661        if (availableClothes.length > 0) {
4662          const randomIdx = inst.index * 137 % availableClothes.length;
4663          clothingGraphic = availableClothes[randomIdx];
4664        }
4665      }
4666      return {
4667        index: inst.index,
4668        instrumentName: uniqueNames[inst.index] || inst.displayName || inst.name,
4669        uniqueName: uniqueNames[inst.index] || inst.displayName || inst.name,
4670        config,
4671        animator,
4672        specificGraphic,
4673        clothingGraphic,
4674        program,
4675        isPercussion: channel === 9,
4676        familyIndex: channel === 9 ? -1 : Math.floor(program / 8)
4677      };
4678    }).filter(Boolean);
4679  }, [processedInstruments, uniqueNames, puppetConfigs, instrumentConfigs, clothingConfigs, hiddenTracks]);
4680  const bpm = ((_a = track.midiMetadata) == null ? void 0 : _a.tempo) || 120;
4681  const bps = bpm / 60;
4682  useAnimationFrame((time) => {
4683    const currentActive = {};
4684    if (isPlaying && pianoRollData.length > 0) {
4685      let left = 0;
4686      let right = pianoRollData.length - 1;
4687      let startIdx = 0;
4688      const searchTime = Math.max(0, currentTime - 4);
4689      while (left <= right) {
4690        const mid = Math.floor((left + right) / 2);
4691        if (pianoRollData[mid].t < searchTime) {
4692          left = mid + 1;
4693          startIdx = left;
4694        } else {
4695          right = mid - 1;
4696        }
4697      }
4698      for (let i = startIdx; i < pianoRollData.length; i++) {
4699        const note = pianoRollData[i];
4700        if (note.t > currentTime + 0.1) break;
4701        if (currentTime >= note.t && currentTime <= note.t + note.d) {
4702          if (!currentActive[note.i]) currentActive[note.i] = [];
4703          currentActive[note.i].push({
4704            noteNumber: note.n,
4705            velocity: note.v,
4706            startTime: note.t,
4707            duration: note.d
4708          });
4709        }
4710      }
4711    }
4712    musicians.forEach((m) => {
4713      const notes = currentActive[m.uniqueName] || [];
4714      m.animator.tick(time, notes, bps, isPlaying, m.specificGraphic ?? null);
4715    });
4716  });
4717  const handleMusicianClick = (e, index) => {
4718    e.stopPropagation();
4719    onSolo(index);
4720  };
4721  const handleMusicianContextMenu = (e, index) => {
4722    e.preventDefault();
4723    e.stopPropagation();
4724    onMute(index);
4725  };
4726  const stageWidth = 1e3;
4727  const stageHeight = 600;
4728  const renderStage = () => {
4729    const count = musicians.length;
4730    if (count === 0) return /* @__PURE__ */ jsxRuntimeExports.jsx("text", { x: stageWidth / 2, y: stageHeight / 2, fill: "white", textAnchor: "middle", className: "text-sm opacity-50", children: "Awaiting Musicians..." });
4731    if (count === 1) {
4732      const m = musicians[0];
4733      return /* @__PURE__ */ jsxRuntimeExports.jsx(
4734        PuppetCharacter,
4735        {
4736          config: { ...m.config, scale: m.config.scale * 1.5 },
4737          state: m.animator.getState(),
4738          x: stageWidth / 2,
4739          y: stageHeight * 0.7,
4740          isSoloed: soloedIndex === m.index,
4741          isMuted: mutedIndices.has(m.index),
4742          isHidden: soloedIndex !== null && soloedIndex !== m.index,
4743          instrumentName: m.instrumentName,
4744          specificGraphic: m.specificGraphic,
4745          clothingGraphic: m.clothingGraphic,
4746          onClick: (e) => handleMusicianClick(e, m.index),
4747          onContextMenu: (e) => handleMusicianContextMenu(e, m.index),
4748          onHover: (h) => synth.setHoverBoost(m.index, h)
4749        },
4750        m.index
4751      );
4752    }
4753    const backRow = [];
4754    const middleRow = [];
4755    const frontRow = [];
4756    const familySortWeights = {
4757      0: 10,
4758      // Pianos -> Left (Back)
4759      1: 20,
4760      // Chrom Perc
4761      2: 30,
4762      // Organ
4763      // Drums are 50 (Back center)
4764      10: 60,
4765      // Synth Lead
4766      11: 70,
4767      // Synth Pad
4768      12: 80,
4769      // Synth FX
4770      13: 90,
4771      // Ethnic
4772      14: 100,
4773      // Percussive
4774      15: 110,
4775      // FX
4776      // Middle Row:
4777      3: 10,
4778      // Guitar -> Left
4779      4: 20,
4780      // Bass -> Mid-left
4781      9: 30,
4782      // Pipe (Flutes) -> Center
4783      8: 40,
4784      // Reed (Oboe) -> Center
4785      6: 50,
4786      // Ensemble -> Center/Right
4787      7: 60,
4788      // Brass -> Right
4789      // Front Row:
4790      5: 10
4791      // Strings (Violin -> 10, Cello -> 12, etc.)
4792    };
4793    musicians.forEach((m) => {
4794      if (m.isPercussion) {
4795        m.sortWeight = 50 * 100;
4796        backRow.push(m);
4797      } else {
4798        const weightBase = familySortWeights[m.familyIndex] || 50;
4799        m.sortWeight = weightBase * 100 + m.program;
4800        if ([0, 1, 2, 10, 11, 12, 13, 14, 15].includes(m.familyIndex)) {
4801          backRow.push(m);
4802        } else if ([3, 4, 6, 7, 8, 9].includes(m.familyIndex)) {
4803          middleRow.push(m);
4804        } else if ([5].includes(m.familyIndex)) {
4805          frontRow.push(m);
4806        } else {
4807          middleRow.push(m);
4808        }
4809      }
4810    });
4811    backRow.sort((a, b) => a.sortWeight - b.sortWeight);
4812    middleRow.sort((a, b) => a.sortWeight - b.sortWeight);
4813    frontRow.sort((a, b) => a.sortWeight - b.sortWeight);
4814    const rows = [backRow, middleRow, frontRow].filter((r) => r.length > 0);
4815    const renderRow = (rowMusicians, rIndex) => {
4816      const rowDepth = 3 - rows.length + rIndex;
4817      return rowMusicians.map((m, i) => {
4818        const rowCount = rowMusicians.length;
4819        let col = i;
4820        let actualRowCount = rowCount;
4821        let stagger = 0;
4822        if (rowCount > 8) {
4823          const subRows = Math.ceil(rowCount / 8);
4824          const subRowIndex = i % subRows;
4825          col = Math.floor(i / subRows);
4826          actualRowCount = Math.ceil(rowCount / subRows);
4827          stagger = subRowIndex * 30;
4828        } else if (rowCount > 5) {
4829          stagger = i % 2 === 0 ? -15 : 15;
4830        }
4831        const stageSpread = 800;
4832        const xOffset = (stageWidth - stageSpread) / 2;
4833        const x = xOffset + stageSpread / (actualRowCount + 1) * (col + 1);
4834        const baseY = stageHeight * 0.35 + rowDepth * 110;
4835        const y = baseY + stagger;
4836        const scaleAdj = 0.9 + rowDepth * 0.3;
4837        return /* @__PURE__ */ jsxRuntimeExports.jsx(
4838          motion.g,
4839          {
4840            drag: true,
4841            dragMomentum: false,
4842            initial: { x, y },
4843            onClick: (e) => e.stopPropagation(),
4844            onDragStart: () => {
4845              window.isPuppetDragging = true;
4846            },
4847            onDrag: (event, info) => {
4848              const target = event.target;
4849              if (!target || !target.getBoundingClientRect) return;
4850              const rect = target.getBoundingClientRect();
4851              const centerX = rect.left + rect.width / 2;
4852              const windowWidth = window.innerWidth;
4853              let pan = centerX / windowWidth * 2 - 1;
4854              pan = Math.max(-1, Math.min(1, pan));
4855              synth.setPan(m.index, pan);
4856            },
4857            onDragEnd: (event, info) => {
4858              const target = event.target;
4859              if (!target || !target.getBoundingClientRect) return;
4860              const rect = target.getBoundingClientRect();
4861              const centerX = rect.left + rect.width / 2;
4862              const windowWidth = window.innerWidth;
4863              let pan = centerX / windowWidth * 2 - 1;
4864              pan = Math.max(-1, Math.min(1, pan));
4865              if (onPanChange) {
4866                onPanChange(m.index, pan);
4867              }
4868              setTimeout(() => {
4869                window.isPuppetDragging = false;
4870              }, 50);
4871            },
4872            style: { cursor: "grab" },
4873            whileDrag: { cursor: "grabbing", scale: 1.1 },
4874            children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4875              PuppetCharacter,
4876              {
4877                config: { ...m.config, scale: m.config.scale * scaleAdj },
4878                state: m.animator.getState(),
4879                x: 0,
4880                y: 0,
4881                isSoloed: soloedIndex === m.index,
4882                isMuted: mutedIndices.has(m.index),
4883                isHidden: soloedIndex !== null && soloedIndex !== m.index,
4884                instrumentName: m.instrumentName,
4885                specificGraphic: m.specificGraphic,
4886                clothingGraphic: m.clothingGraphic,
4887                onClick: (e) =>
4887 handleMusicianClick(e, m.index),
4888                onContextMenu: (e) => handleMusicianContextMenu(e, m.index),
4889                onHover: (h) => synth.setHoverBoost(m.index, h)
4890              },
4891              m.index
4892            )
4893          },
4894          `drag-${m.index}`
4895        );
4896      });
4897    };
4898    return rows.flatMap((rowMusicians, rIndex) => renderRow(rowMusicians, rIndex));
4899  };
4900  const bg = backgroundConfig || defaultBackgroundConfig;
4901  let bgStyle = {};
4902  if (bg.type === "color") {
4903    bgStyle = { backgroundColor: bg.color };
4904  } else if (bg.type === "gradient") {
4905    const { type, angle, stops } = bg.gradient;
4906    const stopStr = stops.slice().sort((a, b) => a.offset - b.offset).map((s) => `${s.color} ${s.offset}%`).join(", ");
4907    if (type === "radial") {
4908      bgStyle = { background: `radial-gradient(circle, ${stopStr})` };
4909    } else {
4910      bgStyle = { background: `linear-gradient(${angle}deg, ${stopStr})` };
4911    }
4912  } else if (bg.type === "image") {
4913    bgStyle = { backgroundColor: bg.color || "#050505" };
4914  }
4915  const imageStyle = bg.type === "image" ? {
4916    backgroundImage: `url("${bg.image.url}")`,
4917    backgroundSize: bg.image.size,
4918    backgroundPosition: bg.image.position,
4919    backgroundRepeat: bg.image.repeat,
4920    filter: bg.image.blur ? `blur(${bg.image.blur}px)` : void 0,
4921    transform: bg.image.blur ? "scale(1.05)" : void 0
4922  } : {};
4923  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
4924    "div",
4925    {
4926      className: "w-full h-full flex items-center justify-center bg-[#050505] overflow-hidden relative",
4927      onContextMenu: (e) => e.preventDefault(),
4928      style: bgStyle,
4929      children: [
4930        bg.type === "image" && ((_b = bg.image) == null ? void 0 : _b.url) && /* @__P
4930URE__ */ jsxRuntimeExports.jsx(
4931          "div",
4932          {
4933            className: "absolute inset-0 bg-cover bg-center",
4934            style: {
4935              ...imageStyle,
4936              zIndex: 1
4937            }
4938          }
4939        ),
4940        ((_c = bg.overlay) == null ? void 0 : _c.enabled) && /* @__PURE__ */ jsxRuntimeExports.jsx(
4941          "div",
4942          {
4943            className: "absolute inset-0 pointer-events-none",
4944            style: {
4945              backgroundColor: bg.overlay.color,
4946              opacity: bg.overlay.opacity,
4947              zIndex: 2
4948            }
4949          }
4950        ),
4951        ((_d = bg.particles) == null ? void 0 : _d.enabled) && /* @__PURE__ */ jsxRuntimeExports.jsx(BackgroundParticlesCanvas, { config: bg.particles, isPlaying }),
4952        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 z-[4] pointer-events-none", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute bottom-0 left-0 right-0 h-1/2 bg-gradient-to-t from-black/80 to-transparent" }) }),
4953        /* @__PURE__ */ jsxRuntimeExports.jsxs(
4954          "svg",
4955          {
4956            viewBox: `0 0 ${stageWidth} ${stageHeight}`,
4957            className: "w-full h-full max-w-6xl relative z-[10]",
4958            preserveAspectRatio: "xMidYMid meet",
4959            children: [
4960              /* @__PURE__ */ jsxRuntimeExports.jsx("defs", { children: /* @__PURE__ */ jsxRuntimeExports.jsxs("radialGradient", { id: "soloGradient", children: [
4961                /* @__PURE__ */ jsxRuntimeExports.jsx("stop", { offset: "0%", stopColor: "#F59E0B", stopOpacity: "0.6" }),
4962                /* @__PURE__ */ jsxRuntimeExports.jsx("stop", { offset: "100%", stopColor: "#F59E0B", stopOpacity: "0" })
4963              ] }) }),
4964              renderStage()
4965            ]
4966          }
4967        )
4968      ]
4969    }
4970  );
4971};
4972export {
4973  BackgroundParticlesCanvas as B,
4974  PuppetCharacter as P,
4975  StagePuppetEngine as S,
4976  defaultBackgroundPresets as a,
4977  defaultClothingGraphics as b,
4978  createAnimator as c,
4979  defaultBackgroundConfig as d,
4980  defaultActionRules as e,
4981  defaultPuppetConfigs as f,
4982  getDefaultBodyActions as g,
4983  mergeAllConfigsWithGorilla as m,
4984  parseSvgSocketsAndPlugs as p
4985};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.