@mova-ui/react 0.4.7 → 0.4.9

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 (484) hide show
  1. package/dist/index.js +2561 -455
  2. package/dist/styles.css +1 -1
  3. package/dist/ui/cards/calendar-day-card.d.ts +6 -0
  4. package/dist/ui/cards/day-trading-order-ticket.d.ts +4 -2
  5. package/dist/ui/cards/imessage-chat.d.ts +3 -1
  6. package/dist/ui/cards/liquid-glass-sheet.d.ts +3 -1
  7. package/dist/ui/cards/voice-assistant-card.d.ts +3 -1
  8. package/package.json +3 -2
  9. package/registry/1.json +1 -1
  10. package/registry/10.json +1 -1
  11. package/registry/11.json +1 -1
  12. package/registry/12.json +1 -1
  13. package/registry/13.json +1 -1
  14. package/registry/14.json +1 -1
  15. package/registry/15.json +1 -1
  16. package/registry/16.json +1 -1
  17. package/registry/17.json +1 -1
  18. package/registry/18.json +1 -1
  19. package/registry/19.json +1 -1
  20. package/registry/2.json +1 -1
  21. package/registry/20.json +1 -1
  22. package/registry/21.json +1 -1
  23. package/registry/22.json +1 -1
  24. package/registry/23.json +1 -1
  25. package/registry/24.json +1 -1
  26. package/registry/25.json +1 -1
  27. package/registry/26.json +1 -1
  28. package/registry/27.json +1 -1
  29. package/registry/28.json +1 -1
  30. package/registry/29.json +1 -1
  31. package/registry/3.json +1 -1
  32. package/registry/30.json +1 -1
  33. package/registry/31.json +1 -1
  34. package/registry/32.json +1 -1
  35. package/registry/33.json +1 -1
  36. package/registry/34.json +1 -1
  37. package/registry/35.json +1 -1
  38. package/registry/36.json +1 -1
  39. package/registry/37.json +1 -1
  40. package/registry/4.json +1 -1
  41. package/registry/5.json +1 -1
  42. package/registry/6.json +1 -1
  43. package/registry/7.json +1 -1
  44. package/registry/8.json +1 -1
  45. package/registry/9.json +1 -1
  46. package/registry/ab-segmented-tabs.json +1 -1
  47. package/registry/accordion-blind.json +1 -1
  48. package/registry/accordion-loader.json +1 -1
  49. package/registry/ai-approval-card.json +1 -1
  50. package/registry/ai-code-block.json +1 -1
  51. package/registry/ai-context-meter.json +1 -1
  52. package/registry/ai-fine-tune-card.json +1 -1
  53. package/registry/ai-prompt-bar.json +1 -1
  54. package/registry/ai-sidebar-nav.json +2 -2
  55. package/registry/ai-sources.json +1 -1
  56. package/registry/ai-suggestions.json +1 -1
  57. package/registry/ai-task-list.json +1 -1
  58. package/registry/ai-tool-call.json +1 -1
  59. package/registry/analyzing-image.json +1 -1
  60. package/registry/animated-sidebar.json +1 -1
  61. package/registry/animated-tags.json +1 -1
  62. package/registry/app-icon-load.json +1 -1
  63. package/registry/apple-breathe.json +1 -1
  64. package/registry/apple-equalizer.json +1 -1
  65. package/registry/apple-icon-morph.json +1 -1
  66. package/registry/apple-pulse-dots.json +1 -1
  67. package/registry/apple-scale-pulse.json +1 -1
  68. package/registry/apple-sound-wave.json +1 -1
  69. package/registry/apple-text-reveal.json +1 -1
  70. package/registry/apple-unlock.json +1 -1
  71. package/registry/arc-tracer.json +1 -1
  72. package/registry/balance-bar-chart-card.json +3 -5
  73. package/registry/bar-cascade.json +1 -1
  74. package/registry/bar-sweep.json +1 -1
  75. package/registry/blur-text.json +1 -1
  76. package/registry/blur-up-char.json +1 -1
  77. package/registry/blur-up-word.json +1 -1
  78. package/registry/bobbing-dots.json +1 -1
  79. package/registry/bookmark-corner-peel.json +1 -1
  80. package/registry/bookmark-save-pill.json +1 -1
  81. package/registry/bookmark-toggle.json +1 -1
  82. package/registry/border-trail-loading.json +1 -1
  83. package/registry/bounce-dots.json +1 -1
  84. package/registry/bounce-in-char.json +1 -1
  85. package/registry/bouncing-bars.json +1 -1
  86. package/registry/bouncing-dots.json +1 -1
  87. package/registry/bouncing-lines.json +1 -1
  88. package/registry/bouncing-square.json +1 -1
  89. package/registry/breathe-ring.json +1 -1
  90. package/registry/breathing-glow.json +1 -1
  91. package/registry/breathing-square.json +1 -1
  92. package/registry/btn-1.json +1 -1
  93. package/registry/btn-10.json +1 -1
  94. package/registry/btn-11.json +1 -1
  95. package/registry/btn-12.json +1 -1
  96. package/registry/btn-13.json +1 -1
  97. package/registry/btn-14.json +1 -1
  98. package/registry/btn-15.json +1 -1
  99. package/registry/btn-16.json +1 -1
  100. package/registry/btn-17.json +1 -1
  101. package/registry/btn-18.json +1 -1
  102. package/registry/btn-19.json +1 -1
  103. package/registry/btn-2.json +1 -1
  104. package/registry/btn-20.json +1 -1
  105. package/registry/btn-21.json +1 -1
  106. package/registry/btn-22.json +1 -1
  107. package/registry/btn-23.json +1 -1
  108. package/registry/btn-24.json +1 -1
  109. package/registry/btn-25.json +1 -1
  110. package/registry/btn-26.json +1 -1
  111. package/registry/btn-27.json +1 -1
  112. package/registry/btn-28.json +1 -1
  113. package/registry/btn-29.json +1 -1
  114. package/registry/btn-3.json +1 -1
  115. package/registry/btn-30.json +1 -1
  116. package/registry/btn-31.json +1 -1
  117. package/registry/btn-32.json +1 -1
  118. package/registry/btn-33.json +1 -1
  119. package/registry/btn-34.json +1 -1
  120. package/registry/btn-35.json +1 -1
  121. package/registry/btn-36.json +1 -1
  122. package/registry/btn-37.json +1 -1
  123. package/registry/btn-4.json +1 -1
  124. package/registry/btn-5.json +1 -1
  125. package/registry/btn-6.json +1 -1
  126. package/registry/btn-7.json +1 -1
  127. package/registry/btn-8.json +1 -1
  128. package/registry/btn-9.json +1 -1
  129. package/registry/button-slide-arrow.json +1 -1
  130. package/registry/cacheable-bandwidth-cost.json +1 -1
  131. package/registry/calendar-day-card.json +4 -5
  132. package/registry/card-arc-5.json +1 -1
  133. package/registry/card-arc-7.json +1 -1
  134. package/registry/card-carousel-mono.json +1 -1
  135. package/registry/card-carousel.json +1 -1
  136. package/registry/card-cascade-stagger.json +1 -1
  137. package/registry/card-corner-fan.json +1 -1
  138. package/registry/card-cover-flow-mono.json +1 -1
  139. package/registry/card-cover-flow.json +1 -1
  140. package/registry/card-deck-cascade.json +1 -1
  141. package/registry/card-glance-preview.json +1 -1
  142. package/registry/card-hover.json +1 -1
  143. package/registry/card-linear-spread.json +1 -1
  144. package/registry/card-long-arc-5.json +1 -1
  145. package/registry/card-scatter-spread.json +1 -1
  146. package/registry/card-stack-peel.json +1 -1
  147. package/registry/card-stamp-arc.json +1 -1
  148. package/registry/card-time-machine-mono.json +1 -1
  149. package/registry/card-time-machine.json +1 -1
  150. package/registry/card-wheel-fan.json +1 -1
  151. package/registry/category-select.json +1 -1
  152. package/registry/character-stagger.json +1 -1
  153. package/registry/checkbox-draw.json +1 -1
  154. package/registry/checkmark-draw.json +1 -1
  155. package/registry/circle-toggle.json +1 -1
  156. package/registry/circuit-trace-draw.json +1 -1
  157. package/registry/circular-bars.json +1 -1
  158. package/registry/circular-create-button.json +1 -1
  159. package/registry/classic-spinner.json +1 -1
  160. package/registry/classic-toggle.json +1 -1
  161. package/registry/clock-spinner.json +1 -1
  162. package/registry/comet-spinner.json +1 -1
  163. package/registry/compass-needle-deflect.json +1 -1
  164. package/registry/concentric-pulse.json +1 -1
  165. package/registry/concentric-ring.json +1 -1
  166. package/registry/concentric-squares.json +1 -1
  167. package/registry/context-menu.json +1 -1
  168. package/registry/conveyor-loop.json +1 -1
  169. package/registry/credit-score-barcode-meter.json +1 -1
  170. package/registry/cross-spinner.json +1 -1
  171. package/registry/cube-flip-spring.json +1 -1
  172. package/registry/cursor-trail.json +1 -1
  173. package/registry/dash-ring.json +1 -1
  174. package/registry/dashed-spiral.json +1 -1
  175. package/registry/date-position-selector.json +1 -1
  176. package/registry/day-trading-order-ticket.json +3 -5
  177. package/registry/dia-text-reveal.json +1 -1
  178. package/registry/diamond-grid.json +1 -1
  179. package/registry/diamond-rotate-spring.json +1 -1
  180. package/registry/dislike-toggle.json +1 -1
  181. package/registry/dither-book.json +1 -1
  182. package/registry/domino-cascade.json +1 -1
  183. package/registry/dot-spinner.json +1 -1
  184. package/registry/dots-ring.json +1 -1
  185. package/registry/double-bounce-toggle.json +1 -1
  186. package/registry/double-ring.json +1 -1
  187. package/registry/download-icons.json +1 -1
  188. package/registry/drop-dot.json +1 -1
  189. package/registry/drop-in-char.json +1 -1
  190. package/registry/droplet-squish.json +1 -1
  191. package/registry/dual-arc.json +1 -1
  192. package/registry/dual-magnet-dipole.json +1 -1
  193. package/registry/dynamic-island.json +1 -1
  194. package/registry/elastic-bars.json +1 -1
  195. package/registry/elastic-square.json +1 -1
  196. package/registry/elastic-tag-snap.json +1 -1
  197. package/registry/error-shake.json +1 -1
  198. package/registry/expanding-cross.json +1 -1
  199. package/registry/expanding-icon-tabs.json +1 -1
  200. package/registry/face-id-scan.json +1 -1
  201. package/registry/fade-arc.json +1 -1
  202. package/registry/fade-dots.json +1 -1
  203. package/registry/fade-down.json +1 -1
  204. package/registry/fade-in-char.json +1 -1
  205. package/registry/fade-in-text.json +1 -1
  206. package/registry/fade-in-word.json +1 -1
  207. package/registry/fade-in.json +1 -1
  208. package/registry/fade-up.json +1 -1
  209. package/registry/file-upload-dropzone.json +1 -1
  210. package/registry/filter-tag-pill.json +1 -1
  211. package/registry/flip-square.json +1 -1
  212. package/registry/flip-x-char.json +1 -1
  213. package/registry/flip-y-char.json +1 -1
  214. package/registry/float-char.json +1 -1
  215. package/registry/float-word.json +1 -1
  216. package/registry/floating-diamonds.json +1 -1
  217. package/registry/floating-label-input.json +1 -1
  218. package/registry/fluid-bars.json +1 -1
  219. package/registry/fluid-diamond.json +1 -1
  220. package/registry/fluid-dot-orbit.json +1 -1
  221. package/registry/fluid-skeleton.json +1 -1
  222. package/registry/focus-blur.json +1 -1
  223. package/registry/form-submit-button.json +1 -1
  224. package/registry/french-doors-3d.json +1 -1
  225. package/registry/gear-tooth-step.json +1 -1
  226. package/registry/gears.json +1 -1
  227. package/registry/gelatin-wobble.json +1 -1
  228. package/registry/geometric-iris-shutter.json +1 -1
  229. package/registry/glassmorphic-card.json +1 -1
  230. package/registry/glow-button.json +1 -1
  231. package/registry/glow-text.json +1 -1
  232. package/registry/gooey-popover.json +1 -1
  233. package/registry/gradient-arc.json +1 -1
  234. package/registry/gradient-shimmer.json +1 -1
  235. package/registry/grid-dots.json +1 -1
  236. package/registry/haptic-ring.json +1 -1
  237. package/registry/heartbeat.json +1 -1
  238. package/registry/hex-lattice-draw.json +1 -1
  239. package/registry/hexagon-spinner.json +1 -1
  240. package/registry/hourglass.json +1 -1
  241. package/registry/hover-lift-char.json +1 -1
  242. package/registry/hover-lift-word.json +1 -1
  243. package/registry/hover-link-card.json +1 -1
  244. package/registry/hover-scale-char.json +1 -1
  245. package/registry/hover-scale-word.json +1 -1
  246. package/registry/imessage-chat.json +2 -2
  247. package/registry/inertia-skid-stop.json +1 -1
  248. package/registry/infinity-path.json +1 -1
  249. package/registry/input-focus-glow.json +1 -1
  250. package/registry/inset-action-button.json +1 -1
  251. package/registry/inset-button.json +1 -1
  252. package/registry/intersecting-rings.json +1 -1
  253. package/registry/ios-spinner.json +1 -1
  254. package/registry/kinetic-metronome-tick.json +1 -1
  255. package/registry/kinetic-tension-capsule.json +1 -1
  256. package/registry/light-dark-morph.json +1 -1
  257. package/registry/like-toggle.json +1 -1
  258. package/registry/line-spinner.json +1 -1
  259. package/registry/liquid-dots.json +1 -1
  260. package/registry/liquid-glass-sheet.json +3 -5
  261. package/registry/mac-terminal.json +1 -1
  262. package/registry/magnetic-button.json +1 -1
  263. package/registry/magnetic-disks.json +1 -1
  264. package/registry/magnetic-dots.json +1 -1
  265. package/registry/magnetic-slide-btn.json +1 -1
  266. package/registry/manifest.json +659 -666
  267. package/registry/matrix-grid-loader.json +1 -1
  268. package/registry/minimal-triangle.json +1 -1
  269. package/registry/modular-tile-snap.json +1 -1
  270. package/registry/mono-activity-blue.json +1 -1
  271. package/registry/mono-activity-green.json +1 -1
  272. package/registry/mono-activity-purple.json +1 -1
  273. package/registry/mono-heatmap.json +1 -1
  274. package/registry/mono-rounded-area.json +1 -1
  275. package/registry/mono-rounded-bar.json +1 -1
  276. package/registry/mono-rounded-bubble.json +1 -1
  277. package/registry/mono-rounded-bullet.json +1 -1
  278. package/registry/mono-rounded-candlestick.json +1 -1
  279. package/registry/mono-rounded-composed.json +1 -1
  280. package/registry/mono-rounded-donut.json +1 -1
  281. package/registry/mono-rounded-funnel.json +1 -1
  282. package/registry/mono-rounded-gauge-arc.json +1 -1
  283. package/registry/mono-rounded-heatmap.json +1 -1
  284. package/registry/mono-rounded-kpi.json +1 -1
  285. package/registry/mono-rounded-line.json +1 -1
  286. package/registry/mono-rounded-meter.json +1 -1
  287. package/registry/mono-rounded-polar.json +1 -1
  288. package/registry/mono-rounded-pyramid.json +1 -1
  289. package/registry/mono-rounded-radar.json +1 -1
  290. package/registry/mono-rounded-radial-gauge.json +1 -1
  291. package/registry/mono-rounded-radial-group.json +1 -1
  292. package/registry/mono-rounded-range.json +1 -1
  293. package/registry/mono-rounded-sankey.json +1 -1
  294. package/registry/mono-rounded-scatter.json +1 -1
  295. package/registry/mono-rounded-sparkline.json +1 -1
  296. package/registry/mono-rounded-stacked-bar.json +1 -1
  297. package/registry/mono-rounded-step.json +1 -1
  298. package/registry/mono-rounded-stream.json +1 -1
  299. package/registry/mono-rounded-treemap.json +1 -1
  300. package/registry/mono-rounded-waterfall.json +1 -1
  301. package/registry/mono-stock.json +1 -1
  302. package/registry/morph-action-pill.json +1 -1
  303. package/registry/morph-dot-ring.json +1 -1
  304. package/registry/morph-loader.json +1 -1
  305. package/registry/morph-toggle.json +1 -1
  306. package/registry/morphing-bars.json +1 -1
  307. package/registry/morphing-infinity.json +1 -1
  308. package/registry/morphing-ring.json +1 -1
  309. package/registry/morphing-shape.json +1 -1
  310. package/registry/mouse-follow.json +1 -1
  311. package/registry/multi-select-chips.json +1 -1
  312. package/registry/multi-tab-bar.json +1 -1
  313. package/registry/neon-sign-draw.json +1 -1
  314. package/registry/nested-orbital-gimbal.json +1 -1
  315. package/registry/network-telemetry-matrix.json +1 -1
  316. package/registry/newtons-cradle.json +1 -1
  317. package/registry/noise-decibel-level.json +1 -1
  318. package/registry/obsidian-liquid-wave.json +1 -1
  319. package/registry/offset-rings.json +1 -1
  320. package/registry/orbiting-circles.json +1 -1
  321. package/registry/orbiting-dot.json +1 -1
  322. package/registry/origami-envelope-unfold.json +1 -1
  323. package/registry/origami-shape.json +1 -1
  324. package/registry/otp-input.json +1 -1
  325. package/registry/overview-bar-scrubber-card.json +1 -1
  326. package/registry/page-transition.json +1 -1
  327. package/registry/page-turn-curl.json +1 -1
  328. package/registry/password-toggle.json +1 -1
  329. package/registry/pendulum-bubble-level.json +1 -1
  330. package/registry/pendulum.json +1 -1
  331. package/registry/perspective-flip-stage.json +1 -1
  332. package/registry/perspective-flip.json +1 -1
  333. package/registry/perspective-layout-switcher.json +1 -1
  334. package/registry/physics-dock.json +1 -1
  335. package/registry/pill-tabs.json +1 -1
  336. package/registry/plus-minus-toggle.json +1 -1
  337. package/registry/presets.json +1 -1
  338. package/registry/prism-block-stack.json +1 -1
  339. package/registry/progress-indicator-piano.json +1 -1
  340. package/registry/progress-indicator.json +1 -1
  341. package/registry/progress-stepper.json +1 -1
  342. package/registry/pulsating-dots.json +1 -1
  343. package/registry/pulse-char.json +1 -1
  344. package/registry/pulse-dot.json +1 -1
  345. package/registry/pulse-dots.json +1 -1
  346. package/registry/pulse-square.json +1 -1
  347. package/registry/pulse-word.json +1 -1
  348. package/registry/pulse.json +1 -1
  349. package/registry/pumping-heart.json +1 -1
  350. package/registry/pyramid-block-build.json +1 -1
  351. package/registry/radar-sweep.json +1 -1
  352. package/registry/radial-iris-mask.json +1 -1
  353. package/registry/radial-spinner.json +1 -1
  354. package/registry/radio-scale.json +1 -1
  355. package/registry/range-slider.json +1 -1
  356. package/registry/receipt-tape-print.json +1 -1
  357. package/registry/rectangle-toggle.json +1 -1
  358. package/registry/repost-toggle.json +1 -1
  359. package/registry/reveal-text.json +1 -1
  360. package/registry/ribbon-banner-slide.json +1 -1
  361. package/registry/ring-sweep.json +1 -1
  362. package/registry/ripple-effect.json +1 -1
  363. package/registry/rise-up-word.json +1 -1
  364. package/registry/roller-blind-drop.json +1 -1
  365. package/registry/rolling-tumble.json +1 -1
  366. package/registry/rotate-in-char.json +1 -1
  367. package/registry/rotating-cross.json +1 -1
  368. package/registry/rotating-louvers.json +1 -1
  369. package/registry/rotating-triangle.json +1 -1
  370. package/registry/rudder-issue-board.json +1 -1
  371. package/registry/sales-analytics-dual-bars.json +1 -1
  372. package/registry/sales-overview-radial-dashboard.json +1 -1
  373. package/registry/sales-target-segmented-arc.json +1 -1
  374. package/registry/scale-in-char.json +1 -1
  375. package/registry/scale-in-text.json +1 -1
  376. package/registry/scale-in-word.json +1 -1
  377. package/registry/scale-in.json +1 -1
  378. package/registry/scramble-text.json +1 -1
  379. package/registry/scroll-canvas-unroll.json +1 -1
  380. package/registry/scroll-reveal.json +1 -1
  381. package/registry/search-expand.json +1 -1
  382. package/registry/segmented-arc-meter.json +1 -1
  383. package/registry/segmented-link-stretch.json +1 -1
  384. package/registry/segmented-step-bar.json +1 -1
  385. package/registry/segmented-stepper-dots.json +1 -1
  386. package/registry/select-dropdown.json +1 -1
  387. package/registry/server-performance-step-bars.json +1 -1
  388. package/registry/shape-shift-grid.json +1 -1
  389. package/registry/shimmer-line.json +1 -1
  390. package/registry/shimmer-text.json +1 -1
  391. package/registry/shutter-slide.json +1 -1
  392. package/registry/shutter-step-slices.json +1 -1
  393. package/registry/siri-wave.json +1 -1
  394. package/registry/skeleton-loader.json +1 -1
  395. package/registry/skeleton.json +1 -1
  396. package/registry/skew-x-char.json +1 -1
  397. package/registry/slide-down-char.json +1 -1
  398. package/registry/slide-down-word.json +1 -1
  399. package/registry/slide-left-char.json +1 -1
  400. package/registry/slide-left.json +1 -1
  401. package/registry/slide-right-char.json +1 -1
  402. package/registry/slide-right.json +1 -1
  403. package/registry/slide-up-char.json +1 -1
  404. package/registry/slide-up-text.json +1 -1
  405. package/registry/slide-up-word.json +1 -1
  406. package/registry/sliding-bars.json +1 -1
  407. package/registry/slinky-coil.json +1 -1
  408. package/registry/smart-card-dispenser.json +1 -1
  409. package/registry/smooth-dot-shift.json +1 -1
  410. package/registry/smooth-ring.json +1 -1
  411. package/registry/smooth-rounded-square.json +1 -1
  412. package/registry/solid-switch.json +1 -1
  413. package/registry/spatial-door-portal.json +1 -1
  414. package/registry/spinning-squares.json +1 -1
  415. package/registry/spiral-spinner.json +1 -1
  416. package/registry/split-gate-reveal.json +1 -1
  417. package/registry/spotlight.json +1 -1
  418. package/registry/spring-bars.json +1 -1
  419. package/registry/spring-dot-matrix.json +1 -1
  420. package/registry/spring-hexagon.json +1 -1
  421. package/registry/spring-ring-expand.json +1 -1
  422. package/registry/spring-text-pop.json +1 -1
  423. package/registry/spring-text.json +1 -1
  424. package/registry/square-accordion.json +1 -1
  425. package/registry/square-grid.json +1 -1
  426. package/registry/square-snake.json +1 -1
  427. package/registry/square-spinner.json +1 -1
  428. package/registry/squash-sphere.json +1 -1
  429. package/registry/stacked-bar-pulse.json +1 -1
  430. package/registry/stagger-text.json +1 -1
  431. package/registry/staggered-glass-curtain.json +1 -1
  432. package/registry/sticky-note-peel.json +1 -1
  433. package/registry/sticky-reveal.json +1 -1
  434. package/registry/stretch-x-char.json +1 -1
  435. package/registry/stretch-y-char.json +1 -1
  436. package/registry/stroke-waveform.json +1 -1
  437. package/registry/submenu-flyout.json +1 -1
  438. package/registry/success-check.json +1 -1
  439. package/registry/sudden-brake.json +1 -1
  440. package/registry/swapping-dots.json +1 -1
  441. package/registry/swing-word.json +1 -1
  442. package/registry/swirling-spinner.json +1 -1
  443. package/registry/symmetric-wave.json +1 -1
  444. package/registry/terminal-loader.json +1 -1
  445. package/registry/text-blink.json +1 -1
  446. package/registry/text-dots.json +1 -1
  447. package/registry/text-morph.json +1 -1
  448. package/registry/text-reveal.json +1 -1
  449. package/registry/text-shimmer-wave.json +1 -1
  450. package/registry/text-shimmer.json +1 -1
  451. package/registry/textarea-auto-grow.json +1 -1
  452. package/registry/theme-toggle.json +1 -1
  453. package/registry/tilt-card.json +1 -1
  454. package/registry/timer-preparation-segmented.json +1 -1
  455. package/registry/tracking-in-text.json +1 -1
  456. package/registry/tracking-out-text.json +1 -1
  457. package/registry/trailing-dots.json +1 -1
  458. package/registry/triple-dot-spinner.json +1 -1
  459. package/registry/twin-orbit.json +1 -1
  460. package/registry/typewriter-text.json +1 -1
  461. package/registry/typing-indicator.json +1 -1
  462. package/registry/typing.json +1 -1
  463. package/registry/use-mouse-position.json +1 -1
  464. package/registry/use-reduced-motion.json +1 -1
  465. package/registry/use-scroll-progress.json +1 -1
  466. package/registry/use-stagger.json +1 -1
  467. package/registry/use-web-haptics.json +1 -1
  468. package/registry/users-growth-pill-progress.json +1 -1
  469. package/registry/utils.json +1 -1
  470. package/registry/vertical-wheel-counter.json +1 -1
  471. package/registry/views-hourly-wave-chart.json +1 -1
  472. package/registry/voice-assistant-card.json +3 -3
  473. package/registry/wandering-cube.json +1 -1
  474. package/registry/wandering-eyes.json +1 -1
  475. package/registry/watch-spinner.json +1 -1
  476. package/registry/wave-dots.json +1 -1
  477. package/registry/wave-physics-loader.json +1 -1
  478. package/registry/wave-reveal.json +1 -1
  479. package/registry/waveform-loader.json +1 -1
  480. package/registry/word-reveal.json +1 -1
  481. package/registry/zig-zag-pulse.json +1 -1
  482. package/registry/zoom-in-text.json +1 -1
  483. package/registry/zoom-in.json +1 -1
  484. package/registry/zoom-out-text.json +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "imessage-chat",
3
3
  "tier": "free",
4
- "version": "0.4.7",
4
+ "version": "0.4.9",
5
5
  "file": "imessage-chat.tsx",
6
6
  "sourcePath": "ui/cards/imessage-chat.tsx",
7
7
  "registryFile": "imessage-chat.json",
@@ -15,5 +15,5 @@
15
15
  ],
16
16
  "license": "MIT",
17
17
  "previewMode": "client",
18
- "content": "\"use client\";\n\"use client\";\n\n/** Original Mova implementation; visual reference documented in docs/provenance/interactive-cards.md. */\nimport * as React from \"react\";\nimport { ArrowUp, RotateCcw, Video } from \"lucide-react\";\n\nexport interface IMessageChatMessage { id: string; text: string; direction: \"incoming\" | \"outgoing\" }\nexport interface IMessageChatProps {\n name?: string;\n avatar?: string;\n deliveryStatus?: string;\n initialMessages?: IMessageChatMessage[];\n onSend?: (text: string) => void;\n onVideoCall?: () => void;\n theme?: \"light\" | \"dark\";\n className?: string;\n style?: React.CSSProperties;\n}\nconst conversation: IMessageChatMessage[] = [\n { id: \"1\", text: \"Are we still on for Saturday?\", direction: \"incoming\" },\n { id: \"2\", text: \"Yes! Trail at 9?\", direction: \"outgoing\" },\n { id: \"3\", text: \"Perfect. I'll bring coffee\", direction: \"incoming\" },\n { id: \"4\", text: \"The good kind this time\", direction: \"incoming\" },\n { id: \"5\", text: \"You're the best\", direction: \"outgoing\" },\n];\nexport function IMessageChat({ name = \"Maya Chen\", avatar, deliveryStatus, initialMessages = conversation, onSend, onVideoCall, theme = \"light\", className = \"\", style }: IMessageChatProps = {}) {\n const [messages, setMessages] = React.useState(initialMessages);\n const [draft, setDraft] = React.useState(\"\");\n const [status, setStatus] = React.useState(\"Read\");\n const thread = React.useRef<HTMLDivElement>(null);\n const sequence = React.useRef(0);\n const instanceId = React.useId();\n React.useEffect(() => { if (thread.current) thread.current.scrollTop = thread.current.scrollHeight; }, [messages]);\n function send(event: React.FormEvent) {\n event.preventDefault();\n const text = draft.trim();\n if (!text) return;\n setMessages(current => [...current, { id: `${instanceId}-sent-${++sequence.current}`, text, direction: \"outgoing\" }]);\n setDraft(\"\"); setStatus(\"Sent\"); onSend?.(text);\n }\n return <section className={`mova-imessage ${className}`} data-theme={theme} style={style} aria-label={`Conversation with ${name}`}>\n <style>{css}</style>\n <header><span className=\"mic-avatar\">{avatar ? <img src={avatar} alt=\"\" /> : name.split(\" \").map(word => word[0]).slice(0, 2).join(\"\")}</span><div className=\"mic-person\"><strong>{name}</strong><span>iMessage</span></div><button type=\"button\" aria-label=\"Restart conversation\" title=\"Restart conversation\" onClick={() => { setMessages(initialMessages); setStatus(\"Read\"); }}><RotateCcw size={16} /></button>{onVideoCall && <button type=\"button\" aria-label={`Video call ${name}`} onClick={onVideoCall}><Video size={17} /></button>}</header>\n <div className=\"mic-thread\" ref={thread} role=\"log\" aria-label=\"Messages\" aria-relevant=\"additions\"><div className=\"mic-messages\"><div className=\"mic-time\">Today 9:41 AM</div>{messages.map(message => <div key={message.id} className={`mic-bubble mic-${message.direction}`}>{message.text}</div>)}<div className=\"mic-status\" role=\"status\">{deliveryStatus ?? status}</div></div></div>\n <form onSubmit={send}><input aria-label=\"Message\" placeholder=\"iMessage\" value={draft} maxLength={2000} onChange={event => setDraft(event.target.value)} /><button type=\"submit\" aria-label=\"Send message\" disabled={!draft.trim()}><ArrowUp size={18} /></button></form>\n </section>;\n}\nconst css = `\n.mova-imessage{--mic-bg:#fff;--mic-fg:#202124;--mic-muted:#727479;--mic-soft:#f0f0f2;--mic-line:#dfdfe3;box-sizing:border-box;width:300px;max-width:100%;border-radius:24px;background:var(--mic-bg);color:var(--mic-fg);font:400 13px/20px -apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif;box-shadow:0 1px 3px #0000000a,0 8px 32px -4px #00000010;border:1px solid #88888818;overflow:hidden}\n.mova-imessage[data-theme=dark]{--mic-bg:#1d1d1d;--mic-fg:#f2f2f2;--mic-muted:#a2a2a8;--mic-soft:#313133;--mic-line:#49494c;color-scheme:dark}\n.mova-imessage *{box-sizing:border-box}.mova-imessage button,.mova-imessage input{font:inherit}.mova-imessage button{border:0;cursor:pointer;display:grid;place-items:center;flex:none;background:transparent;color:inherit;width:32px;height:32px;border-radius:50%;transition:background 160ms,transform 160ms}.mova-imessage button:hover{background:var(--mic-soft)}.mova-imessage button:active{transform:scale(.95)}.mova-imessage :focus-visible{outline:2px solid #3478f6;outline-offset:3px}.mova-imessage header{display:flex;gap:10px;align-items:center;padding:16px;border-bottom:1px solid #88888814}.mic-avatar{display:grid;place-items:center;width:32px;height:32px;flex:none;border-radius:50%;background:#c7b0a1;color:#433b37;font-size:11px;font-weight:600;overflow:hidden}.mic-avatar img{width:100%;height:100%;object-fit:cover}.mic-person{flex:1;min-width:0;display:flex;flex-direction:column}.mic-person strong{font-size:15px;font-weight:500;overflow-wrap:anywhere}.mic-person span,.mic-time,.mic-status{font-size:12px;line-height:16px;color:var(--mic-muted)}.mic-thread{height:252px;overflow:auto;padding:12px 16px;overscroll-behavior:contain}.mic-messages{min-height:100%;display:flex;flex-direction:column;justify-content:flex-end;gap:4px}.mic-time{align-self:center;margin-bottom:8px}.mic-bubble{max-width:82%;padding:8px 12px;border-radius:18px;overflow-wrap:anywhere;animation:mic-enter 240ms ease-out both}.mic-incoming{align-self:flex-start;background:var(--mic-soft);border-bottom-left-radius:5px}.mic-outgoing{align-self:flex-end;background:#2767f4;color:#fff;border-bottom-right-radius:5px}.mic-status{align-self:flex-end;margin-top:2px}.mova-imessage form{margin:12px 16px 16px;border:1px solid var(--mic-line);border-radius:24px;display:flex;align-items:center;padding:3px;gap:4px}.mova-imessage input{min-width:0;width:100%;border:0;outline:none;background:transparent;color:inherit;padding:4px 8px}.mova-imessage form:focus-within{outline:2px solid #3478f6;outline-offset:2px}.mova-imessage form button{background:#2767f4;color:#fff;width:28px;height:28px}.mova-imessage form button:hover{background:#1759e1}.mova-imessage form button:disabled{background:var(--mic-soft);color:var(--mic-muted);cursor:default}@keyframes mic-enter{from{opacity:0;transform:translateY(8px) scale(.97)}to{opacity:1;transform:none}}@media(prefers-reduced-motion:reduce){.mova-imessage *{animation:none!important;transition:none!important}}\n`;\n"
18
+ "content": "\"use client\";\n\"use client\";\n\n/** Original Mova implementation; visual reference documented in docs/provenance/interactive-cards.md. */\n/** Header/send icons: Heroicons (outline), Copyright (c) Tailwind Labs, Inc. MIT License. */\nimport * as React from \"react\";\nimport { Video } from \"lucide-react\";\n\nexport interface IMessageChatMessage { id: string; text: string; direction: \"incoming\" | \"outgoing\" }\nexport interface IMessageChatProps {\n name?: string;\n avatar?: string;\n deliveryStatus?: string;\n initialMessages?: IMessageChatMessage[];\n onSend?: (text: string) => void;\n onAudioCall?: () => void;\n onVideoCall?: () => void;\n theme?: \"light\" | \"dark\";\n className?: string;\n style?: React.CSSProperties;\n}\nconst conversation: IMessageChatMessage[] = [\n { id: \"1\", text: \"Are we still on for Saturday?\", direction: \"incoming\" },\n { id: \"2\", text: \"Yes! Trail at 9?\", direction: \"outgoing\" },\n { id: \"3\", text: \"Perfect. I'll bring coffee\", direction: \"incoming\" },\n { id: \"4\", text: \"The good kind this time\", direction: \"incoming\" },\n { id: \"5\", text: \"You're the best\", direction: \"outgoing\" },\n];\nconst defaultAvatar = \"https://images.unsplash.com/photo-1580489944761-15a19d654956?auto=format&fit=crop&q=80&w=64&h=64\";\nconst replies = [\"Love that\", \"See you there!\", \"Haha deal\", \"Bringing snacks too\"];\n\nexport function IMessageChat({ name = \"Maya Chen\", avatar = defaultAvatar, deliveryStatus, initialMessages = conversation, onSend, onAudioCall, onVideoCall, theme = \"light\", className = \"\", style }: IMessageChatProps = {}) {\n const [messages, setMessages] = React.useState(initialMessages);\n const [draft, setDraft] = React.useState(\"\");\n const [status, setStatus] = React.useState(\"Read\");\n const [statusPhase, setStatusPhase] = React.useState<\"stable\" | \"exit\" | \"enter\">(\"stable\");\n const [receiptAfter, setReceiptAfter] = React.useState(initialMessages[initialMessages.length - 1]?.id);\n const [newMessages, setNewMessages] = React.useState<Set<string>>(() => new Set());\n const [receiptNew, setReceiptNew] = React.useState(false);\n const [typing, setTyping] = React.useState(false);\n const [tooltip, setTooltip] = React.useState({ text: \"\", visible: false, width: 0, x: 0 });\n const [pressed, setPressed] = React.useState<string>();\n const thread = React.useRef<HTMLDivElement>(null);\n const input = React.useRef<HTMLInputElement>(null);\n const statusText = React.useRef<HTMLSpanElement>(null);\n const tooltipText = React.useRef<HTMLSpanElement>(null);\n const actions = React.useRef<HTMLSpanElement>(null);\n const tooltipTrigger = React.useRef<HTMLButtonElement | null>(null);\n const timers = React.useRef<ReturnType<typeof setTimeout>[]>([]);\n const pressTimer = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n const pressStarted = React.useRef(0);\n const initiallyScrolled = React.useRef(false);\n const sequence = React.useRef(0);\n const replyIndex = React.useRef(0);\n const instanceId = React.useId();\n const tooltipId = `${instanceId}-tooltip`;\n\n function clearTimers() {\n timers.current.forEach(clearTimeout);\n timers.current = [];\n }\n function later(callback: () => void, delay: number) {\n timers.current.push(setTimeout(callback, delay));\n }\n React.useEffect(() => () => { clearTimers(); clearTimeout(pressTimer.current); }, []);\n React.useEffect(() => {\n if (!thread.current) return;\n const reduced = window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n thread.current.scrollTo({ top: thread.current.scrollHeight, behavior: !initiallyScrolled.current || reduced ? \"auto\" : \"smooth\" });\n initiallyScrolled.current = true;\n }, [messages, typing]);\n React.useLayoutEffect(() => {\n if (statusPhase !== \"enter\") return;\n // Flush the incoming position before transitioning the new receipt into place.\n void statusText.current?.offsetHeight;\n setStatusPhase(\"stable\");\n }, [statusPhase]);\n React.useLayoutEffect(() => {\n if (!tooltip.visible || !tooltipText.current || !tooltipTrigger.current || !actions.current) return;\n const width = Math.ceil(tooltipText.current.scrollWidth + 24);\n const group = actions.current.getBoundingClientRect();\n const trigger = tooltipTrigger.current.getBoundingClientRect();\n const x = Math.min(trigger.left - group.left + trigger.width / 2 - width / 2, group.width + 6 - width);\n setTooltip(current => current.width === width && current.x === x ? current : { ...current, width, x });\n }, [tooltip.text, tooltip.visible]);\n\n function showTooltip(event: React.SyntheticEvent<HTMLButtonElement>, text: string) {\n tooltipTrigger.current = event.currentTarget;\n setTooltip(current => ({ ...current, text, visible: true }));\n }\n function hideTooltip() { setTooltip(current => ({ ...current, visible: false })); }\n function press(event: React.PointerEvent<HTMLButtonElement>) {\n if (event.pointerType === \"mouse\") return;\n clearTimeout(pressTimer.current);\n pressStarted.current = performance.now();\n setPressed(event.currentTarget.dataset.press);\n event.currentTarget.setPointerCapture(event.pointerId);\n }\n function releasePress() {\n pressTimer.current = setTimeout(() => setPressed(undefined), Math.max(0, 150 - (performance.now() - pressStarted.current)));\n }\n function finishAnimation(id: string, event: React.AnimationEvent) {\n if (event.animationName !== \"mic-pop\") return;\n setNewMessages(current => { const next = new Set(current); next.delete(id); return next; });\n }\n function send(event: React.FormEvent) {\n event.preventDefault();\n const text = draft.trim();\n if (!text) return;\n clearTimers();\n const id = `${instanceId}-sent-${++sequence.current}`;\n setTyping(false);\n setMessages(current => [...current, { id, text, direction: \"outgoing\" }]);\n setNewMessages(current => new Set(current).add(id));\n setReceiptAfter(id);\n setReceiptNew(true);\n setStatus(\"Delivered\");\n setStatusPhase(\"stable\");\n setDraft(\"\");\n later(() => {\n setStatusPhase(\"exit\");\n later(() => { setStatus(\"Read\"); setStatusPhase(\"enter\"); }, 150);\n }, 1200);\n later(() => setTyping(true), 1800);\n later(() => {\n setTyping(false);\n const replyId = `${instanceId}-reply-${++sequence.current}`;\n const reply = replies[replyIndex.current++ % replies.length];\n setMessages(current => [...current, { id: replyId, text: reply, direction: \"incoming\" }]);\n setNewMessages(current => new Set(current).add(replyId));\n }, 3400);\n onSend?.(text);\n }\n const receipt = <div key=\"receipt\" className={`mic-status${receiptNew ? \" mic-new\" : \"\"}`} role=\"status\" onAnimationEnd={event => { if (event.animationName === \"mic-pop\") setReceiptNew(false); }}><span ref={statusText} className={`mic-text-swap${statusPhase === \"exit\" ? \" mic-exit\" : statusPhase === \"enter\" ? \" mic-enter-start\" : \"\"}`}>{deliveryStatus ?? status}</span></div>;\n const contents: React.ReactNode[] = [<div key=\"timestamp\" className=\"mic-time\">Today 9:41 AM</div>];\n messages.forEach(message => {\n contents.push(<div key={message.id} className={`mic-bubble mic-${message.direction}${newMessages.has(message.id) ? \" mic-new\" : \"\"}`} onAnimationEnd={event => finishAnimation(message.id, event)}>{message.text}</div>);\n if (message.id === receiptAfter) contents.push(receipt);\n });\n if (!messages.length) contents.push(receipt);\n if (typing) contents.push(<div key=\"typing\" className=\"mic-bubble mic-incoming mic-typing mic-new\" aria-label={`${name.split(\" \")[0]} is typing`}><i /><i /><i /></div>);\n return <section className={`mova-imessage ${className}`} data-theme={theme} style={style} aria-label={`Conversation with ${name}`}>\n <style>{css}</style>\n <header><span className=\"mic-avatar\">{avatar ? <img src={avatar} alt={name} width={32} height={32} /> : name.split(\" \").map(word => word[0]).slice(0, 2).join(\"\")}</span><div className=\"mic-person\"><strong>{name}</strong><span>iMessage</span></div><span className=\"mic-actions\" ref={actions} onPointerLeave={hideTooltip}>\n <button type=\"button\" className=\"mic-icon\" aria-label=\"Audio call\" aria-describedby={tooltipId} data-press=\"audio\" data-pressed={pressed === \"audio\"} onPointerDown={press} onPointerUp={releasePress} onPointerCancel={releasePress} onPointerEnter={event => showTooltip(event, \"Audio call\")} onFocus={event => showTooltip(event, \"Audio call\")} onBlur={hideTooltip} onClick={() => { hideTooltip(); onAudioCall?.(); }}><svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth=\"2.25\" aria-hidden=\"true\"><path d=\"M2.25 6.75c0 8.284 6.716 15 15 15h2.25a2.25 2.25 0 0 0 2.25-2.25v-1.372c0-.516-.351-.966-.852-1.091l-4.423-1.106c-.44-.11-.902.055-1.173.417l-.97 1.293c-.282.376-.769.542-1.21.38a12.035 12.035 0 0 1-7.143-7.143c-.162-.441.004-.928.38-1.21l1.293-.97c.363-.271.527-.734.417-1.173L6.963 3.102a1.125 1.125 0 0 0-1.091-.852H4.5A2.25 2.25 0 0 0 2.25 4.5v2.25Z\" /></svg></button>\n {onVideoCall && <button type=\"button\" className=\"mic-icon\" aria-label={`Video call ${name}`} aria-describedby={tooltipId} data-press=\"video\" data-pressed={pressed === \"video\"} onPointerDown={press} onPointerUp={releasePress} onPointerCancel={releasePress} onPointerEnter={event => showTooltip(event, \"Video call\")} onFocus={event => showTooltip(event, \"Video call\")} onBlur={hideTooltip} onClick={() => { hideTooltip(); onVideoCall(); }}><Video size={16} /></button>}\n <span className=\"mic-tooltip\" id={tooltipId} role=\"tooltip\" aria-hidden={!tooltip.visible} data-show={tooltip.visible} style={{ width: tooltip.width, \"--mic-tooltip-x\": `${tooltip.x}px` } as React.CSSProperties}><span ref={tooltipText}>{tooltip.text}</span></span>\n </span></header>\n <div className=\"mic-divider\" aria-hidden=\"true\" />\n <div className=\"mic-thread\" ref={thread} role=\"log\" aria-label=\"Messages\" aria-live=\"polite\" aria-relevant=\"additions\"><div className=\"mic-messages\">{contents}</div></div>\n <form onSubmit={send}><label className=\"mic-field\"><input ref={input} type=\"text\" aria-label=\"Message\" placeholder=\"iMessage\" autoComplete=\"off\" value={draft} maxLength={2000} onChange={event => setDraft(event.target.value)} /><button type=\"submit\" className={`mic-send${draft.trim() ? \" mic-ready\" : \"\"}`} aria-label=\"Send\" aria-disabled={!draft.trim()} data-press=\"send\" data-pressed={pressed === \"send\"} onPointerDown={press} onPointerUp={releasePress} onPointerCancel={releasePress} onClick={() => input.current?.focus()}><svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth=\"2.25\" aria-hidden=\"true\"><path d=\"M4.5 10.5 12 3m0 0 7.5 7.5M12 3v18\" /></svg></button></label></form>\n </section>;\n}\nconst css = `\n.mova-imessage{--mic-bg:#fff;--mic-fg:#0d0d0d;--mic-muted:#6c6c6c;--mic-chip:rgba(0,0,0,.04);--mic-bubble-in:rgba(0,0,0,.05);--mic-bubble-out:#1c5ffb;--mic-input-line:rgba(0,0,0,.14);--mic-input-focus:#585858;--mic-divider:rgba(0,0,0,.0392);--mic-tooltip-bg:#fff;--mic-tooltip-fg:#2f2f2f;box-sizing:border-box;width:300px;max-width:100%;display:flex;flex-direction:column;border-radius:24px;background:var(--mic-bg);color:var(--mic-fg);font:400 13px/20px \"Inter Variable\",Inter,-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif;letter-spacing:0;box-shadow:0 0 0 1px #0000000a,0 8px 32px -4px #0000000f,0 1px 3px #0000000a;overflow:hidden;-webkit-font-smoothing:antialiased}\n.mova-imessage[data-theme=dark]{--mic-bg:#1d1d1d;--mic-fg:#f2f2f2;--mic-muted:rgba(202,202,202,.7);--mic-chip:rgba(255,255,255,.07);--mic-bubble-in:rgba(255,255,255,.08);--mic-bubble-out:#2f6bff;--mic-input-line:rgba(255,255,255,.14);--mic-input-focus:#a5a5a5;--mic-divider:rgba(255,255,255,.0392);--mic-tooltip-bg:#262626;--mic-tooltip-fg:#f2f2f2;color-scheme:dark}\n.mova-imessage *{box-sizing:border-box}.mova-imessage button,.mova-imessage input{font:inherit;letter-spacing:inherit}.mova-imessage button{padding:0;border:0;background:none;color:inherit;cursor:pointer;-webkit-tap-highlight-color:transparent;touch-action:manipulation;-webkit-user-select:none;user-select:none;-webkit-touch-callout:none}.mova-imessage button:is(:active,[data-pressed=true]){transform:scale(.95)}.mova-imessage :focus-visible:not(input){outline:none;box-shadow:0 0 0 2px var(--mic-bg),0 0 0 4px var(--mic-fg)}.mova-imessage header{display:flex;gap:12px;align-items:center;padding:16px;position:relative;z-index:2}.mic-avatar{display:inline-grid;place-items:center;flex:none;width:32px;height:32px;border-radius:50%;background:#c7b0a1;color:#433b37;font-size:12.8px;font-weight:500;line-height:1;overflow:hidden}.mic-avatar img{display:block;width:32px;height:32px;border-radius:50%;object-fit:cover;outline:1px solid #00000014;outline-offset:-1px}.mova-imessage[data-theme=dark] .mic-avatar img{outline-color:#ffffff14}.mic-person{flex:1;min-width:0;display:flex;flex-direction:column}.mic-person strong{font-size:15px;line-height:20px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mic-person>span,.mic-time,.mic-status{font-size:12px;line-height:16px;color:var(--mic-muted)}.mic-actions{position:relative;display:inline-flex;flex:none;gap:4px;margin-right:-6px}.mova-imessage .mic-icon{width:32px;height:32px;border-radius:50%;display:grid;place-items:center;color:var(--mic-muted);transition:transform 150ms cubic-bezier(.22,1,.36,1),background-color 150ms cubic-bezier(.22,1,.36,1)}@media(hover:hover) and (pointer:fine){.mova-imessage .mic-icon:hover{background:var(--mic-chip)}}.mic-tooltip{position:absolute;top:calc(100% + 8px);left:0;display:inline-flex;align-items:center;justify-content:center;overflow:hidden;translate:var(--mic-tooltip-x,0px) 0;scale:.98;transform-origin:50% 0;padding:8px 12px;border-radius:8px;background:var(--mic-tooltip-bg);color:var(--mic-tooltip-fg);font-size:12px;line-height:16px;white-space:nowrap;box-shadow:0 0 0 1px #0000000f,0 2px 6px #0000000d,0 4px 42px #0000000f;opacity:0;pointer-events:none;z-index:3;transition:opacity 50ms ease-out,scale 50ms ease-out,translate 160ms cubic-bezier(.22,1,.36,1),width 160ms cubic-bezier(.22,1,.36,1)}.mic-tooltip[data-show=true]{opacity:1;scale:1;transition:opacity 150ms ease-out 80ms,scale 150ms ease-out 80ms,translate 160ms cubic-bezier(.22,1,.36,1),width 160ms cubic-bezier(.22,1,.36,1)}.mic-tooltip>span{white-space:nowrap}.mic-divider{height:1px;flex:none;background:linear-gradient(90deg,transparent,var(--mic-divider) 20%,var(--mic-divider) 80%,transparent)}.mic-thread{box-sizing:content-box;height:228px;display:flex;flex-direction:column;overflow-y:auto;padding:12px 16px;scrollbar-width:none;overscroll-behavior:contain;-webkit-mask-image:linear-gradient(to bottom,transparent,#000 16px);mask-image:linear-gradient(to bottom,transparent,#000 16px)}.mic-thread::-webkit-scrollbar{display:none}.mic-messages{margin-top:auto;display:flex;flex-direction:column;gap:2px}.mic-time{align-self:center;margin-bottom:8px}.mic-bubble{box-sizing:content-box;max-width:78%;padding:8px 12px;border-radius:18px;font-size:13px;line-height:20px;overflow-wrap:anywhere;text-wrap:pretty;transition:border-radius 250ms cubic-bezier(.22,1,.36,1)}.mic-incoming{align-self:flex-start;background:var(--mic-bubble-in);border-bottom-left-radius:6px;transform-origin:0 100%}.mic-outgoing{align-self:flex-end;background:var(--mic-bubble-out);color:#fff;border-bottom-right-radius:6px;transform-origin:100% 100%}.mic-incoming:has(+.mic-incoming){border-bottom-left-radius:18px}.mic-outgoing:has(+.mic-outgoing){border-bottom-right-radius:18px}.mic-incoming+.mic-outgoing,.mic-outgoing+.mic-incoming,.mic-status+.mic-bubble{margin-top:6px}.mic-status{align-self:flex-end;padding:2px 4px 0 0;transform-origin:100% 0}.mic-new{animation:mic-pop 500ms cubic-bezier(.34,1.36,.64,1) both,mic-fade 400ms cubic-bezier(.22,1,.36,1) both}@keyframes mic-pop{from{transform:translateY(8px) scale(.92)}to{transform:none}}@keyframes mic-fade{from{opacity:0;filter:blur(2px)}to{opacity:1;filter:blur(0)}}.mic-bubble.mic-typing{display:flex;align-items:center;gap:4px;padding:14px 12px}.mic-typing i{width:6px;height:6px;border-radius:50%;background:var(--mic-muted);animation:mic-typing 1200ms ease-in-out infinite}.mic-typing i:nth-child(2){animation-delay:180ms}.mic-typing i:nth-child(3){animation-delay:360ms}@keyframes mic-typing{0%,60%,100%{opacity:.35;transform:translateY(0)}30%{opacity:1;transform:translateY(-2px)}}.mic-text-swap{display:inline-block;transform:translateY(0);filter:blur(0);opacity:1;transition:transform 150ms ease-in-out,filter 150ms ease-in-out,opacity 150ms ease-in-out;will-change:transform,filter,opacity}.mic-text-swap.mic-exit{transform:translateY(-4px);filter:blur(2px);opacity:0}.mic-text-swap.mic-enter-start{transform:translateY(4px);filter:blur(2px);opacity:0;transition:none}.mova-imessage form{padding:8px 16px 16px;margin:0}.mic-field{display:flex;align-items:center;gap:8px;height:36px;padding:0 4px 0 14px;border-radius:40px;background:var(--mic-bg);box-shadow:inset 0 0 0 1px var(--mic-input-line);transition:box-shadow 120ms ease;cursor:text}.mic-field:focus-within{box-shadow:inset 0 0 0 1.5px var(--mic-input-focus)}.mova-imessage input{flex:1;min-width:0;height:100%;padding:0;border:0;outline:none;background:none;color:var(--mic-fg);font-size:13px;line-height:20px}.mova-imessage input::placeholder{color:var(--mic-muted)}.mova-imessage .mic-send{flex:none;width:28px;height:28px;border-radius:50%;display:grid;place-items:center;background:var(--mic-chip);color:var(--mic-muted);transition:transform 150ms cubic-bezier(.22,1,.36,1),background-color 150ms cubic-bezier(.22,1,.36,1),color 150ms cubic-bezier(.22,1,.36,1)}.mova-imessage .mic-send[aria-disabled=true]{cursor:default}.mova-imessage .mic-send.mic-ready{background:var(--mic-bubble-out);color:#fff}@media(prefers-reduced-motion:reduce){.mova-imessage *{animation:none!important;transition:none!important}.mova-imessage .mic-typing i{opacity:.6}.mova-imessage button:is(:active,[data-pressed=true]){transform:none}}\n`;\n"
19
19
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "inertia-skid-stop",
3
3
  "tier": "free",
4
- "version": "0.4.7",
4
+ "version": "0.4.9",
5
5
  "file": "inertia-skid-stop.tsx",
6
6
  "sourcePath": "components/css-animations/whimsical/YuiPhysicsExperiments.tsx",
7
7
  "registryFile": "inertia-skid-stop.json",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "infinity-path",
3
3
  "tier": "free",
4
- "version": "0.4.7",
4
+ "version": "0.4.9",
5
5
  "file": "infinity-path.tsx",
6
6
  "sourcePath": "ui/loading/infinity-path.tsx",
7
7
  "registryFile": "infinity-path.json",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "input-focus-glow",
3
3
  "tier": "free",
4
- "version": "0.4.7",
4
+ "version": "0.4.9",
5
5
  "file": "input-focus-glow.tsx",
6
6
  "registryFile": "input-focus-glow.json",
7
7
  "exports": [
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "inset-action-button",
3
3
  "tier": "free",
4
- "version": "0.4.7",
4
+ "version": "0.4.9",
5
5
  "file": "inset-action-button.tsx",
6
6
  "registryFile": "inset-action-button.json",
7
7
  "exports": [
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "inset-button",
3
3
  "tier": "free",
4
- "version": "0.4.7",
4
+ "version": "0.4.9",
5
5
  "file": "inset-button.tsx",
6
6
  "sourcePath": "ui/hover/inset-button.tsx",
7
7
  "registryFile": "inset-button.json",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "intersecting-rings",
3
3
  "tier": "free",
4
- "version": "0.4.7",
4
+ "version": "0.4.9",
5
5
  "file": "intersecting-rings.tsx",
6
6
  "sourcePath": "ui/loading/intersecting-rings.tsx",
7
7
  "registryFile": "intersecting-rings.json",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "ios-spinner",
3
3
  "tier": "free",
4
- "version": "0.4.7",
4
+ "version": "0.4.9",
5
5
  "file": "ios-spinner.tsx",
6
6
  "sourcePath": "ui/loading/ios-spinner.tsx",
7
7
  "registryFile": "ios-spinner.json",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "kinetic-metronome-tick",
3
3
  "tier": "free",
4
- "version": "0.4.7",
4
+ "version": "0.4.9",
5
5
  "file": "kinetic-metronome-tick.tsx",
6
6
  "sourcePath": "components/css-animations/whimsical/RedesignedPhysicsTrios.tsx",
7
7
  "registryFile": "kinetic-metronome-tick.json",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "kinetic-tension-capsule",
3
3
  "tier": "free",
4
- "version": "0.4.7",
4
+ "version": "0.4.9",
5
5
  "file": "kinetic-tension-capsule.tsx",
6
6
  "sourcePath": "components/css-animations/whimsical/KineticTensionCapsule.tsx",
7
7
  "registryFile": "kinetic-tension-capsule.json",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "light-dark-morph",
3
3
  "tier": "free",
4
- "version": "0.4.7",
4
+ "version": "0.4.9",
5
5
  "file": "light-dark-morph.tsx",
6
6
  "sourcePath": "components/css-animations/yui-components/YuiUiKit1.tsx",
7
7
  "registryFile": "light-dark-morph.json",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "like-toggle",
3
3
  "tier": "free",
4
- "version": "0.4.7",
4
+ "version": "0.4.9",
5
5
  "file": "like-toggle.tsx",
6
6
  "registryFile": "like-toggle.json",
7
7
  "exports": [
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "line-spinner",
3
3
  "tier": "free",
4
- "version": "0.4.7",
4
+ "version": "0.4.9",
5
5
  "file": "line-spinner.tsx",
6
6
  "sourcePath": "ui/loading/line-spinner.tsx",
7
7
  "registryFile": "line-spinner.json",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "liquid-dots",
3
3
  "tier": "free",
4
- "version": "0.4.7",
4
+ "version": "0.4.9",
5
5
  "file": "liquid-dots.tsx",
6
6
  "sourcePath": "ui/loading/liquid-dots.tsx",
7
7
  "registryFile": "liquid-dots.json",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "liquid-glass-sheet",
3
3
  "tier": "free",
4
- "version": "0.4.7",
4
+ "version": "0.4.9",
5
5
  "file": "liquid-glass-sheet.tsx",
6
6
  "sourcePath": "ui/cards/liquid-glass-sheet.tsx",
7
7
  "registryFile": "liquid-glass-sheet.json",
@@ -9,10 +9,8 @@
9
9
  "LiquidGlassSheet",
10
10
  "LiquidGlassSheetProps"
11
11
  ],
12
- "dependencies": [
13
- "lucide-react"
14
- ],
12
+ "dependencies": [],
15
13
  "license": "MIT",
16
14
  "previewMode": "client",
17
- "content": "\"use client\";\n\"use client\";\n\n/** Original Mova implementation; visual reference documented in docs/provenance/interactive-cards.md. */\nimport * as React from \"react\";\nimport { Check, Copy, Heart, Link, Play, Share2, X } from \"lucide-react\";\n\nexport interface LiquidGlassSheetProps {\n title?: string; subtitle?: string; backgroundImage?: string; link?: string;\n defaultOpen?: boolean; onOpenChange?: (open: boolean) => void; onSaveChange?: (saved: boolean) => void;\n onPlay?: () => void; onShare?: () => void;\n theme?: \"light\" | \"dark\"; className?: string; style?: React.CSSProperties;\n}\nexport function LiquidGlassSheet({ title = \"Autumn forest\", subtitle = \"Liquid glass sheet\", backgroundImage, link = \"https://mova-ui.com\", defaultOpen = true, onOpenChange, onSaveChange, onPlay, onShare, theme = \"light\", className = \"\", style }: LiquidGlassSheetProps = {}) {\n const [open, setOpen] = React.useState(defaultOpen);\n const [saved, setSaved] = React.useState(false);\n const [status, setStatus] = React.useState(\"\");\n const [drag, setDrag] = React.useState(0);\n const start = React.useRef<number | null>(null);\n const trigger = React.useRef<HTMLButtonElement>(null);\n const panel = React.useRef<HTMLElement>(null);\n const titleId = React.useId();\n const wasOpened = React.useRef(false);\n function changeOpen(value: boolean) { setOpen(value); setDrag(0); onOpenChange?.(value); if (!value) trigger.current?.focus(); }\n React.useEffect(() => { if (open && wasOpened.current) panel.current?.querySelector<HTMLButtonElement>(\"button\")?.focus(); wasOpened.current = true; }, [open]);\n async function copy() {\n try { await navigator.clipboard.writeText(link); setStatus(\"Link copied\"); }\n catch { setStatus(\"Copy unavailable. Select the link below.\"); }\n }\n function keys(event: React.KeyboardEvent<HTMLElement>) {\n if (event.key === \"Escape\") { event.preventDefault(); changeOpen(false); }\n if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n const buttons = Array.from(panel.current?.querySelectorAll<HTMLButtonElement>(\"button\") ?? []);\n const at = buttons.indexOf(document.activeElement as HTMLButtonElement);\n event.preventDefault(); buttons[(at + (event.key === \"ArrowDown\" ? 1 : -1) + buttons.length) % buttons.length]?.focus();\n }\n }\n return <section className={`mova-glass-sheet ${className}`} data-theme={theme} style={{ ...style, ...(backgroundImage ? { backgroundImage: `url(${JSON.stringify(backgroundImage)})` } : {}) }} aria-label={title}>\n <style>{css}</style><div className=\"mgs-forest\" aria-hidden=\"true\"><i /><i /><i /><i /><i /><i /></div>\n <button type=\"button\" className=\"mgs-trigger\" ref={trigger} aria-expanded={open} aria-controls={`${titleId}-sheet`} onClick={() => changeOpen(!open)}>{open ? \"Close sheet\" : \"Open sheet\"}</button>\n {open && <section className=\"mgs-panel\" ref={panel} id={`${titleId}-sheet`} aria-labelledby={titleId} onKeyDown={keys} style={{ transform: `translateY(${drag}px)` }}>\n <div className=\"mgs-grab\" onPointerDown={event => { start.current = event.clientY; event.currentTarget.setPointerCapture(event.pointerId); }} onPointerMove={event => { if (start.current !== null) setDrag(Math.max(0, event.clientY - start.current)); }} onPointerUp={() => { start.current = null; if (drag > 60) changeOpen(false); else setDrag(0); }} onPointerCancel={() => { start.current = null; setDrag(0); }} aria-hidden=\"true\"><span /></div>\n <header><div><h2 id={titleId}>{title}</h2><p>{subtitle}</p></div><button type=\"button\" className=\"mgs-close\" aria-label=\"Close sheet\" onClick={() => changeOpen(false)}><X size={15} /></button></header>\n <button type=\"button\" className=\"mgs-row\" onClick={() => { onPlay?.(); setStatus(\"Preview selected\"); }}><Play size={16} /><span>Play preview</span></button>\n <button type=\"button\" className=\"mgs-row\" onClick={() => { onShare?.(); if (!onShare) void copy(); else setStatus(\"Share selected\"); }}><Share2 size={16} /><span>Share</span></button>\n <button type=\"button\" className=\"mgs-row\" aria-pressed={saved} onClick={() => { setSaved(!saved); onSaveChange?.(!saved); }} >{saved ? <Check size={16} /> : <Heart size={16} />}<span>{saved ? \"Saved to favorites\" : \"Save to favorites\"}</span></button>\n <button type=\"button\" className=\"mgs-row\" onClick={() => void copy()}><Link size={16} /><span>{status === \"Link copied\" ? status : \"Copy link\"}</span><Copy size={14} /></button>\n {status && <p className=\"mgs-status\" role=\"status\">{status}{status.startsWith(\"Copy unavailable\") && <input aria-label=\"Link to copy\" readOnly value={link} onFocus={event => event.currentTarget.select()} />}</p>}\n </section>}\n </section>;\n}\nconst css = `\n.mova-glass-sheet{box-sizing:border-box;position:relative;width:320px;max-width:100%;height:380px;isolation:isolate;border-radius:24px;overflow:hidden;background:radial-gradient(ellipse at 68% 10%,#d6b85a44,transparent 50%),linear-gradient(160deg,#687349,#293e26 45%,#8a5c2f);background-size:cover;background-position:center;color:white;font:400 13px/20px -apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif;box-shadow:0 8px 32px #00000018}.mova-glass-sheet *{box-sizing:border-box}.mgs-forest{position:absolute;inset:0;z-index:-1;overflow:hidden;opacity:.75;background:radial-gradient(ellipse at 65% 12%,#f4d57388,transparent 40%),radial-gradient(ellipse at 20% 70%,#8d633199,transparent 40%)}.mgs-forest i{position:absolute;top:-10%;bottom:-10%;width:12px;background:linear-gradient(90deg,#16251c,#35402c,#1b291f);transform:rotate(-8deg);left:10%;box-shadow:25px 0 60px #13261f55}.mgs-forest i:nth-child(2){left:29%;width:20px;transform:rotate(8deg)}.mgs-forest i:nth-child(3){left:48%;width:8px}.mgs-forest i:nth-child(4){left:68%;width:24px;transform:rotate(5deg)}.mgs-forest i:nth-child(5){left:91%;width:18px}.mgs-forest i:nth-child(6){left:80%;width:5px;opacity:.7}.mova-glass-sheet button{font:inherit;color:inherit;cursor:pointer;border:0;background:none}.mova-glass-sheet button:focus-visible{outline:2px solid #fff;outline-offset:-3px}.mgs-trigger{position:absolute;top:24px;left:50%;transform:translateX(-50%);border:1px solid #ffffff60!important;background:#ffffff20!important;backdrop-filter:blur(10px);padding:10px 20px;border-radius:22px;white-space:nowrap;box-shadow:inset 0 1px #ffffff60,0 3px 10px #0002}.mgs-panel{position:absolute;left:8px;right:8px;bottom:8px;max-height:calc(100% - 82px);overflow:auto;border:1px solid #ffffff50;border-radius:28px;padding:0 8px 8px;background:#43513966;backdrop-filter:blur(14px) saturate(1.5);box-shadow:inset 0 1px 1px #ffffffa0,inset 0 -1px 1px #ffffff40,0 20px 44px #0004;animation:mgs-enter 300ms cubic-bezier(.2,.8,.2,1);transition:transform 180ms ease-out}.mgs-grab{height:24px;display:grid;place-items:center;touch-action:none;cursor:grab}.mgs-grab span{width:36px;height:5px;border-radius:5px;background:#ffffffb0}.mgs-panel header{display:flex;align-items:center;padding:0 12px 8px;gap:8px}.mgs-panel header>div{flex:1;min-width:0}.mgs-panel h2{margin:0;font-size:16px;line-height:22px;font-weight:500;overflow-wrap:anywhere}.mgs-panel header p{margin:0;opacity:.85;font-size:12px}.mgs-close{width:28px;height:28px;display:grid;place-items:center;border-radius:50%;flex:none}.mgs-row{display:flex;align-items:center;text-align:left;gap:10px;width:100%;min-height:42px;border-radius:15px;padding:10px!important;transition:background 160ms}.mgs-row span{flex:1;min-width:0;overflow-wrap:anywhere}.mgs-row svg{flex:none}.mgs-row:hover,.mgs-row:focus-visible,.mgs-close:hover{background:#ffffff26}.mgs-status{font-size:11px;margin:4px 12px;line-height:16px}.mgs-status input{width:100%;background:#0004;color:#fff;border:1px solid #fff8;border-radius:4px;padding:4px;font:inherit}@keyframes mgs-enter{from{opacity:0;translate:0 32px}to{opacity:1;translate:0 0}}@media(prefers-reduced-motion:reduce){.mova-glass-sheet *{animation:none!important;transition:none!important}}@media(prefers-reduced-transparency:reduce){.mgs-panel{background:#3c4a35;backdrop-filter:none}}\n`;\n"
15
+ "content": "\"use client\";\n\"use client\";\n\n/** Original Mova implementation; visual reference documented in docs/provenance/interactive-cards.md. */\nimport * as React from \"react\";\n\nexport interface LiquidGlassSheetProps {\n title?: string;\n subtitle?: string;\n backgroundImage?: string;\n link?: string;\n linkLabel?: string;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n onSaveChange?: (saved: boolean) => void;\n /** Reserved for applications that previously supplied this callback. */\n onPlay?: () => void;\n onShare?: () => void;\n theme?: \"light\" | \"dark\";\n className?: string;\n style?: React.CSSProperties;\n}\n\nconst FOREST_IMAGE = \"https://images.unsplash.com/photo-1523712999610-f77fbcfc3843?auto=format&fit=crop&q=80&w=800\";\nconst DEFAULT_LINK = \"https://liquid-glass-react-studio.vercel.app/components\";\n\n/* The red and green channels encode the surface normal of a rounded pane.\n * Its narrow curved rim bends the backdrop more strongly than its flat face. */\nfunction refractionMap(width: number, height: number, radius: number, depth: number) {\n const resolution = Math.min(1, 280 / Math.max(width, height));\n const w = Math.max(8, Math.round(width * resolution));\n const h = Math.max(8, Math.round(height * resolution));\n const canvas = document.createElement(\"canvas\");\n canvas.width = w;\n canvas.height = h;\n const context = canvas.getContext(\"2d\");\n if (!context) return \"\";\n const pixels = context.createImageData(w, h);\n const halfW = w / 2, halfH = h / 2;\n const corner = Math.min(radius * resolution, halfW - 1, halfH - 1);\n const rimWidth = Math.max(3, (4 + depth / 100 * Math.min(w, h) * .42) * resolution);\n const distance = (x: number, y: number) => {\n const qx = Math.abs(x) - halfW + corner;\n const qy = Math.abs(y) - halfH + corner;\n return Math.hypot(Math.max(0, qx), Math.max(0, qy)) + Math.min(0, Math.max(qx, qy)) - corner;\n };\n for (let index = 0; index < w * h; index++) {\n const x = index % w + .5 - halfW;\n const y = Math.floor(index / w) + .5 - halfH;\n const d = distance(x, y);\n let red = 128, green = 128;\n if (d <= .5) {\n const edge = Math.pow(1 - Math.min(1, Math.max(0, -d) / rimWidth), .72);\n const curved = edge * edge * (3 - 2 * edge);\n const dx = distance(x + 1, y) - distance(x - 1, y);\n const dy = distance(x, y + 1) - distance(x, y - 1);\n const length = Math.hypot(dx, dy) || 1;\n const face = .18 * (1 - curved);\n red = Math.round(128 + 127 * (dx / length * curved + x / halfW * face));\n green = Math.round(128 + 127 * (dy / length * curved + y / halfH * face));\n }\n pixels.data.set([Math.max(0, Math.min(255, red)), Math.max(0, Math.min(255, green)), 128, 255], index * 4);\n }\n context.putImageData(pixels, 0, 0);\n return canvas.toDataURL(\"image/png\");\n}\n\nfunction GlassMaterial({ id, width, height, radius, depth, refraction, dispersion, liquidity }: {\n id: string; width: number; height: number; radius: number; depth: number;\n refraction: number; dispersion: number; liquidity: number;\n}) {\n const [map, setMap] = React.useState(\"\");\n React.useEffect(() => { setMap(refractionMap(width, height, radius, depth)); }, [width, height, radius, depth]);\n const bend = refraction / 100 * .36;\n const spread = bend * dispersion / 100 * .4;\n const wobble = liquidity / 100 * .06;\n const channelMap = wobble > .002 ? \"warped\" : \"normal\";\n return <svg width=\"0\" height=\"0\" style={{ position: \"absolute\" }} aria-hidden=\"true\" focusable=\"false\">\n <filter id={id} x=\"-.08\" y=\"-.08\" width=\"1.16\" height=\"1.16\" filterUnits=\"objectBoundingBox\" primitiveUnits=\"objectBoundingBox\" colorInterpolationFilters=\"sRGB\">\n {map && <>\n <feImage href={map} x=\"0\" y=\"0\" width=\"1\" height=\"1\" preserveAspectRatio=\"none\" result=\"normal\" />\n {wobble > .002 && <><feTurbulence type=\"fractalNoise\" baseFrequency=\".012 .016\" numOctaves={1} seed={7} result=\"noise\" /><feDisplacementMap in=\"normal\" in2=\"noise\" scale={wobble} xChannelSelector=\"R\" yChannelSelector=\"G\" result=\"warped\" /></>}\n <feDisplacementMap in=\"SourceGraphic\" in2={channelMap} scale={Math.max(0, bend - spread)} xChannelSelector=\"R\" yChannelSelector=\"G\" result=\"red\" />\n <feColorMatrix in=\"red\" type=\"matrix\" values=\"1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\" result=\"redOnly\" />\n <feDisplacementMap in=\"SourceGraphic\" in2={channelMap} scale={bend} xChannelSelector=\"R\" yChannelSelector=\"G\" result=\"green\" />\n <feColorMatrix in=\"green\" type=\"matrix\" values=\"0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0\" result=\"greenOnly\" />\n <feDisplacementMap in=\"SourceGraphic\" in2={channelMap} scale={bend + spread * 1.15} xChannelSelector=\"R\" yChannelSelector=\"G\" result=\"blue\" />\n <feColorMatrix in=\"blue\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0\" result=\"blueOnly\" />\n <feComposite in=\"redOnly\" in2=\"greenOnly\" operator=\"arithmetic\" k2=\"1\" k3=\"1\" result=\"redGreen\" />\n <feComposite in=\"redGreen\" in2=\"blueOnly\" operator=\"arithmetic\" k2=\"1\" k3=\"1\" />\n </>}\n </filter>\n </svg>;\n}\n\nfunction Icon({ kind, className = \"\", strokeWidth = 2.25, draw = false }: { kind: \"share\" | \"save\" | \"check\" | \"globe\" | \"copy\"; className?: string; strokeWidth?: number; draw?: boolean }) {\n const paths = {\n share: \"M7.217 10.907a2.25 2.25 0 1 0 0 2.186m0-2.186c.18.324.283.696.283 1.093s-.103.77-.283 1.093m0-2.186 9.566-5.314m-9.566 7.5 9.566 5.314m0 0a2.25 2.25 0 1 0 3.935 2.186 2.25 2.25 0 0 0-3.935-2.186Zm0-12.814a2.25 2.25 0 1 0 3.933-2.185 2.25 2.25 0 0 0-3.933 2.185Z\",\n save: \"M17.593 3.322c1.1.128 1.907 1.077 1.907 2.185V21L12 17.25 4.5 21V5.507c0-1.108.806-2.057 1.907-2.185a48.507 48.507 0 0 1 11.186 0Z\",\n check: \"M5.25 12.7033 9.40385 17.37 18.75 6.87\",\n globe: \"M12 21a9.004 9.004 0 0 0 8.716-6.747M12 21a9.004 9.004 0 0 1-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 0 1 7.843 4.582M12 3a8.997 8.997 0 0 0-7.843 4.582m15.686 0A11.953 11.953 0 0 1 12 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0 1 21 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0 1 12 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 0 1 3 12c0-1.605.42-3.113 1.157-4.418\",\n copy: \"M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 0 1-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 0 1 1.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 0 0-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 0 1-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 0 0-3.375-3.375h-1.5a1.125 1.125 0 0 1-1.125-1.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H9.75\",\n };\n return <svg className={`mgs-icon ${className}`} width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={strokeWidth} aria-hidden=\"true\"><path className={draw ? \"mgs-tick\" : undefined} pathLength={kind === \"check\" ? 1 : undefined} d={paths[kind]} /></svg>;\n}\n\nasync function copyText(text: string) {\n // Synchronous selection also works in embedded previews without Clipboard API permission.\n const previous = document.activeElement as HTMLElement | null;\n const textarea = document.createElement(\"textarea\");\n textarea.value = text;\n textarea.readOnly = true;\n Object.assign(textarea.style, { position: \"fixed\", top: \"0\", left: \"0\", opacity: \"0\", pointerEvents: \"none\" });\n document.body.append(textarea);\n textarea.select();\n let copied = false;\n try { copied = document.execCommand(\"copy\"); } catch { /* Try the modern API below. */ }\n textarea.remove();\n previous?.focus({ preventScroll: true });\n if (copied) return true;\n try { await navigator.clipboard.writeText(text); return true; } catch { return false; }\n}\n\nexport function LiquidGlassSheet({ title = \"Autumn forest\", subtitle = \"Liquid glass sheet\", backgroundImage = FOREST_IMAGE, link = DEFAULT_LINK, linkLabel = \"liquid-glass-react-studio\", defaultOpen = true, onOpenChange, onSaveChange, onShare, theme = \"light\", className = \"\", style }: LiquidGlassSheetProps = {}) {\n const [open, setOpen] = React.useState(defaultOpen);\n const [mounted, setMounted] = React.useState(defaultOpen);\n const [saved, setSaved] = React.useState(false);\n const [status, setStatus] = React.useState<\"idle\" | \"copied\" | \"failed\">(\"idle\");\n const [touched, setTouched] = React.useState(false);\n const [highlight, setHighlight] = React.useState<number | null>(null);\n const [drag, setDrag] = React.useState(0);\n const [dragging, setDragging] = React.useState(false);\n const [closing, setClosing] = React.useState(false);\n const trigger = React.useRef<HTMLButtonElement>(null);\n const list = React.useRef<HTMLDivElement>(null);\n const dragStart = React.useRef<number | null>(null);\n const dragOffset = React.useRef(0);\n const timer = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n const closeTimer = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n const frames = React.useRef<number[]>([]);\n const attempt = React.useRef(0);\n const id = `mgs-${React.useId().replace(/:/g, \"\")}`;\n const wasOpened = React.useRef(false);\n React.useEffect(() => {\n if (open && wasOpened.current) list.current?.querySelector<HTMLButtonElement>(\"button\")?.focus({ preventScroll: true });\n wasOpened.current = true;\n }, [open]);\n React.useEffect(() => () => { clearTimeout(timer.current); clearTimeout(closeTimer.current); frames.current.forEach(cancelAnimationFrame); attempt.current++; }, []);\n function changeOpen(next: boolean) {\n if (next === open) return;\n clearTimeout(closeTimer.current);\n if (next) { setMounted(true); setClosing(false); }\n else { setClosing(true); setHighlight(null); closeTimer.current = setTimeout(() => { setMounted(false); setClosing(false); }, 350); }\n setOpen(next); setDrag(0); dragOffset.current = 0; dragStart.current = null; setDragging(false);\n onOpenChange?.(next);\n if (!next) requestAnimationFrame(() => trigger.current?.focus({ preventScroll: true }));\n }\n async function copy() {\n const current = ++attempt.current;\n clearTimeout(timer.current);\n frames.current.forEach(cancelAnimationFrame);\n const copied = await copyText(link);\n if (current !== attempt.current) return;\n setTouched(true); setStatus(\"idle\");\n frames.current = [requestAnimationFrame(() => {\n frames.current.push(requestAnimationFrame(() => {\n if (current !== attempt.current) return;\n setStatus(copied ? \"copied\" : \"failed\");\n timer.current = setTimeout(() => setStatus(\"idle\"), copied ? 1800 : 1400);\n }));\n })];\n }\n function keys(event: React.KeyboardEvent<HTMLElement>) {\n if (!open) return;\n if (event.key === \"Escape\") { event.preventDefault(); changeOpen(false); return; }\n if (event.key !== \"ArrowDown\" && event.key !== \"ArrowUp\") return;\n const buttons = Array.from(list.current?.querySelectorAll<HTMLButtonElement>(\"button\") ?? []);\n const index = buttons.indexOf(document.activeElement as HTMLButtonElement);\n event.preventDefault();\n buttons[(index + (event.key === \"ArrowDown\" ? 1 : -1) + buttons.length) % buttons.length]?.focus();\n }\n function endDrag(cancelled = false) {\n if (dragStart.current === null) return;\n dragStart.current = null; setDragging(false);\n if (!cancelled && dragOffset.current > 90) changeOpen(false);\n dragOffset.current = 0; setDrag(0);\n }\n const copied = status === \"copied\", failed = status === \"failed\";\n const filterStyle = { \"--mgs-pane-filter\": `url(#${id}-pane)`, \"--mgs-trigger-filter\": `url(#${id}-trigger)` } as React.CSSProperties;\n return <section className={`mova-glass-sheet${open ? \" is-open\" : \"\"} ${className}`} data-theme={theme} onKeyDown={keys} style={{ ...filterStyle, backgroundImage: `url(${JSON.stringify(backgroundImage)})`, ...style }} aria-label={title}>\n <style>{css}</style>\n <GlassMaterial id={`${id}-pane`} width={304} height={244} radius={28} depth={30} refraction={78} dispersion={42} liquidity={6} />\n <GlassMaterial id={`${id}-trigger`} width={132} height={44} radius={22} depth={26} refraction={60} dispersion={30} liquidity={4} />\n <div className={`mgs-scrim mgs-t-modal${open ? \" is-open\" : \"\"}${closing ? \" is-closing\" : \"\"}`} onClick={() => changeOpen(false)} aria-hidden=\"true\" />\n <button type=\"button\" className=\"mgs-trigger\" ref={trigger} aria-label=\"Open sheet\" aria-expanded={open} aria-controls={`${id}-sheet`} aria-hidden={open} tabIndex={open ? -1 : 0} onClick={() => changeOpen(true)}><span>Open sheet</span></button>\n <div className={`mgs-sheet${dragging ? \" is-dragging\" : \"\"}`} role=\"dialog\" aria-labelledby={`${id}-title`} aria-hidden={!open} inert={!open} id={`${id}-sheet`} style={{ \"--drag\": `${drag}px` } as React.CSSProperties}>\n <div className=\"mgs-glass\" data-open={open} />\n <div className=\"mgs-t-panel-slide mgs-panel\" data-open={open}>\n {mounted && <div className=\"mgs-body\">\n <button type=\"button\" className=\"mgs-grab\" aria-label=\"Drag down to close\" onPointerDown={event => { event.currentTarget.setPointerCapture(event.pointerId); dragStart.current = event.clientY; setDragging(true); }} onPointerMove={event => {\n if (dragStart.current === null) return;\n const delta = event.clientY - dragStart.current;\n dragOffset.current = delta >= 0 ? delta : -Math.pow(-delta, .6) * 2;\n setDrag(dragOffset.current);\n }} onPointerUp={() => endDrag()} onPointerCancel={() => endDrag(true)} onClick={event => { if (event.detail === 0) changeOpen(false); }}><span /></button>\n <header className=\"mgs-head\"><h2 id={`${id}-title`} className=\"mgs-title\">{title}</h2><p className=\"mgs-meta\">{subtitle}</p></header>\n <div className=\"mgs-list\" ref={list} onMouseLeave={() => setHighlight(null)}>\n <span className=\"mgs-hl\" style={{ transform: `translateY(${(highlight ?? 0) * 44}px)`, opacity: highlight === null ? 0 : 1 }} />\n <button type=\"button\" className=\"mgs-row\" onMouseEnter={() => setHighlight(0)} onFocus={() => setHighlight(0)} onBlur={() => setHighlight(null)} onClick={() => { onShare?.(); changeOpen(false); }}><Icon kind=\"share\" /><span className=\"mgs-label\">Share</span></button>\n <button type=\"button\" className={`mgs-row${saved ? \" mgs-saved\" : \"\"}`} aria-pressed={saved} onMouseEnter={() => setHighlight(1)} onFocus={() => setHighlight(1)} onBlur={() => setHighlight(null)} onClick={() => { setSaved(!saved); onSaveChange?.(!saved); }}><span className=\"mgs-slot\"><Icon kind=\"save\" className=\"mgs-icon-a\" /><Icon kind=\"check\" className=\"mgs-icon-b\" strokeWidth={3} /></span><span className=\"mgs-label\">{saved ? \"Saved\" : \"Save\"}</span></button>\n <div className={`mgs-linkrow${copied ? \" is-copied\" : \"\"}${failed ? \" is-failed\" : \"\"}`} onMouseEnter={() => setHighlight(2)} onFocus={() => setHighlight(2)} onBlur={() => setHighlight(null)}>\n <button type=\"button\" className=\"mgs-row mgs-linkmain\" aria-label={copied ? \"Link copied\" : failed ? \"Copy blocked\" : \"Copy link\"} onClick={() => void copy()}><span className=\"mgs-slot\"><Icon kind=\"globe\" className=\"mgs-icon-a\" /><Icon kind=\"check\" className=\"mgs-icon-b\" strokeWidth={3} draw /></span><span key={status} className={`mgs-label${touched ? \" mgs-label-anim\" : \"\"}`}>{copied ? \"Link copied\" : failed ? \"Copy blocked\" : linkLabel}</span></button>\n <button type=\"button\" className=\"mgs-copy\" aria-label=\"Copy link\" onClick={() => void copy()}><Icon kind=\"copy\" /></button>\n </div>\n </div>\n </div>}\n </div>\n </div>\n <span className=\"mgs-status\" role=\"status\" aria-live=\"polite\">{copied ? \"Link copied\" : failed ? \"Copy blocked\" : \"\"}</span>\n </section>;\n}\n\nconst css = `\n.mova-glass-sheet{\n --panel-open-dur: 400ms;\n --panel-close-dur: 350ms;\n --panel-translate-y: 100px;\n --panel-blur: 2px;\n --panel-ease: cubic-bezier(0.22, 1, 0.36, 1);\n --modal-open-dur: 250ms;\n --modal-close-dur: 150ms;\n --modal-scale: 0.96;\n --modal-scale-close: 0.96;\n --modal-ease: cubic-bezier(0.22, 1, 0.36, 1);\n --scrim-dur:150ms;\n --hl-dur:250ms;\n --hl-ease:cubic-bezier(0.22,1,0.36,1);\n --icon-dur:250ms;\n --press-scale:0.97;\n --press-dur:150ms;\n --ease-smooth-out:cubic-bezier(0.22,1,0.36,1);\n --ease-spring:cubic-bezier(0.34, 1.56, 0.64, 1);\n --mgs-ease-out: cubic-bezier(0.22, 1, 0.36, 1);\n --mgs-ease-liquid: cubic-bezier(0.16, 1, 0.3, 1);\n --mgs-press: 160ms;\n}\n\n.mova-glass-sheet{position:relative;box-sizing:border-box;width:320px;max-width:100%;height:360px;font:400 13px/20px Inter,-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif;border-radius:24px;overflow:hidden;background-size:cover;background-position:center;box-shadow:0 0 0 1px rgba(0,0,0,.04),0 8px 32px -4px rgba(0,0,0,.06),0 1px 3px 0 rgba(0,0,0,.04);color:#fff}\n.mova-glass-sheet :is(button,a){font:inherit;color:inherit;text-decoration:none}\n.mova-glass-sheet :where(button){border:0;background:none;padding:0;cursor:pointer}\n.mgs-scrim{position:absolute;inset:0;background:rgba(0,0,0,.28)}\n\n/* ───────── Compact iOS 26 glass trigger (44px full-round capsule) ───────── */\n.mova-glass-sheet .mgs-trigger{\n position:absolute;\n left:50%;\n bottom:28px;\n translate:-50% 0;\n isolation:isolate;\n overflow:hidden;\n box-sizing:border-box;\n width:auto;\n height:44px;\n padding:0 22px;\n border-radius:999px;\n display:inline-flex;\n align-items:center;\n justify-content:center;\n white-space:nowrap;\n color:#fff;\n font-size:15px;\n line-height:20px;\n font-weight:600;\n letter-spacing:-0.01em;\n text-shadow:0 1px 2px rgb(0 0 0 / 0.25);\n background:rgb(255 255 255 / 0.10);\n -webkit-backdrop-filter:var(--mgs-trigger-filter) blur(2px) saturate(170%);\n backdrop-filter:var(--mgs-trigger-filter) blur(2px) saturate(170%);\n box-shadow:\n inset 1.5px 1.5px 0.5px -0.5px rgb(255 255 255 / 0.12),\n inset -1px -1px 0.5px -0.5px rgb(255 255 255 / 0.04),\n inset 0 0 0 1px rgb(255 255 255 / 0.18),\n 0 0 0 0.5px rgb(255 255 255 / 0.22),\n 0 8px 24px rgb(0 0 0 / 0.18);\n transition:\n transform 380ms var(--ease-spring),\n box-shadow var(--mgs-press) var(--mgs-ease-out),\n opacity var(--scrim-dur) var(--ease-smooth-out);\n}\n.mgs-trigger::before{\n content:\"\";\n position:absolute;\n inset:0;\n z-index:-1;\n border-radius:inherit;\n pointer-events:none;\n background:radial-gradient(70% 55% at 69.8% 30.2%, rgb(255 255 255 / 0.035) 0%, transparent 46%);\n mix-blend-mode:plus-lighter;\n opacity:0;\n transition:opacity 240ms var(--mgs-ease-out);\n}\n.mgs-trigger:hover::before{opacity:1}\n.mgs-trigger::after{\n content:\"\";\n position:absolute;\n inset:0;\n z-index:-1;\n border-radius:inherit;\n pointer-events:none;\n background:radial-gradient(circle at 50% 50%, rgb(255 255 255 / 0.95) 0%, rgb(255 255 255 / 0.48) 22%, rgb(255 255 255 / 0.1) 48%, transparent 65%);\n mix-blend-mode:plus-lighter;\n opacity:0;\n transform:scale(0.65);\n transition:opacity var(--mgs-press) var(--mgs-ease-out), transform var(--mgs-press) var(--mgs-ease-out);\n}\n.mgs-trigger:active{transform:scale(0.94)}\n.mgs-trigger:active::before{opacity:1}\n.mgs-trigger:active::after{opacity:0.110;transform:scale(1)}\n.mova-glass-sheet.is-open .mgs-trigger{opacity:0;pointer-events:none}\n\n@supports not (backdrop-filter: url(#mgs-filter)){\n .mova-glass-sheet .mgs-trigger{\n -webkit-backdrop-filter:blur(25px) saturate(160%) brightness(1.06);\n backdrop-filter:blur(25px) saturate(160%) brightness(1.06);\n background:linear-gradient(145deg, rgb(255 255 255 / 0.015) 0%, rgb(255 255 255 / 0.004) 45%, rgb(255 255 255 / 0.096) 100%);\n box-shadow:\n inset 2px 2px 1px -0.5px rgb(255 255 255 / 0.066),\n inset -1.5px -1.5px 1px -0.5px rgb(255 255 255 / 0.025),\n inset 0 0 0 1.2px rgb(255 255 255 / 0.224),\n 0 20px 48px rgb(0 0 0 / 0.24);\n }\n .mgs-trigger::before{opacity:0.75}\n}\n@media (prefers-reduced-transparency: reduce){\n .mova-glass-sheet .mgs-trigger{-webkit-backdrop-filter:none;backdrop-filter:none;background:rgb(255 255 255 / 0.88);color:#111;text-shadow:none}\n}\n\n/* ───────── Sheet ───────── */\n.mgs-sheet{position:absolute;left:8px;right:8px;bottom:8px;height:244px;border-radius:28px;transform:translateY(var(--drag,0px));transition:transform var(--panel-open-dur) var(--panel-ease);touch-action:none;pointer-events:none}\n.mova-glass-sheet.is-open .mgs-sheet{pointer-events:auto}\n.mgs-panel{position:absolute;inset:0}\n.mgs-sheet.is-dragging{transition:none!important}\n\n/* ───────── REAL LIQUID GLASS MATERIAL ───────── */\n.mgs-glass{\n position:absolute;\n inset:0;\n isolation:isolate;\n overflow:hidden;\n border-radius:28px;\n corner-shape:squircle;\n pointer-events:none;\n background:rgb(255 255 255 / 0.080);\n -webkit-backdrop-filter:var(--mgs-pane-filter) blur(3px) saturate(160%);\n backdrop-filter:var(--mgs-pane-filter) blur(3px) saturate(160%);\n box-shadow:\n inset 1.5px 1.5px 0.5px -0.5px rgb(255 255 255 / 0.055),\n inset -1px -1px 0.5px -0.5px rgb(255 255 255 / 0.018),\n inset 0 0 0 1.2px rgb(255 255 255 / 0.140),\n 0 0 0 0.5px rgb(255 255 255 / 0.22),\n 0 18px 40px rgb(0 0 0 / 0.2);\n transform:translateY(var(--panel-translate-y));\n opacity:0;\n transition:\n transform var(--panel-close-dur) var(--panel-ease),\n opacity var(--panel-close-dur) var(--panel-ease),\n box-shadow var(--mgs-press) var(--mgs-ease-out);\n}\n.mgs-glass[data-open=\"true\"]{\n transform:none;\n opacity:1;\n transition:\n transform var(--panel-open-dur) var(--panel-ease),\n opacity var(--panel-open-dur) var(--panel-ease),\n box-shadow var(--mgs-press) var(--mgs-ease-out);\n}\n.mgs-glass::before{\n content:\"\";\n position:absolute;\n inset:0;\n z-index:-1;\n border-radius:inherit;\n pointer-events:none;\n background:radial-gradient(70% 55% at 69.8% 30.2%, rgb(255 255 255 / 0.035) 0%, transparent 46%);\n mix-blend-mode:plus-lighter;\n opacity:0;\n transition:opacity 240ms var(--mgs-ease-out);\n}\n.mgs-sheet:hover .mgs-glass::before{opacity:1}\n.mgs-glass::after{\n content:\"\";\n position:absolute;\n inset:0;\n z-index:-1;\n border-radius:inherit;\n pointer-events:none;\n background:radial-gradient(circle at 50% 50%, rgb(255 255 255 / 0.95) 0%, rgb(255 255 255 / 0.48) 22%, rgb(255 255 255 / 0.1) 48%, transparent 65%);\n mix-blend-mode:plus-lighter;\n opacity:0;\n transform:scale(0.65);\n transition:opacity var(--mgs-press) var(--mgs-ease-out), transform var(--mgs-press) var(--mgs-ease-out);\n}\n.mgs-sheet:active .mgs-glass::before{opacity:1}\n.mgs-sheet:active .mgs-glass::after{opacity:0.110;transform:scale(1)}\n\n@supports not (backdrop-filter: url(#mgs-filter)){\n .mgs-glass{\n -webkit-backdrop-filter:blur(25px) saturate(160%) brightness(1.06);\n backdrop-filter:blur(25px) saturate(160%) brightness(1.06);\n background:linear-gradient(145deg, rgb(255 255 255 / 0.015) 0%, rgb(255 255 255 / 0.004) 45%, rgb(255 255 255 / 0.096) 100%);\n box-shadow:\n inset 2px 2px 1px -0.5px rgb(255 255 255 / 0.066),\n inset -1.5px -1.5px 1px -0.5px rgb(255 255 255 / 0.025),\n inset 0 0 0 1.2px rgb(255 255 255 / 0.224),\n 0 20px 48px rgb(0 0 0 / 0.24);\n }\n .mgs-glass::before{opacity:0.75}\n}\n@media (prefers-reduced-transparency: reduce){\n .mgs-glass{-webkit-backdrop-filter:none;backdrop-filter:none;background:rgb(255 255 255 / 0.88)}\n}\n\n/* ───────── Body / grabber / header ───────── */\n.mgs-body{position:relative;padding:0 8px 8px}\n.mgs-grab{display:flex;justify-content:center;width:100%;height:24px;align-items:center;cursor:grab;touch-action:none}\n.mgs-grab:is(:active,.is-pressed){cursor:grabbing}\n.mgs-grab span{width:36px;height:5px;border-radius:5px;background:rgba(255,255,255,.7)}\n.mgs-head{padding:0 12px 8px}\n.mgs-title{margin:0;font-size:16px;line-height:22px;font-weight:500}\n.mgs-meta{margin:0;font-size:12px;line-height:16px;color:rgba(255,255,255,.85)}\n\n/* ───────── List / rows / highlight ───────── */\n.mgs-list{position:relative;display:flex;flex-direction:column;margin-top:4px}\n.mgs-hl{position:absolute;left:0;right:0;top:0;height:44px;border-radius:16px;background:rgba(255,255,255,.2);box-shadow:inset 0 0 0 1px rgba(255,255,255,.18);opacity:0;transition:transform var(--hl-dur) var(--hl-ease),opacity var(--hl-dur) var(--hl-ease);pointer-events:none}\n.mgs-row{box-sizing:border-box;position:relative;display:flex;align-items:center;gap:8px;height:44px;padding:0 12px 0 10px;border-radius:16px;font-size:13px;line-height:20px;text-align:left;width:100%;outline:none}\n.mgs-row .mgs-label{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.mgs-row:focus-visible{box-shadow:0 0 0 2px rgba(255,255,255,.9)}\n.mgs-row .mgs-icon{flex:none}\n.mgs-slot{position:relative;width:16px;height:16px;flex:none}\n.mgs-slot .mgs-icon{position:absolute;inset:0;transition:opacity var(--icon-dur) ease-in-out,transform var(--icon-dur) ease-in-out,filter var(--icon-dur) ease-in-out}\n.mgs-slot .mgs-icon-b{opacity:0;transform:scale(.25);filter:blur(2px)}\n.mgs-saved .mgs-slot .mgs-icon-a{opacity:0;transform:scale(.25);filter:blur(2px)}\n.mgs-saved .mgs-slot .mgs-icon-b{opacity:1;transform:scale(1);filter:blur(0)}\n\n/* ───────── Link row ───────── */\n.mgs-linkrow{position:relative;display:flex;align-items:center;height:44px;border-radius:16px}\n.mgs-linkrow .mgs-row{flex:1;min-width:0;width:auto;padding-right:6px}\n\n/* the link itself: soft flash when copied, shake when blocked */\n.mgs-linkmain{cursor:pointer;transition:transform 380ms var(--ease-spring)}\n.mgs-linkmain:active{transform:scale(var(--press-scale))}\n.mgs-linkrow.is-copied .mgs-linkmain{animation:mgs-row-flash 700ms var(--mgs-ease-out)}\n.mgs-linkrow.is-failed .mgs-linkmain{animation:mgs-copy-shake 420ms var(--mgs-ease-out)}\n\n/* link icon: globe -> drawn checkmark */\n.mgs-linkrow.is-copied .mgs-linkmain .mgs-slot .mgs-icon-a{opacity:0;transform:scale(.25) rotate(-25deg);filter:blur(2px)}\n.mgs-linkrow.is-copied .mgs-linkmain .mgs-slot .mgs-icon-b{opacity:1;transform:scale(1);filter:blur(0)}\n.mgs-tick{stroke-dasharray:1;stroke-dashoffset:1;transition:stroke-dashoffset 380ms var(--mgs-ease-out)}\n.mgs-linkrow.is-copied .mgs-linkmain .mgs-tick{stroke-dashoffset:0;transition-delay:140ms}\n\n/* corner copy icon: static, no copied/failed animation */\n.mgs-linkrow .mgs-copy{\n position:relative;\n display:grid;\n place-items:center;\n flex:none;\n width:32px;\n height:32px;\n margin-right:6px;\n border-radius:11px;\n corner-shape:squircle;\n outline:none;\n color:rgba(255,255,255,.85);\n background:transparent;\n}\n@media (hover: hover) and (pointer: fine){\n .mgs-linkrow .mgs-copy:hover{background:rgba(255,255,255,.18);color:#fff}\n}\n.mgs-linkrow .mgs-copy:focus-visible{box-shadow:0 0 0 2px rgba(255,255,255,.9)}\n\n/* label swaps with a soft blur-in */\n.mgs-label-anim{animation:mgs-label-in 320ms var(--mgs-ease-out)}\n\n@keyframes mgs-copy-shake{0%,100%{transform:translateX(0)}20%{transform:translateX(-4px)}40%{transform:translateX(4px)}60%{transform:translateX(-3px)}80%{transform:translateX(2px)}}\n@keyframes mgs-label-in{from{opacity:0;transform:translateY(6px);filter:blur(3px)}to{opacity:1;transform:none;filter:blur(0)}}\n@keyframes mgs-row-flash{0%{background:rgba(255,255,255,.26)}100%{background:rgba(255,255,255,0)}}\n\n/* ───────── Motion helpers ───────── */\n.mgs-t-panel-slide{transform:translateY(var(--panel-translate-y));opacity:0;filter:blur(var(--panel-blur));pointer-events:none;transition:transform var(--panel-close-dur) var(--panel-ease),opacity var(--panel-close-dur) var(--panel-ease),filter var(--panel-close-dur) var(--panel-ease)}\n.mgs-t-panel-slide[data-open=\"true\"]{transform:translateY(0);opacity:1;filter:none;pointer-events:auto;transition:transform var(--panel-open-dur) var(--panel-ease),opacity var(--panel-open-dur) var(--panel-ease),filter var(--panel-open-dur) var(--panel-ease)}\n.mgs-t-modal{transform-origin:center;transform:scale(var(--modal-scale));opacity:0;pointer-events:none;transition:transform var(--modal-open-dur) var(--modal-ease),opacity var(--modal-open-dur) var(--modal-ease)}\n.mgs-t-modal.is-open{transform:scale(1);opacity:1;pointer-events:auto}\n.mgs-t-modal.is-closing{transform:scale(var(--modal-scale-close));opacity:0;pointer-events:none;transition:transform var(--modal-close-dur) var(--modal-ease),opacity var(--modal-close-dur) var(--modal-ease)}\n@media (prefers-reduced-motion:reduce){\n .mgs-sheet,.mgs-t-panel-slide,.mgs-t-modal,.mgs-hl,.mgs-scrim,.mgs-trigger,.mgs-trigger::before,.mgs-trigger::after,.mgs-slot .mgs-icon,.mgs-glass,.mgs-glass::before,.mgs-glass::after,.mgs-linkmain,.mgs-tick{transition:none!important}\n .mgs-label-anim,.mgs-linkrow.is-copied .mgs-linkmain,.mgs-linkrow.is-failed .mgs-linkmain{animation:none!important}\n}\n\n.mgs-icon-play{translate:1px 0}.mgs-icon-heart{translate:0 1px}\n\n.mova-glass-sheet :where(button,[role=\"button\"],[role=\"tab\"],[role=\"menuitem\"],[role=\"menuitemcheckbox\"],[role=\"menuitemradio\"],[role=\"option\"],[role=\"switch\"],summary,.t-pressable){-webkit-tap-highlight-color:transparent;touch-action:manipulation;-webkit-user-select:none;user-select:none;-webkit-touch-callout:none}\n\n.mova-glass-sheet *{box-sizing:border-box}.mgs-trigger:focus-visible,.mgs-grab:focus-visible{outline:2px solid #fff;outline-offset:-3px}.mgs-status{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}\n`;\n"
18
16
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "mac-terminal",
3
3
  "tier": "free",
4
- "version": "0.4.7",
4
+ "version": "0.4.9",
5
5
  "file": "mac-terminal.tsx",
6
6
  "sourcePath": "ui/loading/mac-terminal.tsx",
7
7
  "registryFile": "mac-terminal.json",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "magnetic-button",
3
3
  "tier": "free",
4
- "version": "0.4.7",
4
+ "version": "0.4.9",
5
5
  "file": "magnetic-button.tsx",
6
6
  "sourcePath": "ui/hover/magnetic-button.tsx",
7
7
  "registryFile": "magnetic-button.json",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "magnetic-disks",
3
3
  "tier": "free",
4
- "version": "0.4.7",
4
+ "version": "0.4.9",
5
5
  "file": "magnetic-disks.tsx",
6
6
  "sourcePath": "components/css-animations/whimsical/WhimsicalVariations.tsx",
7
7
  "registryFile": "magnetic-disks.json",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "magnetic-dots",
3
3
  "tier": "free",
4
- "version": "0.4.7",
4
+ "version": "0.4.9",
5
5
  "file": "magnetic-dots.tsx",
6
6
  "sourcePath": "ui/loading/magnetic-dots.tsx",
7
7
  "registryFile": "magnetic-dots.json",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "magnetic-slide-btn",
3
3
  "tier": "free",
4
- "version": "0.4.7",
4
+ "version": "0.4.9",
5
5
  "file": "magnetic-slide-btn.tsx",
6
6
  "sourcePath": "components/css-animations/yui-components/UiKitTrios.tsx",
7
7
  "registryFile": "magnetic-slide-btn.json",