@mumulinya167/ccm-pet-assets 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/README.md +4 -0
  2. package/assets/calico/calico-collapsing.apng +0 -0
  3. package/assets/calico/calico-dozing.apng +0 -0
  4. package/assets/calico/calico-error.apng +0 -0
  5. package/assets/calico/calico-happy.apng +0 -0
  6. package/assets/calico/calico-idle-follow.svg +324 -0
  7. package/assets/calico/calico-idle.apng +0 -0
  8. package/assets/calico/calico-mini-alert.apng +0 -0
  9. package/assets/calico/calico-mini-crabwalk.apng +0 -0
  10. package/assets/calico/calico-mini-enter.apng +0 -0
  11. package/assets/calico/calico-mini-happy.apng +0 -0
  12. package/assets/calico/calico-mini-idle.apng +0 -0
  13. package/assets/calico/calico-mini-peek.apng +0 -0
  14. package/assets/calico/calico-mini-sleep.apng +0 -0
  15. package/assets/calico/calico-notification.apng +0 -0
  16. package/assets/calico/calico-react-drag.apng +0 -0
  17. package/assets/calico/calico-react-left.apng +0 -0
  18. package/assets/calico/calico-react-poke.apng +0 -0
  19. package/assets/calico/calico-sleeping.apng +0 -0
  20. package/assets/calico/calico-thinking.apng +0 -0
  21. package/assets/calico/calico-waking.apng +0 -0
  22. package/assets/calico/calico-working-building.apng +0 -0
  23. package/assets/calico/calico-working-carrying.apng +0 -0
  24. package/assets/calico/calico-working-conducting.apng +0 -0
  25. package/assets/calico/calico-working-juggling.apng +0 -0
  26. package/assets/calico/calico-working-sweeping.apng +0 -0
  27. package/assets/calico/calico-working-typing.apng +0 -0
  28. package/assets/calico/calico-yawning.apng +0 -0
  29. package/assets/calico.svg +324 -0
  30. package/assets/clawd/LICENSE +22 -0
  31. package/assets/clawd/clawd-about-hero.svg +202 -0
  32. package/assets/clawd/clawd-collapse-sleep.svg +247 -0
  33. package/assets/clawd/clawd-error.svg +94 -0
  34. package/assets/clawd/clawd-happy.svg +161 -0
  35. package/assets/clawd/clawd-headphones-groove.svg +194 -0
  36. package/assets/clawd/clawd-idle-bubble.svg +180 -0
  37. package/assets/clawd/clawd-idle-collapse.svg +101 -0
  38. package/assets/clawd/clawd-idle-doze.svg +72 -0
  39. package/assets/clawd/clawd-idle-follow.svg +64 -0
  40. package/assets/clawd/clawd-idle-living.svg +196 -0
  41. package/assets/clawd/clawd-idle-look.svg +115 -0
  42. package/assets/clawd/clawd-idle-reading.svg +252 -0
  43. package/assets/clawd/clawd-idle-yawn.svg +158 -0
  44. package/assets/clawd/clawd-mini-alert.svg +129 -0
  45. package/assets/clawd/clawd-mini-crabwalk.svg +76 -0
  46. package/assets/clawd/clawd-mini-enter-sleep.svg +64 -0
  47. package/assets/clawd/clawd-mini-enter.svg +114 -0
  48. package/assets/clawd/clawd-mini-happy.svg +124 -0
  49. package/assets/clawd/clawd-mini-idle.svg +83 -0
  50. package/assets/clawd/clawd-mini-peek.svg +82 -0
  51. package/assets/clawd/clawd-mini-sleep.svg +112 -0
  52. package/assets/clawd/clawd-mini-typing.svg +153 -0
  53. package/assets/clawd/clawd-notification.svg +438 -0
  54. package/assets/clawd/clawd-react-annoyed.svg +167 -0
  55. package/assets/clawd/clawd-react-double-jump.svg +229 -0
  56. package/assets/clawd/clawd-react-double.svg +108 -0
  57. package/assets/clawd/clawd-react-drag.svg +102 -0
  58. package/assets/clawd/clawd-react-left.svg +102 -0
  59. package/assets/clawd/clawd-react-right.svg +102 -0
  60. package/assets/clawd/clawd-sleeping.svg +118 -0
  61. package/assets/clawd/clawd-static-base.svg +21 -0
  62. package/assets/clawd/clawd-wake.svg +277 -0
  63. package/assets/clawd/clawd-working-building.svg +329 -0
  64. package/assets/clawd/clawd-working-carrying.svg +178 -0
  65. package/assets/clawd/clawd-working-debugger.svg +245 -0
  66. package/assets/clawd/clawd-working-juggling.svg +183 -0
  67. package/assets/clawd/clawd-working-sweeping.svg +248 -0
  68. package/assets/clawd/clawd-working-thinking.svg +501 -0
  69. package/assets/clawd/clawd-working-typing-boss.svg +307 -0
  70. package/assets/clawd/clawd-working-typing.svg +273 -0
  71. package/assets/clawd/clawd-working-ultrathink.svg +166 -0
  72. package/assets/clawd/clawd-working-wizard.svg +356 -0
  73. package/assets/clawd.svg +64 -0
  74. package/assets/cloudling/cloudling/cloudling-attention.svg +380 -0
  75. package/assets/cloudling/cloudling/cloudling-building.svg +667 -0
  76. package/assets/cloudling/cloudling/cloudling-carrying.svg +604 -0
  77. package/assets/cloudling/cloudling/cloudling-conducting.svg +616 -0
  78. package/assets/cloudling/cloudling/cloudling-dozing-to-sleeping.svg +578 -0
  79. package/assets/cloudling/cloudling/cloudling-dozing.svg +358 -0
  80. package/assets/cloudling/cloudling/cloudling-error.svg +428 -0
  81. package/assets/cloudling/cloudling/cloudling-idle-reading.svg +658 -0
  82. package/assets/cloudling/cloudling/cloudling-idle-to-dozing.svg +568 -0
  83. package/assets/cloudling/cloudling/cloudling-idle-to-sleeping.svg +811 -0
  84. package/assets/cloudling/cloudling/cloudling-idle.svg +255 -0
  85. package/assets/cloudling/cloudling/cloudling-juggling.svg +648 -0
  86. package/assets/cloudling/cloudling/cloudling-mini-alert.svg +390 -0
  87. package/assets/cloudling/cloudling/cloudling-mini-crabwalk.svg +335 -0
  88. package/assets/cloudling/cloudling/cloudling-mini-enter-roll-in.svg +226 -0
  89. package/assets/cloudling/cloudling/cloudling-mini-enter-sleep.svg +232 -0
  90. package/assets/cloudling/cloudling/cloudling-mini-happy.svg +371 -0
  91. package/assets/cloudling/cloudling/cloudling-mini-idle.svg +279 -0
  92. package/assets/cloudling/cloudling/cloudling-mini-peek.svg +328 -0
  93. package/assets/cloudling/cloudling/cloudling-mini-sleep.svg +260 -0
  94. package/assets/cloudling/cloudling/cloudling-mini-typing.svg +692 -0
  95. package/assets/cloudling/cloudling/cloudling-notification.svg +494 -0
  96. package/assets/cloudling/cloudling/cloudling-react-drag.svg +83 -0
  97. package/assets/cloudling/cloudling/cloudling-sleeping-to-idle.svg +555 -0
  98. package/assets/cloudling/cloudling/cloudling-sleeping.svg +500 -0
  99. package/assets/cloudling/cloudling/cloudling-sweeping.svg +292 -0
  100. package/assets/cloudling/cloudling/cloudling-thinking.svg +720 -0
  101. package/assets/cloudling/cloudling/cloudling-typing.svg +354 -0
  102. package/assets/cloudling/cloudling-attention.svg +380 -0
  103. package/assets/cloudling/cloudling-building.svg +667 -0
  104. package/assets/cloudling/cloudling-carrying.svg +604 -0
  105. package/assets/cloudling/cloudling-conducting.svg +616 -0
  106. package/assets/cloudling/cloudling-dozing-to-sleeping.svg +578 -0
  107. package/assets/cloudling/cloudling-dozing.svg +358 -0
  108. package/assets/cloudling/cloudling-error.svg +428 -0
  109. package/assets/cloudling/cloudling-idle-reading.svg +658 -0
  110. package/assets/cloudling/cloudling-idle-to-dozing.svg +568 -0
  111. package/assets/cloudling/cloudling-idle-to-sleeping.svg +811 -0
  112. package/assets/cloudling/cloudling-idle.svg +255 -0
  113. package/assets/cloudling/cloudling-juggling.svg +648 -0
  114. package/assets/cloudling/cloudling-mini-alert.svg +390 -0
  115. package/assets/cloudling/cloudling-mini-crabwalk.svg +335 -0
  116. package/assets/cloudling/cloudling-mini-enter-roll-in.svg +226 -0
  117. package/assets/cloudling/cloudling-mini-enter-sleep.svg +232 -0
  118. package/assets/cloudling/cloudling-mini-happy.svg +371 -0
  119. package/assets/cloudling/cloudling-mini-idle.svg +279 -0
  120. package/assets/cloudling/cloudling-mini-peek.svg +328 -0
  121. package/assets/cloudling/cloudling-mini-sleep.svg +260 -0
  122. package/assets/cloudling/cloudling-mini-typing.svg +692 -0
  123. package/assets/cloudling/cloudling-notification.svg +494 -0
  124. package/assets/cloudling/cloudling-react-drag.svg +83 -0
  125. package/assets/cloudling/cloudling-sleeping-to-idle.svg +555 -0
  126. package/assets/cloudling/cloudling-sleeping.svg +500 -0
  127. package/assets/cloudling/cloudling-sweeping.svg +292 -0
  128. package/assets/cloudling/cloudling-thinking.svg +720 -0
  129. package/assets/cloudling/cloudling-typing.svg +354 -0
  130. package/assets/cloudling.svg +255 -0
  131. package/assets/ghost-attention.svg +19 -0
  132. package/assets/ghost-carrying.svg +19 -0
  133. package/assets/ghost-collapsing.svg +19 -0
  134. package/assets/ghost-dozing.svg +23 -0
  135. package/assets/ghost-error.svg +19 -0
  136. package/assets/ghost-happy.svg +23 -0
  137. package/assets/ghost-idle-action1.svg +19 -0
  138. package/assets/ghost-idle-action2.svg +19 -0
  139. package/assets/ghost-idle-action3.svg +19 -0
  140. package/assets/ghost-idle.svg +11 -0
  141. package/assets/ghost-juggling.svg +19 -0
  142. package/assets/ghost-notification.svg +19 -0
  143. package/assets/ghost-react-double.svg +23 -0
  144. package/assets/ghost-react-drag.svg +23 -0
  145. package/assets/ghost-react-left.svg +19 -0
  146. package/assets/ghost-react-right.svg +19 -0
  147. package/assets/ghost-sleeping.svg +19 -0
  148. package/assets/ghost-sweeping.svg +19 -0
  149. package/assets/ghost-thinking.svg +19 -0
  150. package/assets/ghost-waking.svg +19 -0
  151. package/assets/ghost-working.svg +19 -0
  152. package/assets/ghost-yawning.svg +23 -0
  153. package/assets/ghost.svg +11 -0
  154. package/assets/robot-attention.svg +19 -0
  155. package/assets/robot-carrying.svg +19 -0
  156. package/assets/robot-collapsing.svg +19 -0
  157. package/assets/robot-dozing.svg +23 -0
  158. package/assets/robot-error.svg +19 -0
  159. package/assets/robot-happy.svg +23 -0
  160. package/assets/robot-idle-action1.svg +19 -0
  161. package/assets/robot-idle-action2.svg +19 -0
  162. package/assets/robot-idle-action3.svg +19 -0
  163. package/assets/robot-idle.svg +11 -0
  164. package/assets/robot-juggling.svg +19 -0
  165. package/assets/robot-notification.svg +19 -0
  166. package/assets/robot-react-double.svg +23 -0
  167. package/assets/robot-react-drag.svg +23 -0
  168. package/assets/robot-react-left.svg +19 -0
  169. package/assets/robot-react-right.svg +19 -0
  170. package/assets/robot-sleeping.svg +19 -0
  171. package/assets/robot-sweeping.svg +19 -0
  172. package/assets/robot-thinking.svg +19 -0
  173. package/assets/robot-waking.svg +19 -0
  174. package/assets/robot-working.svg +19 -0
  175. package/assets/robot-yawning.svg +23 -0
  176. package/assets/robot.svg +11 -0
  177. package/manifest.json +889 -0
  178. package/package.json +18 -0
@@ -0,0 +1,616 @@
1
+ <svg id="stageSvg" xmlns="http://www.w3.org/2000/svg" viewBox="-6 -6 36 36" preserveAspectRatio="xMidYMid meet">
2
+ <style><![CDATA[
3
+ #stageBg { fill: #0f0f14; }
4
+ .edge-rim-tight { stroke-opacity: .9; }
5
+ .edge-rim-soft { stroke-opacity: .75; }
6
+ .edge-contact { stroke-opacity: .9; }
7
+ .orbit-track { stroke: rgba(177,167,255,.13); stroke-width: 0.13; fill: none; stroke-dasharray: 0.45 0.65; }
8
+ .trail-spark { pointer-events: none; }
9
+ .spark-shell { stroke: rgba(255,255,255,.82); stroke-width: 0.075; stroke-linejoin: round; }
10
+ .spark-core { fill: #FFFDF0; opacity: .20; }
11
+ @media (prefers-color-scheme: light) {
12
+ #stageBg { fill: #fff; }
13
+ .edge-rim-tight { stroke-opacity: .95; }
14
+ .edge-rim-soft { stroke-opacity: .85; }
15
+ .edge-contact { stroke-opacity: .95; }
16
+ .cloud-drop-shadow { flood-opacity: .13; }
17
+ .orbit-track { stroke: rgba(57,65,255,.22); }
18
+ .spark-shell { stroke: rgba(255,255,255,.82); stroke-width: 0.075; }
19
+ .spark-core { opacity: .20; }
20
+ #taskTrailLayer use, #taskABack, #taskBBack, #taskCBack, #taskDBack, #taskAFront, #taskBFront, #taskCFront, #taskDFront { filter: url(#task-glow); }
21
+ .food-cloud-shadow { flood-opacity: .28; flood-color: #5060FF; }
22
+ .food-cloud > path {
23
+ stroke: #5870E8 !important;
24
+ stroke-width: 1.2 !important;
25
+ stroke-opacity: .9 !important;
26
+ vector-effect: non-scaling-stroke !important;
27
+ }
28
+ }
29
+ ]]></style>
30
+ <defs>
31
+ <linearGradient id="body-grad" x1="12" y1="2" x2="12" y2="22" gradientUnits="userSpaceOnUse">
32
+ <stop offset="0" stop-color="#93c5fd"/>
33
+ <stop offset="0.5" stop-color="#60a5fa"/>
34
+ <stop offset="1" stop-color="#3b82f6"/>
35
+ </linearGradient>
36
+ <linearGradient id="eye-grad" x1="12" y1="3" x2="12" y2="21" gradientUnits="userSpaceOnUse">
37
+ <stop offset="0" stop-color="#B1A7FF"/>
38
+ <stop offset="0.5" stop-color="#7A9DFF"/>
39
+ <stop offset="1" stop-color="#3941FF"/>
40
+ </linearGradient>
41
+ <radialGradient id="hi-grad" cx="0.45" cy="0.25" r="0.55">
42
+ <stop offset="0" stop-color="#93c5fd" stop-opacity="0.9"/>
43
+ <stop offset="1" stop-color="#93c5fd" stop-opacity="0"/>
44
+ </radialGradient>
45
+ <radialGradient id="taskA-grad" cx="0.48" cy="0.38" r="0.82">
46
+ <stop offset="0" stop-color="#FFF7D1"/>
47
+ <stop offset="0.58" stop-color="#FFD978"/>
48
+ <stop offset="1" stop-color="#DFA93C"/>
49
+ </radialGradient>
50
+ <radialGradient id="taskB-grad" cx="0.48" cy="0.38" r="0.82">
51
+ <stop offset="0" stop-color="#F7F0FF"/>
52
+ <stop offset="0.58" stop-color="#CDBBFF"/>
53
+ <stop offset="1" stop-color="#8F78D7"/>
54
+ </radialGradient>
55
+ <radialGradient id="taskC-grad" cx="0.48" cy="0.38" r="0.82">
56
+ <stop offset="0" stop-color="#F1FFFC"/>
57
+ <stop offset="0.58" stop-color="#9FE5DD"/>
58
+ <stop offset="1" stop-color="#50AFA6"/>
59
+ </radialGradient>
60
+ <radialGradient id="taskD-grad" cx="0.48" cy="0.38" r="0.82">
61
+ <stop offset="0" stop-color="#FFF3FA"/>
62
+ <stop offset="0.58" stop-color="#F7AFD4"/>
63
+ <stop offset="1" stop-color="#CE6FA6"/>
64
+ </radialGradient>
65
+ <filter id="cloud-drop" x="-45%" y="-45%" width="190%" height="190%">
66
+ <feDropShadow class="cloud-drop-shadow" dx="0" dy="0.6" stdDeviation="0.8" flood-color="#3941FF" flood-opacity="0.35"/>
67
+ </filter>
68
+ <filter id="edge-blur" x="-40%" y="-40%" width="180%" height="180%">
69
+ <feGaussianBlur stdDeviation="0.16"/>
70
+ </filter>
71
+ <filter id="task-glow" x="-85%" y="-85%" width="270%" height="270%">
72
+ <feGaussianBlur stdDeviation="0.16" result="b"/>
73
+ <feMerge>
74
+ <feMergeNode in="b"/>
75
+ <feMergeNode in="SourceGraphic"/>
76
+ </feMerge>
77
+ </filter>
78
+ <filter id="task-light-glow" x="-90%" y="-90%" width="280%" height="280%">
79
+ <feDropShadow dx="0" dy="0.06" stdDeviation="0.18" flood-color="#4A4E76" flood-opacity="0.30"/>
80
+ <feGaussianBlur stdDeviation="0.08" result="b"/>
81
+ <feMerge>
82
+ <feMergeNode in="b"/>
83
+ <feMergeNode in="SourceGraphic"/>
84
+ </feMerge>
85
+ </filter>
86
+ <clipPath id="cloud-clip">
87
+ <path id="cloud-clip-path"/>
88
+ </clipPath>
89
+ <symbol id="taskA-symbol" viewBox="-1.22 -1.22 2.44 2.44">
90
+ <path class="spark-shell" d="M 0 -1.06 C 0.15 -0.47 0.34 -0.20 0.98 -0.02 C 0.36 0.12 0.14 0.34 0 1.04 C -0.14 0.34 -0.36 0.12 -0.98 -0.02 C -0.34 -0.20 -0.15 -0.47 0 -1.06 Z"
91
+ fill="url(#taskA-grad)" opacity="0.98"/>
92
+ <path d="M 0 -0.48 C 0.07 -0.19 0.18 -0.08 0.42 -0.01 C 0.18 0.06 0.07 0.18 0 0.48 C -0.07 0.18 -0.18 0.06 -0.42 -0.01 C -0.18 -0.08 -0.07 -0.19 0 -0.48 Z"
93
+ class="spark-core"/>
94
+ </symbol>
95
+ <symbol id="taskB-symbol" viewBox="-1.22 -1.22 2.44 2.44">
96
+ <path class="spark-shell" d="M 0 -1.06 C 0.15 -0.47 0.34 -0.20 0.98 -0.02 C 0.36 0.12 0.14 0.34 0 1.04 C -0.14 0.34 -0.36 0.12 -0.98 -0.02 C -0.34 -0.20 -0.15 -0.47 0 -1.06 Z"
97
+ fill="url(#taskB-grad)" opacity="0.98"/>
98
+ <path d="M 0 -0.48 C 0.07 -0.19 0.18 -0.08 0.42 -0.01 C 0.18 0.06 0.07 0.18 0 0.48 C -0.07 0.18 -0.18 0.06 -0.42 -0.01 C -0.18 -0.08 -0.07 -0.19 0 -0.48 Z"
99
+ class="spark-core"/>
100
+ </symbol>
101
+ <symbol id="taskC-symbol" viewBox="-1.22 -1.22 2.44 2.44">
102
+ <path class="spark-shell" d="M 0 -1.06 C 0.15 -0.47 0.34 -0.20 0.98 -0.02 C 0.36 0.12 0.14 0.34 0 1.04 C -0.14 0.34 -0.36 0.12 -0.98 -0.02 C -0.34 -0.20 -0.15 -0.47 0 -1.06 Z"
103
+ fill="url(#taskC-grad)" opacity="0.98"/>
104
+ <path d="M 0 -0.48 C 0.07 -0.19 0.18 -0.08 0.42 -0.01 C 0.18 0.06 0.07 0.18 0 0.48 C -0.07 0.18 -0.18 0.06 -0.42 -0.01 C -0.18 -0.08 -0.07 -0.19 0 -0.48 Z"
105
+ class="spark-core"/>
106
+ </symbol>
107
+ <symbol id="taskD-symbol" viewBox="-1.22 -1.22 2.44 2.44">
108
+ <path class="spark-shell" d="M 0 -1.06 C 0.15 -0.47 0.34 -0.20 0.98 -0.02 C 0.36 0.12 0.14 0.34 0 1.04 C -0.14 0.34 -0.36 0.12 -0.98 -0.02 C -0.34 -0.20 -0.15 -0.47 0 -1.06 Z"
109
+ fill="url(#taskD-grad)" opacity="0.98"/>
110
+ <path d="M 0 -0.48 C 0.07 -0.19 0.18 -0.08 0.42 -0.01 C 0.18 0.06 0.07 0.18 0 0.48 C -0.07 0.18 -0.18 0.06 -0.42 -0.01 C -0.18 -0.08 -0.07 -0.19 0 -0.48 Z"
111
+ class="spark-core"/>
112
+ </symbol>
113
+ </defs>
114
+ <g id="orbitTrackLayer">
115
+ <path id="orbitTrackA" class="orbit-track"/>
116
+ <path id="orbitTrackB" class="orbit-track"/>
117
+ </g>
118
+
119
+ <g id="taskTrailLayer">
120
+ <g id="trailA" class="trail-spark">
121
+ <use id="trailA0" href="#taskA-symbol" filter="url(#task-glow)"/>
122
+ <use id="trailA1" href="#taskA-symbol" filter="url(#task-glow)"/>
123
+ <use id="trailA2" href="#taskA-symbol" filter="url(#task-glow)"/>
124
+ <use id="trailA3" href="#taskA-symbol" filter="url(#task-glow)"/>
125
+ </g>
126
+ <g id="trailB" class="trail-spark">
127
+ <use id="trailB0" href="#taskB-symbol" filter="url(#task-glow)"/>
128
+ <use id="trailB1" href="#taskB-symbol" filter="url(#task-glow)"/>
129
+ <use id="trailB2" href="#taskB-symbol" filter="url(#task-glow)"/>
130
+ <use id="trailB3" href="#taskB-symbol" filter="url(#task-glow)"/>
131
+ </g>
132
+ <g id="trailC" class="trail-spark">
133
+ <use id="trailC0" href="#taskC-symbol" filter="url(#task-glow)"/>
134
+ <use id="trailC1" href="#taskC-symbol" filter="url(#task-glow)"/>
135
+ <use id="trailC2" href="#taskC-symbol" filter="url(#task-glow)"/>
136
+ <use id="trailC3" href="#taskC-symbol" filter="url(#task-glow)"/>
137
+ </g>
138
+ <g id="trailD" class="trail-spark">
139
+ <use id="trailD0" href="#taskD-symbol" filter="url(#task-glow)"/>
140
+ <use id="trailD1" href="#taskD-symbol" filter="url(#task-glow)"/>
141
+ <use id="trailD2" href="#taskD-symbol" filter="url(#task-glow)"/>
142
+ <use id="trailD3" href="#taskD-symbol" filter="url(#task-glow)"/>
143
+ </g>
144
+ </g>
145
+
146
+ <use id="taskABack" href="#taskA-symbol" filter="url(#task-glow)"/>
147
+ <use id="taskBBack" href="#taskB-symbol" filter="url(#task-glow)"/>
148
+ <use id="taskCBack" href="#taskC-symbol" filter="url(#task-glow)"/>
149
+ <use id="taskDBack" href="#taskD-symbol" filter="url(#task-glow)"/>
150
+
151
+ <g id="cloudRoot">
152
+ <g id="cloudEdge">
153
+ <path id="edgeContact" class="edge-contact" fill="none" stroke="#7d8aa7" stroke-width="1.4" stroke-linejoin="round" transform="translate(0 0.18)" filter="url(#edge-blur)"/>
154
+ <path id="edgeRimSoft" class="edge-rim-soft" fill="none" stroke="#7A9DFF" stroke-width="1.2" stroke-linejoin="round" filter="url(#edge-blur)"/>
155
+ <path id="edgeRimTight" class="edge-rim-tight" fill="none" stroke="#aab3c6" stroke-width="0.8" stroke-linejoin="round"/>
156
+ </g>
157
+ <g id="cloudGroup" filter="url(#cloud-drop)">
158
+ <path id="cloudPath" fill="url(#body-grad)"/>
159
+ <rect x="1" y="2" width="22" height="11" fill="url(#hi-grad)" clip-path="url(#cloud-clip)"/>
160
+ </g>
161
+ <g id="eyeGroup">
162
+ <rect id="eyeL" x="7.7" y="9.5" width="2.6" height="5" rx="1.3" ry="1.3" fill="url(#eye-grad)" stroke="#ffffff" stroke-width="0.08" stroke-opacity="0.7"/>
163
+ <rect id="eyeR" x="13.7" y="9.5" width="2.6" height="5" rx="1.3" ry="1.3" fill="url(#eye-grad)" stroke="#ffffff" stroke-width="0.08" stroke-opacity="0.7"/>
164
+ </g>
165
+ </g>
166
+
167
+ <use id="taskAFront" href="#taskA-symbol" filter="url(#task-glow)"/>
168
+ <use id="taskBFront" href="#taskB-symbol" filter="url(#task-glow)"/>
169
+ <use id="taskCFront" href="#taskC-symbol" filter="url(#task-glow)"/>
170
+ <use id="taskDFront" href="#taskD-symbol" filter="url(#task-glow)"/>
171
+ <g id="__cloudling-export-shims" opacity="0" pointer-events="none" style="display:none">
172
+ <g id="readout"></g>
173
+ <g id="scrub"></g>
174
+ <g id="scrubV"></g>
175
+ </g>
176
+
177
+ <script type="application/ecmascript"><![CDATA[
178
+ const CLOUD_D = "M9.064 3.344a4.578 4.578 0 012.285-.312c1 .115 1.891.54 2.673 1.275.01.01.024.017.037.021a.09.09 0 00.043 0 4.55 4.55 0 013.046.275l.047.022.116.057a4.581 4.581 0 012.188 2.399c.209.51.313 1.041.315 1.595a4.24 4.24 0 01-.134 1.223.123.123 0 00.03.115c.594.607.988 1.33 1.183 2.17.289 1.425-.007 2.71-.887 3.854l-.136.166a4.548 4.548 0 01-2.201 1.388.123.123 0 00-.081.076c-.191.551-.383 1.023-.74 1.494-.9 1.187-2.222 1.846-3.711 1.838-1.187-.006-2.239-.44-3.157-1.302a.107.107 0 00-.105-.024c-.388.125-.78.143-1.204.138a4.441 4.441 0 01-1.945-.466 4.544 4.544 0 01-1.61-1.335c-.152-.202-.303-.392-.414-.617a5.81 5.81 0 01-.37-.961 4.582 4.582 0 01-.014-2.298.124.124 0 00.006-.056.085.085 0 00-.027-.048 4.467 4.467 0 01-1.034-1.651 3.896 3.896 0 01-.251-1.192 5.189 5.189 0 01.141-1.6c.337-1.112.982-1.985 1.933-2.618.212-.141.413-.251.601-.33.215-.089.43-.164.646-.227a.098.098 0 00.065-.066 4.51 4.51 0 01.829-1.615 4.535 4.535 0 011.837-1.388z";
179
+
180
+ const CENTER = { x: 12, y: 12 };
181
+
182
+ const els = {
183
+ cloudRoot: document.getElementById('cloudRoot'),
184
+ cloudPath: document.getElementById('cloudPath'),
185
+ cloudClipPath: document.getElementById('cloud-clip-path'),
186
+ edgeContact: document.getElementById('edgeContact'),
187
+ edgeRimSoft: document.getElementById('edgeRimSoft'),
188
+ edgeRimTight: document.getElementById('edgeRimTight'),
189
+ eyeGroup: document.getElementById('eyeGroup'),
190
+ eyeL: document.getElementById('eyeL'),
191
+ eyeR: document.getElementById('eyeR'),
192
+ eyeGrad: document.getElementById('eye-grad'),
193
+ taskAFront: document.getElementById('taskAFront'),
194
+ taskABack: document.getElementById('taskABack'),
195
+ taskBFront: document.getElementById('taskBFront'),
196
+ taskBBack: document.getElementById('taskBBack'),
197
+ taskCFront: document.getElementById('taskCFront'),
198
+ taskCBack: document.getElementById('taskCBack'),
199
+ taskDFront: document.getElementById('taskDFront'),
200
+ taskDBack: document.getElementById('taskDBack'),
201
+ trailA: document.getElementById('trailA'),
202
+ trailA0: document.getElementById('trailA0'),
203
+ trailA1: document.getElementById('trailA1'),
204
+ trailA2: document.getElementById('trailA2'),
205
+ trailA3: document.getElementById('trailA3'),
206
+ trailB: document.getElementById('trailB'),
207
+ trailB0: document.getElementById('trailB0'),
208
+ trailB1: document.getElementById('trailB1'),
209
+ trailB2: document.getElementById('trailB2'),
210
+ trailB3: document.getElementById('trailB3'),
211
+ trailC: document.getElementById('trailC'),
212
+ trailC0: document.getElementById('trailC0'),
213
+ trailC1: document.getElementById('trailC1'),
214
+ trailC2: document.getElementById('trailC2'),
215
+ trailC3: document.getElementById('trailC3'),
216
+ trailD: document.getElementById('trailD'),
217
+ trailD0: document.getElementById('trailD0'),
218
+ trailD1: document.getElementById('trailD1'),
219
+ trailD2: document.getElementById('trailD2'),
220
+ trailD3: document.getElementById('trailD3'),
221
+ orbitTrackA: document.getElementById('orbitTrackA'),
222
+ orbitTrackB: document.getElementById('orbitTrackB'),
223
+ orbitTrackLayer: document.getElementById('orbitTrackLayer'),
224
+ readout: document.getElementById('readout'),
225
+ scrub: document.getElementById('scrub'),
226
+ scrubV: document.getElementById('scrubV')
227
+ };
228
+
229
+ const P = {
230
+ durA: 4.0, rxA: 14, ryA: 4, tiltA: -10, theta0A: 0,
231
+ durB: 6.0, rxB: 4, ryB: 11, tiltB: 20, theta0B: 180,
232
+ starSize: 1.75,
233
+ backScale: 0.74,
234
+ backOpacity: 0.28,
235
+ depthSnap: 0.16,
236
+ starSpin: 65,
237
+ slotRadius: 14.0,
238
+ slotSquash: 0.96,
239
+ slotRot: 0,
240
+ slotAX: 0, slotAY: 0,
241
+ slotBX: 0, slotBY: 0,
242
+ slotCX: 0, slotCY: 0,
243
+ slotDX: 0, slotDY: 0,
244
+ bodyBreath: 0.022,
245
+ bodyFloat: 0.16,
246
+ bodyLean: 0.48,
247
+ bodyTilt: 1.15,
248
+ callMorph: 0.075,
249
+ syncMorph: 0.16,
250
+ morphAmt: 0.04,
251
+ morphPeriod: 5.0,
252
+ cloudR: 7.5,
253
+ eyeShift: 1.10,
254
+ eyeSwitchT: 1.15,
255
+ saccade: 0.13,
256
+ eyeFlowPeriod: 5.5,
257
+ eyeFlowAmp: 4,
258
+ blinkInterval: 6.0,
259
+ blinkDur: 0.16,
260
+ showTracks: false,
261
+ pause: false
262
+ };
263
+
264
+ const lerp = (a, b, t) => a + (b - a) * t;
265
+ const clamp = (v, a, b) => Math.max(a, Math.min(b, v));
266
+ const smoothstep = t => t * t * (3 - 2 * t);
267
+
268
+ const EYE_BASE = {
269
+ l: { y: 9.5, h: 5, rx: 1.3 },
270
+ r: { y: 9.5, h: 5, rx: 1.3 }
271
+ };
272
+
273
+ const TASK_ORDER = ['A', 'B', 'C', 'D'];
274
+ const TASKS = [
275
+ { key: 'A', orbit: 'A', phase: 8, slotAngle: 235, spin: 0.35, front: els.taskAFront, back: els.taskABack, trail: [els.trailA0, els.trailA1, els.trailA2, els.trailA3] },
276
+ { key: 'B', orbit: 'B', phase: 72, slotAngle: 324, spin: -0.28, front: els.taskBFront, back: els.taskBBack, trail: [els.trailB0, els.trailB1, els.trailB2, els.trailB3] },
277
+ { key: 'C', orbit: 'A', phase: 145, slotAngle: 53, spin: 0.30, front: els.taskCFront, back: els.taskCBack, trail: [els.trailC0, els.trailC1, els.trailC2, els.trailC3] },
278
+ { key: 'D', orbit: 'B', phase: 252, slotAngle: 142, spin: -0.32, front: els.taskDFront, back: els.taskDBack, trail: [els.trailD0, els.trailD1, els.trailD2, els.trailD3] }
279
+ ];
280
+
281
+ function samplePath(d, N) {
282
+ const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
283
+ path.setAttribute('d', d);
284
+ const pts = [];
285
+ const len = path.getTotalLength();
286
+ for (let i = 0; i < N; i++) {
287
+ const p = path.getPointAtLength(i / N * len);
288
+ pts.push({ x: p.x, y: p.y });
289
+ }
290
+ return pts;
291
+ }
292
+
293
+ const basePoints = samplePath(CLOUD_D, 64);
294
+
295
+ function morphPoints(pts, R, t) {
296
+ return pts.map(p => {
297
+ const dx = p.x - CENTER.x;
298
+ const dy = p.y - CENTER.y;
299
+ const a = Math.atan2(dy, dx);
300
+ return {
301
+ x: lerp(p.x, CENTER.x + R * Math.cos(a), t),
302
+ y: lerp(p.y, CENTER.y + R * Math.sin(a), t)
303
+ };
304
+ });
305
+ }
306
+
307
+ function pointsToSmoothPath(pts) {
308
+ const n = pts.length;
309
+ let d = `M ${pts[0].x.toFixed(3)} ${pts[0].y.toFixed(3)}`;
310
+ for (let i = 0; i < n; i++) {
311
+ const p0 = pts[(i - 1 + n) % n];
312
+ const p1 = pts[i];
313
+ const p2 = pts[(i + 1) % n];
314
+ const p3 = pts[(i + 2) % n];
315
+ const c1x = p1.x + (p2.x - p0.x) / 6;
316
+ const c1y = p1.y + (p2.y - p0.y) / 6;
317
+ const c2x = p2.x - (p3.x - p1.x) / 6;
318
+ const c2y = p2.y - (p3.y - p1.y) / 6;
319
+ d += ` C ${c1x.toFixed(3)} ${c1y.toFixed(3)} ${c2x.toFixed(3)} ${c2y.toFixed(3)} ${p2.x.toFixed(3)} ${p2.y.toFixed(3)}`;
320
+ }
321
+ return d + ' Z';
322
+ }
323
+
324
+ function orbitPoint(thetaDeg, rx, ry, tiltDeg) {
325
+ const t = thetaDeg * Math.PI / 180;
326
+ const r = tiltDeg * Math.PI / 180;
327
+ const ct = Math.cos(t), st = Math.sin(t);
328
+ const cr = Math.cos(r), sr = Math.sin(r);
329
+ return {
330
+ x: CENTER.x + rx * ct * cr - ry * st * sr,
331
+ y: CENTER.y + rx * ct * sr + ry * st * cr
332
+ };
333
+ }
334
+
335
+ function orbitTrackD(rx, ry, tiltDeg, samples) {
336
+ let d = '';
337
+ for (let i = 0; i <= samples; i++) {
338
+ const theta = i / samples * 360;
339
+ const p = orbitPoint(theta, rx, ry, tiltDeg);
340
+ d += i === 0 ? `M ${p.x.toFixed(3)} ${p.y.toFixed(3)}` : ` L ${p.x.toFixed(3)} ${p.y.toFixed(3)}`;
341
+ }
342
+ return d + ' Z';
343
+ }
344
+
345
+ function getBeat(now) {
346
+ const beatT = Math.max(P.eyeSwitchT, 0.1);
347
+ const cycleT = beatT * 5;
348
+ const cyclePos = (now % cycleT) / beatT;
349
+ const beatIndex = Math.floor(cyclePos);
350
+ const beatPhase = cyclePos - beatIndex;
351
+ return {
352
+ beatT,
353
+ cyclePos,
354
+ beatIndex,
355
+ beatPhase,
356
+ target: beatIndex < TASK_ORDER.length ? TASK_ORDER[beatIndex] : 'SYNC',
357
+ sync: beatIndex >= TASK_ORDER.length
358
+ };
359
+ }
360
+
361
+ function getOrbitParams(task) {
362
+ return task.orbit === 'A'
363
+ ? { dur: P.durA, theta0: P.theta0A, rx: P.rxA, ry: P.ryA, tilt: P.tiltA }
364
+ : { dur: P.durB, theta0: P.theta0B, rx: P.rxB, ry: P.ryB, tilt: P.tiltB };
365
+ }
366
+
367
+ function getTaskSlot(task) {
368
+ const a = (task.slotAngle + P.slotRot) * Math.PI / 180;
369
+ return {
370
+ x: CENTER.x + Math.cos(a) * P.slotRadius + P[`slot${task.key}X`],
371
+ y: CENTER.y + Math.sin(a) * P.slotRadius * P.slotSquash + P[`slot${task.key}Y`]
372
+ };
373
+ }
374
+
375
+ function slotMixForTask(task, beat) {
376
+ const idx = TASK_ORDER.indexOf(task.key);
377
+ const callLocal = beat.cyclePos - idx;
378
+ if (callLocal < 0 || callLocal > 4.88) return 0;
379
+ const arrive = smoothstep(clamp((callLocal - 0.04) / 0.22, 0, 1));
380
+ const release = beat.cyclePos > 4.55 ? 1 - smoothstep(clamp((beat.cyclePos - 4.55) / 0.33, 0, 1)) : 1;
381
+ return arrive * release;
382
+ }
383
+
384
+ function getTaskState(now, task, beat) {
385
+ const p = getOrbitParams(task);
386
+ const progress = (now / p.dur) % 1;
387
+ const theta = p.theta0 + task.phase + progress * 360;
388
+ const depth0 = Math.sin(theta * Math.PI / 180);
389
+ const orbitPos = orbitPoint(theta, p.rx, p.ry, p.tilt);
390
+ const slot = getTaskSlot(task);
391
+ const slotMix = slotMixForTask(task, beat);
392
+ const pos = {
393
+ x: lerp(orbitPos.x, slot.x, slotMix),
394
+ y: lerp(orbitPos.y, slot.y, slotMix)
395
+ };
396
+ const called = beat.target === task.key;
397
+ const callPulse = called ? Math.sin(Math.PI * clamp(beat.beatPhase, 0, 1)) : 0;
398
+ const syncPulse = beat.sync ? Math.sin(Math.PI * clamp(beat.beatPhase, 0, 1)) : 0;
399
+ const frontLift = Math.max(slotMix, callPulse, syncPulse * 0.48);
400
+ const depth = lerp(depth0, 0.68, frontLift);
401
+ const scaleBoost = 1 + callPulse * 0.14 + syncPulse * 0.10 + slotMix * 0.04;
402
+ const glowBoost = callPulse * 0.24 + syncPulse * 0.20 + slotMix * 0.10;
403
+ return { ...task, p: pos, orbitPos, slot, depth, theta, progress, slotMix, called, callPulse, syncPulse, scaleBoost, glowBoost };
404
+ }
405
+
406
+ function setTaskLayer(state, baseSize, spinDeg) {
407
+ const snap = P.depthSnap;
408
+ const depthFrontOp = smoothstep(clamp((state.depth - snap) / (0.58 - snap), 0, 1));
409
+ const frontOp = Math.max(depthFrontOp, state.callPulse * 0.72, state.syncPulse * 0.58);
410
+ const baseBack = state.depth < snap ? P.backOpacity : P.backOpacity * (1 - frontOp);
411
+ const frontBoost = 1 + state.glowBoost;
412
+ const backBoost = 1 + state.glowBoost * 0.45;
413
+ const sizeFront = baseSize * state.scaleBoost;
414
+ const sizeBack = baseSize * P.backScale * (0.96 + state.scaleBoost * 0.04);
415
+ const setUse = (el, sz, op) => {
416
+ el.setAttribute('x', (-sz).toFixed(3));
417
+ el.setAttribute('y', (-sz).toFixed(3));
418
+ el.setAttribute('width', (2 * sz).toFixed(3));
419
+ el.setAttribute('height', (2 * sz).toFixed(3));
420
+ el.setAttribute('transform', `translate(${state.p.x.toFixed(3)} ${state.p.y.toFixed(3)}) rotate(${spinDeg.toFixed(2)})`);
421
+ el.setAttribute('opacity', op.toFixed(3));
422
+ };
423
+ setUse(state.front, sizeFront, clamp(frontOp * frontBoost, 0, 1));
424
+ setUse(state.back, sizeBack, clamp(baseBack * backBoost, 0, 0.8));
425
+ }
426
+
427
+ function taskTrailPoint(state, index) {
428
+ const p = getOrbitParams(state);
429
+ return orbitPoint(state.theta - 8 - index * 6.5, p.rx, p.ry, p.tilt);
430
+ }
431
+
432
+ function setTaskTrail(state) {
433
+ const depthPresence = 0.52 + Math.max(state.depth, 0) * 0.35;
434
+ const baseOp = (1 - state.slotMix) * depthPresence;
435
+ const trailOps = [0.17, 0.09, 0.05, 0.025];
436
+ const trailSizes = [0.28, 0.20, 0.14, 0.09];
437
+ state.trail.forEach((el, i) => {
438
+ const pos = taskTrailPoint(state, i);
439
+ const sz = P.starSize * trailSizes[i];
440
+ el.setAttribute('x', (-sz).toFixed(3));
441
+ el.setAttribute('y', (-sz).toFixed(3));
442
+ el.setAttribute('width', (2 * sz).toFixed(3));
443
+ el.setAttribute('height', (2 * sz).toFixed(3));
444
+ el.setAttribute('transform', `translate(${pos.x.toFixed(3)} ${pos.y.toFixed(3)}) rotate(${(state.theta * 0.28 + i * 17).toFixed(2)})`);
445
+ el.setAttribute('opacity', clamp(baseOp * trailOps[i], 0, 0.3).toFixed(3));
446
+ });
447
+ }
448
+
449
+ let nextBlinkT = 2.4;
450
+ let blinkActive = false;
451
+ let blinkStartT = 0;
452
+ function getBlinkScale(t) {
453
+ if (blinkActive && t < blinkStartT) {
454
+ blinkActive = false;
455
+ nextBlinkT = t + 2.4;
456
+ return 1;
457
+ }
458
+ if (!blinkActive && t > nextBlinkT) {
459
+ blinkActive = true;
460
+ blinkStartT = t;
461
+ }
462
+ if (blinkActive) {
463
+ const dt = t - blinkStartT;
464
+ const half = P.blinkDur / 2;
465
+ if (dt < half) return 1 - 0.94 * (dt / half);
466
+ if (dt < P.blinkDur) return 0.06 + 0.94 * ((dt - half) / half);
467
+ blinkActive = false;
468
+ nextBlinkT = t + P.blinkInterval * (0.82 + Math.random() * 0.36);
469
+ }
470
+ return 1;
471
+ }
472
+
473
+ function applyBlink(rect, base, scale) {
474
+ const h = Math.max(base.h * scale, 0.15);
475
+ const y = base.y + (base.h - h) / 2;
476
+ const rx = Math.min(base.rx, h / 2);
477
+ rect.setAttribute('height', h.toFixed(3));
478
+ rect.setAttribute('y', y.toFixed(3));
479
+ rect.setAttribute('rx', rx.toFixed(3));
480
+ rect.setAttribute('ry', rx.toFixed(3));
481
+ }
482
+
483
+ function getTargetPoint(key, states) {
484
+ if (key === 'SYNC') return { x: CENTER.x, y: CENTER.y - 2.2 };
485
+ return states.find(s => s.key === key)?.p || CENTER;
486
+ }
487
+
488
+ function getEyeTarget(beat, states) {
489
+ const target = beat.target;
490
+ const prevKey = beat.beatIndex === 0 ? 'SYNC' : TASK_ORDER[beat.beatIndex - 1];
491
+ const oldPos = getTargetPoint(prevKey, states);
492
+ const newPos = getTargetPoint(target, states);
493
+ const blendT = clamp((beat.beatPhase * beat.beatT) / P.saccade, 0, 1);
494
+ const eased = smoothstep(blendT);
495
+ return {
496
+ x: lerp(oldPos.x, newPos.x, eased),
497
+ y: lerp(oldPos.y, newPos.y, eased),
498
+ target,
499
+ saccading: blendT < 1
500
+ };
501
+ }
502
+
503
+ function getBodyResponse(now, beat, states) {
504
+ const called = states.find(s => s.key === beat.target);
505
+ const syncPulse = beat.sync ? Math.sin(Math.PI * clamp(beat.beatPhase, 0, 1)) : 0;
506
+ const callPulse = called ? called.callPulse : 0;
507
+ const focus = called ? called.p : { x: CENTER.x, y: CENTER.y - 2.4 };
508
+ const dx = focus.x - CENTER.x;
509
+ const dy = focus.y - CENTER.y;
510
+ const len = Math.max(Math.hypot(dx, dy), 0.001);
511
+ const nx = dx / len;
512
+ const ny = dy / len;
513
+ const breath = Math.sin(2 * Math.PI * now / 3.8);
514
+ const softFloat = Math.sin(2 * Math.PI * now / 4.6 - 0.4);
515
+ const idleTilt = Math.sin(2 * Math.PI * now / 5.2 + 0.7) * 0.42;
516
+ const lean = callPulse * P.bodyLean;
517
+ const tx = nx * lean;
518
+ const ty = ny * lean * 0.48 + softFloat * P.bodyFloat;
519
+ const rot = idleTilt + nx * callPulse * P.bodyTilt + syncPulse * 0.35 * Math.sin(2 * Math.PI * now / 0.9);
520
+ const sx = 1 + breath * P.bodyBreath * 0.62 + callPulse * 0.012 + syncPulse * 0.026;
521
+ const sy = 1 + breath * P.bodyBreath - callPulse * 0.006 + syncPulse * 0.020;
522
+ const morphAdd = callPulse * P.callMorph + syncPulse * P.syncMorph;
523
+ return { tx, ty, rot, sx, sy, callPulse, syncPulse, morphAdd };
524
+ }
525
+
526
+ function setCloudRootTransform(body) {
527
+ els.cloudRoot.setAttribute(
528
+ 'transform',
529
+ `translate(${(CENTER.x + body.tx).toFixed(3)} ${(CENTER.y + body.ty).toFixed(3)}) rotate(${body.rot.toFixed(3)}) scale(${body.sx.toFixed(4)} ${body.sy.toFixed(4)}) translate(${-CENTER.x} ${-CENTER.y})`
530
+ );
531
+ }
532
+
533
+ function renderAt(now) {
534
+ const beat = getBeat(now);
535
+ const taskStates = TASKS.map(task => getTaskState(now, task, beat));
536
+ const body = getBodyResponse(now, beat, taskStates);
537
+ setCloudRootTransform(body);
538
+
539
+ const morphPhase = (now / P.morphPeriod) * 2 * Math.PI;
540
+ const morph = clamp(((Math.sin(morphPhase) * 0.5 + 0.5) * P.morphAmt) + body.morphAdd, 0, 0.32);
541
+ const cloudD = pointsToSmoothPath(morphPoints(basePoints, P.cloudR, morph));
542
+ els.cloudPath.setAttribute('d', cloudD);
543
+ els.cloudClipPath.setAttribute('d', cloudD);
544
+ els.edgeContact.setAttribute('d', cloudD);
545
+ els.edgeRimSoft.setAttribute('d', cloudD);
546
+ els.edgeRimTight.setAttribute('d', cloudD);
547
+
548
+ els.orbitTrackA.setAttribute('d', orbitTrackD(P.rxA, P.ryA, P.tiltA, 96));
549
+ els.orbitTrackB.setAttribute('d', orbitTrackD(P.rxB, P.ryB, P.tiltB, 96));
550
+ els.orbitTrackLayer.setAttribute('opacity', P.showTracks ? 1 : 0);
551
+
552
+ const spin = (now * P.starSpin) % 360;
553
+ taskStates.forEach(state => {
554
+ setTaskTrail(state);
555
+ setTaskLayer(state, P.starSize, spin * state.spin);
556
+ });
557
+
558
+ const target = getEyeTarget(beat, taskStates);
559
+ const lookDx = target.x - CENTER.x;
560
+ const lookDy = target.y - CENTER.y;
561
+ const lookLen = Math.max(Math.hypot(lookDx, lookDy), 0.001);
562
+ const eyeX = lookDx / lookLen * P.eyeShift;
563
+ const eyeY = lookDy / lookLen * P.eyeShift * 0.55;
564
+ els.eyeGroup.setAttribute('transform', `translate(${eyeX.toFixed(3)} ${eyeY.toFixed(3)})`);
565
+
566
+ const blinkScale = getBlinkScale(now);
567
+ applyBlink(els.eyeL, EYE_BASE.l, blinkScale);
568
+ applyBlink(els.eyeR, EYE_BASE.r, blinkScale);
569
+ const flowOffset = Math.sin(2 * Math.PI * now / P.eyeFlowPeriod) * P.eyeFlowAmp;
570
+ els.eyeGrad.setAttribute('y1', (3 + flowOffset).toFixed(2));
571
+ els.eyeGrad.setAttribute('y2', (21 + flowOffset).toFixed(2));
572
+
573
+ const calledState = taskStates.find(s => s.key === target.target);
574
+ const orderedCount = taskStates.filter(s => s.slotMix > 0.82).length;
575
+ const closestPair = taskStates.reduce((best, a, i) => {
576
+ for (let j = i + 1; j < taskStates.length; j++) {
577
+ const b = taskStates[j];
578
+ const dist = Math.hypot(a.p.x - b.p.x, a.p.y - b.p.y);
579
+ if (dist < best.dist) best = { dist, pair: `${a.key}${b.key}` };
580
+ }
581
+ return best;
582
+ }, { dist: Infinity, pair: '--' });
583
+
584
+ els.scrub.value = Math.min(now % 12, 12).toFixed(2);
585
+ els.scrubV.textContent = `${(now % 12).toFixed(2)}s`;
586
+
587
+ els.readout.textContent =
588
+ `t=${now.toFixed(2)}s
589
+ beat=${beat.beatIndex + 1}/5 target=${target.target}${target.saccading ? ' (saccading)' : ''} sync=${beat.sync ? 'yes' : 'no'} ordered=${orderedCount}/4
590
+ ${taskStates.map(s => `${s.key}: theta=${s.theta.toFixed(0)}° depth=${s.depth.toFixed(2)} slot=${s.slotMix.toFixed(2)} pos=(${s.p.x.toFixed(2)}, ${s.p.y.toFixed(2)})`).join('\n')}
591
+ A/C 周期=${P.durA}s B/D 周期=${P.durB}s 比例=${(P.durB/P.durA).toFixed(3)}:1 点名循环=${(P.eyeSwitchT*5).toFixed(2)}s
592
+ called=${calledState ? calledState.key : 'all'} blink=${blinkScale.toFixed(2)} closest=${closestPair.pair} ${closestPair.dist.toFixed(2)}u
593
+ body: morph=${morph.toFixed(3)} call=${body.callPulse.toFixed(2)} sync=${body.syncPulse.toFixed(2)} tilt=${body.rot.toFixed(2)}°
594
+ note: C 方向 = orbit 只做流动路径; conducting 读感靠眼神点名 + spark 响应 + 归位同步.`;
595
+ }
596
+
597
+ function gcd(a, b) { return b ? gcd(b, a % b) : a; }
598
+
599
+ let startTime = performance.now();
600
+ let scrubTime = 0;
601
+
602
+ function loop(nowMs) {
603
+ let now;
604
+ if (P.pause) {
605
+ now = scrubTime;
606
+ } else {
607
+ now = (nowMs - startTime) / 1000;
608
+ scrubTime = now;
609
+ }
610
+ renderAt(now);
611
+ requestAnimationFrame(loop);
612
+ }
613
+
614
+ requestAnimationFrame(loop);
615
+ ]]></script>
616
+ </svg>