@neo-ui-kit/react 1.2.3 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,8 +1,3 @@
1
- import {
2
- ThemeProvider,
3
- themes,
4
- useThemePalette
5
- } from "./chunks/chunk-JZ4GDTH5.js";
6
1
  import {
7
2
  PianoKeyboard,
8
3
  PianoRoll,
@@ -29,18 +24,24 @@ import {
29
24
  Grid,
30
25
  Icon,
31
26
  IconButton,
27
+ ImageShine,
32
28
  InputBase,
33
29
  NumberField,
34
30
  PeoplePicker,
35
31
  Popover,
36
32
  RotaryKnob,
33
+ SegmentedControl,
37
34
  Select,
38
35
  Slider,
39
36
  SvgAsset,
40
37
  Switch,
38
+ Tab,
39
+ Tabs,
41
40
  TagInput,
42
41
  TextArea,
43
42
  TextField,
43
+ ThemePicker,
44
+ ToggleButton,
44
45
  buttonView,
45
46
  clamp01,
46
47
  defaultFieldRegistry,
@@ -60,19 +61,22 @@ import {
60
61
  useSvgId,
61
62
  variantMaterial,
62
63
  vectorElement
63
- } from "./chunks/chunk-RHMNSDIW.js";
64
+ } from "./chunks/chunk-TAQHE3C5.js";
64
65
  import {
66
+ ThemeProvider,
65
67
  getMotionStats,
66
68
  refreshMotion,
67
69
  setMotionFrameRate,
68
70
  subscribeTick,
71
+ themes,
69
72
  useBorder,
70
73
  useDecoration,
71
74
  usePauseOffscreen,
72
75
  useSmoothWheel,
73
76
  useTabShape,
77
+ useThemePalette,
74
78
  useTick
75
- } from "./chunks/chunk-YKCUOZ7V.js";
79
+ } from "./chunks/chunk-ICRXRGIW.js";
76
80
  import {
77
81
  LocaleProvider,
78
82
  addMessages,
@@ -436,7 +440,7 @@ function Loader({ src = loaderDefaultImage, animation = "spin", size, speed = 1,
436
440
  useLoaderStyles();
437
441
  const ref = useRef2(null);
438
442
  usePauseOffscreen(ref);
439
- const builtIn2 = src === loaderDefaultImage;
443
+ const builtIn = src === loaderDefaultImage;
440
444
  const vars = {
441
445
  ...style,
442
446
  ...size !== void 0 ? { "--ad-loader-size": typeof size === "number" ? `${size / 16}rem` : Object.hasOwn(loaderSizes, size) ? loaderSizes[size] : size } : {},
@@ -444,10 +448,10 @@ function Loader({ src = loaderDefaultImage, animation = "spin", size, speed = 1,
444
448
  ...color3 ? { "--ad-loader-color": color3 } : {},
445
449
  ...palette?.[1] ? { "--ad-loader-color-2": palette[1] } : {},
446
450
  ...palette?.[2] ? { "--ad-loader-color-3": palette[2] } : {},
447
- ...builtIn2 || animation === "shine" ? { "--ad-loader-src": `url("${src}")` } : {}
451
+ ...builtIn || animation === "shine" ? { "--ad-loader-src": `url("${src}")` } : {}
448
452
  };
449
453
  const rings = ["wave", "palette"].includes(animation) ? 2 : ["bounce", "orbit", "glow", "radar"].includes(animation) ? 1 : 0;
450
- const picture = builtIn2 ? /* @__PURE__ */ jsx5("span", { className: "ad-loader-img ad-loader-default-image", "aria-hidden": "true" }) : /* @__PURE__ */ jsx5("img", { className: "ad-loader-img", src, alt: "", draggable: false });
454
+ const picture = builtIn ? /* @__PURE__ */ jsx5("span", { className: "ad-loader-img ad-loader-default-image", "aria-hidden": "true" }) : /* @__PURE__ */ jsx5("img", { className: "ad-loader-img", src, alt: "", draggable: false });
451
455
  return /* @__PURE__ */ jsxs4("span", { ...mark("Loader", p), ref, role: "status", "aria-label": label ?? tr2("\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430"), "data-animation": animation, style: vars, children: [
452
456
  Array.from({ length: rings }, (_, i) => /* @__PURE__ */ jsx5("span", { className: "ad-loader-ring", "aria-hidden": "true" }, i)),
453
457
  picture,
@@ -457,170 +461,11 @@ function Loader({ src = loaderDefaultImage, animation = "spin", size, speed = 1,
457
461
  }
458
462
 
459
463
  // src/components/foundation/LoaderGenerator/LoaderGenerator.tsx
460
- import { useEffect, useRef as useRef4, useState as useState2 } from "react";
461
-
462
- // src/components/controls/Tabs/Tabs.tsx
463
- import { useLayoutEffect, useRef as useRef3, useState } from "react";
464
-
465
- // src/components/controls/Tab/Tab.tsx
466
- import { forwardRef } from "react";
467
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
468
- var Tab = /* @__PURE__ */ forwardRef(function Tab2({
469
- selected = false,
470
- panelId,
471
- icon,
472
- endIcon,
473
- label,
474
- children,
475
- tabIndex,
476
- variant: _variant,
477
- loading: _loading,
478
- round: _round,
479
- size: _size,
480
- tone: _tone,
481
- material: _material,
482
- ...p
483
- }, ref) {
484
- return /* @__PURE__ */ jsxs5(
485
- "button",
486
- {
487
- ...p,
488
- ...mark("Tab", { ...p, size: _size }),
489
- ref,
490
- type: "button",
491
- role: "tab",
492
- "aria-selected": selected,
493
- "aria-controls": panelId,
494
- tabIndex: tabIndex ?? (selected ? 0 : -1),
495
- children: [
496
- icon && /* @__PURE__ */ jsx6(Icon, { name: icon }),
497
- /* @__PURE__ */ jsx6("span", { className: "ad-tab-label", children: children ?? label }),
498
- endIcon && /* @__PURE__ */ jsx6(Icon, { name: endIcon })
499
- ]
500
- }
501
- );
502
- });
503
-
504
- // src/components/controls/Tabs/Tabs.tsx
505
- import { Fragment, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
506
- function Tabs(p) {
507
- const tr2 = useTr();
508
- const items = p.items ?? [
509
- { value: "appearance", label: tr2("\u0412\u043D\u0435\u0448\u043D\u0438\u0439 \u0432\u0438\u0434"), icon: "palette" },
510
- { value: "audio", label: tr2("\u0410\u0443\u0434\u0438\u043E"), icon: "audio" },
511
- { value: "advanced", label: tr2("\u0414\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u043E"), icon: "wrench" }
512
- ];
513
- const [value, setValue] = useControllable(
514
- p.value,
515
- p.defaultValue ?? items.find((item) => !item.disabled)?.value ?? "",
516
- p.onValueChange
517
- );
518
- const buttons = useRef3([]);
519
- const selected = items.findIndex((item) => item.value === value && !item.disabled);
520
- const firstEnabled = items.findIndex((item) => !item.disabled);
521
- const fallback = items[firstEnabled]?.value;
522
- useLayoutEffect(() => {
523
- if (p.value === void 0 && selected < 0 && fallback !== void 0) setValue(fallback);
524
- }, [p.value, selected, fallback, setValue]);
525
- const [place, setPlace] = useState();
526
- const [moving, setMoving] = useState(false);
527
- const first = useRef3(true);
528
- useLayoutEffect(() => {
529
- const tab = buttons.current[selected];
530
- if (!tab) return setPlace(void 0);
531
- const update = () => setPlace({
532
- "--ad-tabs-x": `${tab.offsetLeft}px`,
533
- "--ad-tabs-w": `${tab.offsetWidth}px`
534
- });
535
- update();
536
- const observer = createResizeObserver(update);
537
- observer.observe(tab);
538
- if (tab.parentElement) observer.observe(tab.parentElement);
539
- let timer = 0;
540
- if (!first.current) {
541
- setMoving(true);
542
- timer = window.setTimeout(() => setMoving(false), 420);
543
- }
544
- first.current = false;
545
- return () => {
546
- observer.disconnect();
547
- clearTimeout(timer);
548
- };
549
- }, [selected, items.length]);
550
- return /* @__PURE__ */ jsxs6(
551
- "nav",
552
- {
553
- ...mark("Tabs", p),
554
- role: "tablist",
555
- "aria-label": p.label ?? tr2("\u0420\u0430\u0437\u0434\u0435\u043B\u044B"),
556
- "data-moving": moving || void 0,
557
- onPointerDown: (e) => {
558
- const tab = e.target.closest(".ad-tab");
559
- if (tab && !tab.matches(":disabled")) ripple(tab, e.clientX, e.clientY);
560
- },
561
- onKeyDown: (e) => {
562
- if (!["ArrowLeft", "ArrowRight", "Home", "End"].includes(e.key)) return;
563
- const available = items.map((item, index) => ({ item, index })).filter((x) => !x.item.disabled);
564
- if (!available.length) return;
565
- e.preventDefault();
566
- const current = available.findIndex(
567
- (x) => buttons.current[x.index] === document.activeElement
568
- );
569
- const next = e.key === "Home" ? 0 : e.key === "End" ? available.length - 1 : (current + (e.key === "ArrowRight" ? 1 : -1) + available.length) % available.length;
570
- setValue(available[next].item.value);
571
- buttons.current[available[next].index]?.focus();
572
- },
573
- children: [
574
- place && /* @__PURE__ */ jsxs6(Fragment, { children: [
575
- /* @__PURE__ */ jsx7("span", { className: "ad-tabs-trail", style: place, "aria-hidden": true }),
576
- /* @__PURE__ */ jsxs6("span", { className: "ad-tabs-indicator", style: place, "aria-hidden": true, children: [
577
- /* @__PURE__ */ jsx7("span", { className: "ad-tabs-halo" }),
578
- /* @__PURE__ */ jsx7("span", { className: "ad-tabs-blade", children: /* @__PURE__ */ jsx7("span", { className: "ad-tabs-blade-edge" }) })
579
- ] }),
580
- /* @__PURE__ */ jsx7("span", { className: "ad-tabs-rail", style: place, "aria-hidden": true })
581
- ] }),
582
- items.map((item, index) => /* @__PURE__ */ jsx7(
583
- Tab,
584
- {
585
- id: item.id,
586
- ref: (n) => {
587
- buttons.current[index] = n;
588
- },
589
- icon: item.icon,
590
- panelId: item.panelId,
591
- disabled: item.disabled,
592
- size: p.size,
593
- selected: item.value === value,
594
- tabIndex: index === (selected < 0 ? firstEnabled : selected) ? 0 : -1,
595
- onClick: () => setValue(item.value),
596
- children: item.label
597
- },
598
- item.value
599
- ))
600
- ]
601
- }
602
- );
603
- }
604
-
605
- // src/components/controls/SegmentedControl/SegmentedControl.tsx
606
- import { jsx as jsx8 } from "react/jsx-runtime";
607
- var SegmentedControl = (p) => {
608
- const tr2 = useTr();
609
- return /* @__PURE__ */ jsx8("div", { ...mark("SegmentedControl", p), children: /* @__PURE__ */ jsx8(
610
- Tabs,
611
- {
612
- ...p,
613
- items: p.items ?? [
614
- { value: "list", label: tr2("\u0421\u043F\u0438\u0441\u043E\u043A"), icon: "list" },
615
- { value: "grid", label: tr2("\u041F\u043B\u0438\u0442\u043A\u0430"), icon: "grid" }
616
- ]
617
- }
618
- ) });
619
- };
464
+ import { useEffect, useRef as useRef3, useState } from "react";
620
465
 
621
466
  // src/components/layout/Stack/Stack.tsx
622
- import { Children, Fragment as Fragment2, isValidElement } from "react";
623
- import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
467
+ import { Children, Fragment, isValidElement } from "react";
468
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
624
469
  var flex = (value) => value === "start" || value === "end" ? `flex-${value}` : value === "between" || value === "around" || value === "evenly" ? `space-${value}` : value;
625
470
  var wrapValue = (value) => value === true ? "wrap" : value === false ? "nowrap" : value;
626
471
  function Stack({
@@ -637,11 +482,11 @@ function Stack({
637
482
  style,
638
483
  ...props
639
484
  }) {
640
- const content = divider == null ? children : Children.toArray(children).map((child, index) => /* @__PURE__ */ jsxs7(Fragment2, { children: [
641
- index > 0 && /* @__PURE__ */ jsx9("span", { className: "ad-stack__divider", "aria-hidden": "true", children: divider }),
485
+ const content = divider == null ? children : Children.toArray(children).map((child, index) => /* @__PURE__ */ jsxs5(Fragment, { children: [
486
+ index > 0 && /* @__PURE__ */ jsx6("span", { className: "ad-stack__divider", "aria-hidden": "true", children: divider }),
642
487
  child
643
488
  ] }, isValidElement(child) ? child.key : index));
644
- return /* @__PURE__ */ jsx9(
489
+ return /* @__PURE__ */ jsx6(
645
490
  Component,
646
491
  {
647
492
  ...props,
@@ -660,7 +505,7 @@ function Stack({
660
505
  }
661
506
 
662
507
  // src/components/foundation/LoaderGenerator/LoaderGenerator.tsx
663
- import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
508
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
664
509
  var loaderGeneratorAnimations = loaderAnimations.filter(
665
510
  (animation) => animation !== "orbit" && animation !== "radar"
666
511
  );
@@ -1074,7 +919,7 @@ ${loaderCss.trim()}
1074
919
  function LoaderGenerator({ value, defaultValue, onValueChange, ...p }) {
1075
920
  const tr2 = useTr();
1076
921
  const theme = useThemePalette();
1077
- const [own, setOwn] = useState2(() => defaultValue ?? {});
922
+ const [own, setOwn] = useState(() => defaultValue ?? {});
1078
923
  const settings = value ?? {
1079
924
  src: loaderDefaultImage,
1080
925
  animation: "pulse",
@@ -1084,12 +929,12 @@ function LoaderGenerator({ value, defaultValue, onValueChange, ...p }) {
1084
929
  palette: [theme.primary, theme.secondary, theme.primary],
1085
930
  ...own
1086
931
  };
1087
- const [uploaded, setUploaded] = useState2();
1088
- const [removeBackground, setRemoveBackground] = useState2(true);
1089
- const [uploadState, setUploadState] = useState2({ status: "idle" });
932
+ const [uploaded, setUploaded] = useState();
933
+ const [removeBackground, setRemoveBackground] = useState(true);
934
+ const [uploadState, setUploadState] = useState({ status: "idle" });
1090
935
  const processing = uploadState.status === "processing";
1091
- const uploadController = useRef4(null);
1092
- const latest = useRef4({ settings, removeBackground, onValueChange });
936
+ const uploadController = useRef3(null);
937
+ const latest = useRef3({ settings, removeBackground, onValueChange });
1093
938
  latest.current = { settings, removeBackground, onValueChange };
1094
939
  useEffect(() => () => uploadController.current?.abort(), []);
1095
940
  const update = (patch) => {
@@ -1115,11 +960,11 @@ function LoaderGenerator({ value, defaultValue, onValueChange, ...p }) {
1115
960
  setUploadState({ status: "error", error: cause instanceof Error ? cause.message : tr2("\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0430\u0442\u044C \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0435.") });
1116
961
  }
1117
962
  };
1118
- return /* @__PURE__ */ jsxs8(Stack, { ...mark("LoaderGenerator", p), gap: 4, children: [
1119
- /* @__PURE__ */ jsxs8(Grid, { className: "ad-loader-generator-top", columns: { base: 1, sm: "minmax(12rem, 1fr) minmax(0, 1.4fr)" }, gap: 4, children: [
1120
- /* @__PURE__ */ jsx10(Grid, { className: "ad-loader-generator-stage", columns: 1, align: "center", justify: "center", children: /* @__PURE__ */ jsx10(Loader, { src: settings.src, animation: settings.animation, size: `${settings.size}rem`, speed: settings.speed, color: settings.color, palette: settings.palette }, loaderRenderKey(settings)) }),
1121
- /* @__PURE__ */ jsxs8(Stack, { className: "ad-loader-generator-controls", gap: 3, children: [
1122
- /* @__PURE__ */ jsx10(
963
+ return /* @__PURE__ */ jsxs6(Stack, { ...mark("LoaderGenerator", p), gap: 4, children: [
964
+ /* @__PURE__ */ jsxs6(Grid, { className: "ad-loader-generator-top", columns: { base: 1, sm: "minmax(12rem, 1fr) minmax(0, 1.4fr)" }, gap: 4, children: [
965
+ /* @__PURE__ */ jsx7(Grid, { className: "ad-loader-generator-stage", columns: 1, align: "center", justify: "center", children: /* @__PURE__ */ jsx7(Loader, { src: settings.src, animation: settings.animation, size: `${settings.size}rem`, speed: settings.speed, color: settings.color, palette: settings.palette }, loaderRenderKey(settings)) }),
966
+ /* @__PURE__ */ jsxs6(Stack, { className: "ad-loader-generator-controls", gap: 3, children: [
967
+ /* @__PURE__ */ jsx7(
1123
968
  FilePicker,
1124
969
  {
1125
970
  variant: "zone",
@@ -1131,12 +976,12 @@ function LoaderGenerator({ value, defaultValue, onValueChange, ...p }) {
1131
976
  onFiles: ([file]) => file && void upload(file)
1132
977
  }
1133
978
  ),
1134
- uploadState.error && /* @__PURE__ */ jsx10(Typography, { variant: "caption", tone: "danger", children: uploadState.error }),
1135
- /* @__PURE__ */ jsxs8(Stack, { className: "ad-loader-generator-toolbar", direction: "row", align: "end", gap: 3, children: [
1136
- /* @__PURE__ */ jsx10(Stack, { className: "ad-loader-generator-color", children: /* @__PURE__ */ jsx10(ColorPicker, { label: tr2("\u0426\u0432\u0435\u0442 \u044D\u0444\u0444\u0435\u043A\u0442\u043E\u0432"), value: settings.color, onValueChange: (color3) => update({ color: color3 }) }) }),
1137
- /* @__PURE__ */ jsxs8(Stack, { className: "ad-loader-generator-size", gap: 1, children: [
1138
- /* @__PURE__ */ jsx10(Typography, { variant: "label", children: tr2("\u0420\u0430\u0437\u043C\u0435\u0440") }),
1139
- /* @__PURE__ */ jsx10(
979
+ uploadState.error && /* @__PURE__ */ jsx7(Typography, { variant: "caption", tone: "danger", children: uploadState.error }),
980
+ /* @__PURE__ */ jsxs6(Stack, { className: "ad-loader-generator-toolbar", direction: "row", align: "end", gap: 3, children: [
981
+ /* @__PURE__ */ jsx7(Stack, { className: "ad-loader-generator-color", children: /* @__PURE__ */ jsx7(ColorPicker, { label: tr2("\u0426\u0432\u0435\u0442 \u044D\u0444\u0444\u0435\u043A\u0442\u043E\u0432"), value: settings.color, onValueChange: (color3) => update({ color: color3 }) }) }),
982
+ /* @__PURE__ */ jsxs6(Stack, { className: "ad-loader-generator-size", gap: 1, children: [
983
+ /* @__PURE__ */ jsx7(Typography, { variant: "label", children: tr2("\u0420\u0430\u0437\u043C\u0435\u0440") }),
984
+ /* @__PURE__ */ jsx7(
1140
985
  SegmentedControl,
1141
986
  {
1142
987
  label: tr2("\u0420\u0430\u0437\u043C\u0435\u0440"),
@@ -1147,25 +992,25 @@ function LoaderGenerator({ value, defaultValue, onValueChange, ...p }) {
1147
992
  }
1148
993
  )
1149
994
  ] }),
1150
- /* @__PURE__ */ jsxs8(Stack, { as: "label", className: "ad-loader-generator-slider", gap: 1, children: [
1151
- /* @__PURE__ */ jsxs8(Stack, { direction: "row", justify: "between", children: [
1152
- /* @__PURE__ */ jsx10(Typography, { variant: "label", children: tr2("\u0421\u043A\u043E\u0440\u043E\u0441\u0442\u044C") }),
1153
- /* @__PURE__ */ jsxs8(Typography, { variant: "mono", tone: "muted", children: [
995
+ /* @__PURE__ */ jsxs6(Stack, { as: "label", className: "ad-loader-generator-slider", gap: 1, children: [
996
+ /* @__PURE__ */ jsxs6(Stack, { direction: "row", justify: "between", children: [
997
+ /* @__PURE__ */ jsx7(Typography, { variant: "label", children: tr2("\u0421\u043A\u043E\u0440\u043E\u0441\u0442\u044C") }),
998
+ /* @__PURE__ */ jsxs6(Typography, { variant: "mono", tone: "muted", children: [
1154
999
  "\xD7",
1155
1000
  settings.speed
1156
1001
  ] })
1157
1002
  ] }),
1158
- /* @__PURE__ */ jsx10(Slider, { label: tr2("\u0421\u043A\u043E\u0440\u043E\u0441\u0442\u044C"), min: 0.25, max: 3, step: 0.25, value: settings.speed, onValueChange: (speed) => update({ speed }) })
1003
+ /* @__PURE__ */ jsx7(Slider, { label: tr2("\u0421\u043A\u043E\u0440\u043E\u0441\u0442\u044C"), min: 0.25, max: 3, step: 0.25, value: settings.speed, onValueChange: (speed) => update({ speed }) })
1159
1004
  ] }),
1160
- /* @__PURE__ */ jsx10(Switch, { label: tr2("\u0423\u0431\u0440\u0430\u0442\u044C \u0444\u043E\u043D"), checked: removeBackground, onValueChange: (enabled) => {
1005
+ /* @__PURE__ */ jsx7(Switch, { label: tr2("\u0423\u0431\u0440\u0430\u0442\u044C \u0444\u043E\u043D"), checked: removeBackground, onValueChange: (enabled) => {
1161
1006
  setRemoveBackground(enabled);
1162
1007
  if (uploaded) update({ src: enabled ? uploaded.transparent : uploaded.original });
1163
1008
  } })
1164
1009
  ] })
1165
1010
  ] })
1166
1011
  ] }),
1167
- /* @__PURE__ */ jsx10(Typography, { variant: "label", children: tr2("\u0410\u043D\u0438\u043C\u0430\u0446\u0438\u044F") }),
1168
- /* @__PURE__ */ jsx10(Stack, { className: "ad-loader-generator-grid", direction: "row", gap: 2, role: "radiogroup", "aria-label": tr2("\u0410\u043D\u0438\u043C\u0430\u0446\u0438\u044F"), children: loaderGeneratorAnimations.map((animation) => /* @__PURE__ */ jsxs8(
1012
+ /* @__PURE__ */ jsx7(Typography, { variant: "label", children: tr2("\u0410\u043D\u0438\u043C\u0430\u0446\u0438\u044F") }),
1013
+ /* @__PURE__ */ jsx7(Stack, { className: "ad-loader-generator-grid", direction: "row", gap: 2, role: "radiogroup", "aria-label": tr2("\u0410\u043D\u0438\u043C\u0430\u0446\u0438\u044F"), children: loaderGeneratorAnimations.map((animation) => /* @__PURE__ */ jsxs6(
1169
1014
  Button,
1170
1015
  {
1171
1016
  variant: "ghost",
@@ -1175,8 +1020,8 @@ function LoaderGenerator({ value, defaultValue, onValueChange, ...p }) {
1175
1020
  className: "ad-loader-generator-option",
1176
1021
  onClick: () => update({ animation }),
1177
1022
  children: [
1178
- /* @__PURE__ */ jsx10(Loader, { src: settings.src, animation, size: "3.5rem", speed: settings.speed, color: settings.color, palette: settings.palette }),
1179
- /* @__PURE__ */ jsx10(Typography, { variant: "caption", children: loaderAnimationLabel(animation, tr2) })
1023
+ /* @__PURE__ */ jsx7(Loader, { src: settings.src, animation, size: "3.5rem", speed: settings.speed, color: settings.color, palette: settings.palette }),
1024
+ /* @__PURE__ */ jsx7(Typography, { variant: "caption", children: loaderAnimationLabel(animation, tr2) })
1180
1025
  ]
1181
1026
  },
1182
1027
  animation
@@ -1195,17 +1040,17 @@ function MelodixText({ children, as = "span", finish = "silver", glow: glow3 = t
1195
1040
  }
1196
1041
 
1197
1042
  // src/components/foundation/ThemeEditor/ThemeEditor.tsx
1198
- import { useEffect as useEffect2, useLayoutEffect as useLayoutEffect2, useRef as useRef5, useState as useState3 } from "react";
1043
+ import { useEffect as useEffect2, useLayoutEffect, useRef as useRef4, useState as useState2 } from "react";
1199
1044
 
1200
1045
  // src/components/feedback/Badge/Badge.tsx
1201
- import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
1202
- var Badge = (p) => /* @__PURE__ */ jsxs9("span", { ...mark("Badge", p), children: [
1203
- p.tone && /* @__PURE__ */ jsx11("i", { className: "ad-badge-dot", "aria-hidden": true }),
1046
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1047
+ var Badge = (p) => /* @__PURE__ */ jsxs7("span", { ...mark("Badge", p), children: [
1048
+ p.tone && /* @__PURE__ */ jsx8("i", { className: "ad-badge-dot", "aria-hidden": true }),
1204
1049
  p.children ?? p.label ?? "GPU"
1205
1050
  ] });
1206
1051
 
1207
1052
  // src/components/feedback/CollapsibleSection/CollapsibleSection.tsx
1208
- import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1053
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1209
1054
  var CollapsibleSection = (p) => {
1210
1055
  const tr2 = useTr();
1211
1056
  const [open, setOpen] = useControllable(
@@ -1213,7 +1058,7 @@ var CollapsibleSection = (p) => {
1213
1058
  p.defaultOpen ?? false,
1214
1059
  p.onOpenChange
1215
1060
  );
1216
- return /* @__PURE__ */ jsxs10(
1061
+ return /* @__PURE__ */ jsxs8(
1217
1062
  "details",
1218
1063
  {
1219
1064
  ...mark("CollapsibleSection", p, "card"),
@@ -1225,34 +1070,34 @@ var CollapsibleSection = (p) => {
1225
1070
  setOpen(next);
1226
1071
  },
1227
1072
  children: [
1228
- /* @__PURE__ */ jsxs10("summary", { children: [
1229
- /* @__PURE__ */ jsx12(Icon, { name: p.icon ?? "braces" }),
1230
- /* @__PURE__ */ jsxs10("span", { className: "ad-collapse-heading", children: [
1231
- /* @__PURE__ */ jsx12("span", { children: p.title ?? tr2("\u0422\u0435\u0445\u043D\u0438\u0447\u0435\u0441\u043A\u0438\u0439 JSON") }),
1232
- p.description && /* @__PURE__ */ jsx12("small", { children: p.description })
1073
+ /* @__PURE__ */ jsxs8("summary", { children: [
1074
+ /* @__PURE__ */ jsx9(Icon, { name: p.icon ?? "braces" }),
1075
+ /* @__PURE__ */ jsxs8("span", { className: "ad-collapse-heading", children: [
1076
+ /* @__PURE__ */ jsx9("span", { children: p.title ?? tr2("\u0422\u0435\u0445\u043D\u0438\u0447\u0435\u0441\u043A\u0438\u0439 JSON") }),
1077
+ p.description && /* @__PURE__ */ jsx9("small", { children: p.description })
1233
1078
  ] }),
1234
- /* @__PURE__ */ jsx12(Icon, { name: "chevron", size: 18 })
1079
+ /* @__PURE__ */ jsx9(Icon, { name: "chevron", size: 18 })
1235
1080
  ] }),
1236
- /* @__PURE__ */ jsx12("div", { className: "ad-collapse-content", children: p.children ?? tr2("\u0421\u043E\u0434\u0435\u0440\u0436\u0438\u043C\u043E\u0435 \u0440\u0430\u0441\u043A\u0440\u044B\u0432\u0430\u044E\u0449\u0435\u0433\u043E\u0441\u044F \u0440\u0430\u0437\u0434\u0435\u043B\u0430.") })
1081
+ /* @__PURE__ */ jsx9("div", { className: "ad-collapse-content", children: p.children ?? tr2("\u0421\u043E\u0434\u0435\u0440\u0436\u0438\u043C\u043E\u0435 \u0440\u0430\u0441\u043A\u0440\u044B\u0432\u0430\u044E\u0449\u0435\u0433\u043E\u0441\u044F \u0440\u0430\u0437\u0434\u0435\u043B\u0430.") })
1237
1082
  ]
1238
1083
  }
1239
1084
  );
1240
1085
  };
1241
1086
 
1242
1087
  // src/components/feedback/MessageBar/MessageBar.tsx
1243
- import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1088
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1244
1089
  var icons2 = { success: "check", error: "warning", warning: "warning" };
1245
1090
  var MessageBar = ({ action, tone = "warning", ...p }) => {
1246
1091
  const tr2 = useTr();
1247
- return /* @__PURE__ */ jsxs11(
1092
+ return /* @__PURE__ */ jsxs9(
1248
1093
  "div",
1249
1094
  {
1250
1095
  ...mark("MessageBar", { ...p, tone }),
1251
1096
  role: tone === "error" ? "alert" : "status",
1252
1097
  children: [
1253
- /* @__PURE__ */ jsx13("span", { className: "ad-message-bar-icon", "aria-hidden": true, children: /* @__PURE__ */ jsx13(Icon, { name: icons2[tone] ?? "info" }) }),
1254
- /* @__PURE__ */ jsx13("span", { className: "ad-message-bar-text", children: p.children ?? tr2("\u0414\u043B\u044F \u043E\u043F\u0435\u0440\u0430\u0446\u0438\u0438 \u043D\u0443\u0436\u043D\u043E \u0431\u043E\u043B\u044C\u0448\u0435 \u0441\u0432\u043E\u0431\u043E\u0434\u043D\u043E\u0433\u043E \u043C\u0435\u0441\u0442\u0430.") }),
1255
- action && /* @__PURE__ */ jsx13("span", { className: "ad-message-bar-action", children: action })
1098
+ /* @__PURE__ */ jsx10("span", { className: "ad-message-bar-icon", "aria-hidden": true, children: /* @__PURE__ */ jsx10(Icon, { name: icons2[tone] ?? "info" }) }),
1099
+ /* @__PURE__ */ jsx10("span", { className: "ad-message-bar-text", children: p.children ?? tr2("\u0414\u043B\u044F \u043E\u043F\u0435\u0440\u0430\u0446\u0438\u0438 \u043D\u0443\u0436\u043D\u043E \u0431\u043E\u043B\u044C\u0448\u0435 \u0441\u0432\u043E\u0431\u043E\u0434\u043D\u043E\u0433\u043E \u043C\u0435\u0441\u0442\u0430.") }),
1100
+ action && /* @__PURE__ */ jsx10("span", { className: "ad-message-bar-action", children: action })
1256
1101
  ]
1257
1102
  }
1258
1103
  );
@@ -1429,7 +1274,7 @@ function resolveToken(scope, name, kind = "color") {
1429
1274
  }
1430
1275
 
1431
1276
  // src/components/foundation/ThemeEditor/ThemeEditor.tsx
1432
- import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1277
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
1433
1278
  var paletteStrip = ["primary-900", "primary-800", "primary-700", "primary-600", "primary", "secondary", "secondary-200", "secondary-100", "secondary-50"];
1434
1279
  var neutralStrip = ["neutral-950", "neutral-900", "neutral-850", "neutral-800", "neutral-700", "neutral-600", "neutral-500", "neutral-400", "neutral-300", "neutral-200"];
1435
1280
  var tokensWord = (n, locale, translate2) => `${n} ${plural(n, translate2("\u0442\u043E\u043A\u0435\u043D"), translate2("\u0442\u043E\u043A\u0435\u043D\u0430"), translate2("\u0442\u043E\u043A\u0435\u043D\u043E\u0432"), locale)}`;
@@ -1438,18 +1283,18 @@ function ThemeEditor({ value: controlled, defaultValue = defaultThemeConfig, onV
1438
1283
  const locale = useLocale();
1439
1284
  const [config, setConfig] = useControllable(controlled, defaultValue, onValueChange);
1440
1285
  const update = (patch) => setConfig({ ...config, ...patch });
1441
- const root = useRef5(null);
1442
- const [resolved, setResolved] = useState3({});
1443
- const [search, setSearch] = useState3("");
1444
- const [notice, setNotice] = useState3();
1445
- const [pasteOpen, setPasteOpen] = useState3(false);
1446
- const [pasted, setPasted] = useState3("");
1447
- const pasteAnchor = useRef5(null);
1448
- const importVersion = useRef5(0);
1286
+ const root = useRef4(null);
1287
+ const [resolved, setResolved] = useState2({});
1288
+ const [search, setSearch] = useState2("");
1289
+ const [notice, setNotice] = useState2();
1290
+ const [pasteOpen, setPasteOpen] = useState2(false);
1291
+ const [pasted, setPasted] = useState2("");
1292
+ const pasteAnchor = useRef4(null);
1293
+ const importVersion = useRef4(0);
1449
1294
  useEffect2(() => () => {
1450
1295
  importVersion.current += 1;
1451
1296
  }, []);
1452
- useLayoutEffect2(() => {
1297
+ useLayoutEffect(() => {
1453
1298
  if (config.mode !== "advanced") return;
1454
1299
  const node = root.current;
1455
1300
  if (!node) return;
@@ -1480,9 +1325,9 @@ function ThemeEditor({ value: controlled, defaultValue = defaultThemeConfig, onV
1480
1325
  }
1481
1326
  };
1482
1327
  const needle = search.trim().toLowerCase();
1483
- return /* @__PURE__ */ jsxs12("div", { ...mark("ThemeEditor", p), ref: root, children: [
1484
- /* @__PURE__ */ jsxs12("div", { className: "ad-theme-editor-bar", children: [
1485
- /* @__PURE__ */ jsx14(
1328
+ return /* @__PURE__ */ jsxs10("div", { ...mark("ThemeEditor", p), ref: root, children: [
1329
+ /* @__PURE__ */ jsxs10("div", { className: "ad-theme-editor-bar", children: [
1330
+ /* @__PURE__ */ jsx11(
1486
1331
  SegmentedControl,
1487
1332
  {
1488
1333
  size: "sm",
@@ -1494,13 +1339,13 @@ function ThemeEditor({ value: controlled, defaultValue = defaultThemeConfig, onV
1494
1339
  ]
1495
1340
  }
1496
1341
  ),
1497
- /* @__PURE__ */ jsxs12("span", { className: "ad-theme-editor-actions", children: [
1498
- /* @__PURE__ */ jsx14(Button, { size: "sm", icon: "download", onClick: () => downloadFile(fileName, exportTheme(config)), children: tr2("\u042D\u043A\u0441\u043F\u043E\u0440\u0442") }),
1499
- /* @__PURE__ */ jsx14(IconButton, { size: "sm", icon: "copy", label: tr2("\u0421\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C JSON \u0442\u0435\u043C\u044B"), onClick: async () => {
1342
+ /* @__PURE__ */ jsxs10("span", { className: "ad-theme-editor-actions", children: [
1343
+ /* @__PURE__ */ jsx11(Button, { size: "sm", icon: "download", onClick: () => downloadFile(fileName, exportTheme(config)), children: tr2("\u042D\u043A\u0441\u043F\u043E\u0440\u0442") }),
1344
+ /* @__PURE__ */ jsx11(IconButton, { size: "sm", icon: "copy", label: tr2("\u0421\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C JSON \u0442\u0435\u043C\u044B"), onClick: async () => {
1500
1345
  const done = await copyText(exportTheme(config));
1501
1346
  setNotice(done ? { tone: "success", text: tr2("JSON \u0442\u0435\u043C\u044B \u0441\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u043D.") } : { tone: "error", text: tr2("\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0441\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C.") });
1502
1347
  } }),
1503
- /* @__PURE__ */ jsx14(
1348
+ /* @__PURE__ */ jsx11(
1504
1349
  FilePicker,
1505
1350
  {
1506
1351
  size: "sm",
@@ -1520,28 +1365,28 @@ function ThemeEditor({ value: controlled, defaultValue = defaultThemeConfig, onV
1520
1365
  }
1521
1366
  }
1522
1367
  ),
1523
- /* @__PURE__ */ jsx14(IconButton, { ref: pasteAnchor, size: "sm", icon: "document", label: tr2("\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044C JSON \u0442\u0435\u043C\u044B"), "aria-expanded": pasteOpen, onClick: () => setPasteOpen((v) => !v) }),
1524
- /* @__PURE__ */ jsx14(IconButton, { size: "sm", variant: "ghost", icon: "reset", label: tr2("\u0421\u0431\u0440\u043E\u0441\u0438\u0442\u044C \u0442\u0435\u043C\u0443"), onClick: () => {
1368
+ /* @__PURE__ */ jsx11(IconButton, { ref: pasteAnchor, size: "sm", icon: "document", label: tr2("\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044C JSON \u0442\u0435\u043C\u044B"), "aria-expanded": pasteOpen, onClick: () => setPasteOpen((v) => !v) }),
1369
+ /* @__PURE__ */ jsx11(IconButton, { size: "sm", variant: "ghost", icon: "reset", label: tr2("\u0421\u0431\u0440\u043E\u0441\u0438\u0442\u044C \u0442\u0435\u043C\u0443"), onClick: () => {
1525
1370
  importVersion.current += 1;
1526
1371
  setConfig(defaultThemeConfig);
1527
1372
  setNotice(void 0);
1528
1373
  } })
1529
1374
  ] })
1530
1375
  ] }),
1531
- /* @__PURE__ */ jsxs12(Popover, { open: pasteOpen, onOpenChange: setPasteOpen, anchorRef: pasteAnchor, align: "end", label: tr2("\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0442\u0435\u043C\u0443"), className: "ad-theme-editor-paste", children: [
1532
- /* @__PURE__ */ jsx14(TextArea, { label: tr2("JSON \u0442\u0435\u043C\u044B"), value: pasted, onValueChange: setPasted, rows: 6, placeholder: '{ "$type": "neo-ui-theme", \u2026 }' }),
1533
- /* @__PURE__ */ jsx14(Button, { size: "sm", variant: "primary", icon: "check", disabled: !pasted.trim(), onClick: () => {
1376
+ /* @__PURE__ */ jsxs10(Popover, { open: pasteOpen, onOpenChange: setPasteOpen, anchorRef: pasteAnchor, align: "end", label: tr2("\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0442\u0435\u043C\u0443"), className: "ad-theme-editor-paste", children: [
1377
+ /* @__PURE__ */ jsx11(TextArea, { label: tr2("JSON \u0442\u0435\u043C\u044B"), value: pasted, onValueChange: setPasted, rows: 6, placeholder: '{ "$type": "neo-ui-theme", \u2026 }' }),
1378
+ /* @__PURE__ */ jsx11(Button, { size: "sm", variant: "primary", icon: "check", disabled: !pasted.trim(), onClick: () => {
1534
1379
  if (load(pasted)) {
1535
1380
  setPasted("");
1536
1381
  setPasteOpen(false);
1537
1382
  }
1538
1383
  }, children: tr2("\u041F\u0440\u0438\u043C\u0435\u043D\u0438\u0442\u044C") })
1539
1384
  ] }),
1540
- notice && /* @__PURE__ */ jsx14(MessageBar, { tone: notice.tone, children: notice.text }),
1541
- config.mode === "simple" ? /* @__PURE__ */ jsxs12("div", { className: "ad-theme-editor-simple", children: [
1542
- /* @__PURE__ */ jsxs12("div", { className: "ad-theme-editor-colors", children: [
1543
- /* @__PURE__ */ jsx14(ColorPicker, { label: tr2("\u041E\u0441\u043D\u043E\u0432\u043D\u043E\u0439 \u0446\u0432\u0435\u0442"), value: primary, onValueChange: (hex2) => update({ primary: hex2 }) }),
1544
- /* @__PURE__ */ jsx14(
1385
+ notice && /* @__PURE__ */ jsx11(MessageBar, { tone: notice.tone, children: notice.text }),
1386
+ config.mode === "simple" ? /* @__PURE__ */ jsxs10("div", { className: "ad-theme-editor-simple", children: [
1387
+ /* @__PURE__ */ jsxs10("div", { className: "ad-theme-editor-colors", children: [
1388
+ /* @__PURE__ */ jsx11(ColorPicker, { label: tr2("\u041E\u0441\u043D\u043E\u0432\u043D\u043E\u0439 \u0446\u0432\u0435\u0442"), value: primary, onValueChange: (hex2) => update({ primary: hex2 }) }),
1389
+ /* @__PURE__ */ jsx11(
1545
1390
  IconButton,
1546
1391
  {
1547
1392
  round: true,
@@ -1551,7 +1396,7 @@ function ThemeEditor({ value: controlled, defaultValue = defaultThemeConfig, onV
1551
1396
  onClick: () => update({ primary: secondary, secondary: primary, autoSecondary: false })
1552
1397
  }
1553
1398
  ),
1554
- /* @__PURE__ */ jsx14(
1399
+ /* @__PURE__ */ jsx11(
1555
1400
  ColorPicker,
1556
1401
  {
1557
1402
  label: tr2("\u0412\u0442\u043E\u0440\u043E\u0439 \u0446\u0432\u0435\u0442"),
@@ -1562,7 +1407,7 @@ function ThemeEditor({ value: controlled, defaultValue = defaultThemeConfig, onV
1562
1407
  }
1563
1408
  )
1564
1409
  ] }),
1565
- /* @__PURE__ */ jsx14(
1410
+ /* @__PURE__ */ jsx11(
1566
1411
  Switch,
1567
1412
  {
1568
1413
  label: tr2("\u0412\u0442\u043E\u0440\u043E\u0439 \u0446\u0432\u0435\u0442 \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044B\u0432\u0430\u0442\u044C \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438"),
@@ -1570,29 +1415,29 @@ function ThemeEditor({ value: controlled, defaultValue = defaultThemeConfig, onV
1570
1415
  onValueChange: (autoSecondary) => update({ autoSecondary, primary: config.primary ?? primary, secondary })
1571
1416
  }
1572
1417
  ),
1573
- /* @__PURE__ */ jsx14(Typography, { variant: "caption", tone: "muted", children: tr2("\u041F\u0430\u043B\u0438\u0442\u0440\u0430, \u043F\u043E\u0441\u0442\u0440\u043E\u0435\u043D\u043D\u0430\u044F \u0438\u0437 \u044D\u0442\u0438\u0445 \u0446\u0432\u0435\u0442\u043E\u0432:") }),
1574
- /* @__PURE__ */ jsx14("div", { className: "ad-theme-editor-strip", children: paletteStrip.map((name) => /* @__PURE__ */ jsx14("i", { title: name, style: { background: `var(--ad-${name})` } }, name)) }),
1575
- /* @__PURE__ */ jsx14("div", { className: "ad-theme-editor-strip", children: neutralStrip.map((name) => /* @__PURE__ */ jsx14("i", { title: name, style: { background: `var(--ad-${name})` } }, name)) })
1576
- ] }) : /* @__PURE__ */ jsxs12("div", { className: "ad-theme-editor-advanced", children: [
1577
- /* @__PURE__ */ jsxs12("div", { className: "ad-theme-editor-search", children: [
1578
- /* @__PURE__ */ jsx14(TextField, { size: "sm", placeholder: tr2("\u041D\u0430\u0439\u0442\u0438 \u0442\u043E\u043A\u0435\u043D\u2026"), startAdornment: /* @__PURE__ */ jsx14(Icon, { name: "search" }), clearable: true, value: search, onValueChange: setSearch, "aria-label": tr2("\u041D\u0430\u0439\u0442\u0438 \u0442\u043E\u043A\u0435\u043D") }),
1579
- /* @__PURE__ */ jsx14(Badge, { tone: changed ? "warning" : void 0, children: changed ? tr2("\u0418\u0437\u043C\u0435\u043D\u0435\u043D\u043E: {count}", { count: changed }) : tr2("\u0412\u0441\u0451 \u0438\u0437 \u0442\u0435\u043C\u044B") })
1418
+ /* @__PURE__ */ jsx11(Typography, { variant: "caption", tone: "muted", children: tr2("\u041F\u0430\u043B\u0438\u0442\u0440\u0430, \u043F\u043E\u0441\u0442\u0440\u043E\u0435\u043D\u043D\u0430\u044F \u0438\u0437 \u044D\u0442\u0438\u0445 \u0446\u0432\u0435\u0442\u043E\u0432:") }),
1419
+ /* @__PURE__ */ jsx11("div", { className: "ad-theme-editor-strip", children: paletteStrip.map((name) => /* @__PURE__ */ jsx11("i", { title: name, style: { background: `var(--ad-${name})` } }, name)) }),
1420
+ /* @__PURE__ */ jsx11("div", { className: "ad-theme-editor-strip", children: neutralStrip.map((name) => /* @__PURE__ */ jsx11("i", { title: name, style: { background: `var(--ad-${name})` } }, name)) })
1421
+ ] }) : /* @__PURE__ */ jsxs10("div", { className: "ad-theme-editor-advanced", children: [
1422
+ /* @__PURE__ */ jsxs10("div", { className: "ad-theme-editor-search", children: [
1423
+ /* @__PURE__ */ jsx11(TextField, { size: "sm", placeholder: tr2("\u041D\u0430\u0439\u0442\u0438 \u0442\u043E\u043A\u0435\u043D\u2026"), startAdornment: /* @__PURE__ */ jsx11(Icon, { name: "search" }), clearable: true, value: search, onValueChange: setSearch, "aria-label": tr2("\u041D\u0430\u0439\u0442\u0438 \u0442\u043E\u043A\u0435\u043D") }),
1424
+ /* @__PURE__ */ jsx11(Badge, { tone: changed ? "warning" : void 0, children: changed ? tr2("\u0418\u0437\u043C\u0435\u043D\u0435\u043D\u043E: {count}", { count: changed }) : tr2("\u0412\u0441\u0451 \u0438\u0437 \u0442\u0435\u043C\u044B") })
1580
1425
  ] }),
1581
1426
  themeTokenGroups.map((group, index) => {
1582
1427
  const rows = group.tokens.filter((token) => !needle || `${token.name} ${token.label} ${group.title}`.toLowerCase().includes(needle));
1583
1428
  if (!rows.length) return null;
1584
1429
  const own = rows.filter((token) => tokens[token.name] !== void 0).length;
1585
- return /* @__PURE__ */ jsx14(
1430
+ return /* @__PURE__ */ jsx11(
1586
1431
  CollapsibleSection,
1587
1432
  {
1588
1433
  title: tr2(group.title),
1589
1434
  defaultOpen: index < 2,
1590
1435
  open: needle ? true : void 0,
1591
1436
  description: own ? tr2("\u0438\u0437\u043C\u0435\u043D\u0435\u043D\u043E: {own} \u0438\u0437 {total}", { own, total: rows.length }) : tokensWord(rows.length, locale, tr2),
1592
- children: /* @__PURE__ */ jsx14("div", { className: "ad-theme-editor-tokens", children: rows.map((token) => {
1437
+ children: /* @__PURE__ */ jsx11("div", { className: "ad-theme-editor-tokens", children: rows.map((token) => {
1593
1438
  const current = tokens[token.name] ?? resolved[token.name] ?? "";
1594
- return /* @__PURE__ */ jsxs12("div", { className: "ad-theme-editor-token", "data-changed": tokens[token.name] !== void 0 || void 0, children: [
1595
- token.kind === "color" ? /* @__PURE__ */ jsx14(
1439
+ return /* @__PURE__ */ jsxs10("div", { className: "ad-theme-editor-token", "data-changed": tokens[token.name] !== void 0 || void 0, children: [
1440
+ token.kind === "color" ? /* @__PURE__ */ jsx11(
1596
1441
  ColorPicker,
1597
1442
  {
1598
1443
  size: "sm",
@@ -1600,7 +1445,7 @@ function ThemeEditor({ value: controlled, defaultValue = defaultThemeConfig, onV
1600
1445
  value: /^#[0-9a-f]{6}$/i.test(current) ? current : "#000000",
1601
1446
  onValueChange: (hex2) => setToken(token.name, hex2)
1602
1447
  }
1603
- ) : /* @__PURE__ */ jsx14(
1448
+ ) : /* @__PURE__ */ jsx11(
1604
1449
  TextField,
1605
1450
  {
1606
1451
  size: "sm",
@@ -1610,11 +1455,11 @@ function ThemeEditor({ value: controlled, defaultValue = defaultThemeConfig, onV
1610
1455
  onValueChange: (value) => setToken(token.name, value)
1611
1456
  }
1612
1457
  ),
1613
- /* @__PURE__ */ jsxs12("code", { children: [
1458
+ /* @__PURE__ */ jsxs10("code", { children: [
1614
1459
  "--ad-",
1615
1460
  token.name
1616
1461
  ] }),
1617
- tokens[token.name] !== void 0 && /* @__PURE__ */ jsx14(IconButton, { size: "xs", variant: "ghost", icon: "reset", label: tr2("\u0412\u0435\u0440\u043D\u0443\u0442\u044C {name}", { name: tr2(token.label) }), onClick: () => setToken(token.name, void 0) })
1462
+ tokens[token.name] !== void 0 && /* @__PURE__ */ jsx11(IconButton, { size: "xs", variant: "ghost", icon: "reset", label: tr2("\u0412\u0435\u0440\u043D\u0443\u0442\u044C {name}", { name: tr2(token.label) }), onClick: () => setToken(token.name, void 0) })
1618
1463
  ] }, token.name);
1619
1464
  }) })
1620
1465
  },
@@ -1626,7 +1471,7 @@ function ThemeEditor({ value: controlled, defaultValue = defaultThemeConfig, onV
1626
1471
  }
1627
1472
 
1628
1473
  // src/components/effects/Tilt/Tilt.tsx
1629
- import { createElement as createElement5, useRef as useRef6 } from "react";
1474
+ import { createElement as createElement5, useRef as useRef5 } from "react";
1630
1475
  function Tilt({
1631
1476
  as = "div",
1632
1477
  max = 14,
@@ -1634,7 +1479,7 @@ function Tilt({
1634
1479
  children,
1635
1480
  ...p
1636
1481
  }) {
1637
- const pauseRef = useRef6(null);
1482
+ const pauseRef = useRef5(null);
1638
1483
  const motion = useMotion();
1639
1484
  usePauseOffscreen(pauseRef);
1640
1485
  const set = (el, x, y) => {
@@ -1666,28 +1511,28 @@ function Tilt({
1666
1511
  }
1667
1512
 
1668
1513
  // src/components/layout/StatTile/StatTile.tsx
1669
- import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
1514
+ import { Fragment as Fragment2, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
1670
1515
  function StatTile({ icon = "music", value, label, onClick, tilt = true, badge, badgeLabel, ...p }) {
1671
- const card = /* @__PURE__ */ jsxs13(Card, { border: true, padding: "sm", className: "ad-stat-tile-card", children: [
1672
- /* @__PURE__ */ jsxs13("span", { className: "ad-stat-tile-icon", "aria-hidden": "true", children: [
1673
- /* @__PURE__ */ jsx15(Icon, { name: icon }),
1674
- /* @__PURE__ */ jsx15(Icon, { name: "sparkle", className: "ad-stat-tile-spark" })
1516
+ const card = /* @__PURE__ */ jsxs11(Card, { border: true, padding: "sm", className: "ad-stat-tile-card", children: [
1517
+ /* @__PURE__ */ jsxs11("span", { className: "ad-stat-tile-icon", "aria-hidden": "true", children: [
1518
+ /* @__PURE__ */ jsx12(Icon, { name: icon }),
1519
+ /* @__PURE__ */ jsx12(Icon, { name: "sparkle", className: "ad-stat-tile-spark" })
1675
1520
  ] }),
1676
- /* @__PURE__ */ jsxs13("span", { className: "ad-stat-tile-text", children: [
1677
- /* @__PURE__ */ jsx15(Typography, { as: "strong", variant: "h2", children: value }),
1678
- /* @__PURE__ */ jsx15(Typography, { variant: "body-sm", tone: "muted", children: label })
1521
+ /* @__PURE__ */ jsxs11("span", { className: "ad-stat-tile-text", children: [
1522
+ /* @__PURE__ */ jsx12(Typography, { as: "strong", variant: "h2", children: value }),
1523
+ /* @__PURE__ */ jsx12(Typography, { variant: "body-sm", tone: "muted", children: label })
1679
1524
  ] })
1680
1525
  ] });
1681
- const body = /* @__PURE__ */ jsxs13(Fragment3, { children: [
1682
- tilt ? /* @__PURE__ */ jsx15(Tilt, { max: 8, children: card }) : card,
1683
- badge !== void 0 && badge !== null && /* @__PURE__ */ jsx15("span", { className: "ad-stat-tile-badge", "aria-label": badgeLabel, children: badge })
1526
+ const body = /* @__PURE__ */ jsxs11(Fragment2, { children: [
1527
+ tilt ? /* @__PURE__ */ jsx12(Tilt, { max: 8, children: card }) : card,
1528
+ badge !== void 0 && badge !== null && /* @__PURE__ */ jsx12("span", { className: "ad-stat-tile-badge", "aria-label": badgeLabel, children: badge })
1684
1529
  ] });
1685
- return onClick ? /* @__PURE__ */ jsx15("button", { type: "button", ...mark("StatTile", p), onClick, children: body }) : /* @__PURE__ */ jsx15("div", { ...mark("StatTile", p), children: body });
1530
+ return onClick ? /* @__PURE__ */ jsx12("button", { type: "button", ...mark("StatTile", p), onClick, children: body }) : /* @__PURE__ */ jsx12("div", { ...mark("StatTile", p), children: body });
1686
1531
  }
1687
1532
 
1688
1533
  // src/components/effects/Equalizer/Equalizer.tsx
1689
- import { useRef as useRef7 } from "react";
1690
- import { jsx as jsx16 } from "react/jsx-runtime";
1534
+ import { useRef as useRef6 } from "react";
1535
+ import { jsx as jsx13 } from "react/jsx-runtime";
1691
1536
  function Equalizer({
1692
1537
  bars = 5,
1693
1538
  playing = true,
@@ -1697,9 +1542,9 @@ function Equalizer({
1697
1542
  ...p
1698
1543
  }) {
1699
1544
  const tr2 = useTr();
1700
- const pauseRef = useRef7(null);
1545
+ const pauseRef = useRef6(null);
1701
1546
  usePauseOffscreen(pauseRef);
1702
- return /* @__PURE__ */ jsx16(
1547
+ return /* @__PURE__ */ jsx13(
1703
1548
  "span",
1704
1549
  {
1705
1550
  ...mark("Equalizer", p),
@@ -1708,7 +1553,7 @@ function Equalizer({
1708
1553
  "aria-label": label ?? (playing ? tr2("\u0418\u0433\u0440\u0430\u0435\u0442") : tr2("\u041F\u0430\u0443\u0437\u0430")),
1709
1554
  "data-playing": playing && !levels || void 0,
1710
1555
  "data-live": levels ? "" : void 0,
1711
- children: Array.from({ length: bars }, (_, i) => /* @__PURE__ */ jsx16(
1556
+ children: Array.from({ length: bars }, (_, i) => /* @__PURE__ */ jsx13(
1712
1557
  "i",
1713
1558
  {
1714
1559
  style: levels ? { scale: `1 ${Math.max(0.08, Math.min(1, levels[Math.floor(i / bars * levels.length)] ?? 0))}` } : {
@@ -1723,26 +1568,26 @@ function Equalizer({
1723
1568
  }
1724
1569
 
1725
1570
  // src/components/media/MediaCard/MediaCard.tsx
1726
- import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
1571
+ import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1727
1572
  function MediaCard({ title, subtitle, image, levels, phase = 0, badge, actions, tilt = true, children, ...p }) {
1728
- const card = /* @__PURE__ */ jsxs14(Card, { border: true, padding: "none", className: "ad-media-card-card", "data-image": image ? "" : void 0, children: [
1729
- image && /* @__PURE__ */ jsx17("img", { className: "ad-media-card-image", src: image, alt: "", loading: "lazy" }),
1730
- /* @__PURE__ */ jsx17("span", { className: "ad-media-card-art", "aria-hidden": "true", children: /* @__PURE__ */ jsx17(Equalizer, { bars: 16, levels, phase }) }),
1731
- /* @__PURE__ */ jsxs14("div", { className: "ad-media-card-content", children: [
1732
- /* @__PURE__ */ jsxs14("div", { className: "ad-media-card-head", children: [
1733
- /* @__PURE__ */ jsxs14("div", { className: "ad-media-card-title", children: [
1734
- /* @__PURE__ */ jsx17(Typography, { as: "strong", variant: "title", truncate: true, children: title }),
1735
- subtitle && /* @__PURE__ */ jsx17(Typography, { variant: "body-sm", tone: "muted", truncate: true, children: subtitle })
1573
+ const card = /* @__PURE__ */ jsxs12(Card, { border: true, padding: "none", className: "ad-media-card-card", "data-image": image ? "" : void 0, children: [
1574
+ image && /* @__PURE__ */ jsx14("img", { className: "ad-media-card-image", src: image, alt: "", loading: "lazy" }),
1575
+ /* @__PURE__ */ jsx14("span", { className: "ad-media-card-art", "aria-hidden": "true", children: /* @__PURE__ */ jsx14(Equalizer, { bars: 16, levels, phase }) }),
1576
+ /* @__PURE__ */ jsxs12("div", { className: "ad-media-card-content", children: [
1577
+ /* @__PURE__ */ jsxs12("div", { className: "ad-media-card-head", children: [
1578
+ /* @__PURE__ */ jsxs12("div", { className: "ad-media-card-title", children: [
1579
+ /* @__PURE__ */ jsx14(Typography, { as: "strong", variant: "title", truncate: true, children: title }),
1580
+ subtitle && /* @__PURE__ */ jsx14(Typography, { variant: "body-sm", tone: "muted", truncate: true, children: subtitle })
1736
1581
  ] }),
1737
1582
  badge
1738
1583
  ] }),
1739
- (children || actions) && /* @__PURE__ */ jsxs14("div", { className: "ad-media-card-actions", children: [
1584
+ (children || actions) && /* @__PURE__ */ jsxs12("div", { className: "ad-media-card-actions", children: [
1740
1585
  children,
1741
1586
  actions
1742
1587
  ] })
1743
1588
  ] })
1744
1589
  ] });
1745
- return /* @__PURE__ */ jsx17("div", { ...mark("MediaCard", p), children: tilt ? /* @__PURE__ */ jsx17(Tilt, { max: typeof tilt === "number" ? tilt : 10, children: card }) : card });
1590
+ return /* @__PURE__ */ jsx14("div", { ...mark("MediaCard", p), children: tilt ? /* @__PURE__ */ jsx14(Tilt, { max: typeof tilt === "number" ? tilt : 10, children: card }) : card });
1746
1591
  }
1747
1592
 
1748
1593
  // src/components/layout/DialogActions/DialogActions.tsx
@@ -1752,8 +1597,8 @@ var DialogActions = part("DialogActions", "footer");
1752
1597
  var DialogBody = part("DialogBody", "div");
1753
1598
 
1754
1599
  // src/components/layout/Divider/Divider.tsx
1755
- import { jsx as jsx18 } from "react/jsx-runtime";
1756
- var Divider = (p) => /* @__PURE__ */ jsx18(
1600
+ import { jsx as jsx15 } from "react/jsx-runtime";
1601
+ var Divider = (p) => /* @__PURE__ */ jsx15(
1757
1602
  "div",
1758
1603
  {
1759
1604
  ...mark("Divider", p),
@@ -1763,13 +1608,13 @@ var Divider = (p) => /* @__PURE__ */ jsx18(
1763
1608
  );
1764
1609
 
1765
1610
  // src/components/layout/ScrollArea/ScrollArea.tsx
1766
- import { useRef as useRef8 } from "react";
1767
- import { jsx as jsx19 } from "react/jsx-runtime";
1611
+ import { useRef as useRef7 } from "react";
1612
+ import { jsx as jsx16 } from "react/jsx-runtime";
1768
1613
  var scrollHeight = (height) => typeof height === "number" ? `calc(var(--ad-fluid-unit) * ${height})` : height ?? "clamp(10rem, 32dvh, 18rem)";
1769
1614
  function ScrollArea(p) {
1770
- const ref = useRef8(null);
1615
+ const ref = useRef7(null);
1771
1616
  useSmoothWheel(ref);
1772
- return /* @__PURE__ */ jsx19(
1617
+ return /* @__PURE__ */ jsx16(
1773
1618
  "div",
1774
1619
  {
1775
1620
  ...mark("ScrollArea", p),
@@ -1783,8 +1628,8 @@ function ScrollArea(p) {
1783
1628
  }
1784
1629
 
1785
1630
  // src/components/layout/TabPanel/TabPanel.tsx
1786
- import { jsx as jsx20 } from "react/jsx-runtime";
1787
- var TabPanel = (p) => /* @__PURE__ */ jsx20(
1631
+ import { jsx as jsx17 } from "react/jsx-runtime";
1632
+ var TabPanel = (p) => /* @__PURE__ */ jsx17(
1788
1633
  "div",
1789
1634
  {
1790
1635
  ...mark("TabPanel", p),
@@ -1792,7 +1637,7 @@ var TabPanel = (p) => /* @__PURE__ */ jsx20(
1792
1637
  "aria-labelledby": p.labelledBy,
1793
1638
  hidden: p.hidden,
1794
1639
  tabIndex: 0,
1795
- children: /* @__PURE__ */ jsx20("div", { className: "ad-tab-panel-content", children: p.children }, p.labelledBy)
1640
+ children: /* @__PURE__ */ jsx17("div", { className: "ad-tab-panel-content", children: p.children }, p.labelledBy)
1796
1641
  }
1797
1642
  );
1798
1643
 
@@ -1808,7 +1653,7 @@ var Text = ({ as = "span", ...p }) => createElement6(
1808
1653
  var Toolbar = part("Toolbar", "div");
1809
1654
 
1810
1655
  // src/components/controls/Link/Link.tsx
1811
- import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
1656
+ import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
1812
1657
  function Link({
1813
1658
  href = "#",
1814
1659
  icon,
@@ -1818,7 +1663,7 @@ function Link({
1818
1663
  children,
1819
1664
  ...props
1820
1665
  }) {
1821
- return /* @__PURE__ */ jsxs15(
1666
+ return /* @__PURE__ */ jsxs13(
1822
1667
  "a",
1823
1668
  {
1824
1669
  ...props,
@@ -1828,23 +1673,23 @@ function Link({
1828
1673
  rel: external ? "noreferrer" : props.rel,
1829
1674
  "data-ad-underline": underline,
1830
1675
  children: [
1831
- icon && /* @__PURE__ */ jsx21(Icon, { name: icon }),
1676
+ icon && /* @__PURE__ */ jsx18(Icon, { name: icon }),
1832
1677
  children,
1833
- endIcon && /* @__PURE__ */ jsx21(Icon, { name: endIcon })
1678
+ endIcon && /* @__PURE__ */ jsx18(Icon, { name: endIcon })
1834
1679
  ]
1835
1680
  }
1836
1681
  );
1837
1682
  }
1838
1683
 
1839
1684
  // src/components/controls/SplitButton/SplitButton.tsx
1840
- import { useRef as useRef9, useState as useState4 } from "react";
1685
+ import { useRef as useRef8, useState as useState3 } from "react";
1841
1686
 
1842
1687
  // src/components/feedback/MenuItem/MenuItem.tsx
1843
- import { jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
1688
+ import { jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
1844
1689
  var MenuItem = (p) => {
1845
1690
  const tr2 = useTr();
1846
1691
  const label = p.label ?? p.children ?? tr2("\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0435");
1847
- return /* @__PURE__ */ jsxs16(
1692
+ return /* @__PURE__ */ jsxs14(
1848
1693
  "button",
1849
1694
  {
1850
1695
  ...mark("MenuItem", { ...p, tone: p.danger ? "error" : p.tone }),
@@ -1853,17 +1698,17 @@ var MenuItem = (p) => {
1853
1698
  disabled: p.disabled,
1854
1699
  onClick: p.onSelect,
1855
1700
  children: [
1856
- /* @__PURE__ */ jsx22("span", { className: "ad-menu-item-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx22(Icon, { name: p.icon ?? "more" }) }),
1857
- /* @__PURE__ */ jsx22("span", { className: "ad-menu-item-label", children: label }),
1858
- p.endIcon && /* @__PURE__ */ jsx22("span", { className: "ad-menu-item-end", "aria-hidden": "true", children: /* @__PURE__ */ jsx22(Icon, { name: p.endIcon }) })
1701
+ /* @__PURE__ */ jsx19("span", { className: "ad-menu-item-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx19(Icon, { name: p.icon ?? "more" }) }),
1702
+ /* @__PURE__ */ jsx19("span", { className: "ad-menu-item-label", children: label }),
1703
+ p.endIcon && /* @__PURE__ */ jsx19("span", { className: "ad-menu-item-end", "aria-hidden": "true", children: /* @__PURE__ */ jsx19(Icon, { name: p.endIcon }) })
1859
1704
  ]
1860
1705
  }
1861
1706
  );
1862
1707
  };
1863
1708
 
1864
1709
  // src/components/feedback/Menu/Menu.tsx
1865
- import { jsx as jsx23 } from "react/jsx-runtime";
1866
- var Menu = (p) => /* @__PURE__ */ jsx23(
1710
+ import { jsx as jsx20 } from "react/jsx-runtime";
1711
+ var Menu = (p) => /* @__PURE__ */ jsx20(
1867
1712
  Popover,
1868
1713
  {
1869
1714
  ...p,
@@ -1887,7 +1732,7 @@ var Menu = (p) => /* @__PURE__ */ jsx23(
1887
1732
  buttons[next]?.focus();
1888
1733
  },
1889
1734
  children: (p.items ?? []).map(
1890
- (item, i) => item.separator ? /* @__PURE__ */ jsx23(Divider, {}, item.id ?? String(i)) : /* @__PURE__ */ jsx23(
1735
+ (item, i) => item.separator ? /* @__PURE__ */ jsx20(Divider, {}, item.id ?? String(i)) : /* @__PURE__ */ jsx20(
1891
1736
  MenuItem,
1892
1737
  {
1893
1738
  ...item,
@@ -1904,19 +1749,19 @@ var Menu = (p) => /* @__PURE__ */ jsx23(
1904
1749
  );
1905
1750
 
1906
1751
  // src/components/controls/SplitButton/SplitButton.tsx
1907
- import { jsx as jsx24, jsxs as jsxs17 } from "react/jsx-runtime";
1752
+ import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
1908
1753
  var SplitButton = (p) => {
1909
1754
  const tr2 = useTr();
1910
- const [open, setOpen] = useState4(false);
1911
- const anchor = useRef9(null);
1755
+ const [open, setOpen] = useState3(false);
1756
+ const anchor = useRef8(null);
1912
1757
  const variant = p.variant ?? "primary";
1913
- return /* @__PURE__ */ jsxs17(
1758
+ return /* @__PURE__ */ jsxs15(
1914
1759
  "div",
1915
1760
  {
1916
1761
  ...mark("SplitButton", p, variantMaterial[variant]),
1917
1762
  "data-ad-variant": variant,
1918
1763
  children: [
1919
- /* @__PURE__ */ jsx24(
1764
+ /* @__PURE__ */ jsx21(
1920
1765
  Button,
1921
1766
  {
1922
1767
  className: "ad-split-button-main",
@@ -1927,7 +1772,7 @@ var SplitButton = (p) => {
1927
1772
  children: p.children ?? p.label ?? tr2("\u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C")
1928
1773
  }
1929
1774
  ),
1930
- /* @__PURE__ */ jsx24(
1775
+ /* @__PURE__ */ jsx21(
1931
1776
  IconButton,
1932
1777
  {
1933
1778
  className: "ad-split-button-trigger",
@@ -1941,7 +1786,7 @@ var SplitButton = (p) => {
1941
1786
  onClick: () => setOpen((v) => !v)
1942
1787
  }
1943
1788
  ),
1944
- /* @__PURE__ */ jsx24(
1789
+ /* @__PURE__ */ jsx21(
1945
1790
  Menu,
1946
1791
  {
1947
1792
  open,
@@ -1956,132 +1801,9 @@ var SplitButton = (p) => {
1956
1801
  );
1957
1802
  };
1958
1803
 
1959
- // src/components/effects/ImageShine/ImageShine.tsx
1960
- import { useRef as useRef10 } from "react";
1961
- import { jsx as jsx25, jsxs as jsxs18 } from "react/jsx-runtime";
1962
- function ImageShine({ src, glow: glow3 = true, label, style, ...p }) {
1963
- const pauseRef = useRef10(null);
1964
- usePauseOffscreen(pauseRef);
1965
- return /* @__PURE__ */ jsxs18(
1966
- "span",
1967
- {
1968
- ...mark("ImageShine", p),
1969
- ref: pauseRef,
1970
- style: { ...style, "--ad-shine-src": `url("${src}")` },
1971
- "data-glow": glow3 || void 0,
1972
- role: label ? "img" : void 0,
1973
- "aria-label": label,
1974
- "aria-hidden": label ? void 0 : true,
1975
- children: [
1976
- glow3 && /* @__PURE__ */ jsx25("img", { className: "ad-image-shine-glow", src, alt: "", draggable: false }),
1977
- /* @__PURE__ */ jsx25("img", { className: "ad-image-shine-picture", src, alt: "", draggable: false }),
1978
- /* @__PURE__ */ jsx25("span", { className: "ad-image-shine-sweep", children: /* @__PURE__ */ jsx25("i", {}) })
1979
- ]
1980
- }
1981
- );
1982
- }
1983
-
1984
- // src/components/controls/ThemePicker/ThemePicker.tsx
1985
- import { jsx as jsx26, jsxs as jsxs19 } from "react/jsx-runtime";
1986
- var builtIn = {
1987
- ruby: "Ruby",
1988
- light: "Light",
1989
- green: "Green",
1990
- violet: "Violet"
1991
- };
1992
- function ThemePicker(p) {
1993
- const tr2 = useTr();
1994
- const first = p.options?.[0]?.value ?? "ruby";
1995
- const [value, setValue] = useControllable(p.value, p.defaultValue ?? first, p.onValueChange);
1996
- const options = p.options ?? Object.keys(builtIn).map((key) => ({
1997
- value: key,
1998
- label: builtIn[key],
1999
- color: themes[key][0]
2000
- }));
2001
- const active = Math.max(0, options.findIndex((option) => option.value === value));
2002
- return /* @__PURE__ */ jsx26(
2003
- "div",
2004
- {
2005
- ...mark("ThemePicker", p),
2006
- "data-gallery": p.options ? "" : void 0,
2007
- role: "radiogroup",
2008
- "aria-label": p.label ?? tr2("\u0422\u0435\u043C\u0430"),
2009
- onKeyDown: (event) => {
2010
- const buttons = [...event.currentTarget.querySelectorAll('[role="radio"]:not(:disabled)')];
2011
- const index = buttons.indexOf(document.activeElement);
2012
- const moves = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1, Home: -index, End: buttons.length - index - 1 };
2013
- const move = moves[event.key];
2014
- if (move === void 0 || !buttons.length) return;
2015
- event.preventDefault();
2016
- const next = buttons[(index + move + buttons.length) % buttons.length];
2017
- next.focus();
2018
- next.click();
2019
- },
2020
- children: options.map((option, index) => {
2021
- const chosen = option.value === value;
2022
- const radio = { role: "radio", "aria-checked": chosen, tabIndex: index === active ? 0 : -1, disabled: p.disabled, onClick: () => setValue(option.value) };
2023
- if (!p.options) return /* @__PURE__ */ jsxs19(Button, { size: p.size ?? "sm", ...radio, children: [
2024
- /* @__PURE__ */ jsx26("span", { className: "ad-theme-swatch", style: { background: option.color } }),
2025
- option.label
2026
- ] }, option.value);
2027
- return /* @__PURE__ */ jsxs19(
2028
- "button",
2029
- {
2030
- type: "button",
2031
- className: "ad-theme-card",
2032
- "aria-label": option.label,
2033
- ...radio,
2034
- style: option.color ? { "--ad-theme-card": option.color } : void 0,
2035
- children: [
2036
- /* @__PURE__ */ jsx26("span", { className: "ad-theme-card-preview", children: option.image && (chosen ? /* @__PURE__ */ jsx26(ImageShine, { src: option.image, glow: false }) : /* @__PURE__ */ jsx26("img", { src: option.image, alt: "", loading: "lazy" })) }),
2037
- /* @__PURE__ */ jsxs19("span", { className: "ad-theme-card-caption", children: [
2038
- /* @__PURE__ */ jsx26("strong", { children: option.label }),
2039
- option.description && /* @__PURE__ */ jsx26("span", { "aria-hidden": "true", children: option.description })
2040
- ] }),
2041
- chosen && /* @__PURE__ */ jsx26("span", { className: "ad-theme-card-check", "aria-hidden": "true", children: /* @__PURE__ */ jsx26(Icon, { name: "check" }) })
2042
- ]
2043
- },
2044
- option.value
2045
- );
2046
- })
2047
- }
2048
- );
2049
- }
2050
-
2051
- // src/components/controls/ToggleButton/ToggleButton.tsx
2052
- import { forwardRef as forwardRef2 } from "react";
2053
- var ToggleButton = /* @__PURE__ */ forwardRef2(function ToggleButton2({
2054
- checked,
2055
- defaultChecked = false,
2056
- onValueChange,
2057
- onClick,
2058
- ...p
2059
- }, ref) {
2060
- const [value, setValue] = useControllable(
2061
- checked,
2062
- defaultChecked,
2063
- onValueChange
2064
- );
2065
- return buttonView(
2066
- {
2067
- ...p,
2068
- ref,
2069
- "aria-label": p["aria-label"] ?? (!p.children ? p.label : void 0),
2070
- "aria-pressed": value,
2071
- children: p.children ?? (p.icon ? null : p.label),
2072
- label: void 0,
2073
- onClick: (e) => {
2074
- onClick?.(e);
2075
- if (!e.defaultPrevented) setValue(!value);
2076
- }
2077
- },
2078
- "ToggleButton"
2079
- );
2080
- });
2081
-
2082
1804
  // src/components/feedback/DataTable/DataTable.tsx
2083
- import { useEffect as useEffect3, useMemo as useMemo2, useRef as useRef11, useState as useState5 } from "react";
2084
- import { Fragment as Fragment4, jsx as jsx27, jsxs as jsxs20 } from "react/jsx-runtime";
1805
+ import { useEffect as useEffect3, useMemo as useMemo2, useRef as useRef9, useState as useState4 } from "react";
1806
+ import { Fragment as Fragment3, jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
2085
1807
  var sortStates = {
2086
1808
  asc: { next: "desc", aria: "ascending" },
2087
1809
  desc: { next: null, aria: "descending" }
@@ -2124,7 +1846,7 @@ function FilterEditor({
2124
1846
  onChange
2125
1847
  }) {
2126
1848
  const tr2 = useTr();
2127
- const [search, setSearch] = useState5("");
1849
+ const [search, setSearch] = useState4("");
2128
1850
  const text = filter?.kind === "text" ? filter.text : "";
2129
1851
  const chosen = filter?.kind === "values" ? filter.values : [];
2130
1852
  const query = (kind === "text" ? text : search).trim().toLocaleLowerCase();
@@ -2133,13 +1855,13 @@ function FilterEditor({
2133
1855
  (value) => (!query || value.toLocaleLowerCase().includes(query)) && !excluded.has(value)
2134
1856
  ).slice(0, 100);
2135
1857
  if (kind === "text")
2136
- return /* @__PURE__ */ jsx27(
1858
+ return /* @__PURE__ */ jsx22(
2137
1859
  Autocomplete,
2138
1860
  {
2139
1861
  size: "sm",
2140
1862
  autoFocus: true,
2141
1863
  placeholder: tr2("\u0421\u043E\u0434\u0435\u0440\u0436\u0438\u0442\u2026"),
2142
- startAdornment: /* @__PURE__ */ jsx27(Icon, { name: "search" }),
1864
+ startAdornment: /* @__PURE__ */ jsx22(Icon, { name: "search" }),
2143
1865
  clearable: true,
2144
1866
  options: suggestions,
2145
1867
  value: text,
@@ -2152,19 +1874,19 @@ function FilterEditor({
2152
1874
  const next = { ...range, [part2]: value === "" ? void 0 : value };
2153
1875
  onChange(next.min === void 0 && next.max === void 0 ? void 0 : next);
2154
1876
  };
2155
- return /* @__PURE__ */ jsxs20("div", { className: "ad-data-table-range", children: [
2156
- /* @__PURE__ */ jsx27(NumberField, { size: "sm", label: tr2("\u041E\u0442"), controls: false, value: range.min ?? "", onValueChange: (v) => set("min", v) }),
2157
- /* @__PURE__ */ jsx27(NumberField, { size: "sm", label: tr2("\u0414\u043E"), controls: false, value: range.max ?? "", onValueChange: (v) => set("max", v) })
1877
+ return /* @__PURE__ */ jsxs16("div", { className: "ad-data-table-range", children: [
1878
+ /* @__PURE__ */ jsx22(NumberField, { size: "sm", label: tr2("\u041E\u0442"), controls: false, value: range.min ?? "", onValueChange: (v) => set("min", v) }),
1879
+ /* @__PURE__ */ jsx22(NumberField, { size: "sm", label: tr2("\u0414\u043E"), controls: false, value: range.max ?? "", onValueChange: (v) => set("max", v) })
2158
1880
  ] });
2159
1881
  }
2160
- return /* @__PURE__ */ jsxs20("div", { className: "ad-data-table-values", children: [
2161
- /* @__PURE__ */ jsx27(
1882
+ return /* @__PURE__ */ jsxs16("div", { className: "ad-data-table-values", children: [
1883
+ /* @__PURE__ */ jsx22(
2162
1884
  Autocomplete,
2163
1885
  {
2164
1886
  size: "sm",
2165
1887
  autoFocus: true,
2166
1888
  placeholder: tr2("\u041D\u0430\u0439\u0442\u0438 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435\u2026"),
2167
- startAdornment: /* @__PURE__ */ jsx27(Icon, { name: "search" }),
1889
+ startAdornment: /* @__PURE__ */ jsx22(Icon, { name: "search" }),
2168
1890
  options: suggestions,
2169
1891
  value: search,
2170
1892
  onValueChange: setSearch,
@@ -2174,9 +1896,9 @@ function FilterEditor({
2174
1896
  }
2175
1897
  }
2176
1898
  ),
2177
- chosen.length > 0 && /* @__PURE__ */ jsx27("div", { className: "ad-data-table-values-list", children: chosen.map((value) => /* @__PURE__ */ jsxs20("span", { className: "ad-data-table-chip", children: [
1899
+ chosen.length > 0 && /* @__PURE__ */ jsx22("div", { className: "ad-data-table-values-list", children: chosen.map((value) => /* @__PURE__ */ jsxs16("span", { className: "ad-data-table-chip", children: [
2178
1900
  value || "\u2014",
2179
- /* @__PURE__ */ jsx27(
1901
+ /* @__PURE__ */ jsx22(
2180
1902
  "button",
2181
1903
  {
2182
1904
  type: "button",
@@ -2185,7 +1907,7 @@ function FilterEditor({
2185
1907
  const next = chosen.filter((item) => item !== value);
2186
1908
  onChange(next.length ? { kind: "values", values: next } : void 0);
2187
1909
  },
2188
- children: /* @__PURE__ */ jsx27(Icon, { name: "close" })
1910
+ children: /* @__PURE__ */ jsx22(Icon, { name: "close" })
2189
1911
  }
2190
1912
  )
2191
1913
  ] }, value)) })
@@ -2200,15 +1922,15 @@ function ColumnFilter({
2200
1922
  onChange
2201
1923
  }) {
2202
1924
  const tr2 = useTr();
2203
- const [open, setOpen] = useState5(false);
2204
- const anchor = useRef11(null);
1925
+ const [open, setOpen] = useState4(false);
1926
+ const anchor = useRef9(null);
2205
1927
  const title = typeof column.title === "string" ? column.title : column.key;
2206
1928
  const suggestions = useMemo2(
2207
1929
  () => open && kind !== "range" && !options.length ? distinct(column, rows) : options,
2208
1930
  [open, kind, column, rows, options]
2209
1931
  );
2210
- return /* @__PURE__ */ jsxs20(Fragment4, { children: [
2211
- /* @__PURE__ */ jsx27(
1932
+ return /* @__PURE__ */ jsxs16(Fragment3, { children: [
1933
+ /* @__PURE__ */ jsx22(
2212
1934
  IconButton,
2213
1935
  {
2214
1936
  ref: anchor,
@@ -2222,7 +1944,7 @@ function ColumnFilter({
2222
1944
  onClick: () => setOpen((v) => !v)
2223
1945
  }
2224
1946
  ),
2225
- /* @__PURE__ */ jsx27(Popover, { open, onOpenChange: setOpen, anchorRef: anchor, label: tr2("\u0424\u0438\u043B\u044C\u0442\u0440: {title}", { title }), className: "ad-data-table-filter-popover", children: /* @__PURE__ */ jsx27(FilterEditor, { kind, options: suggestions, filter, onChange }) })
1947
+ /* @__PURE__ */ jsx22(Popover, { open, onOpenChange: setOpen, anchorRef: anchor, label: tr2("\u0424\u0438\u043B\u044C\u0442\u0440: {title}", { title }), className: "ad-data-table-filter-popover", children: /* @__PURE__ */ jsx22(FilterEditor, { kind, options: suggestions, filter, onChange }) })
2226
1948
  ] });
2227
1949
  }
2228
1950
  var field = (row, key) => Object.hasOwn(row, key) ? row[key] : void 0;
@@ -2344,8 +2066,8 @@ function DataTable({
2344
2066
  defaultSelected,
2345
2067
  onSelectionChange
2346
2068
  );
2347
- const generatedKeys = useRef11(/* @__PURE__ */ new WeakMap());
2348
- const nextGeneratedKey = useRef11(0);
2069
+ const generatedKeys = useRef9(/* @__PURE__ */ new WeakMap());
2070
+ const nextGeneratedKey = useRef9(0);
2349
2071
  const keyed = useMemo2(() => {
2350
2072
  const identify = (row, index) => {
2351
2073
  if (rowKey) return rowKey(row, index);
@@ -2364,17 +2086,17 @@ function DataTable({
2364
2086
  return rows.map((row, index) => ({ row, index, key: identify(row, index) }));
2365
2087
  }, [rows, rowKey]);
2366
2088
  const [query, setQuery] = useControllable(controlledQuery, defaultQuery, onQueryChange);
2367
- const [hiddenColumns, setHiddenColumns] = useState5([]);
2089
+ const [hiddenColumns, setHiddenColumns] = useState4([]);
2368
2090
  const [columnOrder, setColumnOrder] = useControllable(controlledColumnOrder, defaultColumnOrder, onColumnOrderChange);
2369
2091
  const [columnPinning, setColumnPinning] = useControllable(controlledColumnPinning, defaultColumnPinning, onColumnPinningChange);
2370
2092
  const [expandedRows, setExpandedRows] = useControllable(controlledExpandedRows, defaultExpandedRows, onExpandedRowsChange);
2371
2093
  const [dense, setDense] = useControllable(controlledDense, false, onDenseChange);
2372
- const [columnWidths, setColumnWidths] = useState5({});
2094
+ const [columnWidths, setColumnWidths] = useState4({});
2373
2095
  const customWidth = (key) => Object.hasOwn(columnWidths, key) ? columnWidths[key] : void 0;
2374
- const [containerWidth, setContainerWidth] = useState5(0);
2375
- const resizing = useRef11(null);
2376
- const [columnsOpen, setColumnsOpen] = useState5(false);
2377
- const columnsAnchor = useRef11(null);
2096
+ const [containerWidth, setContainerWidth] = useState4(0);
2097
+ const resizing = useRef9(null);
2098
+ const [columnsOpen, setColumnsOpen] = useState4(false);
2099
+ const columnsAnchor = useRef9(null);
2378
2100
  const orderedCols = useMemo2(() => {
2379
2101
  const byKey = new Map(cols.map((column) => [column.key, column]));
2380
2102
  return [.../* @__PURE__ */ new Set([...columnOrder, ...cols.map((column) => column.key)])].flatMap((key) => byKey.has(key) ? [byKey.get(key)] : []);
@@ -2407,7 +2129,7 @@ function DataTable({
2407
2129
  }, [pageSize]);
2408
2130
  const [filters, setFilters] = useControllable(controlledFilters, defaultFilters, onFiltersChange);
2409
2131
  const [groupBy, setGroupBy] = useControllable(controlledGroupBy, defaultGroupBy, onGroupByChange);
2410
- const [collapsed, setCollapsed] = useState5(/* @__PURE__ */ new Set());
2132
+ const [collapsed, setCollapsed] = useState4(/* @__PURE__ */ new Set());
2411
2133
  const setFilter = (key, filter) => {
2412
2134
  const next = filter ? { ...filters, [key]: filter } : { ...filters };
2413
2135
  if (!filter) delete next[key];
@@ -2508,15 +2230,15 @@ function DataTable({
2508
2230
  return side ? { [side]: `var(--ad-table-pin-${side}, ${preceding.reduce((sum, key) => sum + widthOf(key), side === "left" ? leading * 40 : renderRowActions ? actionWidth : 0)}px)` } : {};
2509
2231
  };
2510
2232
  const pinnedSide = (key) => pinning.left.includes(key) ? "left" : pinning.right.includes(key) ? "right" : void 0;
2511
- const groupRow = (group, viewIndex) => /* @__PURE__ */ jsx27("tr", { "data-virtual-key": `g:${group.name}`, "aria-rowindex": virtualize ? viewIndex + 2 : void 0, className: "ad-data-table-group", "data-collapsed": collapsed.has(group.name) || void 0, children: /* @__PURE__ */ jsx27("td", { colSpan: span, children: /* @__PURE__ */ jsxs20("div", { className: "ad-data-table-group-content", children: [
2512
- /* @__PURE__ */ jsxs20("button", { type: "button", className: "ad-data-table-group-toggle", "aria-expanded": !collapsed.has(group.name), onClick: () => toggleGroup(group.name), children: [
2513
- /* @__PURE__ */ jsx27(Icon, { name: "chevron" }),
2514
- /* @__PURE__ */ jsx27("span", { children: group.name || "\u2014" }),
2515
- /* @__PURE__ */ jsx27("span", { className: "ad-data-table-group-count", children: group.items.length })
2233
+ const groupRow = (group, viewIndex) => /* @__PURE__ */ jsx22("tr", { "data-virtual-key": `g:${group.name}`, "aria-rowindex": virtualize ? viewIndex + 2 : void 0, className: "ad-data-table-group", "data-collapsed": collapsed.has(group.name) || void 0, children: /* @__PURE__ */ jsx22("td", { colSpan: span, children: /* @__PURE__ */ jsxs16("div", { className: "ad-data-table-group-content", children: [
2234
+ /* @__PURE__ */ jsxs16("button", { type: "button", className: "ad-data-table-group-toggle", "aria-expanded": !collapsed.has(group.name), onClick: () => toggleGroup(group.name), children: [
2235
+ /* @__PURE__ */ jsx22(Icon, { name: "chevron" }),
2236
+ /* @__PURE__ */ jsx22("span", { children: group.name || "\u2014" }),
2237
+ /* @__PURE__ */ jsx22("span", { className: "ad-data-table-group-count", children: group.items.length })
2516
2238
  ] }),
2517
- /* @__PURE__ */ jsx27("div", { className: "ad-data-table-group-summaries", children: visibleCols.filter((column) => column.aggregate && column.key !== visibleCols[0]?.key).map((column) => /* @__PURE__ */ jsxs20("span", { className: "ad-data-table-group-summary", children: [
2518
- /* @__PURE__ */ jsx27("span", { children: column.title }),
2519
- /* @__PURE__ */ jsx27("strong", { children: group.summaries.get(column.key) })
2239
+ /* @__PURE__ */ jsx22("div", { className: "ad-data-table-group-summaries", children: visibleCols.filter((column) => column.aggregate && column.key !== visibleCols[0]?.key).map((column) => /* @__PURE__ */ jsxs16("span", { className: "ad-data-table-group-summary", children: [
2240
+ /* @__PURE__ */ jsx22("span", { children: column.title }),
2241
+ /* @__PURE__ */ jsx22("strong", { children: group.summaries.get(column.key) })
2520
2242
  ] }, column.key)) })
2521
2243
  ] }) }) }, `g:${group.name}`);
2522
2244
  const chosen = useMemo2(() => new Set(selection), [selection]);
@@ -2574,7 +2296,7 @@ function DataTable({
2574
2296
  });
2575
2297
  }, [visible, groupColumn, groupNames, groups, collapsed, current, perPage, expanded, !!renderRowDetails]);
2576
2298
  const viewIndexes = useMemo2(() => new Map(viewItems.map((item, index) => [item.key, index])), [viewItems]);
2577
- const scrollRef = useRef11(null);
2299
+ const scrollRef = useRef9(null);
2578
2300
  useEffect3(() => {
2579
2301
  const element = scrollRef.current;
2580
2302
  if (!pinned || !element) return;
@@ -2585,15 +2307,15 @@ function DataTable({
2585
2307
  observer.observe(element);
2586
2308
  return () => observer.disconnect();
2587
2309
  }, [pinned]);
2588
- const bodyRef = useRef11(null);
2589
- const headerRef = useRef11(null);
2590
- const heights = useRef11(/* @__PURE__ */ new Map());
2591
- const headerHeight = useRef11(44);
2592
- const frameRef = useRef11(0);
2593
- const measuredWidth = useRef11(0);
2594
- const geometryChanged = useRef11(false);
2595
- const [geometryVersion, setGeometryVersion] = useState5(0);
2596
- const [viewport, setViewport] = useState5({ top: 0, height: typeof maxHeight === "number" ? maxHeight : 480, width: 0 });
2310
+ const bodyRef = useRef9(null);
2311
+ const headerRef = useRef9(null);
2312
+ const heights = useRef9(/* @__PURE__ */ new Map());
2313
+ const headerHeight = useRef9(44);
2314
+ const frameRef = useRef9(0);
2315
+ const measuredWidth = useRef9(0);
2316
+ const geometryChanged = useRef9(false);
2317
+ const [geometryVersion, setGeometryVersion] = useState4(0);
2318
+ const [viewport, setViewport] = useState4({ top: 0, height: typeof maxHeight === "number" ? maxHeight : 480, width: 0 });
2597
2319
  const scheduleViewport = () => {
2598
2320
  if (!virtualize || frameRef.current) return;
2599
2321
  frameRef.current = requestAnimationFrame(() => {
@@ -2654,8 +2376,8 @@ function DataTable({
2654
2376
  }
2655
2377
  }, [virtualize, viewItems, virtualHeight, viewport.height]);
2656
2378
  const measurementIdentity = JSON.stringify([dense, estimatedRowHeight, columnWidths, visibleCols.map((column) => column.key)]);
2657
- const previousMeasurementIdentity = useRef11(measurementIdentity);
2658
- const previousOffsets = useRef11(offsets);
2379
+ const previousMeasurementIdentity = useRef9(measurementIdentity);
2380
+ const previousOffsets = useRef9(offsets);
2659
2381
  useEffect3(() => {
2660
2382
  if (previousMeasurementIdentity.current === measurementIdentity) return;
2661
2383
  previousMeasurementIdentity.current = measurementIdentity;
@@ -2716,12 +2438,12 @@ function DataTable({
2716
2438
  }, [virtualize]);
2717
2439
  const toggleDetails = (key) => setExpandedRows(expanded.has(key) ? expandedRows.filter((item) => item !== key) : [...expandedRows, key]);
2718
2440
  const specialStyle = (index) => pinning.left.length ? { position: "sticky", left: `var(--ad-table-pin-left, ${index * 40}px)`, zIndex: 2 } : {};
2719
- const body = loading ? Array.from({ length: Math.min(perPage ?? 4, 6) }, (_, i) => /* @__PURE__ */ jsx27("tr", { className: "ad-data-table-loading", children: Array.from({ length: span }, (__, j) => /* @__PURE__ */ jsx27("td", { children: /* @__PURE__ */ jsx27("i", {}) }, j)) }, i)) : visible.length === 0 && !groups.some((group) => collapsed.has(group.name)) ? /* @__PURE__ */ jsx27("tr", { children: /* @__PURE__ */ jsx27("td", { className: "ad-data-table-empty", colSpan: span, children: empty }) }) : viewItems.slice(start, end).map((entry, indexInWindow) => {
2441
+ const body = loading ? Array.from({ length: Math.min(perPage ?? 4, 6) }, (_, i) => /* @__PURE__ */ jsx22("tr", { className: "ad-data-table-loading", children: Array.from({ length: span }, (__, j) => /* @__PURE__ */ jsx22("td", { children: /* @__PURE__ */ jsx22("i", {}) }, j)) }, i)) : visible.length === 0 && !groups.some((group) => collapsed.has(group.name)) ? /* @__PURE__ */ jsx22("tr", { children: /* @__PURE__ */ jsx22("td", { className: "ad-data-table-empty", colSpan: span, children: empty }) }) : viewItems.slice(start, end).map((entry, indexInWindow) => {
2720
2442
  const viewIndex = start + indexInWindow;
2721
2443
  if (entry.kind === "group") return groupRow(entry.group, viewIndex);
2722
2444
  const { row, index, key } = entry.item;
2723
- if (entry.kind === "details") return /* @__PURE__ */ jsx27("tr", { "data-virtual-key": entry.key, "aria-rowindex": virtualize ? viewIndex + 2 : void 0, "data-details-key": key, className: "ad-data-table-details", children: /* @__PURE__ */ jsx27("td", { colSpan: span, children: renderRowDetails?.(row, index) }) }, entry.key);
2724
- return /* @__PURE__ */ jsxs20(
2445
+ if (entry.kind === "details") return /* @__PURE__ */ jsx22("tr", { "data-virtual-key": entry.key, "aria-rowindex": virtualize ? viewIndex + 2 : void 0, "data-details-key": key, className: "ad-data-table-details", children: /* @__PURE__ */ jsx22("td", { colSpan: span, children: renderRowDetails?.(row, index) }) }, entry.key);
2446
+ return /* @__PURE__ */ jsxs16(
2725
2447
  "tr",
2726
2448
  {
2727
2449
  "data-virtual-key": entry.key,
@@ -2741,7 +2463,7 @@ function DataTable({
2741
2463
  }
2742
2464
  } : void 0,
2743
2465
  children: [
2744
- renderRowDetails && /* @__PURE__ */ jsx27("td", { className: "ad-data-table-expand", "data-label": tr2("\u041F\u043E\u0434\u0440\u043E\u0431\u043D\u043E\u0441\u0442\u0438"), style: specialStyle(0), onClick: (event) => event.stopPropagation(), children: /* @__PURE__ */ jsx27(
2466
+ renderRowDetails && /* @__PURE__ */ jsx22("td", { className: "ad-data-table-expand", "data-label": tr2("\u041F\u043E\u0434\u0440\u043E\u0431\u043D\u043E\u0441\u0442\u0438"), style: specialStyle(0), onClick: (event) => event.stopPropagation(), children: /* @__PURE__ */ jsx22(
2745
2467
  IconButton,
2746
2468
  {
2747
2469
  size: "xs",
@@ -2753,25 +2475,25 @@ function DataTable({
2753
2475
  onClick: () => toggleDetails(key)
2754
2476
  }
2755
2477
  ) }),
2756
- rowNumbers && /* @__PURE__ */ jsx27("td", { className: "ad-data-table-row-number", style: specialStyle(Number(!!renderRowDetails)), "data-label": tr2("\u041D\u043E\u043C\u0435\u0440 \u0441\u0442\u0440\u043E\u043A\u0438"), children: entry.position + 1 }),
2757
- selectable && /* @__PURE__ */ jsx27(
2478
+ rowNumbers && /* @__PURE__ */ jsx22("td", { className: "ad-data-table-row-number", style: specialStyle(Number(!!renderRowDetails)), "data-label": tr2("\u041D\u043E\u043C\u0435\u0440 \u0441\u0442\u0440\u043E\u043A\u0438"), children: entry.position + 1 }),
2479
+ selectable && /* @__PURE__ */ jsx22(
2758
2480
  "td",
2759
2481
  {
2760
2482
  className: "ad-data-table-check",
2761
2483
  style: specialStyle(Number(!!renderRowDetails) + Number(rowNumbers)),
2762
2484
  onClick: (e) => e.stopPropagation(),
2763
- children: /* @__PURE__ */ jsx27(
2485
+ children: /* @__PURE__ */ jsx22(
2764
2486
  Checkbox,
2765
2487
  {
2766
2488
  size: "sm",
2767
2489
  checked: chosen.has(key),
2768
2490
  onValueChange: () => toggle(key),
2769
- label: /* @__PURE__ */ jsx27("span", { className: "ad-sr-only", children: tr2("\u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0441\u0442\u0440\u043E\u043A\u0443") })
2491
+ label: /* @__PURE__ */ jsx22("span", { className: "ad-sr-only", children: tr2("\u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0441\u0442\u0440\u043E\u043A\u0443") })
2770
2492
  }
2771
2493
  )
2772
2494
  }
2773
2495
  ),
2774
- visibleCols.map((column) => /* @__PURE__ */ jsx27(
2496
+ visibleCols.map((column) => /* @__PURE__ */ jsx22(
2775
2497
  "td",
2776
2498
  {
2777
2499
  "data-align": column.align,
@@ -2784,13 +2506,13 @@ function DataTable({
2784
2506
  },
2785
2507
  column.key
2786
2508
  )),
2787
- renderRowActions && /* @__PURE__ */ jsx27("td", { className: "ad-data-table-actions", "data-label": tr2("\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044F"), style: pinning.right.length ? { position: "sticky", right: "var(--ad-table-pin-right, 0px)", zIndex: 2 } : void 0, onClick: (event) => event.stopPropagation(), children: renderRowActions(row, index) })
2509
+ renderRowActions && /* @__PURE__ */ jsx22("td", { className: "ad-data-table-actions", "data-label": tr2("\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044F"), style: pinning.right.length ? { position: "sticky", right: "var(--ad-table-pin-right, 0px)", zIndex: 2 } : void 0, onClick: (event) => event.stopPropagation(), children: renderRowActions(row, index) })
2788
2510
  ]
2789
2511
  },
2790
2512
  entry.key
2791
2513
  );
2792
2514
  });
2793
- return /* @__PURE__ */ jsxs20(
2515
+ return /* @__PURE__ */ jsxs16(
2794
2516
  "div",
2795
2517
  {
2796
2518
  ...mark("DataTable", p),
@@ -2801,9 +2523,9 @@ function DataTable({
2801
2523
  "data-pinned": pinned || void 0,
2802
2524
  "aria-busy": loading || void 0,
2803
2525
  children: [
2804
- (caption || searchable || groupable || densityToggle || cols.length > 1 || viewChanged) && /* @__PURE__ */ jsxs20("div", { className: "ad-data-table-bar", children: [
2805
- caption && /* @__PURE__ */ jsx27("strong", { children: caption }),
2806
- groupable && /* @__PURE__ */ jsx27(
2526
+ (caption || searchable || groupable || densityToggle || cols.length > 1 || viewChanged) && /* @__PURE__ */ jsxs16("div", { className: "ad-data-table-bar", children: [
2527
+ caption && /* @__PURE__ */ jsx22("strong", { children: caption }),
2528
+ groupable && /* @__PURE__ */ jsx22(
2807
2529
  Select,
2808
2530
  {
2809
2531
  size: "sm",
@@ -2822,7 +2544,7 @@ function DataTable({
2822
2544
  ]
2823
2545
  }
2824
2546
  ),
2825
- searchable && /* @__PURE__ */ jsx27(
2547
+ searchable && /* @__PURE__ */ jsx22(
2826
2548
  TextField,
2827
2549
  {
2828
2550
  size: "sm",
@@ -2832,13 +2554,13 @@ function DataTable({
2832
2554
  setQuery(value);
2833
2555
  setPage(0);
2834
2556
  },
2835
- startAdornment: /* @__PURE__ */ jsx27(Icon, { name: "search" }),
2557
+ startAdornment: /* @__PURE__ */ jsx22(Icon, { name: "search" }),
2836
2558
  clearable: true,
2837
2559
  "aria-label": tr2("\u041F\u043E\u0438\u0441\u043A \u043F\u043E \u0442\u0430\u0431\u043B\u0438\u0446\u0435")
2838
2560
  }
2839
2561
  ),
2840
- /* @__PURE__ */ jsxs20("span", { className: "ad-data-table-tools", children: [
2841
- viewChanged && /* @__PURE__ */ jsx27(
2562
+ /* @__PURE__ */ jsxs16("span", { className: "ad-data-table-tools", children: [
2563
+ viewChanged && /* @__PURE__ */ jsx22(
2842
2564
  IconButton,
2843
2565
  {
2844
2566
  size: "sm",
@@ -2848,9 +2570,9 @@ function DataTable({
2848
2570
  onClick: resetView
2849
2571
  }
2850
2572
  ),
2851
- densityToggle && /* @__PURE__ */ jsx27(IconButton, { size: "sm", variant: "ghost", icon: "list", label: tr2("\u041A\u043E\u043C\u043F\u0430\u043A\u0442\u043D\u044B\u0435 \u0441\u0442\u0440\u043E\u043A\u0438"), "aria-pressed": dense, onClick: () => setDense(!dense) }),
2852
- cols.length > 1 && /* @__PURE__ */ jsxs20(Fragment4, { children: [
2853
- /* @__PURE__ */ jsx27(
2573
+ densityToggle && /* @__PURE__ */ jsx22(IconButton, { size: "sm", variant: "ghost", icon: "list", label: tr2("\u041A\u043E\u043C\u043F\u0430\u043A\u0442\u043D\u044B\u0435 \u0441\u0442\u0440\u043E\u043A\u0438"), "aria-pressed": dense, onClick: () => setDense(!dense) }),
2574
+ cols.length > 1 && /* @__PURE__ */ jsxs16(Fragment3, { children: [
2575
+ /* @__PURE__ */ jsx22(
2854
2576
  IconButton,
2855
2577
  {
2856
2578
  ref: columnsAnchor,
@@ -2862,8 +2584,8 @@ function DataTable({
2862
2584
  onClick: () => setColumnsOpen((open) => !open)
2863
2585
  }
2864
2586
  ),
2865
- /* @__PURE__ */ jsx27(Popover, { open: columnsOpen, onOpenChange: setColumnsOpen, anchorRef: columnsAnchor, label: tr2("\u0421\u0442\u043E\u043B\u0431\u0446\u044B"), children: /* @__PURE__ */ jsx27("div", { className: "ad-data-table-columns", children: orderedCols.map((column, index) => /* @__PURE__ */ jsxs20("div", { className: "ad-data-table-column-option", children: [
2866
- /* @__PURE__ */ jsx27(
2587
+ /* @__PURE__ */ jsx22(Popover, { open: columnsOpen, onOpenChange: setColumnsOpen, anchorRef: columnsAnchor, label: tr2("\u0421\u0442\u043E\u043B\u0431\u0446\u044B"), children: /* @__PURE__ */ jsx22("div", { className: "ad-data-table-columns", children: orderedCols.map((column, index) => /* @__PURE__ */ jsxs16("div", { className: "ad-data-table-column-option", children: [
2588
+ /* @__PURE__ */ jsx22(
2867
2589
  Checkbox,
2868
2590
  {
2869
2591
  size: "sm",
@@ -2872,7 +2594,7 @@ function DataTable({
2872
2594
  onValueChange: (shown) => setHiddenColumns((current2) => shown ? current2.filter((key) => key !== column.key) : visibleCols.length > 1 ? [...current2, column.key] : current2)
2873
2595
  }
2874
2596
  ),
2875
- /* @__PURE__ */ jsx27("span", { className: "ad-data-table-column-actions", children: [
2597
+ /* @__PURE__ */ jsx22("span", { className: "ad-data-table-column-actions", children: [
2876
2598
  { label: "\u041F\u0435\u0440\u0435\u043C\u0435\u0441\u0442\u0438\u0442\u044C \u0432\u043B\u0435\u0432\u043E: {title}", icon: "prev", disabled: index === 0, action: () => {
2877
2599
  const next = orderedCols.map((column2) => column2.key);
2878
2600
  [next[index - 1], next[index]] = [next[index], next[index - 1]];
@@ -2891,7 +2613,7 @@ function DataTable({
2891
2613
  right: pinning.right.includes(column.key) ? pinning.right.filter((key) => key !== column.key) : [...pinning.right, column.key],
2892
2614
  left: pinning.left.filter((key) => key !== column.key)
2893
2615
  }) }
2894
- ].map((action) => /* @__PURE__ */ jsx27(
2616
+ ].map((action) => /* @__PURE__ */ jsx22(
2895
2617
  IconButton,
2896
2618
  {
2897
2619
  size: "xs",
@@ -2906,7 +2628,7 @@ function DataTable({
2906
2628
  )) })
2907
2629
  ] }, column.key)) }) })
2908
2630
  ] }),
2909
- /* @__PURE__ */ jsx27(
2631
+ /* @__PURE__ */ jsx22(
2910
2632
  IconButton,
2911
2633
  {
2912
2634
  size: "sm",
@@ -2925,38 +2647,38 @@ function DataTable({
2925
2647
  )
2926
2648
  ] })
2927
2649
  ] }),
2928
- activeFilters.length > 0 && /* @__PURE__ */ jsxs20("div", { className: "ad-data-table-chips", children: [
2929
- activeFilters.map((column) => /* @__PURE__ */ jsxs20("span", { className: "ad-data-table-chip", children: [
2930
- /* @__PURE__ */ jsx27("strong", { children: column.title }),
2650
+ activeFilters.length > 0 && /* @__PURE__ */ jsxs16("div", { className: "ad-data-table-chips", children: [
2651
+ activeFilters.map((column) => /* @__PURE__ */ jsxs16("span", { className: "ad-data-table-chip", children: [
2652
+ /* @__PURE__ */ jsx22("strong", { children: column.title }),
2931
2653
  " ",
2932
2654
  describe(filters[column.key], tr2),
2933
- /* @__PURE__ */ jsx27("button", { type: "button", "aria-label": tr2("\u0423\u0431\u0440\u0430\u0442\u044C \u0444\u0438\u043B\u044C\u0442\u0440"), onClick: () => setFilter(column.key, void 0), children: /* @__PURE__ */ jsx27(Icon, { name: "close" }) })
2655
+ /* @__PURE__ */ jsx22("button", { type: "button", "aria-label": tr2("\u0423\u0431\u0440\u0430\u0442\u044C \u0444\u0438\u043B\u044C\u0442\u0440"), onClick: () => setFilter(column.key, void 0), children: /* @__PURE__ */ jsx22(Icon, { name: "close" }) })
2934
2656
  ] }, column.key)),
2935
- /* @__PURE__ */ jsx27(Button, { size: "xs", variant: "ghost", icon: "reset", onClick: () => {
2657
+ /* @__PURE__ */ jsx22(Button, { size: "xs", variant: "ghost", icon: "reset", onClick: () => {
2936
2658
  setFilters({});
2937
2659
  setPage(0);
2938
2660
  }, children: tr2("\u0421\u0431\u0440\u043E\u0441\u0438\u0442\u044C \u0444\u0438\u043B\u044C\u0442\u0440\u044B") })
2939
2661
  ] }),
2940
- /* @__PURE__ */ jsx27(
2662
+ /* @__PURE__ */ jsx22(
2941
2663
  "div",
2942
2664
  {
2943
2665
  ref: scrollRef,
2944
2666
  className: "ad-data-table-scroll",
2945
2667
  onScroll: virtualize ? scheduleViewport : void 0,
2946
2668
  style: { maxHeight: maxHeight ?? (virtualize ? 480 : void 0), height: virtualize ? maxHeight ?? 480 : void 0 },
2947
- children: /* @__PURE__ */ jsxs20("table", { "aria-rowcount": knownTotal ? (virtualize ? viewItems.length : total + viewItems.length - visible.length) + 1 : -1, style: pinned ? { width: visibleCols.reduce((sum, column) => sum + widthOf(column.key), leading * 40 + (renderRowActions ? actionWidth : 0)) } : void 0, children: [
2948
- /* @__PURE__ */ jsxs20("colgroup", { children: [
2949
- Array.from({ length: leading }, (_, index) => /* @__PURE__ */ jsx27("col", { style: { width: 40 } }, `leading:${index}`)),
2950
- visibleCols.map((column) => /* @__PURE__ */ jsx27("col", { style: { width: columnWidth(column) } }, column.key)),
2951
- renderRowActions && /* @__PURE__ */ jsx27("col", { style: { width: actionWidth } })
2669
+ children: /* @__PURE__ */ jsxs16("table", { "aria-rowcount": knownTotal ? (virtualize ? viewItems.length : total + viewItems.length - visible.length) + 1 : -1, style: pinned ? { width: visibleCols.reduce((sum, column) => sum + widthOf(column.key), leading * 40 + (renderRowActions ? actionWidth : 0)) } : void 0, children: [
2670
+ /* @__PURE__ */ jsxs16("colgroup", { children: [
2671
+ Array.from({ length: leading }, (_, index) => /* @__PURE__ */ jsx22("col", { style: { width: 40 } }, `leading:${index}`)),
2672
+ visibleCols.map((column) => /* @__PURE__ */ jsx22("col", { style: { width: columnWidth(column) } }, column.key)),
2673
+ renderRowActions && /* @__PURE__ */ jsx22("col", { style: { width: actionWidth } })
2952
2674
  ] }),
2953
- /* @__PURE__ */ jsx27("thead", { ref: headerRef, children: /* @__PURE__ */ jsxs20("tr", { children: [
2954
- renderRowDetails && /* @__PURE__ */ jsx27("th", { className: "ad-data-table-expand", style: specialStyle(0), children: /* @__PURE__ */ jsx27("span", { className: "ad-sr-only", children: tr2("\u041F\u043E\u0434\u0440\u043E\u0431\u043D\u043E\u0441\u0442\u0438") }) }),
2955
- rowNumbers && /* @__PURE__ */ jsxs20("th", { className: "ad-data-table-row-number", style: specialStyle(Number(!!renderRowDetails)), scope: "col", children: [
2956
- /* @__PURE__ */ jsx27("span", { className: "ad-sr-only", children: tr2("\u041D\u043E\u043C\u0435\u0440 \u0441\u0442\u0440\u043E\u043A\u0438") }),
2675
+ /* @__PURE__ */ jsx22("thead", { ref: headerRef, children: /* @__PURE__ */ jsxs16("tr", { children: [
2676
+ renderRowDetails && /* @__PURE__ */ jsx22("th", { className: "ad-data-table-expand", style: specialStyle(0), children: /* @__PURE__ */ jsx22("span", { className: "ad-sr-only", children: tr2("\u041F\u043E\u0434\u0440\u043E\u0431\u043D\u043E\u0441\u0442\u0438") }) }),
2677
+ rowNumbers && /* @__PURE__ */ jsxs16("th", { className: "ad-data-table-row-number", style: specialStyle(Number(!!renderRowDetails)), scope: "col", children: [
2678
+ /* @__PURE__ */ jsx22("span", { className: "ad-sr-only", children: tr2("\u041D\u043E\u043C\u0435\u0440 \u0441\u0442\u0440\u043E\u043A\u0438") }),
2957
2679
  "#"
2958
2680
  ] }),
2959
- selectable && /* @__PURE__ */ jsx27("th", { className: "ad-data-table-check", style: specialStyle(Number(!!renderRowDetails) + Number(rowNumbers)), children: /* @__PURE__ */ jsx27(
2681
+ selectable && /* @__PURE__ */ jsx22("th", { className: "ad-data-table-check", style: specialStyle(Number(!!renderRowDetails) + Number(rowNumbers)), children: /* @__PURE__ */ jsx22(
2960
2682
  Checkbox,
2961
2683
  {
2962
2684
  size: "sm",
@@ -2964,7 +2686,7 @@ function DataTable({
2964
2686
  indeterminate: someChosen,
2965
2687
  onValueChange: toggleAll,
2966
2688
  "aria-label": tr2("\u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0432\u0441\u0435"),
2967
- label: /* @__PURE__ */ jsx27("span", { className: "ad-data-table-select-all-label", children: tr2("\u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0432\u0441\u0435") })
2689
+ label: /* @__PURE__ */ jsx22("span", { className: "ad-data-table-select-all-label", children: tr2("\u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0432\u0441\u0435") })
2968
2690
  }
2969
2691
  ) }),
2970
2692
  visibleCols.map((column) => {
@@ -2974,7 +2696,7 @@ function DataTable({
2974
2696
  const filter = columnFilters.get(column.key);
2975
2697
  const width = customWidth(column.key);
2976
2698
  const resize = (delta, base) => setColumnWidths((current2) => ({ ...current2, [column.key]: Math.max(96, Math.min(800, Math.round(base + delta))) }));
2977
- return /* @__PURE__ */ jsxs20(
2699
+ return /* @__PURE__ */ jsxs16(
2978
2700
  "th",
2979
2701
  {
2980
2702
  scope: "col",
@@ -2984,8 +2706,8 @@ function DataTable({
2984
2706
  style: columnStyle(column),
2985
2707
  "aria-sort": direction ? sortStates[direction].aria : void 0,
2986
2708
  children: [
2987
- /* @__PURE__ */ jsxs20("span", { className: "ad-data-table-head", children: [
2988
- sortable ? /* @__PURE__ */ jsxs20(
2709
+ /* @__PURE__ */ jsxs16("span", { className: "ad-data-table-head", children: [
2710
+ sortable ? /* @__PURE__ */ jsxs16(
2989
2711
  "button",
2990
2712
  {
2991
2713
  type: "button",
@@ -2994,12 +2716,12 @@ function DataTable({
2994
2716
  onClick: (event) => cycleSort(column.key, !!event?.shiftKey),
2995
2717
  children: [
2996
2718
  column.title,
2997
- /* @__PURE__ */ jsx27(Icon, { name: "chevron" }),
2998
- sorting.length > 1 && sortIndex >= 0 && /* @__PURE__ */ jsx27("small", { className: "ad-data-table-sort-priority", children: sortIndex + 1 })
2719
+ /* @__PURE__ */ jsx22(Icon, { name: "chevron" }),
2720
+ sorting.length > 1 && sortIndex >= 0 && /* @__PURE__ */ jsx22("small", { className: "ad-data-table-sort-priority", children: sortIndex + 1 })
2999
2721
  ]
3000
2722
  }
3001
2723
  ) : column.title,
3002
- filter?.kind && /* @__PURE__ */ jsx27(
2724
+ filter?.kind && /* @__PURE__ */ jsx22(
3003
2725
  ColumnFilter,
3004
2726
  {
3005
2727
  column,
@@ -3011,7 +2733,7 @@ function DataTable({
3011
2733
  }
3012
2734
  )
3013
2735
  ] }),
3014
- resizableColumns && column.resizable !== false && /* @__PURE__ */ jsx27(
2736
+ resizableColumns && column.resizable !== false && /* @__PURE__ */ jsx22(
3015
2737
  "span",
3016
2738
  {
3017
2739
  className: "ad-data-table-resize",
@@ -3059,21 +2781,21 @@ function DataTable({
3059
2781
  column.key
3060
2782
  );
3061
2783
  }),
3062
- renderRowActions && /* @__PURE__ */ jsx27("th", { className: "ad-data-table-actions", scope: "col", style: pinning.right.length ? { right: "var(--ad-table-pin-right, 0px)", zIndex: 3 } : void 0, children: tr2("\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044F") })
2784
+ renderRowActions && /* @__PURE__ */ jsx22("th", { className: "ad-data-table-actions", scope: "col", style: pinning.right.length ? { right: "var(--ad-table-pin-right, 0px)", zIndex: 3 } : void 0, children: tr2("\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044F") })
3063
2785
  ] }) }),
3064
- /* @__PURE__ */ jsxs20("tbody", { ref: bodyRef, children: [
3065
- virtualize && !loading && start > 0 && /* @__PURE__ */ jsx27("tr", { className: "ad-data-table-spacer", "aria-hidden": "true", children: /* @__PURE__ */ jsx27("td", { colSpan: span, style: { height: offsets[start] } }) }),
2786
+ /* @__PURE__ */ jsxs16("tbody", { ref: bodyRef, children: [
2787
+ virtualize && !loading && start > 0 && /* @__PURE__ */ jsx22("tr", { className: "ad-data-table-spacer", "aria-hidden": "true", children: /* @__PURE__ */ jsx22("td", { colSpan: span, style: { height: offsets[start] } }) }),
3066
2788
  body,
3067
- virtualize && !loading && end < viewItems.length && /* @__PURE__ */ jsx27("tr", { className: "ad-data-table-spacer", "aria-hidden": "true", children: /* @__PURE__ */ jsx27("td", { colSpan: span, style: { height: virtualHeight - offsets[end] } }) })
2789
+ virtualize && !loading && end < viewItems.length && /* @__PURE__ */ jsx22("tr", { className: "ad-data-table-spacer", "aria-hidden": "true", children: /* @__PURE__ */ jsx22("td", { colSpan: span, style: { height: virtualHeight - offsets[end] } }) })
3068
2790
  ] })
3069
2791
  ] })
3070
2792
  }
3071
2793
  ),
3072
- (perPage || activeFilters.length > 0 || selectable && selection.length > 0) && /* @__PURE__ */ jsxs20("div", { className: "ad-data-table-foot", children: [
3073
- /* @__PURE__ */ jsx27("span", { children: selectable && selection.length > 0 ? tr2("\u0412\u044B\u0431\u0440\u0430\u043D\u043E: {count}", { count: selection.length }) : perPage ? tr2(knownTotal ? "{from}\u2013{to} \u0438\u0437 {count}" : "{from}\u2013{to}", { from: visible.length ? current * perPage + 1 : 0, to: visible.length ? current * perPage + visible.length : 0, count: total }) : tr2(query || activeFilters.length ? "{count} \u043D\u0430\u0439\u0434\u0435\u043D\u043E" : "{count} \u0432\u0441\u0435\u0433\u043E", { count: sorted.length }) }),
3074
- selectable && selection.length > 0 && /* @__PURE__ */ jsx27(Button, { size: "xs", variant: "ghost", icon: "close", onClick: () => setSelection([]), children: tr2("\u0421\u043D\u044F\u0442\u044C \u0432\u044B\u0434\u0435\u043B\u0435\u043D\u0438\u0435") }),
3075
- perPage && /* @__PURE__ */ jsxs20("span", { className: "ad-data-table-page-controls", children: [
3076
- /* @__PURE__ */ jsx27(
2794
+ (perPage || activeFilters.length > 0 || selectable && selection.length > 0) && /* @__PURE__ */ jsxs16("div", { className: "ad-data-table-foot", children: [
2795
+ /* @__PURE__ */ jsx22("span", { children: selectable && selection.length > 0 ? tr2("\u0412\u044B\u0431\u0440\u0430\u043D\u043E: {count}", { count: selection.length }) : perPage ? tr2(knownTotal ? "{from}\u2013{to} \u0438\u0437 {count}" : "{from}\u2013{to}", { from: visible.length ? current * perPage + 1 : 0, to: visible.length ? current * perPage + visible.length : 0, count: total }) : tr2(query || activeFilters.length ? "{count} \u043D\u0430\u0439\u0434\u0435\u043D\u043E" : "{count} \u0432\u0441\u0435\u0433\u043E", { count: sorted.length }) }),
2796
+ selectable && selection.length > 0 && /* @__PURE__ */ jsx22(Button, { size: "xs", variant: "ghost", icon: "close", onClick: () => setSelection([]), children: tr2("\u0421\u043D\u044F\u0442\u044C \u0432\u044B\u0434\u0435\u043B\u0435\u043D\u0438\u0435") }),
2797
+ perPage && /* @__PURE__ */ jsxs16("span", { className: "ad-data-table-page-controls", children: [
2798
+ /* @__PURE__ */ jsx22(
3077
2799
  Select,
3078
2800
  {
3079
2801
  size: "sm",
@@ -3085,8 +2807,8 @@ function DataTable({
3085
2807
  }
3086
2808
  }
3087
2809
  ),
3088
- pages > 1 && /* @__PURE__ */ jsxs20("span", { className: "ad-data-table-pages", children: [
3089
- /* @__PURE__ */ jsx27(
2810
+ pages > 1 && /* @__PURE__ */ jsxs16("span", { className: "ad-data-table-pages", children: [
2811
+ /* @__PURE__ */ jsx22(
3090
2812
  IconButton,
3091
2813
  {
3092
2814
  size: "xs",
@@ -3097,12 +2819,12 @@ function DataTable({
3097
2819
  onClick: () => setPage(current - 1)
3098
2820
  }
3099
2821
  ),
3100
- /* @__PURE__ */ jsxs20("span", { children: [
2822
+ /* @__PURE__ */ jsxs16("span", { children: [
3101
2823
  current + 1,
3102
2824
  " / ",
3103
2825
  knownTotal ? pages : "\u2026"
3104
2826
  ] }),
3105
- /* @__PURE__ */ jsx27(
2827
+ /* @__PURE__ */ jsx22(
3106
2828
  IconButton,
3107
2829
  {
3108
2830
  size: "xs",
@@ -3123,8 +2845,8 @@ function DataTable({
3123
2845
  }
3124
2846
 
3125
2847
  // src/components/feedback/Dialog/Dialog.tsx
3126
- import { useId, useLayoutEffect as useLayoutEffect3, useRef as useRef12, useState as useState6 } from "react";
3127
- import { jsx as jsx28, jsxs as jsxs21 } from "react/jsx-runtime";
2848
+ import { useId, useLayoutEffect as useLayoutEffect2, useRef as useRef10, useState as useState5 } from "react";
2849
+ import { jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
3128
2850
  var outside = (dialog, x, y, target) => {
3129
2851
  if (target !== dialog) return false;
3130
2852
  const box = dialog.getBoundingClientRect();
@@ -3136,9 +2858,9 @@ var Dialog = (p) => {
3136
2858
  p.open,
3137
2859
  p.defaultOpen ?? false,
3138
2860
  p.onOpenChange
3139
- ), [pending, setPending] = useState6(false), [error, setError] = useState6();
3140
- const ref = useRef12(null), confirmation = useRef12(0), confirming = useRef12(false), pressedOutside = useRef12(false), titleId = useId(), descId = useId();
3141
- useLayoutEffect3(() => {
2861
+ ), [pending, setPending] = useState5(false), [error, setError] = useState5();
2862
+ const ref = useRef10(null), confirmation = useRef10(0), confirming = useRef10(false), pressedOutside = useRef10(false), titleId = useId(), descId = useId();
2863
+ useLayoutEffect2(() => {
3142
2864
  confirming.current = false;
3143
2865
  setPending(false);
3144
2866
  setError(void 0);
@@ -3147,7 +2869,7 @@ var Dialog = (p) => {
3147
2869
  confirming.current = false;
3148
2870
  };
3149
2871
  }, [open]);
3150
- useLayoutEffect3(() => {
2872
+ useLayoutEffect2(() => {
3151
2873
  const d = ref.current;
3152
2874
  if (!d) return;
3153
2875
  const opener = document.activeElement instanceof HTMLElement ? document.activeElement : null;
@@ -3167,7 +2889,7 @@ var Dialog = (p) => {
3167
2889
  };
3168
2890
  }, [open]);
3169
2891
  if (!open) return null;
3170
- return /* @__PURE__ */ jsxs21(
2892
+ return /* @__PURE__ */ jsxs17(
3171
2893
  "dialog",
3172
2894
  {
3173
2895
  ...mark("Dialog", p, "dialog"),
@@ -3197,14 +2919,14 @@ var Dialog = (p) => {
3197
2919
  if (!pending) setOpen(false);
3198
2920
  },
3199
2921
  children: [
3200
- p.art && /* @__PURE__ */ jsx28("div", { className: "ad-dialog-art", "aria-hidden": "true", children: p.art }),
3201
- /* @__PURE__ */ jsx28(
2922
+ p.art && /* @__PURE__ */ jsx23("div", { className: "ad-dialog-art", "aria-hidden": "true", children: p.art }),
2923
+ /* @__PURE__ */ jsx23(
3202
2924
  Header,
3203
2925
  {
3204
- title: /* @__PURE__ */ jsx28("span", { id: titleId, children: p.title ?? tr2("\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435") }),
2926
+ title: /* @__PURE__ */ jsx23("span", { id: titleId, children: p.title ?? tr2("\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435") }),
3205
2927
  icon: p.icon,
3206
2928
  level: 2,
3207
- actions: /* @__PURE__ */ jsx28(
2929
+ actions: /* @__PURE__ */ jsx23(
3208
2930
  IconButton,
3209
2931
  {
3210
2932
  variant: "ghost",
@@ -3216,14 +2938,14 @@ var Dialog = (p) => {
3216
2938
  )
3217
2939
  }
3218
2940
  ),
3219
- /* @__PURE__ */ jsxs21(DialogBody, { children: [
3220
- p.description && /* @__PURE__ */ jsx28("p", { id: descId, children: p.description }),
2941
+ /* @__PURE__ */ jsxs17(DialogBody, { children: [
2942
+ p.description && /* @__PURE__ */ jsx23("p", { id: descId, children: p.description }),
3221
2943
  p.children,
3222
- error && /* @__PURE__ */ jsx28(MessageBar, { tone: "error", children: error })
2944
+ error && /* @__PURE__ */ jsx23(MessageBar, { tone: "error", children: error })
3223
2945
  ] }),
3224
- (p.cancelLabel !== false || p.confirmLabel !== false) && /* @__PURE__ */ jsxs21(DialogActions, { children: [
3225
- p.cancelLabel !== false && /* @__PURE__ */ jsx28(Button, { disabled: pending, onClick: () => setOpen(false), children: p.cancelLabel ?? tr2("\u041E\u0442\u043C\u0435\u043D\u0430") }),
3226
- p.confirmLabel !== false && /* @__PURE__ */ jsx28(
2946
+ (p.cancelLabel !== false || p.confirmLabel !== false) && /* @__PURE__ */ jsxs17(DialogActions, { children: [
2947
+ p.cancelLabel !== false && /* @__PURE__ */ jsx23(Button, { disabled: pending, onClick: () => setOpen(false), children: p.cancelLabel ?? tr2("\u041E\u0442\u043C\u0435\u043D\u0430") }),
2948
+ p.confirmLabel !== false && /* @__PURE__ */ jsx23(
3227
2949
  Button,
3228
2950
  {
3229
2951
  variant: p.danger ? "danger" : "primary",
@@ -3263,28 +2985,28 @@ var Dialog = (p) => {
3263
2985
  };
3264
2986
 
3265
2987
  // src/components/feedback/EmptyState/EmptyState.tsx
3266
- import { jsx as jsx29, jsxs as jsxs22 } from "react/jsx-runtime";
2988
+ import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
3267
2989
  var EmptyState = (p) => {
3268
2990
  const tr2 = useTr();
3269
- return /* @__PURE__ */ jsxs22("div", { ...mark("EmptyState", p), children: [
3270
- /* @__PURE__ */ jsx29(Icon, { name: p.icon ?? "music", size: 44 }),
3271
- /* @__PURE__ */ jsx29("h3", { children: p.title ?? tr2("\u041F\u043E\u043A\u0430 \u043D\u0435\u0442 \u0437\u0430\u043F\u0438\u0441\u0435\u0439") }),
3272
- /* @__PURE__ */ jsx29("p", { children: p.description ?? tr2("\u0414\u043E\u0431\u0430\u0432\u044C\u0442\u0435 \u0437\u0430\u043F\u0438\u0441\u044C, \u0447\u0442\u043E\u0431\u044B \u043D\u0430\u0447\u0430\u0442\u044C.") }),
2991
+ return /* @__PURE__ */ jsxs18("div", { ...mark("EmptyState", p), children: [
2992
+ /* @__PURE__ */ jsx24(Icon, { name: p.icon ?? "music", size: 44 }),
2993
+ /* @__PURE__ */ jsx24("h3", { children: p.title ?? tr2("\u041F\u043E\u043A\u0430 \u043D\u0435\u0442 \u0437\u0430\u043F\u0438\u0441\u0435\u0439") }),
2994
+ /* @__PURE__ */ jsx24("p", { children: p.description ?? tr2("\u0414\u043E\u0431\u0430\u0432\u044C\u0442\u0435 \u0437\u0430\u043F\u0438\u0441\u044C, \u0447\u0442\u043E\u0431\u044B \u043D\u0430\u0447\u0430\u0442\u044C.") }),
3273
2995
  p.action
3274
2996
  ] });
3275
2997
  };
3276
2998
 
3277
2999
  // src/components/feedback/KeyValueList/KeyValueList.tsx
3278
- import { jsx as jsx30, jsxs as jsxs23 } from "react/jsx-runtime";
3000
+ import { jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
3279
3001
  var KeyValueList = (p) => {
3280
3002
  const tr2 = useTr();
3281
- return /* @__PURE__ */ jsx30("dl", { ...mark("KeyValueList", p), children: (p.items ?? [
3003
+ return /* @__PURE__ */ jsx25("dl", { ...mark("KeyValueList", p), children: (p.items ?? [
3282
3004
  ["Python Backend", "Ready"],
3283
3005
  ["AudioService", "Running"],
3284
3006
  [tr2("\u0411\u0430\u0437\u0430 \u0434\u0430\u043D\u043D\u044B\u0445"), tr2("\u0418\u0441\u043F\u0440\u0430\u0432\u043D\u043E")]
3285
- ]).map(([key, value], i) => /* @__PURE__ */ jsxs23("div", { children: [
3286
- /* @__PURE__ */ jsx30("dt", { children: key }),
3287
- /* @__PURE__ */ jsx30("dd", { children: value })
3007
+ ]).map(([key, value], i) => /* @__PURE__ */ jsxs19("div", { children: [
3008
+ /* @__PURE__ */ jsx25("dt", { children: key }),
3009
+ /* @__PURE__ */ jsx25("dd", { children: value })
3288
3010
  ] }, i)) });
3289
3011
  };
3290
3012
 
@@ -3293,11 +3015,11 @@ import {
3293
3015
  cloneElement,
3294
3016
  isValidElement as isValidElement2,
3295
3017
  useId as useId2,
3296
- useLayoutEffect as useLayoutEffect4,
3297
- useRef as useRef13,
3298
- useState as useState7
3018
+ useLayoutEffect as useLayoutEffect3,
3019
+ useRef as useRef11,
3020
+ useState as useState6
3299
3021
  } from "react";
3300
- import { jsx as jsx31, jsxs as jsxs24 } from "react/jsx-runtime";
3022
+ import { jsx as jsx26, jsxs as jsxs20 } from "react/jsx-runtime";
3301
3023
  function Tooltip({
3302
3024
  content,
3303
3025
  children,
@@ -3305,11 +3027,11 @@ function Tooltip({
3305
3027
  ...p
3306
3028
  }) {
3307
3029
  const id = useId2();
3308
- const trigger = useRef13(null);
3309
- const tip = useRef13(null);
3310
- const hovered = useRef13(false);
3311
- const [open, setOpen] = useState7(false);
3312
- useLayoutEffect4(() => {
3030
+ const trigger = useRef11(null);
3031
+ const tip = useRef11(null);
3032
+ const hovered = useRef11(false);
3033
+ const [open, setOpen] = useState6(false);
3034
+ useLayoutEffect3(() => {
3313
3035
  const node = tip.current;
3314
3036
  const target = trigger.current;
3315
3037
  if (!open || !node || !target) return;
@@ -3358,7 +3080,7 @@ function Tooltip({
3358
3080
  }, [open, placement]);
3359
3081
  const show = () => setOpen(true);
3360
3082
  const hide = () => setOpen(false);
3361
- return /* @__PURE__ */ jsxs24(
3083
+ return /* @__PURE__ */ jsxs20(
3362
3084
  "span",
3363
3085
  {
3364
3086
  ref: trigger,
@@ -3388,7 +3110,7 @@ function Tooltip({
3388
3110
  "aria-describedby": [children.props["aria-describedby"], open ? id : void 0].filter(Boolean).join(" ") || void 0
3389
3111
  }
3390
3112
  ) : children,
3391
- open && /* @__PURE__ */ jsx31(
3113
+ open && /* @__PURE__ */ jsx26(
3392
3114
  "span",
3393
3115
  {
3394
3116
  ...mark("Tooltip", p),
@@ -3406,7 +3128,7 @@ function Tooltip({
3406
3128
 
3407
3129
  // src/components/feedback/ProgressBar/ProgressBar.tsx
3408
3130
  import { useId as useId3 } from "react";
3409
- import { jsx as jsx32, jsxs as jsxs25 } from "react/jsx-runtime";
3131
+ import { jsx as jsx27, jsxs as jsxs21 } from "react/jsx-runtime";
3410
3132
  var WAVE = Array.from(
3411
3133
  { length: 64 },
3412
3134
  (_, i) => 0.25 + 0.75 * Math.abs(Math.sin(i * 0.37) * Math.cos(i * 0.11 + 1.3))
@@ -3422,7 +3144,7 @@ var ProgressBar = (p) => {
3422
3144
  const value = clamp(p.value ?? 56, 0, max);
3423
3145
  const share = p.indeterminate ? 35 : value / max * 100;
3424
3146
  const clip = `ad-wave-${useId3().replace(/:/g, "")}`;
3425
- return /* @__PURE__ */ jsx32(
3147
+ return /* @__PURE__ */ jsx27(
3426
3148
  "div",
3427
3149
  {
3428
3150
  ...mark("ProgressBar", p),
@@ -3433,17 +3155,17 @@ var ProgressBar = (p) => {
3433
3155
  "aria-valuenow": p.indeterminate ? void 0 : value,
3434
3156
  "data-indeterminate": p.indeterminate || void 0,
3435
3157
  "data-variant": p.variant === "wave" ? "wave" : void 0,
3436
- children: p.variant === "wave" ? /* @__PURE__ */ jsxs25("svg", { viewBox: "0 0 128 24", preserveAspectRatio: "none", "aria-hidden": "true", children: [
3437
- /* @__PURE__ */ jsx32("defs", { children: /* @__PURE__ */ jsx32("clipPath", { id: clip, children: /* @__PURE__ */ jsx32("rect", { width: 128 * share / 100, height: "24" }) }) }),
3438
- /* @__PURE__ */ jsx32("path", { className: "ad-progress-wave-idle", d: wavePath }),
3439
- /* @__PURE__ */ jsx32("path", { className: "ad-progress-wave-done", d: wavePath, clipPath: `url(#${clip})` })
3440
- ] }) : /* @__PURE__ */ jsx32("span", { style: { width: `${share}%` } })
3158
+ children: p.variant === "wave" ? /* @__PURE__ */ jsxs21("svg", { viewBox: "0 0 128 24", preserveAspectRatio: "none", "aria-hidden": "true", children: [
3159
+ /* @__PURE__ */ jsx27("defs", { children: /* @__PURE__ */ jsx27("clipPath", { id: clip, children: /* @__PURE__ */ jsx27("rect", { width: 128 * share / 100, height: "24" }) }) }),
3160
+ /* @__PURE__ */ jsx27("path", { className: "ad-progress-wave-idle", d: wavePath }),
3161
+ /* @__PURE__ */ jsx27("path", { className: "ad-progress-wave-done", d: wavePath, clipPath: `url(#${clip})` })
3162
+ ] }) : /* @__PURE__ */ jsx27("span", { style: { width: `${share}%` } })
3441
3163
  }
3442
3164
  );
3443
3165
  };
3444
3166
 
3445
3167
  // src/components/feedback/StatusIndicator/StatusIndicator.tsx
3446
- import { jsx as jsx33, jsxs as jsxs26 } from "react/jsx-runtime";
3168
+ import { jsx as jsx28, jsxs as jsxs22 } from "react/jsx-runtime";
3447
3169
  var StatusIndicator = ({
3448
3170
  status = "success",
3449
3171
  ...p
@@ -3458,14 +3180,14 @@ var StatusIndicator = ({
3458
3180
  offline: tr2("\u041D\u0435 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u043E"),
3459
3181
  info: tr2("\u0418\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044F")
3460
3182
  };
3461
- return /* @__PURE__ */ jsxs26("span", { ...mark("StatusIndicator", { ...p, tone: status }), children: [
3462
- /* @__PURE__ */ jsx33("span", { className: "ad-status-dot", "aria-hidden": true, children: /* @__PURE__ */ jsx33("i", {}) }),
3463
- /* @__PURE__ */ jsx33("span", { children: p.label ?? labels[status] })
3183
+ return /* @__PURE__ */ jsxs22("span", { ...mark("StatusIndicator", { ...p, tone: status }), children: [
3184
+ /* @__PURE__ */ jsx28("span", { className: "ad-status-dot", "aria-hidden": true, children: /* @__PURE__ */ jsx28("i", {}) }),
3185
+ /* @__PURE__ */ jsx28("span", { children: p.label ?? labels[status] })
3464
3186
  ] });
3465
3187
  };
3466
3188
 
3467
3189
  // src/components/feedback/Steps/Steps.tsx
3468
- import { jsx as jsx34, jsxs as jsxs27 } from "react/jsx-runtime";
3190
+ import { jsx as jsx29, jsxs as jsxs23 } from "react/jsx-runtime";
3469
3191
  var Steps = (p) => {
3470
3192
  const tr2 = useTr();
3471
3193
  const steps = p.steps ?? [
@@ -3476,17 +3198,17 @@ var Steps = (p) => {
3476
3198
  tr2("\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430")
3477
3199
  ];
3478
3200
  const current = p.current ?? 3;
3479
- return /* @__PURE__ */ jsx34("ol", { ...mark("Steps", p), children: steps.map((label, i) => {
3201
+ return /* @__PURE__ */ jsx29("ol", { ...mark("Steps", p), children: steps.map((label, i) => {
3480
3202
  const state = i < current ? "done" : i === current ? "current" : "todo";
3481
- return /* @__PURE__ */ jsxs27(
3203
+ return /* @__PURE__ */ jsxs23(
3482
3204
  "li",
3483
3205
  {
3484
3206
  "data-state": state,
3485
3207
  "aria-current": state === "current" ? "step" : void 0,
3486
3208
  children: [
3487
- /* @__PURE__ */ jsx34("span", { className: "ad-step-node", "aria-hidden": true, children: state === "done" ? /* @__PURE__ */ jsx34(Icon, { name: "check" }) : i + 1 }),
3488
- /* @__PURE__ */ jsx34("span", { className: "ad-step-label", children: label }),
3489
- state === "current" && i > 0 && /* @__PURE__ */ jsx34("span", { className: "ad-steps-glint", "aria-hidden": true })
3209
+ /* @__PURE__ */ jsx29("span", { className: "ad-step-node", "aria-hidden": true, children: state === "done" ? /* @__PURE__ */ jsx29(Icon, { name: "check" }) : i + 1 }),
3210
+ /* @__PURE__ */ jsx29("span", { className: "ad-step-label", children: label }),
3211
+ state === "current" && i > 0 && /* @__PURE__ */ jsx29("span", { className: "ad-steps-glint", "aria-hidden": true })
3490
3212
  ]
3491
3213
  },
3492
3214
  `${i}-${label}`
@@ -3495,8 +3217,8 @@ var Steps = (p) => {
3495
3217
  };
3496
3218
 
3497
3219
  // src/components/feedback/Toast/Toast.tsx
3498
- import { useEffect as useEffect4, useRef as useRef14 } from "react";
3499
- import { jsx as jsx35, jsxs as jsxs28 } from "react/jsx-runtime";
3220
+ import { useEffect as useEffect4, useRef as useRef12 } from "react";
3221
+ import { jsx as jsx30, jsxs as jsxs24 } from "react/jsx-runtime";
3500
3222
  var Toast = ({
3501
3223
  open = true,
3502
3224
  duration = 3600,
@@ -3504,7 +3226,7 @@ var Toast = ({
3504
3226
  ...p
3505
3227
  }) => {
3506
3228
  const tr2 = useTr();
3507
- const close = useRef14(onClose);
3229
+ const close = useRef12(onClose);
3508
3230
  close.current = onClose;
3509
3231
  useEffect4(() => {
3510
3232
  if (!open || !onClose || duration <= 0) return;
@@ -3512,7 +3234,7 @@ var Toast = ({
3512
3234
  return () => clearTimeout(id);
3513
3235
  }, [open, duration, !!onClose]);
3514
3236
  if (!open) return null;
3515
- return /* @__PURE__ */ jsxs28(
3237
+ return /* @__PURE__ */ jsxs24(
3516
3238
  "div",
3517
3239
  {
3518
3240
  ...mark(
@@ -3524,14 +3246,14 @@ var Toast = ({
3524
3246
  role: p.tone === "error" ? "alert" : "status",
3525
3247
  "aria-live": p.tone === "error" ? "assertive" : "polite",
3526
3248
  children: [
3527
- /* @__PURE__ */ jsx35("span", { className: "ad-toast-icon", "aria-hidden": true, children: /* @__PURE__ */ jsx35(
3249
+ /* @__PURE__ */ jsx30("span", { className: "ad-toast-icon", "aria-hidden": true, children: /* @__PURE__ */ jsx30(
3528
3250
  Icon,
3529
3251
  {
3530
3252
  name: p.tone === "error" || p.tone === "warning" ? "warning" : p.tone === "info" ? "info" : "check"
3531
3253
  }
3532
3254
  ) }),
3533
- /* @__PURE__ */ jsx35("span", { children: p.message ?? p.children ?? tr2("\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u044B") }),
3534
- onClose && duration > 0 && /* @__PURE__ */ jsx35(
3255
+ /* @__PURE__ */ jsx30("span", { children: p.message ?? p.children ?? tr2("\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u044B") }),
3256
+ onClose && duration > 0 && /* @__PURE__ */ jsx30(
3535
3257
  "span",
3536
3258
  {
3537
3259
  className: "ad-toast-timer",
@@ -3545,7 +3267,7 @@ var Toast = ({
3545
3267
  };
3546
3268
 
3547
3269
  // src/components/effects/AnimatedBorder/AnimatedBorder.tsx
3548
- import { createElement as createElement7, useRef as useRef15 } from "react";
3270
+ import { createElement as createElement7, useRef as useRef13 } from "react";
3549
3271
  function AnimatedBorder({
3550
3272
  as = "div",
3551
3273
  shell = false,
@@ -3553,7 +3275,7 @@ function AnimatedBorder({
3553
3275
  children,
3554
3276
  ...props
3555
3277
  }) {
3556
- const ref = useRef15(null);
3278
+ const ref = useRef13(null);
3557
3279
  useBorder(ref, true, shell, round);
3558
3280
  usePauseOffscreen(ref);
3559
3281
  return createElement7(
@@ -3604,8 +3326,8 @@ import {
3604
3326
  createElement as createElement9,
3605
3327
  isValidElement as isValidElement3,
3606
3328
  useEffect as useEffect5,
3607
- useRef as useRef16,
3608
- useState as useState8
3329
+ useRef as useRef14,
3330
+ useState as useState7
3609
3331
  } from "react";
3610
3332
  function Reveal({
3611
3333
  as = "div",
@@ -3616,8 +3338,8 @@ function Reveal({
3616
3338
  children,
3617
3339
  ...p
3618
3340
  }) {
3619
- const ref = useRef16(null);
3620
- const [shown, setShown] = useState8(false);
3341
+ const ref = useRef14(null);
3342
+ const [shown, setShown] = useState7(false);
3621
3343
  useEffect5(() => {
3622
3344
  const node = ref.current;
3623
3345
  if (!node) return;
@@ -3657,15 +3379,15 @@ function Reveal({
3657
3379
  }
3658
3380
 
3659
3381
  // src/components/effects/GlowText/GlowText.tsx
3660
- import { createElement as createElement10, useRef as useRef17 } from "react";
3661
- import { jsx as jsx36 } from "react/jsx-runtime";
3382
+ import { createElement as createElement10, useRef as useRef15 } from "react";
3383
+ import { jsx as jsx31 } from "react/jsx-runtime";
3662
3384
  function GlowText({
3663
3385
  as = "span",
3664
3386
  flicker = false,
3665
3387
  children,
3666
3388
  ...p
3667
3389
  }) {
3668
- const pauseRef = useRef17(null);
3390
+ const pauseRef = useRef15(null);
3669
3391
  usePauseOffscreen(pauseRef);
3670
3392
  return createElement10(
3671
3393
  as,
@@ -3676,32 +3398,32 @@ function GlowText({
3676
3398
  },
3677
3399
  // The halo is a still copy of the text on a layer of its own, painted once: the flowing
3678
3400
  // gradient repaints only the letters and never re-blurs the glow.
3679
- /* @__PURE__ */ jsx36("span", { className: "ad-glow-text-halo", "aria-hidden": "true", children }),
3401
+ /* @__PURE__ */ jsx31("span", { className: "ad-glow-text-halo", "aria-hidden": "true", children }),
3680
3402
  children
3681
3403
  );
3682
3404
  }
3683
3405
 
3684
3406
  // src/components/feedback/SignalBars/SignalBars.tsx
3685
- import { jsx as jsx37 } from "react/jsx-runtime";
3407
+ import { jsx as jsx32 } from "react/jsx-runtime";
3686
3408
  function SignalBars({ level = 3, bars = 4, weak = false, label, ...p }) {
3687
3409
  const tr2 = useTr();
3688
3410
  const count = Math.max(1, Number.isFinite(bars) ? Math.floor(bars) : 4);
3689
3411
  const strength = Math.max(0, Math.min(count, Number.isFinite(level) ? Math.round(level) : 0));
3690
- return /* @__PURE__ */ jsx37(
3412
+ return /* @__PURE__ */ jsx32(
3691
3413
  "span",
3692
3414
  {
3693
3415
  ...mark("SignalBars", p),
3694
3416
  role: "img",
3695
3417
  "aria-label": label ?? tr2("\u0421\u0438\u0433\u043D\u0430\u043B: {level} \u0438\u0437 {bars}", { level: strength, bars: count }),
3696
3418
  "data-weak": weak || void 0,
3697
- children: Array.from({ length: count }, (_, i) => /* @__PURE__ */ jsx37("i", { "data-lit": i < strength || void 0, style: { height: `${(i + 1) / count * 100}%` } }, i))
3419
+ children: Array.from({ length: count }, (_, i) => /* @__PURE__ */ jsx32("i", { "data-lit": i < strength || void 0, style: { height: `${(i + 1) / count * 100}%` } }, i))
3698
3420
  }
3699
3421
  );
3700
3422
  }
3701
3423
 
3702
3424
  // src/components/effects/Shimmer/Shimmer.tsx
3703
- import { useRef as useRef18 } from "react";
3704
- import { jsx as jsx38, jsxs as jsxs29 } from "react/jsx-runtime";
3425
+ import { useRef as useRef16 } from "react";
3426
+ import { jsx as jsx33, jsxs as jsxs25 } from "react/jsx-runtime";
3705
3427
  function Shimmer({
3706
3428
  lines = 3,
3707
3429
  circle = false,
@@ -3709,9 +3431,9 @@ function Shimmer({
3709
3431
  ...p
3710
3432
  }) {
3711
3433
  const tr2 = useTr();
3712
- const ref = useRef18(null);
3434
+ const ref = useRef16(null);
3713
3435
  usePauseOffscreen(ref);
3714
- return /* @__PURE__ */ jsxs29(
3436
+ return /* @__PURE__ */ jsxs25(
3715
3437
  "div",
3716
3438
  {
3717
3439
  ...mark("Shimmer", p),
@@ -3720,8 +3442,8 @@ function Shimmer({
3720
3442
  "aria-label": label ?? tr2("\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430"),
3721
3443
  "aria-busy": "true",
3722
3444
  children: [
3723
- circle && /* @__PURE__ */ jsx38("i", { className: "ad-shimmer-circle" }),
3724
- /* @__PURE__ */ jsx38("span", { className: "ad-shimmer-lines", children: Array.from({ length: lines }, (_, i) => /* @__PURE__ */ jsx38(
3445
+ circle && /* @__PURE__ */ jsx33("i", { className: "ad-shimmer-circle" }),
3446
+ /* @__PURE__ */ jsx33("span", { className: "ad-shimmer-lines", children: Array.from({ length: lines }, (_, i) => /* @__PURE__ */ jsx33(
3725
3447
  "i",
3726
3448
  {
3727
3449
  style: { width: i === lines - 1 && lines > 1 ? "60%" : "100%" }
@@ -3734,8 +3456,8 @@ function Shimmer({
3734
3456
  }
3735
3457
 
3736
3458
  // src/components/effects/Sparkles/Sparkles.tsx
3737
- import { useRef as useRef19 } from "react";
3738
- import { jsx as jsx39, jsxs as jsxs30 } from "react/jsx-runtime";
3459
+ import { useRef as useRef17 } from "react";
3460
+ import { jsx as jsx34, jsxs as jsxs26 } from "react/jsx-runtime";
3739
3461
  function Sparkles({
3740
3462
  count = 10,
3741
3463
  color: color3,
@@ -3743,9 +3465,9 @@ function Sparkles({
3743
3465
  children,
3744
3466
  ...p
3745
3467
  }) {
3746
- const pauseRef = useRef19(null);
3468
+ const pauseRef = useRef17(null);
3747
3469
  usePauseOffscreen(pauseRef);
3748
- return /* @__PURE__ */ jsxs30(
3470
+ return /* @__PURE__ */ jsxs26(
3749
3471
  "span",
3750
3472
  {
3751
3473
  ...mark("Sparkles", p),
@@ -3756,7 +3478,7 @@ function Sparkles({
3756
3478
  Array.from({ length: count }, (_, i) => {
3757
3479
  const angle = i * 137.5;
3758
3480
  const reach = 55 + i * 29 % 40;
3759
- return /* @__PURE__ */ jsx39(
3481
+ return /* @__PURE__ */ jsx34(
3760
3482
  "svg",
3761
3483
  {
3762
3484
  className: "ad-sparkle",
@@ -3768,7 +3490,7 @@ function Sparkles({
3768
3490
  width: `${0.45 + i * 7 % 5 / 10}em`,
3769
3491
  animationDelay: `${(i * 0.37 % 2.4).toFixed(2)}s`
3770
3492
  },
3771
- children: /* @__PURE__ */ jsx39("path", { d: "M5 0C5.6 3.6 6.4 4.4 10 5C6.4 5.6 5.6 6.4 5 10C4.4 6.4 3.6 5.6 0 5C3.6 4.4 4.4 3.6 5 0Z" })
3493
+ children: /* @__PURE__ */ jsx34("path", { d: "M5 0C5.6 3.6 6.4 4.4 10 5C6.4 5.6 5.6 6.4 5 10C4.4 6.4 3.6 5.6 0 5C3.6 4.4 4.4 3.6 5 0Z" })
3772
3494
  },
3773
3495
  i
3774
3496
  );
@@ -3779,8 +3501,8 @@ function Sparkles({
3779
3501
  }
3780
3502
 
3781
3503
  // src/components/effects/Beacon/Beacon.tsx
3782
- import { useRef as useRef20 } from "react";
3783
- import { jsx as jsx40, jsxs as jsxs31 } from "react/jsx-runtime";
3504
+ import { useRef as useRef18 } from "react";
3505
+ import { jsx as jsx35, jsxs as jsxs27 } from "react/jsx-runtime";
3784
3506
  function Beacon({
3785
3507
  active = true,
3786
3508
  color: color3,
@@ -3788,9 +3510,9 @@ function Beacon({
3788
3510
  children,
3789
3511
  ...p
3790
3512
  }) {
3791
- const pauseRef = useRef20(null);
3513
+ const pauseRef = useRef18(null);
3792
3514
  usePauseOffscreen(pauseRef);
3793
- return /* @__PURE__ */ jsxs31(
3515
+ return /* @__PURE__ */ jsxs27(
3794
3516
  "span",
3795
3517
  {
3796
3518
  ...mark("Beacon", p),
@@ -3798,9 +3520,9 @@ function Beacon({
3798
3520
  style: { ...style, "--ad-beacon": color3 },
3799
3521
  "data-active": active || void 0,
3800
3522
  children: [
3801
- /* @__PURE__ */ jsx40("i", { "aria-hidden": true }),
3802
- /* @__PURE__ */ jsx40("i", { "aria-hidden": true }),
3803
- /* @__PURE__ */ jsx40("i", { "aria-hidden": true }),
3523
+ /* @__PURE__ */ jsx35("i", { "aria-hidden": true }),
3524
+ /* @__PURE__ */ jsx35("i", { "aria-hidden": true }),
3525
+ /* @__PURE__ */ jsx35("i", { "aria-hidden": true }),
3804
3526
  children
3805
3527
  ]
3806
3528
  }
@@ -3808,8 +3530,8 @@ function Beacon({
3808
3530
  }
3809
3531
 
3810
3532
  // src/components/effects/Marquee/Marquee.tsx
3811
- import { useRef as useRef21 } from "react";
3812
- import { jsx as jsx41, jsxs as jsxs32 } from "react/jsx-runtime";
3533
+ import { useRef as useRef19 } from "react";
3534
+ import { jsx as jsx36, jsxs as jsxs28 } from "react/jsx-runtime";
3813
3535
  function Marquee({
3814
3536
  duration = 20,
3815
3537
  reverse = false,
@@ -3817,7 +3539,7 @@ function Marquee({
3817
3539
  children,
3818
3540
  ...p
3819
3541
  }) {
3820
- const pauseRef = useRef21(null);
3542
+ const pauseRef = useRef19(null);
3821
3543
  const motion = useMotion();
3822
3544
  usePauseOffscreen(pauseRef);
3823
3545
  const updatePause = () => {
@@ -3826,7 +3548,7 @@ function Marquee({
3826
3548
  node.toggleAttribute("data-ad-paused", node.matches(":hover, :focus-within"));
3827
3549
  refreshMotion();
3828
3550
  };
3829
- return /* @__PURE__ */ jsx41(
3551
+ return /* @__PURE__ */ jsx36(
3830
3552
  "div",
3831
3553
  {
3832
3554
  ...mark("Marquee", p),
@@ -3838,9 +3560,9 @@ function Marquee({
3838
3560
  onPointerLeave: updatePause,
3839
3561
  onFocus: updatePause,
3840
3562
  onBlur: updatePause,
3841
- children: /* @__PURE__ */ jsxs32("div", { className: "ad-marquee-track", children: [
3842
- /* @__PURE__ */ jsx41("div", { className: "ad-marquee-group", children }),
3843
- /* @__PURE__ */ jsx41("div", { className: "ad-marquee-group", "aria-hidden": true, ref: (node) => {
3563
+ children: /* @__PURE__ */ jsxs28("div", { className: "ad-marquee-track", children: [
3564
+ /* @__PURE__ */ jsx36("div", { className: "ad-marquee-group", children }),
3565
+ /* @__PURE__ */ jsx36("div", { className: "ad-marquee-group", "aria-hidden": true, ref: (node) => {
3844
3566
  if (node) node.inert = true;
3845
3567
  }, children })
3846
3568
  ] })
@@ -3849,7 +3571,7 @@ function Marquee({
3849
3571
  }
3850
3572
 
3851
3573
  // src/components/artwork/Landscape/Landscape.tsx
3852
- import { useRef as useRef22 } from "react";
3574
+ import { useRef as useRef20 } from "react";
3853
3575
 
3854
3576
  // src/components/artwork/useArtwork.ts
3855
3577
  import { useEffect as useEffect6 } from "react";
@@ -3897,7 +3619,7 @@ function useArtwork(ref, key, width, height, painting) {
3897
3619
  }
3898
3620
 
3899
3621
  // src/components/artwork/Landscape/Landscape.tsx
3900
- import { jsx as jsx42, jsxs as jsxs33 } from "react/jsx-runtime";
3622
+ import { jsx as jsx37, jsxs as jsxs29 } from "react/jsx-runtime";
3901
3623
  var W = 1220;
3902
3624
  var H = 168;
3903
3625
  var centerX = 1129;
@@ -4091,20 +3813,20 @@ var NEAR_RIDGE = [
4091
3813
  [1220, 132]
4092
3814
  ];
4093
3815
  function Landscape({ shade = true, children, ...p }) {
4094
- const pauseRef = useRef22(null);
3816
+ const pauseRef = useRef20(null);
4095
3817
  usePauseOffscreen(pauseRef);
4096
- const ref = useRef22(null);
3818
+ const ref = useRef20(null);
4097
3819
  useArtwork(ref, "landscape", W, H, landscape);
4098
- return /* @__PURE__ */ jsxs33("div", { ...mark("Landscape", p), ref: pauseRef, "data-shade": shade || void 0, children: [
4099
- /* @__PURE__ */ jsx42("canvas", { ref, "aria-hidden": true }),
4100
- /* @__PURE__ */ jsx42("span", { className: "ad-landscape-glow", "aria-hidden": true }),
4101
- children && /* @__PURE__ */ jsx42("div", { className: "ad-landscape-content", children })
3820
+ return /* @__PURE__ */ jsxs29("div", { ...mark("Landscape", p), ref: pauseRef, "data-shade": shade || void 0, children: [
3821
+ /* @__PURE__ */ jsx37("canvas", { ref, "aria-hidden": true }),
3822
+ /* @__PURE__ */ jsx37("span", { className: "ad-landscape-glow", "aria-hidden": true }),
3823
+ children && /* @__PURE__ */ jsx37("div", { className: "ad-landscape-content", children })
4102
3824
  ] });
4103
3825
  }
4104
3826
 
4105
3827
  // src/components/artwork/Planet/Planet.tsx
4106
- import { useRef as useRef23 } from "react";
4107
- import { jsx as jsx43, jsxs as jsxs34 } from "react/jsx-runtime";
3828
+ import { useRef as useRef21 } from "react";
3829
+ import { jsx as jsx38, jsxs as jsxs30 } from "react/jsx-runtime";
4108
3830
  var W2 = 515;
4109
3831
  var H2 = 114;
4110
3832
  var planet = {
@@ -4146,19 +3868,19 @@ var planet = {
4146
3868
  }
4147
3869
  };
4148
3870
  function Planet({ children, ...p }) {
4149
- const pauseRef = useRef23(null);
3871
+ const pauseRef = useRef21(null);
4150
3872
  usePauseOffscreen(pauseRef);
4151
- const ref = useRef23(null);
3873
+ const ref = useRef21(null);
4152
3874
  useArtwork(ref, "planet", W2, H2, planet);
4153
- return /* @__PURE__ */ jsxs34("div", { ...mark("Planet", p), ref: pauseRef, children: [
4154
- /* @__PURE__ */ jsx43("canvas", { ref, "aria-hidden": true }),
4155
- children && /* @__PURE__ */ jsx43("div", { className: "ad-planet-content", children })
3875
+ return /* @__PURE__ */ jsxs30("div", { ...mark("Planet", p), ref: pauseRef, children: [
3876
+ /* @__PURE__ */ jsx38("canvas", { ref, "aria-hidden": true }),
3877
+ children && /* @__PURE__ */ jsx38("div", { className: "ad-planet-content", children })
4156
3878
  ] });
4157
3879
  }
4158
3880
 
4159
3881
  // src/components/artwork/NeonWaves/NeonWaves.tsx
4160
- import { useEffect as useEffect7, useMemo as useMemo3, useRef as useRef24 } from "react";
4161
- import { jsx as jsx44, jsxs as jsxs35 } from "react/jsx-runtime";
3882
+ import { useEffect as useEffect7, useMemo as useMemo3, useRef as useRef22 } from "react";
3883
+ import { jsx as jsx39, jsxs as jsxs31 } from "react/jsx-runtime";
4162
3884
  var W3 = 600;
4163
3885
  var H3 = 120;
4164
3886
  var SKYLINE = [[0, 0.93], [0.16, 0.86], [0.3, 0.82], [0.42, 0.72], [0.55, 0.64], [0.64, 0.7], [0.74, 0.52], [0.84, 0.1], [0.9, 0.56], [0.95, 0.46], [1, 0.08]];
@@ -4260,15 +3982,15 @@ function NeonWaves({
4260
3982
  comets = 0,
4261
3983
  ...p
4262
3984
  }) {
4263
- const rootRef = useRef24(null);
4264
- const ref = useRef24(null);
4265
- const cometsRef = useRef24(null);
4266
- const lastDraw = useRef24(-1);
4267
- const tracks = useRef24(/* @__PURE__ */ new Map());
4268
- const paths = useRef24(null);
4269
- const cometElements = useRef24(null);
4270
- const size = useRef24({ width: 0, height: 0 });
4271
- const lastTime = useRef24(0);
3985
+ const rootRef = useRef22(null);
3986
+ const ref = useRef22(null);
3987
+ const cometsRef = useRef22(null);
3988
+ const lastDraw = useRef22(-1);
3989
+ const tracks = useRef22(/* @__PURE__ */ new Map());
3990
+ const paths = useRef22(null);
3991
+ const cometElements = useRef22(null);
3992
+ const size = useRef22({ width: 0, height: 0 });
3993
+ const lastTime = useRef22(0);
4272
3994
  const motion = useMotion();
4273
3995
  const id = useSvgId();
4274
3996
  const dots = useMemo3(() => {
@@ -4333,11 +4055,11 @@ function NeonWaves({
4333
4055
  comet.style.opacity = Math.min(1, Math.max(0, (x + 20) / ((W3 + 40) * 0.35))).toFixed(3);
4334
4056
  });
4335
4057
  };
4336
- const painter = useRef24(paint);
4058
+ const painter = useRef22(paint);
4337
4059
  painter.current = paint;
4338
4060
  useDecoration(ref, paint);
4339
- return /* @__PURE__ */ jsxs35("span", { ...mark("NeonWaves", p), ref: rootRef, "aria-hidden": "true", children: [
4340
- /* @__PURE__ */ jsxs35(
4061
+ return /* @__PURE__ */ jsxs31("span", { ...mark("NeonWaves", p), ref: rootRef, "aria-hidden": "true", children: [
4062
+ /* @__PURE__ */ jsxs31(
4341
4063
  "svg",
4342
4064
  {
4343
4065
  ref,
@@ -4345,14 +4067,14 @@ function NeonWaves({
4345
4067
  viewBox: `0 0 ${W3} ${H3}`,
4346
4068
  preserveAspectRatio: "none",
4347
4069
  children: [
4348
- /* @__PURE__ */ jsx44("defs", { children: /* @__PURE__ */ jsxs35("linearGradient", { id: `${id}-strand`, children: [
4349
- /* @__PURE__ */ jsx44("stop", { stopColor: "var(--ad-primary-700)", stopOpacity: "0" }),
4350
- /* @__PURE__ */ jsx44("stop", { offset: ".16", stopColor: "var(--ad-primary-600)", stopOpacity: ".35" }),
4351
- /* @__PURE__ */ jsx44("stop", { offset: ".57", stopColor: "var(--ad-red)", stopOpacity: ".74" }),
4352
- /* @__PURE__ */ jsx44("stop", { offset: ".78", stopColor: "var(--ad-pink)", stopOpacity: ".85" }),
4353
- /* @__PURE__ */ jsx44("stop", { offset: "1", stopColor: "var(--ad-primary-600)", stopOpacity: ".44" })
4070
+ /* @__PURE__ */ jsx39("defs", { children: /* @__PURE__ */ jsxs31("linearGradient", { id: `${id}-strand`, children: [
4071
+ /* @__PURE__ */ jsx39("stop", { stopColor: "var(--ad-primary-700)", stopOpacity: "0" }),
4072
+ /* @__PURE__ */ jsx39("stop", { offset: ".16", stopColor: "var(--ad-primary-600)", stopOpacity: ".35" }),
4073
+ /* @__PURE__ */ jsx39("stop", { offset: ".57", stopColor: "var(--ad-red)", stopOpacity: ".74" }),
4074
+ /* @__PURE__ */ jsx39("stop", { offset: ".78", stopColor: "var(--ad-pink)", stopOpacity: ".85" }),
4075
+ /* @__PURE__ */ jsx39("stop", { offset: "1", stopColor: "var(--ad-primary-600)", stopOpacity: ".44" })
4354
4076
  ] }) }),
4355
- Array.from({ length: strands }, (_, i) => /* @__PURE__ */ jsx44(
4077
+ Array.from({ length: strands }, (_, i) => /* @__PURE__ */ jsx39(
4356
4078
  "path",
4357
4079
  {
4358
4080
  className: "ad-neon-waves-strand",
@@ -4367,8 +4089,8 @@ function NeonWaves({
4367
4089
  ]
4368
4090
  }
4369
4091
  ),
4370
- comets > 0 && motion && /* @__PURE__ */ jsx44("span", { ref: cometsRef, className: "ad-neon-waves-comets", children: Array.from({ length: comets }, (_, i) => /* @__PURE__ */ jsx44("i", { className: "ad-neon-waves-comet" }, i)) }),
4371
- dots.map((d, i) => /* @__PURE__ */ jsx44(
4092
+ comets > 0 && motion && /* @__PURE__ */ jsx39("span", { ref: cometsRef, className: "ad-neon-waves-comets", children: Array.from({ length: comets }, (_, i) => /* @__PURE__ */ jsx39("i", { className: "ad-neon-waves-comet" }, i)) }),
4093
+ dots.map((d, i) => /* @__PURE__ */ jsx39(
4372
4094
  "i",
4373
4095
  {
4374
4096
  className: "ad-neon-waves-star",
@@ -4386,7 +4108,7 @@ function NeonWaves({
4386
4108
  }
4387
4109
 
4388
4110
  // src/components/artwork/QuantumField/QuantumField.tsx
4389
- import { useEffect as useEffect8, useRef as useRef25, useState as useState9 } from "react";
4111
+ import { useEffect as useEffect8, useRef as useRef23, useState as useState8 } from "react";
4390
4112
 
4391
4113
  // src/components/artwork/QuantumField/renderer.ts
4392
4114
  var TAU = Math.PI * 2;
@@ -4729,7 +4451,7 @@ function createQuantumFieldRenderer(canvas, mode, count) {
4729
4451
  }
4730
4452
 
4731
4453
  // src/components/artwork/QuantumField/QuantumField.tsx
4732
- import { jsx as jsx45, jsxs as jsxs36 } from "react/jsx-runtime";
4454
+ import { jsx as jsx40, jsxs as jsxs32 } from "react/jsx-runtime";
4733
4455
  var quantumFieldModes = ["orbit", "vortex", "lattice", "wave", "bloom", "helix", "terrain"];
4734
4456
  var PIXEL_BUDGET = { low: 32e4, high: 96e4 };
4735
4457
  var POINT_BUDGET = { low: 1400, high: 2800 };
@@ -4820,18 +4542,18 @@ function QuantumField({
4820
4542
  }) {
4821
4543
  const theme = useThemePalette();
4822
4544
  const motion = useMotion();
4823
- const canvas = useRef25(null);
4824
- const root = useRef25(null);
4825
- const paint = useRef25(() => void 0);
4826
- const pointer = useRef25({ x: 0, y: 0, active: false });
4827
- const camera = useRef25({ zoom: 3.5, yaw: 0, pitch: 0.16 });
4828
- const drag = useRef25(null);
4829
- const visible = useRef25(false);
4830
- const [onscreen, setOnscreen] = useState9(typeof IntersectionObserver === "undefined");
4831
- const frame = useRef25({ last: 0, time: 0 });
4832
- const connectedAudio = useRef25(null);
4833
- const live = useRef25({ speed, audio, palette, theme });
4834
- const updatePalette = useRef25(() => void 0);
4545
+ const canvas = useRef23(null);
4546
+ const root = useRef23(null);
4547
+ const paint = useRef23(() => void 0);
4548
+ const pointer = useRef23({ x: 0, y: 0, active: false });
4549
+ const camera = useRef23({ zoom: 3.5, yaw: 0, pitch: 0.16 });
4550
+ const drag = useRef23(null);
4551
+ const visible = useRef23(false);
4552
+ const [onscreen, setOnscreen] = useState8(typeof IntersectionObserver === "undefined");
4553
+ const frame = useRef23({ last: 0, time: 0 });
4554
+ const connectedAudio = useRef23(null);
4555
+ const live = useRef23({ speed, audio, palette, theme });
4556
+ const updatePalette = useRef23(() => void 0);
4835
4557
  const followsTheme = !palette?.[0] || !palette?.[1];
4836
4558
  live.current = { speed, audio, palette, theme };
4837
4559
  useEffect8(() => {
@@ -5029,7 +4751,7 @@ function QuantumField({
5029
4751
  doc.addEventListener("wheel", wheel, { passive: false, capture: true });
5030
4752
  return () => doc.removeEventListener("wheel", wheel, true);
5031
4753
  }, [interactive, motion, paused]);
5032
- return /* @__PURE__ */ jsxs36(
4754
+ return /* @__PURE__ */ jsxs32(
5033
4755
  "div",
5034
4756
  {
5035
4757
  ...mark("QuantumField", p),
@@ -5065,7 +4787,7 @@ function QuantumField({
5065
4787
  }
5066
4788
  } : void 0,
5067
4789
  children: [
5068
- /* @__PURE__ */ jsx45("canvas", { ref: canvas, "aria-hidden": "true" }),
4790
+ /* @__PURE__ */ jsx40("canvas", { ref: canvas, "aria-hidden": "true" }),
5069
4791
  p.children
5070
4792
  ]
5071
4793
  }
@@ -5073,13 +4795,13 @@ function QuantumField({
5073
4795
  }
5074
4796
 
5075
4797
  // src/components/artwork/QuantumField/QuantumFieldExperience.tsx
5076
- import { forwardRef as forwardRef3, useCallback, useEffect as useEffect9, useImperativeHandle, useLayoutEffect as useLayoutEffect5, useRef as useRef26, useState as useState10 } from "react";
4798
+ import { forwardRef, useCallback, useEffect as useEffect9, useImperativeHandle, useLayoutEffect as useLayoutEffect4, useRef as useRef24, useState as useState9 } from "react";
5077
4799
 
5078
4800
  // src/components/artwork/QuantumField/reference.html?raw
5079
- var reference_default = "<!DOCTYPE html>\n<!-- Quantum Fields Contributors, MIT License. Full notice: UPSTREAM-LICENSE.txt in this package. -->\n<html lang=\"en\">\n\n<head>\n <meta charset=\"UTF-8\">\n <title>Quantum Field: Interactive</title>\n <style>\n /* The kit hides this page's own interface, so it loads no web fonts: the scene makes no requests beyond three.js. */\n\n * {\n box-sizing: border-box;\n }\n\n :root {\n --ar: 167; --ag: 139; --ab: 250;\n }\n\n html, body {\n margin: 0;\n overflow: hidden;\n background: transparent !important;\n color-scheme: dark;\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n }\n\n canvas {\n display: block;\n }\n\n #scanlines {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: radial-gradient(ellipse at center, transparent 0%, transparent 50%, rgba(0, 0, 0, 0.5) 100%);\n pointer-events: none;\n z-index: 10;\n }\n\n #overlay {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n background: linear-gradient(180deg, rgba(15, 10, 25, 0.97) 0%, rgba(10, 10, 20, 0.98) 100%);\n z-index: 100;\n backdrop-filter: blur(20px);\n transition: opacity 1s ease;\n pointer-events: none;\n /* Allow clicks to pass through to canvas */\n }\n\n .hud-panel {\n background: linear-gradient(165deg, rgba(255, 255, 255, 0.04) 0%, rgba(255, 255, 255, 0.01) 100%);\n border: 1px solid rgba(255, 255, 255, 0.07);\n padding: 55px 75px;\n text-align: center;\n border-radius: 28px;\n box-shadow: 0 30px 80px rgba(0, 0, 0, 0.5);\n pointer-events: auto;\n /* Re-enable clicks for buttons */\n }\n\n h1 {\n font-family: 'Space Grotesk', sans-serif;\n font-weight: 500;\n letter-spacing: 1px;\n font-size: 34px;\n margin: 0 0 8px 0;\n background: linear-gradient(135deg, #ffffff 0%, rgba(var(--ar),var(--ag),var(--ab),0.75) 40%, rgb(var(--ar),var(--ag),var(--ab)) 100%);\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent;\n background-clip: text;\n }\n\n .hud-panel p {\n color: rgba(255, 255, 255, 0.35);\n font-size: 13px;\n margin-bottom: 40px;\n letter-spacing: 0.5px;\n font-weight: 300;\n }\n\n .btn-group {\n display: flex;\n gap: 14px;\n justify-content: center;\n }\n\n button,\n .file-upload {\n background: linear-gradient(135deg, rgba(var(--ar),var(--ag),var(--ab),0.12) 0%, rgba(var(--ar),var(--ag),var(--ab),0.06) 100%);\n border: 1px solid rgba(var(--ar),var(--ag),var(--ab),0.25);\n color: rgba(255, 255, 255, 0.85);\n padding: 15px 30px;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n letter-spacing: 0.3px;\n border-radius: 14px;\n font-family: 'Inter', sans-serif;\n }\n\n button:hover,\n .file-upload:hover {\n background: linear-gradient(135deg, rgba(var(--ar),var(--ag),var(--ab),0.25) 0%, rgba(var(--ar),var(--ag),var(--ab),0.18) 100%);\n border-color: rgba(var(--ar),var(--ag),var(--ab),0.45);\n transform: translateY(-2px);\n box-shadow: 0 12px 35px rgba(139, 92, 246, 0.2);\n }\n\n input[type=\"file\"] {\n display: none;\n }\n\n #status-bar {\n position: absolute;\n bottom: 80px;\n left: 28px;\n color: rgba(255, 255, 255, 0.3);\n font-size: 10px;\n letter-spacing: 0.5px;\n z-index: 200;\n pointer-events: none;\n }\n\n #status-bar span {\n color: rgba(196, 181, 253, 0.7);\n font-weight: 500;\n }\n\n #interaction-hint {\n position: absolute;\n top: 28px;\n width: 100%;\n text-align: center;\n color: rgba(255, 255, 255, 0.5);\n font-size: 11px;\n letter-spacing: 2px;\n pointer-events: none;\n z-index: 9;\n text-transform: lowercase;\n }\n\n #zoom-indicator {\n position: absolute;\n bottom: 80px;\n right: 28px;\n color: rgba(255, 255, 255, 0.3);\n font-size: 10px;\n z-index: 20;\n pointer-events: none;\n }\n\n #zoom-indicator span {\n color: rgba(196, 181, 253, 0.7);\n }\n\n #audio-controls {\n position: fixed;\n bottom: 24px;\n left: 50%;\n transform: translateX(-50%);\n display: flex;\n gap: 10px;\n align-items: center;\n padding: 12px 20px;\n background: linear-gradient(180deg, rgba(35, 28, 50, 0.88) 0%, rgba(22, 20, 38, 0.92) 100%);\n border: 1px solid rgba(255, 255, 255, 0.05);\n border-radius: 18px;\n z-index: 200;\n opacity: 0;\n pointer-events: none;\n transition: all 0.4s ease;\n backdrop-filter: blur(25px);\n }\n\n #audio-controls.visible {\n opacity: 1;\n pointer-events: auto;\n }\n\n #audio-controls button,\n #audio-controls .file-upload {\n padding: 9px 14px;\n font-size: 11px;\n background: rgba(255, 255, 255, 0.03);\n border: 1px solid rgba(255, 255, 255, 0.07);\n color: rgba(255, 255, 255, 0.65);\n border-radius: 10px;\n }\n\n #play-pause-btn {\n min-width: 78px;\n background: linear-gradient(135deg, rgba(var(--ar),var(--ag),var(--ab),0.18) 0%, rgba(var(--ar),var(--ag),var(--ab),0.10) 100%) !important;\n border-color: rgba(var(--ar),var(--ag),var(--ab),0.28) !important;\n color: #e9d5ff !important;\n }\n\n #play-pause-btn.playing {\n background: linear-gradient(135deg, rgba(251, 113, 133, 0.18) 0%, rgba(244, 114, 182, 0.12) 100%) !important;\n border-color: rgba(251, 113, 133, 0.35) !important;\n }\n\n #progress-container {\n width: 85px;\n height: 4px;\n background: rgba(255, 255, 255, 0.06);\n border-radius: 4px;\n cursor: pointer;\n }\n\n #progress-bar {\n height: 100%;\n background: linear-gradient(90deg, rgb(var(--ar),var(--ag),var(--ab)), rgba(var(--ar),var(--ag),var(--ab),0.6), rgba(var(--ar),var(--ag),var(--ab),0.3));\n border-radius: 4px;\n width: 0%;\n }\n\n #time-display {\n color: rgba(255, 255, 255, 0.35);\n font-size: 10px;\n min-width: 62px;\n text-align: center;\n }\n\n #track-name {\n color: rgba(255, 255, 255, 0.3);\n font-size: 10px;\n max-width: 85px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* --- Center Panel Refinement --- */\n .hud-panel {\n background: rgba(15, 10, 25, 0.85);\n border: 1px solid rgba(255, 255, 255, 0.08);\n /* Darkened container */\n padding: 24px;\n /* Reduced padding */\n border-radius: 24px;\n box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);\n backdrop-filter: blur(40px);\n min-width: 420px;\n /* Wider for buttons */\n }\n\n .hud-panel h1,\n .hud-panel p {\n display: none;\n /* Hide title as per screenshot showing only buttons */\n }\n\n .btn-group {\n display: flex;\n gap: 16px;\n justify-content: center;\n width: 100%;\n }\n\n .btn-group button {\n flex: 1;\n background: transparent;\n border: 1px solid rgba(var(--ar),var(--ag),var(--ab),0.3);\n color: rgb(var(--ar),var(--ag),var(--ab));\n padding: 14px 0;\n border-radius: 12px;\n font-size: 12px;\n letter-spacing: 1px;\n text-transform: uppercase;\n font-weight: 600;\n transition: all 0.3s ease;\n }\n\n .btn-group button:hover {\n background: rgba(var(--ar),var(--ag),var(--ab),0.1);\n border-color: rgb(var(--ar),var(--ag),var(--ab));\n box-shadow: 0 0 20px rgba(var(--ar),var(--ag),var(--ab),0.2);\n }\n #interaction-hint, #audio-controls, #status-bar, #zoom-indicator { display: none !important; }\n </style>\n</head>\n\n<body>\n <div id=\"scanlines\" style=\"display:none\"></div>\n <div id=\"interaction-hint\">space \xB7 play/pause &nbsp;\xB7&nbsp; scroll \xB7 zoom &nbsp;\xB7&nbsp; drag \xB7 rotate &nbsp;\xB7&nbsp; F \xB7 focus mode</div>\n <div id=\"overlay\" style=\"display:none\">\n <div class=\"hud-panel\">\n <h1>Quantum Fields</h1>\n <p>Audio-reactive particle simulation</p>\n <div class=\"btn-group\">\n <button id=\"enter-btn\">Enter</button>\n </div>\n </div>\n </div>\n <div id=\"audio-controls\">\n <button id=\"play-pause-btn\" class=\"playing\">\u23F8 pause</button>\n <div id=\"progress-container\">\n <div id=\"progress-bar\"></div>\n </div>\n <span id=\"time-display\">0:00 / 0:00</span>\n <span id=\"track-name\">no track</span>\n <label class=\"file-upload\" title=\"Load audio\">\u{1F4C1}<input type=\"file\" id=\"file-input-2\" accept=\"audio/*\"></label>\n <button id=\"mic-btn-2\" title=\"Microphone\">\u{1F399}</button>\n <button id=\"screenshot-btn\" title=\"Screenshot\">\u{1F4F7}</button>\n <button id=\"fullscreen-btn\" title=\"Fullscreen\">\u26F6</button>\n </div>\n <div id=\"status-bar\"><span>ready</span> \xB7 awaiting input</div>\n <div id=\"zoom-indicator\">zoom <span id=\"zoom-level\">100%</span></div>\n\n <!-- Global Error Handler (Must be before module imports) -->\n <script>\n window.onerror = function (msg, url, lineNo, columnNo, error) {\n const status = document.getElementById('status-bar');\n if (status) {\n status.innerHTML = `ERROR: <span style=\"color:#ff6b6b\">${msg}</span>`;\n status.style.opacity = 1;\n }\n console.error('Global Error:', msg, url, lineNo, error);\n return false;\n };\n // Catch unhandled promise rejections\n window.onunhandledrejection = function (event) {\n const status = document.getElementById('status-bar');\n if (status) {\n status.innerHTML = `ERROR: <span style=\"color:#ff6b6b\">${event.reason}</span>`;\n status.style.opacity = 1;\n }\n console.error('Unhandled Rejection:', event.reason);\n };\n <\/script>\n\n <script type=\"importmap\">\n{\n \"imports\": {\n \"three\": \"https://unpkg.com/three@0.182.0/build/three.module.js\",\n \"three/addons/\": \"https://unpkg.com/three@0.182.0/examples/jsm/\"\n }\n}\n<\/script>\n\n <script type=\"module\">\n import * as THREE from 'three';\n import { OrbitControls } from 'three/addons/controls/OrbitControls.js';\n import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';\n import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';\n import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';\n import { AfterimagePass } from 'three/addons/postprocessing/AfterimagePass.js';\n import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';\n // BokehPass removed - using custom DOF shader\n import { OutputPass } from 'three/addons/postprocessing/OutputPass.js';\n\n function fitPixelRatio() {\n return Math.min(window.devicePixelRatio || 1, 1.5,\n Math.sqrt(2_100_000 / Math.max(1, window.innerWidth * window.innerHeight)));\n }\n\n const CONFIG = {\n maxParticles: 250000,\n maxDensity: 0.14,\n secondaryParticles: 50000,\n initialDensity: 0.043,\n fieldRadius: 90,\n minZoom: 1,\n maxZoom: 250,\n defaultZoom: 200\n };\n let particleBudget = Number(window.frameElement?.getAttribute('data-particle-budget') ?? Infinity);\n particleBudget = Number.isFinite(particleBudget) ? Math.max(0, Math.floor(particleBudget)) : Infinity;\n // Requested number of particles across both layers; null follows the density setting.\n let particleCount = window.frameElement?.getAttribute('data-particle-count');\n particleCount = particleCount != null && Number.isFinite(Number(particleCount)) ? Math.max(0, Math.floor(Number(particleCount))) : null;\n\n const STATE = {\n field: 'Gluon (Strong)',\n density: CONFIG.initialDensity,\n fieldSize: 0.78,\n sensitivity: 1.1,\n trails: 0.66144,\n bloom: 0.3,\n timeScale: 1,\n pixelRatio: fitPixelRatio(),\n chromaticAberration: 0.004,\n filmGrain: 0.001,\n cameraShake: 0.65,\n vortexStrength: 0.73,\n pulseIntensity: 0.15,\n anamorphicStretch: 0.1,\n bassGateThreshold: 0.12,\n bassGateAttack: 0.3,\n bassGateRelease: 0.136,\n bassGateRatio: 2.142,\n bassGateEnabled: false,\n // NEW EFFECTS\n godRaysEnabled: false,\n godRaysIntensity: 0.078,\n godRaysDecay: 0.92,\n dofEnabled: false,\n dofFocus: 0.04,\n dofFocalLength: 0.40,\n dofBokehStrength: 0.36,\n lensFlareEnabled: true,\n lensFlareIntensity: 0.1,\n particleTrailsEnabled: true,\n particleTrailOpacity: 1,\n nebulaEnabled: false,\n nebulaIntensity: 0.3,\n connectionsEnabled: true,\n connectionThreshold: 20.0,\n connectionOpacity: 0.75,\n // === ENHANCEMENT PACK v2.0 ===\n // 1. Motion Blur\n motionBlurEnabled: true,\n motionBlurStrength: 0.1,\n // 2. Adaptive Quality\n adaptiveQualityEnabled: true,\n targetFPS: 45,\n qualityLevel: 1,\n // 3. Onset Detection\n onsetSensitivity: 1,\n onsetDecay: 0.92,\n // 4. Audio-Reactive Camera\n audioCameraEnabled: true,\n audioCameraSmoothing: 0.2,\n audioCameraIntensity: 0.3,\n // 5. Waveform Terrain\n waveformTerrainScale: 1.0,\n waveformTerrainHeight: 20.0\n };\n\n // --- AUDIO SYSTEM ---\n // Spectrum supplied by the host app (e.g. a native audio engine the iframe cannot hear):\n // band levels 0..1, log-spaced from `low` to `high` Hz. It is turned into the same frequency\n // array an analyser would give, so bands, beats, flux, onsets and the terrain all follow it.\n const EXTERNAL = {\n target: null, low: 40, high: 12000,\n sampleRate: 48000, data: new Uint8Array(2048), smoothed: new Float32Array(2048), prev: new Float32Array(2048),\n fresh() { return this.target !== null; },\n set(bands, range) {\n this.target = Array.isArray(bands) && bands.length ? bands.map((v) => Math.max(0, Math.min(1, Number(v) || 0))) : null;\n if (Array.isArray(range) && range[0] > 0 && range[1] > range[0]) [this.low, this.high] = range;\n },\n // Host frames arrive at their own pace (often 20 Hz); easing toward them every rendered\n // frame keeps the flux continuous, like an analyser's smoothing does.\n fill() {\n const bands = this.target, n = bands.length, len = this.data.length, nyquist = this.sampleRate / 2;\n const span = Math.log(this.high / this.low);\n for (let i = 0; i < len; i++) {\n const freq = Math.max(1, (i + 0.5) / len * nyquist);\n const pos = Math.log(freq / this.low) / span * (n - 1);\n let level;\n if (pos <= 0) level = bands[0];\n else if (pos >= n - 1) level = bands[n - 1] * Math.max(0, 1 - Math.log(freq / this.high) / Math.log(nyquist / this.high));\n else { const k = Math.floor(pos), t = pos - k; level = bands[k] * (1 - t) + bands[k + 1] * t; }\n this.smoothed[i] += (level - this.smoothed[i]) * 0.35;\n this.data[i] = Math.min(255, Math.round(this.smoothed[i] * 255));\n }\n }\n };\n\n const AUDIO = {\n ctx: null, analyser: null, source: null, gainNode: null, data: null,\n audioBuffer: null, isPlaying: false, isPaused: false,\n startTime: 0, pauseTime: 0, currentTrackName: 'No Track',\n mode: null, micStream: null,\n bands: { subBass: 0, bass: 0, lowMid: 0, mid: 0, highMid: 0, high: 0, ultraHigh: 0 },\n gatedBands: { subBass: 0, bass: 0, lowMid: 0, mid: 0, highMid: 0, high: 0, ultraHigh: 0 },\n beatThreshold: 0.15, beatDecay: 0.93, beatEnergy: 0,\n lastBeatTime: 0, beatDetected: false, peakHistory: [],\n spectralCentroid: 0, spectralFlux: 0, prevSpectrum: null, active: false, idleCleared: false,\n // === ENHANCEMENT: Onset Detection ===\n onsetEnergy: 0, onsetDetected: false, onsetHistory: [], onsetThreshold: 0,\n // === ENHANCEMENT: Waveform Data ===\n waveformData: new Float32Array(128), waveformSmoothed: new Float32Array(128),\n gateState: {\n isOpen: false, envelope: 0, holdTime: 0, noiseFloor: 0.05,\n calibrationSamples: [], isCalibrating: true, calibrationFrames: 30\n },\n\n init() {\n if (this.ctx) return;\n this.ctx = new (window.AudioContext || window.webkitAudioContext)();\n this.analyser = this.ctx.createAnalyser();\n this.analyser.fftSize = 4096;\n this.analyser.smoothingTimeConstant = 0.65;\n this.data = new Uint8Array(this.analyser.frequencyBinCount);\n this.prevSpectrum = new Float32Array(this.analyser.frequencyBinCount);\n this.active = true;\n },\n\n loadTrack(arrayBuffer, fileName) {\n return new Promise((resolve, reject) => {\n this.ctx.decodeAudioData(arrayBuffer, (buffer) => {\n this.stopCurrentSource();\n this.audioBuffer = buffer;\n this.currentTrackName = fileName || 'Unknown';\n this.mode = 'file';\n this.pauseTime = 0;\n this.resetCalibration();\n resolve(buffer);\n }, reject);\n });\n },\n\n play() {\n if (!this.audioBuffer || this.mode !== 'file' || this.isPlaying) return;\n this.source = this.ctx.createBufferSource();\n this.source.buffer = this.audioBuffer;\n this.source.loop = true;\n this.source.connect(this.analyser);\n this.analyser.connect(this.ctx.destination);\n this.source.start(0, this.pauseTime);\n this.startTime = this.ctx.currentTime - this.pauseTime;\n this.isPlaying = true;\n this.isPaused = false;\n },\n\n pause() {\n if (!this.isPlaying || this.mode !== 'file') return;\n this.pauseTime = this.getCurrentTime();\n this.isPaused = true;\n this.isPlaying = false;\n if (this.source) { try { this.source.stop(); } catch (e) { } this.source.disconnect(); this.source = null; }\n },\n\n togglePlayPause() {\n if (this.mode === 'mic') return false;\n if (this.isPlaying) { this.pause(); return false; }\n else { this.play(); return true; }\n },\n\n getCurrentTime() {\n if (!this.isPlaying) return this.pauseTime;\n if (!this.audioBuffer) return 0;\n return (this.ctx.currentTime - this.startTime) % this.audioBuffer.duration;\n },\n\n getDuration() { return this.audioBuffer ? this.audioBuffer.duration : 0; },\n\n seek(time) {\n if (this.mode !== 'file' || !this.audioBuffer) return;\n const wasPlaying = this.isPlaying;\n if (wasPlaying) this.pause();\n this.pauseTime = Math.max(0, Math.min(time, this.audioBuffer.duration));\n if (wasPlaying) this.play();\n },\n\n stopCurrentSource() {\n if (this.source) { try { this.source.stop(); this.source.disconnect(); } catch (e) { } this.source = null; }\n if (this.gainNode) { try { this.gainNode.disconnect(); } catch (e) { } this.gainNode = null; }\n if (this.micStream) { this.micStream.getTracks().forEach(t => t.stop()); this.micStream = null; }\n this.isPlaying = false; this.isPaused = false; this.pauseTime = 0;\n },\n\n async startMic(preferredLabel = null) {\n console.log('startMic called with preferredLabel:', preferredLabel);\n const status = document.getElementById('status-bar');\n if (status) status.innerHTML = 'SYSTEM: <span>REQUESTING ACCESS...</span>';\n\n try {\n // Ensure context is running\n if (this.ctx.state === 'suspended') {\n console.log('Resuming AudioContext...');\n await this.ctx.resume();\n }\n\n // 1. Get initial stream (activates permissions so labels are visible)\n console.log('Requesting initial getUserMedia...');\n let stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n console.log('Initial stream obtained:', stream.id);\n\n // 2. Search for preferred device (loopback)\n let target = null;\n if (preferredLabel) {\n const devices = await navigator.mediaDevices.enumerateDevices();\n console.log(\"Audio Devices Found:\", devices.length, devices.map(d => `${d.label} (${d.kind})`));\n\n target = devices.find(d => d.kind === 'audioinput' &&\n d.label.toLowerCase().includes(preferredLabel.toLowerCase()));\n }\n\n if (target) {\n console.log(`Switching audio input to: ${target.label} [${target.deviceId}]`);\n stream.getTracks().forEach(t => t.stop()); // Stop generic stream\n\n // Request specific device - being explicit with constraints\n stream = await navigator.mediaDevices.getUserMedia({\n audio: {\n deviceId: { exact: target.deviceId },\n echoCancellation: false,\n noiseSuppression: false,\n autoGainControl: false,\n channelCount: 2\n }\n });\n this.currentTrackName = target.label;\n } else {\n if (preferredLabel) console.warn(`Preferred device '${preferredLabel}' not found. Using default.`);\n // Keep the initial stream\n this.currentTrackName = stream.getAudioTracks()[0]?.label || 'Live Microphone';\n }\n\n this.stopCurrentSource();\n this.micStream = stream;\n this.source = this.ctx.createMediaStreamSource(stream);\n this.gainNode = this.ctx.createGain();\n this.gainNode.gain.value = 3.0; // Boost loopback/mic signal before FFT \u2014 tune via sensitivity slider\n this.source.connect(this.gainNode);\n this.gainNode.connect(this.analyser);\n this.mode = 'mic';\n this.isPlaying = true;\n this.resetCalibration();\n\n if (status) status.innerHTML = 'SYSTEM: <span>LISTENING (' + (preferredLabel ? 'Loopback' : 'Mic') + ')</span>';\n updateUI();\n\n return stream;\n } catch (e) {\n console.error(\"AUDIO.startMic failed:\", e);\n throw e;\n }\n },\n\n resetCalibration() { this.gateState.isCalibrating = true; this.gateState.calibrationSamples = []; },\n\n applyBassGate(rawValue) {\n if (!STATE.bassGateEnabled) return rawValue;\n const { bassGateThreshold: threshold, bassGateAttack: attack, bassGateRelease: release, bassGateRatio: ratio } = STATE;\n if (this.gateState.isCalibrating) {\n this.gateState.calibrationSamples.push(rawValue);\n if (this.gateState.calibrationSamples.length >= this.gateState.calibrationFrames) {\n const sorted = [...this.gateState.calibrationSamples].sort((a, b) => a - b);\n this.gateState.noiseFloor = sorted.slice(0, Math.floor(sorted.length * 0.2)).reduce((a, b) => a + b, 0) / Math.floor(sorted.length * 0.2);\n this.gateState.isCalibrating = false;\n }\n }\n const effectiveThreshold = Math.max(threshold, this.gateState.noiseFloor * 1.5);\n if (rawValue > effectiveThreshold) { this.gateState.isOpen = true; this.gateState.holdTime = 10; }\n else if (rawValue < effectiveThreshold * 0.7 && this.gateState.holdTime <= 0) { this.gateState.isOpen = false; }\n if (this.gateState.holdTime > 0) this.gateState.holdTime--;\n this.gateState.envelope += ((this.gateState.isOpen ? 1.0 : 0.0) - this.gateState.envelope) * (this.gateState.isOpen ? attack : release);\n let output = rawValue;\n if (this.gateState.isOpen && rawValue > effectiveThreshold) {\n output = effectiveThreshold + (rawValue - effectiveThreshold) / ratio;\n }\n return output * this.gateState.envelope;\n },\n\n update() {\n const own = this.active && this.isPlaying && this.source;\n this.externalActive = !own && EXTERNAL.fresh();\n if (!own && !this.externalActive) {\n if (!this.idleCleared) {\n for (const band in this.bands) this.bands[band] = this.gatedBands[band] = 0;\n this.spectralCentroid = this.spectralFlux = 0;\n this.beatDetected = this.onsetDetected = false;\n this.idleCleared = true;\n }\n this.beatEnergy *= this.beatDecay;\n this.onsetEnergy *= STATE.onsetDecay;\n return;\n }\n this.idleCleared = false;\n let data = this.data, prevSpectrum = this.prevSpectrum, nyquist;\n if (this.externalActive) {\n EXTERNAL.fill();\n data = EXTERNAL.data; prevSpectrum = EXTERNAL.prev; nyquist = EXTERNAL.sampleRate / 2;\n } else {\n this.analyser.getByteFrequencyData(this.data);\n nyquist = this.ctx.sampleRate / 2;\n }\n const len = data.length;\n const getFreqIndex = (freq) => Math.round(freq / nyquist * len);\n const ranges = { subBass: [50, 120], bass: [120, 200], lowMid: [200, 450], mid: [450, 2000], highMid: [2000, 3500], high: [3500, 12000], ultraHigh: [12000, 20000] };\n let totalEnergy = 0, weightedFreqSum = 0;\n for (const [band, [low, high]] of Object.entries(ranges)) {\n const lowIdx = getFreqIndex(low), highIdx = getFreqIndex(high);\n let sum = 0;\n for (let i = lowIdx; i < highIdx && i < len; i++) {\n const v = data[i] / 255.0;\n sum += v; totalEnergy += v; weightedFreqSum += v * i;\n }\n const rawBand = sum / (highIdx - lowIdx);\n this.bands[band] = rawBand;\n this.gatedBands[band] = (band === 'subBass' || band === 'bass' || band === 'lowMid') ? this.applyBassGate(rawBand) : rawBand;\n }\n this.spectralCentroid = totalEnergy > 0 ? weightedFreqSum / totalEnergy / len : 0;\n let flux = 0;\n for (let i = 0; i < len; i++) {\n const current = data[i] / 255.0, diff = current - prevSpectrum[i];\n flux += diff > 0 ? diff * diff : 0;\n prevSpectrum[i] = current;\n }\n this.spectralFlux = Math.sqrt(flux / len);\n const currentEnergy = this.gatedBands.bass * 0.6 + this.gatedBands.subBass * 0.4;\n this.peakHistory.push(currentEnergy);\n if (this.peakHistory.length > 50) this.peakHistory.shift();\n const avgEnergy = this.peakHistory.reduce((a, b) => a + b, 0) / this.peakHistory.length;\n const variance = this.peakHistory.reduce((a, b) => a + Math.pow(b - avgEnergy, 2), 0) / this.peakHistory.length;\n const dynamicThreshold = avgEnergy + Math.sqrt(variance) * 1.8;\n const now = performance.now();\n if (currentEnergy > dynamicThreshold && currentEnergy > this.beatThreshold && now - this.lastBeatTime > 120) {\n this.beatDetected = true; this.beatEnergy = 1.0; this.lastBeatTime = now;\n } else { this.beatDetected = false; }\n this.beatEnergy *= this.beatDecay;\n\n // === ENHANCEMENT: Improved Onset Detection ===\n // Uses spectral flux derivative for sharper transient response\n const onsetSens = STATE.onsetSensitivity;\n this.onsetHistory.push(this.spectralFlux);\n if (this.onsetHistory.length > 8) this.onsetHistory.shift();\n const recentAvg = this.onsetHistory.slice(-4).reduce((a, b) => a + b, 0) / 4;\n const olderAvg = this.onsetHistory.slice(0, 4).reduce((a, b) => a + b, 0) / Math.max(1, this.onsetHistory.slice(0, 4).length);\n const onsetDelta = (recentAvg - olderAvg) * onsetSens;\n this.onsetDetected = onsetDelta > 0.015 && now - this.lastBeatTime > 50;\n this.onsetEnergy = this.onsetDetected ? 1.0 : this.onsetEnergy * STATE.onsetDecay;\n\n // === ENHANCEMENT: Waveform Data for Terrain Field ===\n const waveformBins = 128;\n const binSize = Math.floor(len / waveformBins);\n for (let i = 0; i < waveformBins; i++) {\n let sum = 0;\n for (let j = 0; j < binSize; j++) {\n sum += data[i * binSize + j] / 255.0;\n }\n const target = sum / binSize;\n this.waveformSmoothed[i] += (target - this.waveformSmoothed[i]) * 0.3;\n this.waveformData[i] = this.waveformSmoothed[i];\n }\n }\n };\n\n // --- SCENE SETUP ---\n const scene = new THREE.Scene();\n scene.fog = new THREE.FogExp2(0x000508, 0.005);\n const camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 0.1, 800);\n camera.position.set(0, 12, CONFIG.defaultZoom);\n const cameraShakeOffset = new THREE.Vector3();\n const baseCameraPos = camera.position.clone();\n const zoomIndicator = document.getElementById('zoom-level');\n\n const renderer = new THREE.WebGLRenderer({ powerPreference: \"high-performance\", antialias: true, alpha: true });\n renderer.setClearColor(0x000000, 0);\n renderer.setSize(window.innerWidth, window.innerHeight);\n renderer.setPixelRatio(STATE.pixelRatio);\n renderer.toneMapping = THREE.ACESFilmicToneMapping;\n renderer.toneMappingExposure = 2.1;\n document.body.appendChild(renderer.domElement);\n\n const controls = new OrbitControls(camera, renderer.domElement);\n controls.enableDamping = true;\n controls.dampingFactor = 0.06;\n controls.autoRotate = false;\n controls.autoRotateSpeed = 0.3;\n controls.minDistance = CONFIG.minZoom;\n controls.maxDistance = CONFIG.maxZoom;\n controls.maxPolarAngle = Math.PI * 0.85;\n controls.minPolarAngle = Math.PI * 0.15;\n\n controls.addEventListener('change', () => {\n const dist = camera.position.length();\n const zoomPercent = Math.round((1 - (dist - CONFIG.minZoom) / (CONFIG.maxZoom - CONFIG.minZoom)) * 100);\n zoomIndicator.textContent = Math.max(0, Math.min(100, zoomPercent)) + '%';\n baseCameraPos.copy(camera.position);\n });\n\n const raycaster = new THREE.Raycaster();\n const mouse = new THREE.Vector2(9999, 9999);\n const plane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0);\n const mouseWorldPos = new THREE.Vector3();\n let mouseVelocity = new THREE.Vector2();\n let prevMouse = new THREE.Vector2();\n\n window.addEventListener('mousemove', (e) => {\n const newMouse = new THREE.Vector2((e.clientX / window.innerWidth) * 2 - 1, -(e.clientY / window.innerHeight) * 2 + 1);\n mouseVelocity.subVectors(newMouse, prevMouse);\n prevMouse.copy(newMouse);\n mouse.copy(newMouse);\n raycaster.setFromCamera(mouse, camera);\n const target = new THREE.Vector3();\n raycaster.ray.intersectPlane(plane, target);\n if (target) mouseWorldPos.copy(target);\n });\n\n window.addEventListener('keydown', (e) => {\n if (e.key === '+' || e.key === '=') { camera.position.multiplyScalar(0.9); controls.update(); }\n else if (e.key === '-' || e.key === '_') { camera.position.multiplyScalar(1.1); controls.update(); }\n else if (e.key === '0') { camera.position.normalize().multiplyScalar(CONFIG.defaultZoom); controls.update(); }\n else if (e.key === ' ') { e.preventDefault(); togglePlayPause(); }\n else if (e.key === 'f' || e.key === 'F') { STATE.dofEnabled = !STATE.dofEnabled; customDOFPass.enabled = STATE.dofEnabled; }\n });\n\n // Every field type shares one screen-fitted footprint; switching presets never reallocates particles.\n // uFieldFill: share of the viewport the field spans; above 1.0 it runs past the edges.\n const shapeFunction = `\nuniform float uFieldFill;\nvec3 shapedPosition(vec3 seed) {\n vec2 plane = seed.xy * 2.0 - 1.0;\n return vec3(plane.x * uViewportAspect * uViewportHalfHeight * uFieldFill,\n plane.y * uViewportHalfHeight * uFieldFill,\n (seed.z - 0.5) * 12.0);\n}`;\n\n // --- PARTICLE VERTEX SHADER ---\n const vertexShader = `\nuniform float uTime, uPixelRatio, uSizeBase, uNoiseScale, uCurlStrength;\nuniform float uViewportAspect, uViewportHalfHeight;\nuniform vec3 uColor1, uColor2, uColor3;\nuniform float uSubBass, uBass, uLowMid, uMid, uHighMid, uHigh, uUltraHigh;\nuniform float uBeatEnergy, uSpectralCentroid, uSpectralFlux;\nuniform float uOnsetEnergy, uTerrainMode, uTerrainHeight;\nuniform float uVortexStrength, uPulseIntensity, uZoomFactor;\nuniform vec3 uMousePos;\nuniform vec2 uMouseVelocity;\nattribute vec3 aRandom;\nattribute float aPhase, aLayer;\nvarying vec3 vColor;\nvarying float vAlpha, vEnergy, vDepth, vRimLight, vGlint;\n\nvec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }\nvec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }\nvec4 permute(vec4 x) { return mod289(((x*34.0)+1.0)*x); }\nvec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }\n\nfloat snoise(vec3 v) {\n const vec2 C = vec2(1.0/6.0, 1.0/3.0);\n const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);\n vec3 i = floor(v + dot(v, C.yyy));\n vec3 x0 = v - i + dot(i, C.xxx);\n vec3 g = step(x0.yzx, x0.xyz);\n vec3 l = 1.0 - g;\n vec3 i1 = min(g.xyz, l.zxy);\n vec3 i2 = max(g.xyz, l.zxy);\n vec3 x1 = x0 - i1 + C.xxx;\n vec3 x2 = x0 - i2 + C.yyy;\n vec3 x3 = x0 - D.yyy;\n i = mod289(i);\n vec4 p = permute(permute(permute(i.z + vec4(0.0, i1.z, i2.z, 1.0)) + i.y + vec4(0.0, i1.y, i2.y, 1.0)) + i.x + vec4(0.0, i1.x, i2.x, 1.0));\n float n_ = 0.142857142857;\n vec3 ns = n_ * D.wyz - D.xzx;\n vec4 j = p - 49.0 * floor(p * ns.z * ns.z);\n vec4 x_ = floor(j * ns.z);\n vec4 y_ = floor(j - 7.0 * x_);\n vec4 x = x_ * ns.x + ns.yyyy;\n vec4 y = y_ * ns.x + ns.yyyy;\n vec4 h = 1.0 - abs(x) - abs(y);\n vec4 b0 = vec4(x.xy, y.xy);\n vec4 b1 = vec4(x.zw, y.zw);\n vec4 s0 = floor(b0) * 2.0 + 1.0;\n vec4 s1 = floor(b1) * 2.0 + 1.0;\n vec4 sh = -step(h, vec4(0.0));\n vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;\n vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww;\n vec3 p0 = vec3(a0.xy, h.x);\n vec3 p1 = vec3(a0.zw, h.y);\n vec3 p2 = vec3(a1.xy, h.z);\n vec3 p3 = vec3(a1.zw, h.w);\n vec4 norm = taylorInvSqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));\n p0 *= norm.x; p1 *= norm.y; p2 *= norm.z; p3 *= norm.w;\n vec4 m = max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0);\n m = m * m;\n return 42.0 * dot(m * m, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3)));\n}\n\nvec3 curl(float x, float y, float z) {\n float eps = 0.08;\n return vec3(\n snoise(vec3(x, y+eps, z)) - snoise(vec3(x, y-eps, z)),\n snoise(vec3(x, y, z+eps)) - snoise(vec3(x, y, z-eps)),\n snoise(vec3(x+eps, y, z)) - snoise(vec3(x-eps, y, z))\n );\n}\n\n${shapeFunction}\n\nvoid main() {\n float t = uTime * (1.0 + uMid * 0.5);\n vec3 noisePos = position * 0.035 + aRandom;\n float layerMod = 1.0 + aLayer * 0.5;\n\n vec3 flow1 = curl(noisePos.x * uNoiseScale + t*0.1, noisePos.y * uNoiseScale, noisePos.z * uNoiseScale);\n vec3 flow2 = curl(noisePos.x * uNoiseScale * 2.2 + t*0.07, noisePos.y * uNoiseScale * 2.2 + t*0.08, noisePos.z * uNoiseScale * 2.2) * 0.4;\n vec3 flow3 = curl(noisePos.x * uNoiseScale * 0.5 - t*0.05, noisePos.y * uNoiseScale * 0.5, noisePos.z * uNoiseScale * 0.5) * 0.6 * uLowMid;\n vec3 flow = flow1 + flow2 * uHigh + flow3;\n\n vec3 newPos = shapedPosition(aRandom) + (flow * uCurlStrength * layerMod * (1.0 + uSpectralFlux * 2.5));\n\n float vortexAngle = uVortexStrength * uBass * 0.45 * (0.75 + aRandom.z * 0.5);\n float cosA = cos(vortexAngle), sinA = sin(vortexAngle);\n vec2 rotated = vec2(newPos.x * cosA - newPos.z * sinA, newPos.x * sinA + newPos.z * cosA);\n newPos.xz = mix(newPos.xz, rotated, uBass * uVortexStrength);\n\n float yVortex = sin(newPos.y * 0.1 + uTime) * uMid * uVortexStrength * 2.0;\n newPos.x += cos(newPos.y * 0.15) * yVortex;\n newPos.z += sin(newPos.y * 0.15) * yVortex;\n\n float distToMouse = distance(newPos, uMousePos);\n float repulsion = smoothstep(25.0, 0.0, distToMouse);\n vec3 repelDir = normalize(newPos - uMousePos + vec3(0.001));\n newPos += repelDir * repulsion * (12.0 + length(uMouseVelocity) * 360.0);\n\n // Audio changes depth throughout the plane, without evacuating its centre.\n float beatWave = sin(aRandom.x * 12.0 + aRandom.y * 7.0 - uTime * 6.0);\n float bassDepth = (beatWave * uBeatEnergy * uPulseIntensity * 3.5\n + sin(uTime * 1.8 + aPhase) * uSubBass * 4.0\n + smoothstep(0.3, 0.8, uBass) * 6.0\n + uOnsetEnergy * 8.0) * (0.6 + aRandom.z * 0.4);\n newPos.z += bassDepth;\n float fieldWave = sin(aRandom.x * 11.0 - uTime * 0.8 + sin(aRandom.y * 7.0 + uTime * 0.35));\n newPos.z += fieldWave * (1.8 + uBass * 2.4 + uBeatEnergy * 1.6);\n\n // Waveform relief moves toward the camera while retaining the shared rectangular footprint.\n if (uTerrainMode > 0.5) {\n float freqBin = aRandom.x;\n float waveHeight = uBass * sin(freqBin * 6.28 * 8.0 + uTime * 3.0) * 0.5;\n waveHeight += uMid * sin(freqBin * 6.28 * 16.0 + uTime * 5.0) * 0.3;\n waveHeight += uHigh * sin(freqBin * 6.28 * 32.0 + uTime * 8.0) * 0.2;\n waveHeight *= (1.0 + uSpectralFlux * 3.0);\n newPos.z += waveHeight * uTerrainHeight;\n newPos.z += sin(aRandom.y * 6.28 * 4.0 + uTime) * uSubBass * 5.0;\n }\n\n vec4 mvPosition = modelViewMatrix * vec4(newPos, 1.0);\n gl_Position = projectionMatrix * mvPosition;\n\n float size = uSizeBase * (0.85 + uBass * 0.5 + uBeatEnergy * 0.5) * (1.0 + (uZoomFactor - 1.0) * 0.3);\n size *= 1.0 + sin(uTime * 10.0 + aPhase * 6.28) * uHigh * 0.35;\n vGlint = smoothstep(0.985, 0.998, aRandom.z) * (0.7 + 0.3 * sin(uTime * 3.0 + aPhase * 6.28)) * (0.75 + uHigh * 0.15 + uOnsetEnergy * 0.1);\n size *= 0.85 + vGlint * 2.1;\n gl_PointSize = clamp(max(size * uPixelRatio * (90.0 / -mvPosition.z) * layerMod, vGlint * 4.5 * uPixelRatio), 0.5, 120.0);\n\n float colorMix = smoothstep(0.0, 2.8, length(flow) + uHighMid * 3.0);\n vec3 baseColor = mix(uColor1, uColor2, colorMix);\n baseColor = mix(baseColor, uColor3, uSpectralCentroid * uHigh * 2.5);\n\n float energyLevel = uBeatEnergy * 0.35 + repulsion * uMid * 2.0 + uSpectralFlux * 0.8;\n vColor = mix(baseColor, vec3(1.0), energyLevel * 0.4);\n vColor += vec3(0.08, 0.04, 0.12) * uUltraHigh * 2.5;\n\n vRimLight = pow(1.0 - max(0.0, dot(normalize(-mvPosition.xyz), normalize(newPos))), 3.0);\n vEnergy = energyLevel;\n vDepth = clamp(-mvPosition.z / 120.0, 0.0, 1.0);\n\n float alpha = 1.0 - smoothstep(0.92, 1.05, max(abs(newPos.x) / (uViewportAspect * uViewportHalfHeight * uFieldFill * 1.05), abs(newPos.y) / (uViewportHalfHeight * uFieldFill * 1.05)));\n float sparkle = 1.0 + (aRandom.x > 0.8 ? sin(uTime * 20.0 + aRandom.y * 150.0) * uHigh * 2.5 : 0.0);\n vAlpha = alpha * sparkle * (0.7 + uMid * 0.35) * layerMod;\n}`;\n\n const fragmentShader = `\nvarying vec3 vColor;\nvarying float vAlpha, vEnergy, vDepth, vRimLight, vGlint;\n\nvoid main() {\n vec2 xy = gl_PointCoord.xy - vec2(0.5);\n float r = length(xy);\n if(r > 0.5) discard;\n\n float core = smoothstep(0.16 - vEnergy * 0.04, 0.12 - vEnergy * 0.04, r);\n float glow = exp(-r * r * (20.0 - vEnergy * 10.0));\n float halo = exp(-r * r * 5.0) * 0.35 + exp(-r * r * 2.5) * 0.15;\n float glint = vGlint * (1.0 - smoothstep(0.012, 0.045, min(abs(xy.x), abs(xy.y)))) * (1.0 - smoothstep(0.16, 0.48, r));\n\n vec3 finalColor = mix(vColor, vec3(1.0), core * 0.58 + glint * 0.38);\n finalColor += vColor * vRimLight * 0.1;\n finalColor = mix(finalColor, vec3(0.02, 0.03, 0.05), vDepth * 0.4);\n\n float finalAlpha = clamp(vAlpha * (glow * 0.85 + halo * 0.55 + pow(1.0 - r * 2.0, 2.5) * 0.55 + glint * 0.9), 0.0, 1.0);\n gl_FragColor = vec4(finalColor, finalAlpha);\n}`;\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550 POST-PROCESSING SHADERS \u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n // GOD RAYS SHADER (Radial Light Scattering)\n const GodRaysShader = {\n uniforms: {\n tDiffuse: { value: null },\n uCenter: { value: new THREE.Vector2(0.5, 0.5) },\n uIntensity: { value: 0.35 },\n uDecay: { value: 0.94 },\n uBeatEnergy: { value: 0.0 }\n },\n vertexShader: `varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`,\n fragmentShader: `\n uniform sampler2D tDiffuse;\n uniform vec2 uCenter;\n uniform float uIntensity, uDecay, uBeatEnergy;\n varying vec2 vUv;\n\n void main() {\n vec4 color = texture2D(tDiffuse, vUv);\n vec2 dir = vUv - uCenter;\n float dist = length(dir);\n dir = normalize(dir);\n\n vec3 rays = vec3(0.0);\n float decay = 1.0;\n\n for(int i = 0; i < 32; i++) {\n float t = float(i) / 32.0;\n vec2 samplePos = vUv - dir * t * 0.15 * (1.0 + uBeatEnergy);\n vec4 s = texture2D(tDiffuse, samplePos);\n float lum = dot(s.rgb, vec3(0.3, 0.59, 0.11));\n rays += s.rgb * decay * step(0.35, lum);\n decay *= uDecay;\n }\n rays /= 16.0;\n rays *= (1.0 - smoothstep(0.3, 0.9, dist));\n\n gl_FragColor = vec4(color.rgb + rays * uIntensity * (1.0 + uBeatEnergy * 0.8), color.a);\n }`\n };\n\n // LENS FLARE POST-PROCESS SHADER (Cinematic)\n const LensFlareShader = {\n uniforms: {\n tDiffuse: { value: null },\n uIntensity: { value: 1.0 },\n uBeatEnergy: { value: 0.0 },\n uTime: { value: 0.0 },\n uTint: { value: new THREE.Color(0.6, 0.7, 1.0) }\n },\n vertexShader: `varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`,\n fragmentShader: `\n uniform sampler2D tDiffuse;\n uniform float uIntensity, uBeatEnergy, uTime;\n uniform vec3 uTint;\n varying vec2 vUv;\n\n float sdHex(vec2 p) {\n p = abs(p);\n return max(p.x * 0.866 + p.y * 0.5, p.y) - 1.0;\n }\n\n void main() {\n vec4 color = texture2D(tDiffuse, vUv);\n vec2 center = vec2(0.5);\n vec2 toCenter = center - vUv;\n float dist = length(toCenter);\n vec2 dir = normalize(toCenter + 0.0001);\n\n vec3 flare = vec3(0.0);\n\n // Sample bright areas for flare source\n float brightness = dot(color.rgb, vec3(0.299, 0.587, 0.114));\n float threshold = smoothstep(0.6, 0.9, brightness);\n\n // Anamorphic horizontal streak\n float streak = 0.0;\n for(float i = -8.0; i <= 8.0; i += 1.0) {\n vec2 streakUV = vUv + vec2(i * 0.012, 0.0);\n vec4 streakSample = texture2D(tDiffuse, streakUV);\n float streakBright = dot(streakSample.rgb, vec3(0.299, 0.587, 0.114));\n float falloff = 1.0 - abs(i) / 10.0;\n streak += smoothstep(0.7, 1.0, streakBright) * falloff * falloff;\n }\n flare += uTint * streak * 0.08;\n\n // Subtle ghosts (reflections through lens elements)\n for(int i = 1; i <= 3; i++) {\n float scale = 1.0 - float(i) * 0.25;\n vec2 ghostUV = center + (center - vUv) * scale * 0.8;\n if(ghostUV.x > 0.0 && ghostUV.x < 1.0 && ghostUV.y > 0.0 && ghostUV.y < 1.0) {\n vec4 ghostSample = texture2D(tDiffuse, ghostUV);\n float ghostBright = dot(ghostSample.rgb, vec3(0.299, 0.587, 0.114));\n float ghostAlpha = smoothstep(0.5, 0.8, ghostBright) * (0.25 / float(i));\n vec3 ghostTint = mix(vec3(1.0), uTint, 0.6) * (1.0 - float(i) * 0.2);\n flare += ghostSample.rgb * ghostTint * ghostAlpha;\n }\n }\n\n // Soft circular halo around bright center\n float halo = smoothstep(0.25, 0.15, abs(dist - 0.2)) * threshold * 0.25;\n flare += uTint * 0.85 * halo;\n\n // Subtle vignette-edge glow\n float edgeGlow = smoothstep(0.5, 0.7, dist) * (1.0 - smoothstep(0.7, 0.85, dist));\n flare += uTint * 0.55 * edgeGlow * brightness * 0.15;\n\n gl_FragColor = vec4(color.rgb + flare * uIntensity * (1.0 + uBeatEnergy * 0.3), color.a);\n }`\n };\n\n // ANAMORPHIC FLARE SHADER\n const AnamorphicShader = {\n uniforms: {\n tDiffuse: { value: null },\n uStretch: { value: 0.1 },\n uThreshold: { value: 0.7 },\n uIntensity: { value: 0.4 },\n uTint: { value: new THREE.Color(0.6, 0.8, 1.0) }\n },\n vertexShader: `varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`,\n fragmentShader: `\n uniform sampler2D tDiffuse;\n uniform float uStretch, uThreshold, uIntensity;\n uniform vec3 uTint;\n varying vec2 vUv;\n void main() {\n vec4 color = texture2D(tDiffuse, vUv);\n vec3 streak = vec3(0.0);\n for(float i = -15.0; i <= 15.0; i++) {\n vec4 s = texture2D(tDiffuse, vUv + vec2(i * uStretch * 0.01, 0.0));\n float bright = dot(s.rgb, vec3(0.299, 0.587, 0.114));\n if(bright > uThreshold) {\n float w = 1.0 - abs(i) / 15.0;\n streak += s.rgb * w * w;\n }\n }\n gl_FragColor = vec4(color.rgb + streak / 7.5 * uTint * uIntensity, color.a);\n }`\n };\n\n // CHROMATIC ABERRATION SHADER\n const ChromaticShader = {\n uniforms: {\n tDiffuse: { value: null },\n uIntensity: { value: 0.004 },\n uBeatEnergy: { value: 0.0 }\n },\n vertexShader: `varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`,\n fragmentShader: `\n uniform sampler2D tDiffuse;\n uniform float uIntensity, uBeatEnergy;\n varying vec2 vUv;\n void main() {\n vec2 dir = vUv - vec2(0.5);\n float dist = length(dir);\n vec2 offset = dir * dist * uIntensity * (1.0 + uBeatEnergy * 1.5);\n gl_FragColor = vec4(\n texture2D(tDiffuse, vUv + offset * 1.2).r,\n texture2D(tDiffuse, vUv).g,\n texture2D(tDiffuse, vUv - offset * 1.2).b,\n texture2D(tDiffuse, vUv).a\n );\n }`\n };\n\n // FILM GRAIN SHADER\n const GrainShader = {\n uniforms: {\n tDiffuse: { value: null },\n uTime: { value: 0.0 },\n uIntensity: { value: 0.0 }\n },\n vertexShader: `varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`,\n fragmentShader: `\n uniform sampler2D tDiffuse;\n uniform float uTime, uIntensity;\n varying vec2 vUv;\n float rand(vec2 co) { return fract(sin(dot(co, vec2(12.9898,78.233))) * 43758.5453); }\n void main() {\n vec4 color = texture2D(tDiffuse, vUv);\n float grain = (rand(vUv + fract(uTime)) * 2.0 - 1.0) * uIntensity;\n gl_FragColor = vec4(color.rgb + grain * (1.0 - dot(color.rgb, vec3(0.299, 0.587, 0.114)) * 0.5), color.a);\n }`\n };\n\n // CUSTOM DEPTH OF FIELD SHADER (Radial + Edge blur)\n const CustomDOFShader = {\n uniforms: {\n tDiffuse: { value: null },\n uFocus: { value: 0.05 },\n uFocalLength: { value: 0.25 },\n uBokehStrength: { value: 0.5 },\n uResolution: { value: new THREE.Vector2() },\n uBeatEnergy: { value: 0.0 }\n },\n vertexShader: `\n varying vec2 vUv;\n void main() {\n vUv = uv;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n }\n `,\n fragmentShader: `\n uniform sampler2D tDiffuse;\n uniform float uFocus;\n uniform float uFocalLength;\n uniform float uBokehStrength;\n uniform vec2 uResolution;\n uniform float uBeatEnergy;\n varying vec2 vUv;\n\n void main() {\n vec2 center = vec2(0.5);\n float dist = length(vUv - center);\n\n // Focus ring - sharp in a ring around center, blurry inside and outside\n float focusRing = abs(dist - uFocus);\n float blur = smoothstep(0.0, uFocalLength, focusRing) * uBokehStrength;\n\n vec3 color = vec3(0.0);\n float total = 0.0;\n vec2 texel = 1.0 / uResolution;\n\n if(blur < 0.01) {\n color = texture2D(tDiffuse, vUv).rgb;\n total = 1.0;\n } else {\n // Golden angle spiral sampling\n const float GOLDEN_ANGLE = 2.39996323;\n const int SAMPLES = 24;\n float radius = blur * 20.0 * (1.0 + uBeatEnergy * 0.1);\n\n for(int i = 0; i < SAMPLES; i++) {\n float angle = float(i) * GOLDEN_ANGLE;\n float r = sqrt(float(i) / float(SAMPLES)) * radius;\n vec2 offset = vec2(cos(angle), sin(angle)) * r * texel;\n vec3 s = texture2D(tDiffuse, vUv + offset).rgb;\n\n // Subtle highlight boost\n float lum = dot(s, vec3(0.299, 0.587, 0.114));\n s *= 1.0 + smoothstep(0.7, 1.0, lum) * blur * 0.3;\n\n color += s;\n total += 1.0;\n }\n }\n\n gl_FragColor = vec4(color / total, texture2D(tDiffuse, vUv).a);\n }\n `\n };\n\n // VIGNETTE + GLOW SHADER\n const VignetteShader = {\n uniforms: {\n tDiffuse: { value: null },\n uBeatEnergy: { value: 0.0 }\n },\n vertexShader: `varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`,\n fragmentShader: `\n uniform sampler2D tDiffuse;\n uniform float uBeatEnergy;\n varying vec2 vUv;\n void main() {\n vec4 color = texture2D(tDiffuse, vUv);\n vec2 uv = vUv * (1.0 - vUv.yx);\n float vig = pow(uv.x * uv.y * 15.0, 0.38 * (1.0 - uBeatEnergy * 0.15));\n float lum = dot(color.rgb, vec3(0.299, 0.587, 0.114));\n float glow = smoothstep(0.5, 0.9, lum) * 0.1 * (1.0 + uBeatEnergy * 0.5);\n gl_FragColor = vec4(color.rgb * vig * (1.0 + glow), color.a);\n }`\n };\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550 GEOMETRY \u2550\u2550\u2550\u2550\u2550\u2550\u2550\n const geometry = new THREE.BufferGeometry();\n const mainCapacity = Math.ceil(CONFIG.maxParticles * CONFIG.maxDensity);\n const pos = new Float32Array(mainCapacity * 3);\n const rnd = new Float32Array(mainCapacity * 3);\n const phase = new Float32Array(mainCapacity);\n const layer = new Float32Array(mainCapacity);\n const vec = new THREE.Vector3();\n\n for (let i = 0; i < mainCapacity; i++) {\n vec.setFromSphericalCoords(\n CONFIG.fieldRadius * Math.pow(Math.random(), 0.33),\n Math.acos(2 * Math.random() - 1),\n Math.random() * Math.PI * 2\n );\n pos[i * 3] = vec.x;\n pos[i * 3 + 1] = vec.y;\n pos[i * 3 + 2] = vec.z;\n rnd[i * 3] = Math.random();\n rnd[i * 3 + 1] = Math.random();\n rnd[i * 3 + 2] = Math.random();\n phase[i] = Math.random() * Math.PI * 2;\n }\n\n // Secondary particles now use separate instanced system below\n\n geometry.setAttribute('position', new THREE.BufferAttribute(pos, 3));\n geometry.setAttribute('aRandom', new THREE.BufferAttribute(rnd, 3));\n geometry.setAttribute('aPhase', new THREE.BufferAttribute(phase, 1));\n geometry.setAttribute('aLayer', new THREE.BufferAttribute(layer, 1));\n geometry.setDrawRange(0, Math.floor(CONFIG.maxParticles * STATE.density));\n\n const FIELD_TYPES = {\n 'Photon (EM)': { c1: '#00f2ff', c2: '#0051ff', c3: '#ffffff', s: 1.0, curl: 1.0, sz: 3.0, sc1: '#66d9ff', sc2: '#99eeff' },\n 'Gluon (Strong)': { c1: '#ff0055', c2: '#7000ff', c3: '#ff8800', s: 2.5, curl: 2.4, sz: 3.4, sc1: '#cc66ff', sc2: '#ff6699' },\n 'Higgs (Mass)': { c1: '#ffae00', c2: '#ff4800', c3: '#ffff00', s: 0.6, curl: 0.3, sz: 4.0, sc1: '#ffcc66', sc2: '#ffdd99' },\n 'Gravity': { c1: '#ffffff', c2: '#4466ff', c3: '#8844ff', s: 0.75, curl: 0.18, sz: 2.8, sc1: '#aabbff', sc2: '#ccaaff' },\n 'Dark Energy': { c1: '#1a0033', c2: '#6600ff', c3: '#ff00ff', s: 1.9, curl: 1.6, sz: 3.1, sc1: '#9933ff', sc2: '#cc66ff' },\n 'Neutrino': { c1: '#00ff88', c2: '#00ffcc', c3: '#88ffff', s: 3.0, curl: 3.0, sz: 2.6, sc1: '#66ffbb', sc2: '#99ffdd' },\n // === ENHANCEMENT: Waveform Terrain Field ===\n 'Waveform Terrain': { c1: '#ff3366', c2: '#33ff99', c3: '#6633ff', s: 0.4, curl: 0.15, sz: 2.4, sc1: '#ff6699', sc2: '#66ffcc', terrain: true }\n };\n\n // === ENHANCEMENT: Adaptive Quality System ===\n const QUALITY_SYSTEM = {\n frameCount: 0,\n frameTimes: [],\n lastAdjustTime: 0,\n currentFPS: 60,\n\n update(deltaTime) {\n this.frameCount++;\n this.frameTimes.push(deltaTime);\n if (this.frameTimes.length > 30) this.frameTimes.shift();\n\n if (this.frameCount % 30 === 0) {\n const avgDelta = this.frameTimes.reduce((a, b) => a + b, 0) / this.frameTimes.length;\n this.currentFPS = 1000 / avgDelta;\n\n if (STATE.adaptiveQualityEnabled && performance.now() - this.lastAdjustTime > 1000) {\n if (this.currentFPS < STATE.targetFPS - 10 && STATE.qualityLevel > 0.3) {\n STATE.qualityLevel = Math.max(0.3, STATE.qualityLevel - (this.currentFPS < STATE.targetFPS * 0.6 ? 0.2 : 0.1));\n this.applyQuality();\n this.lastAdjustTime = performance.now();\n } else if (this.currentFPS > STATE.targetFPS + 5 && STATE.qualityLevel < 1.0) {\n STATE.qualityLevel = Math.min(1.0, STATE.qualityLevel + 0.05);\n this.applyQuality();\n this.lastAdjustTime = performance.now();\n }\n }\n }\n },\n\n applyQuality() {\n const q = STATE.qualityLevel;\n const pixelRatio = Math.min(STATE.pixelRatio, Math.max(0.35, STATE.pixelRatio * q));\n const requestedMain = Math.floor(CONFIG.maxParticles * Math.min(STATE.density, CONFIG.maxDensity));\n const densityTotal = requestedMain + CONFIG.secondaryParticles;\n const requestedTotal = particleCount === null ? densityTotal : Math.min(particleCount, mainCapacity + CONFIG.secondaryParticles);\n const visibleParticles = Math.floor(Math.min(requestedTotal, particleBudget) * q);\n // Both layers keep the density's mix; whatever one layer cannot hold goes to the other.\n let mainCount = Math.min(mainCapacity, Math.round(visibleParticles * requestedMain / densityTotal));\n mainCount = Math.min(mainCapacity, Math.max(mainCount, visibleParticles - CONFIG.secondaryParticles));\n geometry.setDrawRange(0, mainCount);\n secondaryGeometry.instanceCount = visibleParticles - mainCount;\n if (renderer.getPixelRatio() !== pixelRatio) {\n renderer.setPixelRatio(pixelRatio);\n composer.setPixelRatio(pixelRatio);\n }\n material.uniforms.uPixelRatio.value = pixelRatio;\n secondaryMaterial.uniforms.uPixelRatio.value = pixelRatio;\n }\n };\n\n // === ENHANCEMENT: Audio-Reactive Camera System ===\n const AUDIO_CAMERA = {\n targetOffset: new THREE.Vector3(),\n currentOffset: new THREE.Vector3(),\n targetFOV: 55,\n baseFOV: 55,\n breathPhase: 0,\n\n update(deltaTime) {\n if (!STATE.audioCameraEnabled || !(AUDIO.active || AUDIO.externalActive)) return;\n\n const bass = AUDIO.gatedBands.bass;\n const mid = AUDIO.gatedBands.mid;\n const high = AUDIO.gatedBands.high;\n const intensity = STATE.audioCameraIntensity;\n const smooth = STATE.audioCameraSmoothing;\n\n // Bass-driven zoom pulse\n this.targetFOV = this.baseFOV - bass * 8 * intensity + AUDIO.beatEnergy * 5 * intensity;\n camera.fov += (this.targetFOV - camera.fov) * smooth;\n camera.updateProjectionMatrix();\n\n // Subtle sway based on mid frequencies\n this.breathPhase += deltaTime * 0.001 * (1 + mid * 2);\n this.targetOffset.x = Math.sin(this.breathPhase * 0.7) * mid * 3 * intensity;\n this.targetOffset.y = Math.cos(this.breathPhase * 0.5) * high * 2 * intensity;\n this.targetOffset.z = Math.sin(this.breathPhase * 0.3) * bass * 4 * intensity;\n\n // Smooth interpolation\n this.currentOffset.lerp(this.targetOffset, smooth);\n\n // Apply onset punch\n if (AUDIO.onsetDetected) {\n this.currentOffset.z -= AUDIO.onsetEnergy * 2 * intensity;\n }\n }\n };\n\n const def = FIELD_TYPES[STATE.field];\n const material = new THREE.ShaderMaterial({\n vertexShader, fragmentShader,\n uniforms: {\n uTime: { value: 0 },\n uPixelRatio: { value: STATE.pixelRatio },\n uSizeBase: { value: def.sz },\n uNoiseScale: { value: def.s },\n uCurlStrength: { value: def.curl },\n uViewportAspect: { value: window.innerWidth / window.innerHeight },\n uViewportHalfHeight: { value: Math.tan(camera.fov * Math.PI / 360) * CONFIG.defaultZoom },\n uFieldFill: { value: STATE.fieldSize },\n uColor1: { value: new THREE.Color(def.c1) },\n uColor2: { value: new THREE.Color(def.c2) },\n uColor3: { value: new THREE.Color(def.c3) },\n uSubBass: { value: 0 }, uBass: { value: 0 }, uLowMid: { value: 0 },\n uMid: { value: 0 }, uHighMid: { value: 0 }, uHigh: { value: 0 }, uUltraHigh: { value: 0 },\n uBeatEnergy: { value: 0 }, uSpectralCentroid: { value: 0 }, uSpectralFlux: { value: 0 },\n uOnsetEnergy: { value: 0 }, uTerrainMode: { value: 0 }, uTerrainHeight: { value: 15.0 },\n uVortexStrength: { value: STATE.vortexStrength },\n uPulseIntensity: { value: STATE.pulseIntensity },\n uZoomFactor: { value: 1.0 },\n uMousePos: { value: new THREE.Vector3(999, 999, 999) },\n uMouseVelocity: { value: new THREE.Vector2(0, 0) }\n },\n transparent: true, depthWrite: false, blending: THREE.AdditiveBlending\n });\n\n const points = new THREE.Points(geometry, material);\n scene.add(points);\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550 INSTANCED SECONDARY PARTICLES \u2550\u2550\u2550\u2550\u2550\u2550\u2550\n const secondaryVertexShader = `\nuniform float uTime, uPixelRatio;\nuniform float uViewportAspect, uViewportHalfHeight;\nuniform float uSubBass, uBass, uLowMid, uMid, uHighMid, uHigh, uUltraHigh;\nuniform float uBeatEnergy, uSpectralCentroid, uSpectralFlux, uOnsetEnergy;\nuniform vec3 uColor1, uColor2;\nuniform float uVortexStrength;\n\nattribute vec3 instancePosition;\nattribute vec3 instanceRandom;\nattribute float instancePhase;\n\nvarying vec3 vColor;\nvarying float vAlpha;\nvarying float vEnergy;\nvarying float vDepth;\n\nvec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }\nvec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }\nvec4 permute(vec4 x) { return mod289(((x*34.0)+1.0)*x); }\nvec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }\n\nfloat snoise(vec3 v) {\n const vec2 C = vec2(1.0/6.0, 1.0/3.0);\n const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);\n vec3 i = floor(v + dot(v, C.yyy));\n vec3 x0 = v - i + dot(i, C.xxx);\n vec3 g = step(x0.yzx, x0.xyz);\n vec3 l = 1.0 - g;\n vec3 i1 = min(g.xyz, l.zxy);\n vec3 i2 = max(g.xyz, l.zxy);\n vec3 x1 = x0 - i1 + C.xxx;\n vec3 x2 = x0 - i2 + C.yyy;\n vec3 x3 = x0 - D.yyy;\n i = mod289(i);\n vec4 p = permute(permute(permute(i.z + vec4(0.0, i1.z, i2.z, 1.0)) + i.y + vec4(0.0, i1.y, i2.y, 1.0)) + i.x + vec4(0.0, i1.x, i2.x, 1.0));\n float n_ = 0.142857142857;\n vec3 ns = n_ * D.wyz - D.xzx;\n vec4 j = p - 49.0 * floor(p * ns.z * ns.z);\n vec4 x_ = floor(j * ns.z);\n vec4 y_ = floor(j - 7.0 * x_);\n vec4 x = x_ * ns.x + ns.yyyy;\n vec4 y = y_ * ns.x + ns.yyyy;\n vec4 h = 1.0 - abs(x) - abs(y);\n vec4 b0 = vec4(x.xy, y.xy);\n vec4 b1 = vec4(x.zw, y.zw);\n vec4 s0 = floor(b0) * 2.0 + 1.0;\n vec4 s1 = floor(b1) * 2.0 + 1.0;\n vec4 sh = -step(h, vec4(0.0));\n vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;\n vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww;\n vec3 p0 = vec3(a0.xy, h.x);\n vec3 p1 = vec3(a0.zw, h.y);\n vec3 p2 = vec3(a1.xy, h.z);\n vec3 p3 = vec3(a1.zw, h.w);\n vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2,p2), dot(p3,p3)));\n p0 *= norm.x; p1 *= norm.y; p2 *= norm.z; p3 *= norm.w;\n vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);\n m = m * m;\n return 42.0 * dot(m*m, vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3)));\n}\n\n${shapeFunction}\n\nvoid main() {\n // Secondary particles run at a slower timescale \u2014 they are the atmosphere, not the core\n float t = uTime * 0.45;\n\n // Multi-scale noise flow \u2014 low freqs drive large drift, high freqs add fine turbulence\n vec3 noisePos = instancePosition * 0.035 + instanceRandom;\n vec3 drift = vec3(\n snoise(noisePos + t * 0.06),\n snoise(noisePos * 0.9 - t * 0.05),\n snoise(noisePos * 1.1 + t * 0.04)\n ) * (5.0 + uSubBass * 10.0 + uLowMid * 6.0);\n\n vec3 turbulence = vec3(\n snoise(noisePos * 2.5 + t * 0.18),\n snoise(noisePos * 2.5 - t * 0.14),\n snoise(noisePos * 2.5 + t * 0.12)\n ) * (uHigh * 5.0 + uSpectralFlux * 8.0);\n\n vec3 offset = drift + turbulence;\n\n // Orbital drift \u2014 vortex-aware, modulated by bass\n float orbitSpeed = 0.12 + instanceRandom.x * 0.15 + uBass * 0.08;\n float angle = uTime * orbitSpeed + instancePhase;\n float orbitRadius = length(instancePosition.xz) * (1.0 + uSubBass * 0.25);\n // Vortex coupling \u2014 secondary shell rotates with the main field vortex\n float vortexInfluence = uVortexStrength * uBass * 0.4;\n float vAngle = angle + vortexInfluence;\n vec3 orbit = vec3(\n cos(vAngle) * orbitRadius - instancePosition.x,\n sin(uTime * 0.5 + instancePhase) * (uMid * 4.0 + uLowMid * 2.0),\n sin(vAngle) * orbitRadius - instancePosition.z\n ) * 0.07;\n\n vec3 newPos = shapedPosition(instanceRandom) + offset + orbit;\n\n // Transients and sub-bass move the atmosphere in depth, not away from the centre.\n newPos.z += uOnsetEnergy * 14.0 * (0.6 + instanceRandom.z * 0.4);\n newPos.z += sin(uTime * 1.2 + instancePhase) * uSubBass * 5.0;\n\n vec4 mvPosition = modelViewMatrix * vec4(newPos, 1.0);\n gl_Position = projectionMatrix * mvPosition;\n\n // Size: driven by different bands than the main system\n // Sparkle on ultraHigh, swell on subBass, punch on beatEnergy\n float sparkle = 1.0 + (instanceRandom.x > 0.85 ? sin(uTime * 15.0 + instanceRandom.y * 200.0) * uUltraHigh * 3.0 : 0.0);\n float swell = 1.0 + uSubBass * 0.6 + uLowMid * 0.3;\n float size = (1.1 + uBeatEnergy * 0.7) * swell * sparkle;\n gl_PointSize = clamp(size * uPixelRatio * (55.0 / -mvPosition.z), 0.5, 55.0);\n\n // Colour: spectral centroid shifts hue, highMid brightens toward white\n float centroidMix = clamp(uSpectralCentroid * 2.2 + uHighMid * 0.4, 0.0, 1.0);\n vColor = mix(uColor1, uColor2, centroidMix);\n // SubBass warmth \u2014 hint of deep colour saturation on heavy lows\n vColor = mix(vColor, vColor * 1.3, uSubBass * 0.4);\n // Beat flash \u2014 brief white-hot core\n vColor = mix(vColor, vec3(1.0), uBeatEnergy * 0.25);\n\n // Alpha: outer shell fades at edge, mid-frequency presence sustains visibility\n float edgeFade = 1.0 - smoothstep(0.92, 1.05, max(abs(newPos.x) / (uViewportAspect * uViewportHalfHeight * uFieldFill * 1.05), abs(newPos.y) / (uViewportHalfHeight * uFieldFill * 1.05)));\n vAlpha = edgeFade * (0.45 + uMid * 0.35 + uLowMid * 0.2) * sparkle;\n\n vEnergy = uBeatEnergy * 0.4 + uHighMid * 0.3 + uOnsetEnergy * 0.3;\n vDepth = clamp(-mvPosition.z / 160.0, 0.0, 1.0);\n}\n`;\n\n const secondaryFragmentShader = `\nvarying vec3 vColor;\nvarying float vAlpha;\nvarying float vEnergy;\nvarying float vDepth;\n\nvoid main() {\n vec2 xy = gl_PointCoord.xy - vec2(0.5);\n float r = length(xy);\n if (r > 0.5) discard;\n\n // Wider, softer glow than main particles \u2014 atmospheric haze\n float glow = exp(-r * r * 8.0);\n float softGlow = exp(-r * r * 3.0) * 0.4;\n float core = smoothstep(0.18 - vEnergy * 0.06, 0.10, r);\n\n vec3 finalColor = mix(vColor, vec3(1.0, 0.97, 0.92), core * 0.55);\n // Depth haze \u2014 far particles shift slightly cooler/dimmer\n finalColor = mix(finalColor, vec3(0.02, 0.03, 0.06), vDepth * 0.35);\n\n float finalAlpha = clamp(vAlpha * (glow * 0.65 + softGlow + core * 0.35), 0.0, 1.0);\n gl_FragColor = vec4(finalColor, finalAlpha);\n}\n`;\n\n // Create instanced geometry for secondary particles\n const secondaryGeometry = new THREE.InstancedBufferGeometry();\n\n // Base geometry (single point)\n const basePositions = new Float32Array([0, 0, 0]);\n secondaryGeometry.setAttribute('position', new THREE.BufferAttribute(basePositions, 3));\n\n // Instance attributes\n const instancePositions = new Float32Array(CONFIG.secondaryParticles * 3);\n const instanceRandoms = new Float32Array(CONFIG.secondaryParticles * 3);\n const instancePhases = new Float32Array(CONFIG.secondaryParticles);\n\n for (let i = 0; i < CONFIG.secondaryParticles; i++) {\n // Distribute in outer shell\n const r = CONFIG.fieldRadius * (0.65 + Math.random() * 0.45);\n const theta = Math.acos(2 * Math.random() - 1);\n const phi = Math.random() * Math.PI * 2;\n\n instancePositions[i * 3] = r * Math.sin(theta) * Math.cos(phi);\n instancePositions[i * 3 + 1] = r * Math.sin(theta) * Math.sin(phi);\n instancePositions[i * 3 + 2] = r * Math.cos(theta);\n\n instanceRandoms[i * 3] = Math.random();\n instanceRandoms[i * 3 + 1] = Math.random();\n instanceRandoms[i * 3 + 2] = Math.random();\n\n instancePhases[i] = Math.random() * Math.PI * 2;\n }\n\n secondaryGeometry.setAttribute('instancePosition', new THREE.InstancedBufferAttribute(instancePositions, 3));\n secondaryGeometry.setAttribute('instanceRandom', new THREE.InstancedBufferAttribute(instanceRandoms, 3));\n secondaryGeometry.setAttribute('instancePhase', new THREE.InstancedBufferAttribute(instancePhases, 1));\n\n const secondaryMaterial = new THREE.ShaderMaterial({\n vertexShader: secondaryVertexShader,\n fragmentShader: secondaryFragmentShader,\n uniforms: {\n uTime: { value: 0 },\n uPixelRatio: { value: STATE.pixelRatio },\n uSubBass: { value: 0 },\n uBass: { value: 0 },\n uLowMid: { value: 0 },\n uMid: { value: 0 },\n uHighMid: { value: 0 },\n uHigh: { value: 0 },\n uUltraHigh: { value: 0 },\n uBeatEnergy: { value: 0 },\n uSpectralCentroid: { value: 0 },\n uSpectralFlux: { value: 0 },\n uOnsetEnergy: { value: 0 },\n uVortexStrength: { value: STATE.vortexStrength },\n uColor1: { value: new THREE.Color(def.sc1) },\n uColor2: { value: new THREE.Color(def.sc2) },\n uViewportAspect: { value: window.innerWidth / window.innerHeight },\n uViewportHalfHeight: { value: Math.tan(camera.fov * Math.PI / 360) * CONFIG.defaultZoom },\n uFieldFill: { value: STATE.fieldSize }\n },\n transparent: true,\n depthWrite: false,\n blending: THREE.AdditiveBlending\n });\n\n const secondaryPoints = new THREE.Points(secondaryGeometry, secondaryMaterial);\n scene.add(secondaryPoints);\n\n // Expose secondary-layer controls to the host component.\n STATE.secondaryEnabled = true;\n STATE.secondaryOpacity = 1.0;\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550 PARTICLE TRAIL SYSTEM \u2550\u2550\u2550\u2550\u2550\u2550\u2550\n const particleSeeds = geometry.getAttribute('aRandom');\n function fieldParticlePosition(index, target) {\n const halfHeight = material.uniforms.uViewportHalfHeight.value * STATE.fieldSize;\n const halfWidth = halfHeight * material.uniforms.uViewportAspect.value;\n return target.set(\n (particleSeeds.getX(index) * 2 - 1) * halfWidth,\n (particleSeeds.getY(index) * 2 - 1) * halfHeight,\n (particleSeeds.getZ(index) - 0.5) * 12\n );\n }\n\n class TrailSystem {\n constructor(count, length) {\n this.trails = [];\n this.meshes = [];\n this.nextPosition = new THREE.Vector3();\n\n for (let i = 0; i < count; i++) {\n const anchor = new THREE.Vector2(Math.random() * 1.5 - 0.75, Math.random() * 1.5 - 0.75);\n const start = new THREE.Vector3(\n anchor.x * material.uniforms.uViewportAspect.value * material.uniforms.uViewportHalfHeight.value * STATE.fieldSize,\n anchor.y * material.uniforms.uViewportHalfHeight.value * STATE.fieldSize,\n 0\n );\n const trail = {\n positions: Array.from({ length }, () => start.clone()),\n anchor,\n phase: Math.random() * Math.PI * 2,\n speed: 0.3 + Math.random() * 0.7,\n color: new THREE.Color(def.c1).lerp(new THREE.Color(def.c2), i / Math.max(1, count - 1))\n };\n this.trails.push(trail);\n\n const geom = new THREE.BufferGeometry();\n const positions = new Float32Array(length * 3);\n const alphas = new Float32Array(length);\n for (let j = 0; j < length; j++) alphas[j] = 1.0 - j / length;\n\n geom.setAttribute('position', new THREE.BufferAttribute(positions, 3));\n geom.setAttribute('alpha', new THREE.BufferAttribute(alphas, 1));\n\n const mat = new THREE.ShaderMaterial({\n vertexShader: `attribute float alpha; varying float vAlpha; void main() { vAlpha = alpha; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`,\n fragmentShader: `uniform vec3 uColor; uniform float uOpacity, uBeat; varying float vAlpha; void main() { gl_FragColor = vec4(uColor * (1.0 + uBeat * 0.3), vAlpha * uOpacity * 0.6 * (1.0 + uBeat * 0.5)); }`,\n uniforms: { uColor: { value: trail.color }, uOpacity: { value: 0.4 }, uBeat: { value: 0 } },\n transparent: true, depthWrite: false, blending: THREE.AdditiveBlending\n });\n\n const line = new THREE.Line(geom, mat);\n this.meshes.push(line);\n scene.add(line);\n }\n }\n\n update(time, audio, fieldColor) {\n const halfHeight = material.uniforms.uViewportHalfHeight.value * STATE.fieldSize;\n const halfWidth = halfHeight * material.uniforms.uViewportAspect.value;\n for (let i = 0; i < this.trails.length; i++) {\n const trail = this.trails[i], mesh = this.meshes[i];\n mesh.visible = STATE.particleTrailsEnabled && i < this.trails.length * STATE.qualityLevel;\n if (!mesh.visible) continue;\n\n const angle = time * 0.5 * trail.speed + trail.phase;\n const newPos = this.nextPosition.set(\n trail.anchor.x * halfWidth + Math.cos(angle) * (4 + audio.bass * 8),\n trail.anchor.y * halfHeight + Math.sin(time * 0.3 + trail.phase) * (4 + audio.mid * 8),\n Math.sin(angle) * (4 + audio.high * 7)\n );\n newPos.x = THREE.MathUtils.clamp(newPos.x, -halfWidth * 0.94, halfWidth * 0.94);\n newPos.y = THREE.MathUtils.clamp(newPos.y, -halfHeight * 0.94, halfHeight * 0.94);\n\n for (let j = trail.positions.length - 1; j > 0; j--) trail.positions[j].copy(trail.positions[j - 1]);\n trail.positions[0].copy(newPos);\n\n const posAttr = mesh.geometry.getAttribute('position');\n for (let j = 0; j < trail.positions.length; j++) {\n posAttr.setXYZ(j, trail.positions[j].x, trail.positions[j].y, trail.positions[j].z);\n }\n posAttr.needsUpdate = true;\n\n mesh.material.uniforms.uBeat.value = audio.beat;\n mesh.material.uniforms.uOpacity.value = STATE.particleTrailOpacity;\n }\n }\n }\n\n const trailSystem = new TrailSystem(50, 12);\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550 PARTICLE CONNECTIONS (Force Network) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\n const connectionCount = 500; // Number of particles to check for connections\n const maxConnections = 800; // Maximum line segments\n const connectionGeometry = new THREE.BufferGeometry();\n const connectionPositions = new Float32Array(maxConnections * 6); // 2 points per line, 3 coords each\n const connectionAlphas = new Float32Array(maxConnections * 2); // Alpha per vertex\n connectionGeometry.setAttribute('position', new THREE.BufferAttribute(connectionPositions, 3));\n connectionGeometry.setAttribute('alpha', new THREE.BufferAttribute(connectionAlphas, 1));\n connectionGeometry.setDrawRange(0, 0);\n\n const connectionMaterial = new THREE.ShaderMaterial({\n vertexShader: `\n attribute float alpha;\n varying float vAlpha;\n void main() {\n vAlpha = alpha;\n vec4 mvPos = modelViewMatrix * vec4(position, 1.0);\n gl_Position = projectionMatrix * mvPos;\n }\n `,\n fragmentShader: `\n uniform vec3 uColor;\n uniform vec3 uColor2;\n uniform float uOpacity;\n uniform float uBeatEnergy;\n uniform float uHighMid;\n uniform float uSpectralCentroid;\n varying float vAlpha;\n void main() {\n // Blend hue with spectral centroid (brighter/higher = more c2)\n vec3 color = mix(uColor, uColor2, clamp(uSpectralCentroid * 2.0, 0.0, 1.0));\n // Beat pulse flash\n float beatFlash = 1.0 + uBeatEnergy * 1.2;\n color *= beatFlash;\n // Per-line alpha reacts to highMid (attack/presence energy)\n float lineAlpha = vAlpha * uOpacity * (0.4 + uHighMid * 0.5 + uBeatEnergy * 0.4);\n gl_FragColor = vec4(color, clamp(lineAlpha, 0.0, 1.0));\n }\n `,\n uniforms: {\n uColor: { value: new THREE.Color(def.c2) },\n uColor2: { value: new THREE.Color(def.c1) },\n uOpacity: { value: 1.0 },\n uBeatEnergy: { value: 0 },\n uHighMid: { value: 0 },\n uSpectralCentroid: { value: 0 }\n },\n transparent: true,\n depthWrite: false,\n blending: THREE.AdditiveBlending\n });\n\n const connectionMesh = new THREE.LineSegments(connectionGeometry, connectionMaterial);\n scene.add(connectionMesh);\n\n // Particle indices to track for connections \u2014 refreshed periodically\n const trackedParticles = [];\n const positions = Array.from({ length: connectionCount }, () => new THREE.Vector3());\n let connectionRefreshCounter = 0;\n const CONNECTION_REFRESH_INTERVAL = 40; // 20 Hz updates preserve the original ~2 s selection cycle\n\n function refreshTrackedParticles() {\n trackedParticles.length = 0;\n const visibleParticles = Math.max(1, geometry.drawRange.count);\n const visibleFraction = Math.sqrt(visibleParticles / (CONFIG.maxParticles * CONFIG.initialDensity));\n const trackedCount = Math.max(2, Math.floor(connectionCount * Math.min(1, STATE.qualityLevel, visibleFraction)));\n for (let i = 0; i < trackedCount; i++) {\n trackedParticles.push(Math.floor(Math.random() * visibleParticles));\n }\n }\n refreshTrackedParticles();\n let lastConnectionState = null;\n\n function shouldRebuildConnections(activeAudio, current, previous, idleElapsed) {\n return activeAudio || idleElapsed >= 2 || !previous || current.some((value, index) => value !== previous[index]);\n }\n\n function updateConnections(threshold, bassBoost, highMid, mid, forceRefresh = false) {\n // Refresh particle selection every N frames \u2014 network evolves over time\n connectionRefreshCounter++;\n if (forceRefresh || connectionRefreshCounter >= CONNECTION_REFRESH_INTERVAL) {\n refreshTrackedParticles();\n connectionRefreshCounter = 0;\n }\n\n // Mirror the shader's screen-fitted seed mapping for the line systems.\n for (let i = 0; i < trackedParticles.length; i++) {\n fieldParticlePosition(trackedParticles[i], positions[i]);\n }\n\n // Threshold breathes with bass, tightens on high energy\n const effectiveThreshold = threshold * (1.0 + bassBoost * 0.6) * (1.0 - highMid * 0.15);\n const thresholdSq = effectiveThreshold * effectiveThreshold;\n\n // Dynamic connection cap \u2014 expands with mid+highMid energy\n const dynamicMax = Math.floor(maxConnections * (0.3 + (mid + highMid) * 0.7));\n\n let connIdx = 0;\n for (let i = 0; i < trackedParticles.length && connIdx < dynamicMax; i++) {\n for (let j = i + 1; j < trackedParticles.length && connIdx < dynamicMax; j++) {\n const distSq = positions[i].distanceToSquared(positions[j]);\n if (distSq < thresholdSq && distSq > 0.1) {\n const dist = Math.sqrt(distSq);\n const alpha = (1.0 - dist / effectiveThreshold) * 0.8;\n\n connectionPositions[connIdx * 6 + 0] = positions[i].x;\n connectionPositions[connIdx * 6 + 1] = positions[i].y;\n connectionPositions[connIdx * 6 + 2] = positions[i].z;\n connectionPositions[connIdx * 6 + 3] = positions[j].x;\n connectionPositions[connIdx * 6 + 4] = positions[j].y;\n connectionPositions[connIdx * 6 + 5] = positions[j].z;\n\n connectionAlphas[connIdx * 2 + 0] = alpha;\n connectionAlphas[connIdx * 2 + 1] = alpha;\n\n connIdx++;\n }\n }\n }\n\n connectionGeometry.attributes.position.needsUpdate = true;\n connectionGeometry.attributes.alpha.needsUpdate = true;\n connectionGeometry.setDrawRange(0, connIdx * 2);\n }\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550 NETWORK CRAWLERS \u2550\u2550\u2550\u2550\u2550\u2550\u2550\n const CRAWLER_COUNT = 24; // concurrent crawlers\n const CRAWLER_TAIL = 10; // trail positions per crawler\n const crawlerMaxSegs = CRAWLER_COUNT * (CRAWLER_TAIL - 1);\n\n const crawlerGeo = new THREE.BufferGeometry();\n const crawlerPos = new Float32Array(crawlerMaxSegs * 6);\n const crawlerAlp = new Float32Array(crawlerMaxSegs * 2);\n crawlerGeo.setAttribute('position', new THREE.BufferAttribute(crawlerPos, 3));\n crawlerGeo.setAttribute('alpha', new THREE.BufferAttribute(crawlerAlp, 1));\n crawlerGeo.setDrawRange(0, 0);\n\n // Bloom only lights pixels above 0.85 luminance. Preset greens and cyans got there on their own;\n // a theme's reds and violets carry the same energy at a fraction of the luminance, so reaction\n // colours keep the theme's hue but are lifted (past 1.0 if needed) to the brightness of the originals.\n function glowColor(color, luminance) {\n const current = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;\n return color.multiplyScalar(Math.min(4, Math.max(1, luminance / Math.max(current, 0.02))));\n }\n const crawlerLow = new THREE.Color(def.c1), crawlerHigh = new THREE.Color(def.c3);\n const crawlerTarget = new THREE.Color();\n const crawlerMaterial = new THREE.ShaderMaterial({\n vertexShader: `\n attribute float alpha;\n varying float vAlpha;\n void main() {\n vAlpha = alpha;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n }\n `,\n fragmentShader: `\n uniform vec3 uColor;\n uniform float uBeatEnergy;\n varying float vAlpha;\n void main() {\n vec3 c = uColor * (1.2 + uBeatEnergy * 1.5);\n gl_FragColor = vec4(c, vAlpha);\n }\n `,\n uniforms: {\n uColor: { value: new THREE.Color(def.c1) },\n uBeatEnergy: { value: 0 }\n },\n transparent: true,\n depthWrite: false,\n blending: THREE.AdditiveBlending\n });\n\n const crawlerMesh = new THREE.LineSegments(crawlerGeo, crawlerMaterial);\n scene.add(crawlerMesh);\n\n // Initialise crawler agents\n const crawlers = [];\n const CRAWLER_MIN_STEP = 1.8; // min world-space distance before adding a tail point\n for (let i = 0; i < CRAWLER_COUNT; i++) {\n crawlers.push({\n fromIdx: Math.floor(Math.random() * connectionCount),\n toIdx: Math.floor(Math.random() * connectionCount),\n t: Math.random(),\n speed: 0.0015 + Math.random() * 0.002,\n tail: [],\n lastTailPos: null, // last world position a tail point was recorded\n hue: Math.random()\n });\n }\n\n function updateCrawlers(dt, highMid, beatEnergy, spectralCentroid) {\n // Helper: world position of a tracked particle\n function particlePos(idx) {\n return positions[idx % trackedParticles.length];\n }\n\n // Helper: find nearest neighbour within threshold\n function nearestNeighbour(fromIdx, excludeIdx) {\n const origin = particlePos(fromIdx);\n const threshold = STATE.connectionThreshold * (1.2 + highMid * 0.4);\n const threshSq = threshold * threshold;\n let bestDist = Infinity, bestIdx = -1;\n const check = Math.min(trackedParticles.length, 80); // sample subset for perf\n const start = Math.floor(Math.random() * (trackedParticles.length - check));\n for (let k = start; k < start + check; k++) {\n const ki = k % trackedParticles.length;\n if (ki === fromIdx || ki === excludeIdx) continue;\n const d = origin.distanceToSquared(particlePos(ki));\n if (d < threshSq && d < bestDist) { bestDist = d; bestIdx = ki; }\n }\n // fallback: pick random if no neighbour in range\n return bestIdx === -1 ? Math.floor(Math.random() * trackedParticles.length) : bestIdx;\n }\n\n let segIdx = 0;\n\n for (let i = 0; i < crawlers.length; i++) {\n const c = crawlers[i];\n\n // Speed reacts to highMid (attack energy) and beat\n const spd = c.speed * (1.0 + highMid * 0.8 + beatEnergy * 0.5);\n c.t += spd;\n\n // Reached target \u2014 advance to next node\n if (c.t >= 1.0) {\n c.t -= 1.0;\n c.fromIdx = c.toIdx;\n c.toIdx = nearestNeighbour(c.fromIdx, c.fromIdx);\n // Occasionally teleport to keep crawlers spread across the field\n if (Math.random() < 0.04) {\n c.fromIdx = Math.floor(Math.random() * trackedParticles.length);\n c.toIdx = nearestNeighbour(c.fromIdx, c.fromIdx);\n c.tail = [];\n c.lastTailPos = null;\n }\n }\n\n // Current head position\n const from = particlePos(c.fromIdx);\n const to = particlePos(c.toIdx);\n const head = new THREE.Vector3().lerpVectors(from, to, c.t);\n\n // Only record a tail point when head has moved far enough in world space\n const distMoved = c.lastTailPos ? head.distanceTo(c.lastTailPos) : Infinity;\n if (distMoved >= CRAWLER_MIN_STEP) {\n c.tail.push(head.clone());\n if (c.tail.length > CRAWLER_TAIL) c.tail.shift();\n c.lastTailPos = head.clone();\n }\n\n // Write tail segments\n const tailLen = c.tail.length;\n for (let s = 0; s < tailLen - 1 && segIdx < crawlerMaxSegs; s++) {\n const p0 = c.tail[s];\n const p1 = c.tail[s + 1];\n // Alpha: head is brightest, tail fades\n const baseAlpha = ((s + 1) / (tailLen - 1));\n const alpha = baseAlpha * (0.25 + spectralCentroid * 0.3 + beatEnergy * 0.4);\n\n crawlerPos[segIdx * 6 + 0] = p0.x; crawlerPos[segIdx * 6 + 1] = p0.y; crawlerPos[segIdx * 6 + 2] = p0.z;\n crawlerPos[segIdx * 6 + 3] = p1.x; crawlerPos[segIdx * 6 + 4] = p1.y; crawlerPos[segIdx * 6 + 5] = p1.z;\n crawlerAlp[segIdx * 2 + 0] = alpha;\n crawlerAlp[segIdx * 2 + 1] = alpha;\n segIdx++;\n }\n }\n\n crawlerGeo.attributes.position.needsUpdate = true;\n crawlerGeo.attributes.alpha.needsUpdate = true;\n crawlerGeo.setDrawRange(0, segIdx * 2);\n }\n\n STATE.crawlersEnabled = true;\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550 NEBULA BACKGROUND \u2550\u2550\u2550\u2550\u2550\u2550\u2550\n const nebulaVertexShader = `\nvarying vec3 vWorldPosition;\nvarying vec2 vUv;\nvoid main() {\n vUv = uv;\n vec4 worldPos = modelMatrix * vec4(position, 1.0);\n vWorldPosition = worldPos.xyz;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}\n`;\n\n const nebulaFragmentShader = `\nuniform float uTime;\nuniform vec3 uColor1;\nuniform vec3 uColor2;\nuniform vec3 uColor3;\nuniform float uBass;\nuniform float uMid;\nuniform float uHigh;\nuniform float uBeatEnergy;\nuniform float uIntensity;\nvarying vec3 vWorldPosition;\nvarying vec2 vUv;\n\n// Simplex 3D noise\nvec4 permute(vec4 x) { return mod(((x*34.0)+1.0)*x, 289.0); }\nvec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }\n\nfloat snoise(vec3 v) {\n const vec2 C = vec2(1.0/6.0, 1.0/3.0);\n const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);\n vec3 i = floor(v + dot(v, C.yyy));\n vec3 x0 = v - i + dot(i, C.xxx);\n vec3 g = step(x0.yzx, x0.xyz);\n vec3 l = 1.0 - g;\n vec3 i1 = min(g.xyz, l.zxy);\n vec3 i2 = max(g.xyz, l.zxy);\n vec3 x1 = x0 - i1 + C.xxx;\n vec3 x2 = x0 - i2 + C.yyy;\n vec3 x3 = x0 - D.yyy;\n i = mod(i, 289.0);\n vec4 p = permute(permute(permute(i.z + vec4(0.0, i1.z, i2.z, 1.0)) + i.y + vec4(0.0, i1.y, i2.y, 1.0)) + i.x + vec4(0.0, i1.x, i2.x, 1.0));\n float n_ = 1.0/7.0;\n vec3 ns = n_ * D.wyz - D.xzx;\n vec4 j = p - 49.0 * floor(p * ns.z * ns.z);\n vec4 x_ = floor(j * ns.z);\n vec4 y_ = floor(j - 7.0 * x_);\n vec4 x = x_ *ns.x + ns.yyyy;\n vec4 y = y_ *ns.x + ns.yyyy;\n vec4 h = 1.0 - abs(x) - abs(y);\n vec4 b0 = vec4(x.xy, y.xy);\n vec4 b1 = vec4(x.zw, y.zw);\n vec4 s0 = floor(b0)*2.0 + 1.0;\n vec4 s1 = floor(b1)*2.0 + 1.0;\n vec4 sh = -step(h, vec4(0.0));\n vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy;\n vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww;\n vec3 p0 = vec3(a0.xy, h.x);\n vec3 p1 = vec3(a0.zw, h.y);\n vec3 p2 = vec3(a1.xy, h.z);\n vec3 p3 = vec3(a1.zw, h.w);\n vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2,p2), dot(p3,p3)));\n p0 *= norm.x; p1 *= norm.y; p2 *= norm.z; p3 *= norm.w;\n vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);\n return 42.0 * dot(m*m*m*m, vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3)));\n}\n\nfloat fbm(vec3 p) {\n float value = 0.0;\n float amplitude = 0.5;\n float frequency = 1.0;\n for(int i = 0; i < 3; i++) {\n value += amplitude * snoise(p * frequency);\n amplitude *= 0.5;\n frequency *= 2.0;\n }\n return value;\n}\n\nvoid main() {\n vec3 dir = normalize(vWorldPosition);\n float t = uTime * 0.02;\n\n // Layered nebula clouds\n vec3 p1 = dir * 2.0 + t * 0.5;\n vec3 p2 = dir * 4.0 - t * 0.3;\n vec3 p3 = dir * 1.5 + vec3(t * 0.2, -t * 0.1, t * 0.15);\n\n float n1 = fbm(p1) * 0.5 + 0.5;\n float n2 = fbm(p2) * 0.5 + 0.5;\n float n3 = fbm(p3) * 0.5 + 0.5;\n\n // Create cloud density\n float density = n1 * n2;\n density = pow(density, 1.5) * 1.5;\n\n // Wispy tendrils\n float tendrils = pow(n3, 3.0) * 0.8;\n\n // Color mixing based on field colors\n vec3 nebulaColor = mix(uColor1 * 0.3, uColor2 * 0.5, n1);\n nebulaColor = mix(nebulaColor, uColor3 * 0.4, n2 * 0.5);\n\n // Add some complementary deep space colors\n vec3 deepSpace = vec3(0.02, 0.01, 0.05);\n vec3 starGlow = vec3(0.1, 0.08, 0.15);\n\n // Audio reactivity\n float bassGlow = uBass * 0.3;\n float midShimmer = uMid * 0.2 * sin(uTime * 2.0 + n1 * 10.0);\n float beatPulse = uBeatEnergy * 0.4;\n\n // Combine layers\n vec3 color = deepSpace;\n color += nebulaColor * density * (0.4 + bassGlow + beatPulse);\n color += uColor2 * 0.15 * tendrils * (1.0 + uHigh * 0.5);\n color += starGlow * pow(n1 * n2, 4.0) * (1.0 + midShimmer);\n\n // Scattered stars\n float stars = pow(snoise(dir * 50.0) * 0.5 + 0.5, 20.0);\n stars += pow(snoise(dir * 80.0 + 100.0) * 0.5 + 0.5, 25.0) * 0.5;\n color += vec3(1.0, 0.95, 0.9) * stars * (0.5 + uHigh * 0.5);\n\n // Vignette toward horizon\n float horizonFade = 1.0 - pow(abs(dir.y), 0.5) * 0.3;\n color *= horizonFade;\n\n // Apply intensity\n color *= uIntensity;\n\n gl_FragColor = vec4(color, 1.0);\n}\n`;\n\n const nebulaMaterial = new THREE.ShaderMaterial({\n vertexShader: nebulaVertexShader,\n fragmentShader: nebulaFragmentShader,\n uniforms: {\n uTime: { value: 0 },\n uColor1: { value: new THREE.Color(def.c1) },\n uColor2: { value: new THREE.Color(def.c2) },\n uColor3: { value: new THREE.Color(def.c3) },\n uBass: { value: 0 },\n uMid: { value: 0 },\n uHigh: { value: 0 },\n uBeatEnergy: { value: 0 },\n uIntensity: { value: STATE.nebulaIntensity }\n },\n side: THREE.BackSide,\n depthWrite: false\n });\n\n const nebulaSphere = new THREE.Mesh(\n new THREE.SphereGeometry(500, 64, 64),\n nebulaMaterial\n );\n scene.add(nebulaSphere);\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550 POST-PROCESSING COMPOSER \u2550\u2550\u2550\u2550\u2550\u2550\u2550\n const composer = new EffectComposer(renderer);\n composer.addPass(new RenderPass(scene, camera));\n\n const afterimagePass = new AfterimagePass();\n afterimagePass.uniforms['damp'].value = STATE.trails;\n composer.addPass(afterimagePass);\n\n const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85);\n bloomPass.threshold = 0.25;\n bloomPass.strength = STATE.bloom;\n bloomPass.radius = 0.45;\n composer.addPass(bloomPass);\n\n const godRaysPass = new ShaderPass(GodRaysShader);\n godRaysPass.enabled = false;\n composer.addPass(godRaysPass);\n\n const lensFlarePass = new ShaderPass(LensFlareShader);\n lensFlarePass.enabled = STATE.lensFlareEnabled && STATE.lensFlareIntensity > 0;\n composer.addPass(lensFlarePass);\n\n const anamorphicPass = new ShaderPass(AnamorphicShader);\n composer.addPass(anamorphicPass);\n\n const chromaticPass = new ShaderPass(ChromaticShader);\n chromaticPass.enabled = STATE.chromaticAberration > 0;\n composer.addPass(chromaticPass);\n\n // Custom DOF pass (radial focus)\n const customDOFPass = new ShaderPass(CustomDOFShader);\n customDOFPass.uniforms.uFocus.value = STATE.dofFocus;\n customDOFPass.uniforms.uFocalLength.value = STATE.dofFocalLength;\n customDOFPass.uniforms.uBokehStrength.value = STATE.dofBokehStrength;\n customDOFPass.uniforms.uResolution.value.set(window.innerWidth, window.innerHeight);\n customDOFPass.enabled = STATE.dofEnabled;\n composer.addPass(customDOFPass);\n\n const vignettePass = new ShaderPass(VignetteShader);\n composer.addPass(vignettePass);\n\n const grainPass = new ShaderPass(GrainShader);\n grainPass.enabled = STATE.filmGrain > 0;\n composer.addPass(grainPass);\n\n const AlphaFromLumaShader = {\n uniforms: { tDiffuse: { value: null } },\n vertexShader: `varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`,\n fragmentShader: `uniform sampler2D tDiffuse; varying vec2 vUv;\n void main() {\n vec3 rgb = texture2D(tDiffuse, vUv).rgb;\n float luminance = max(max(rgb.r, rgb.g), rgb.b);\n float alpha = smoothstep(0.004, 0.06, luminance);\n gl_FragColor = vec4(rgb, alpha);\n }`\n };\n composer.addPass(new OutputPass());\n composer.addPass(new ShaderPass(AlphaFromLumaShader));\n\n // Keep the embedded CSS accents aligned with the kit palette.\n function applyCssTheme(accent) {\n const luminance = accent.r * 0.299 + accent.g * 0.587 + accent.b * 0.114;\n const boost = luminance < 0.35 ? 0.35 / Math.max(luminance, 0.001) : 1;\n for (const [channel, value] of Object.entries({ ar: accent.r, ag: accent.g, ab: accent.b })) {\n document.documentElement.style.setProperty(`--${channel}`, Math.min(Math.round(value * boost * 255), 255));\n }\n }\n applyCssTheme(new THREE.Color(def.c1));\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550 UI & EVENTS \u2550\u2550\u2550\u2550\u2550\u2550\u2550\n const overlay = document.getElementById('overlay');\n const audioControls = document.getElementById('audio-controls');\n const playPauseBtn = document.getElementById('play-pause-btn');\n const progressBar = document.getElementById('progress-bar');\n const progressContainer = document.getElementById('progress-container');\n const timeDisplay = document.getElementById('time-display');\n const trackName = document.getElementById('track-name');\n const status = document.getElementById('status-bar');\n\n const formatTime = s => `${Math.floor(s / 60)}:${Math.floor(s % 60).toString().padStart(2, '0')}`;\n\n function updateUI() {\n if (AUDIO.mode === 'mic') {\n playPauseBtn.textContent = '\u{1F399} live';\n playPauseBtn.disabled = true;\n } else {\n playPauseBtn.textContent = AUDIO.isPlaying ? '\u23F8 pause' : '\u25B6 play';\n playPauseBtn.classList.toggle('playing', AUDIO.isPlaying);\n playPauseBtn.disabled = false;\n }\n if (AUDIO.mode === 'file' && AUDIO.audioBuffer) {\n const cur = AUDIO.getCurrentTime(), dur = AUDIO.getDuration();\n progressBar.style.width = (cur / dur * 100) + '%';\n timeDisplay.textContent = `${formatTime(cur)} / ${formatTime(dur)}`;\n } else {\n progressBar.style.width = '0%';\n timeDisplay.textContent = AUDIO.mode === 'mic' ? 'LIVE' : '0:00 / 0:00';\n }\n }\n\n function togglePlayPause() {\n if (AUDIO.mode !== 'file') return;\n AUDIO.togglePlayPause();\n updateUI();\n status.innerHTML = AUDIO.isPlaying ? 'SYSTEM: <span>PLAYING</span>' : 'SYSTEM: <span>PAUSED</span>';\n }\n\n function activate() {\n AUDIO.init();\n if (AUDIO.ctx.state === 'suspended') AUDIO.ctx.resume();\n overlay.style.opacity = 0;\n setTimeout(() => overlay.style.display = 'none', 800);\n audioControls.classList.add('visible');\n }\n\n async function loadTrack(file) {\n activate();\n const buffer = await AUDIO.loadTrack(await file.arrayBuffer(), file.name);\n const samples = buffer.getChannelData(0);\n const bars = 160;\n const peaks = Array.from({ length: bars }, (_, bar) => {\n const from = Math.floor(bar * samples.length / bars);\n const to = Math.floor((bar + 1) * samples.length / bars);\n const stride = Math.max(1, Math.floor((to - from) / 64));\n let peak = 0;\n for (let i = from; i < to; i += stride) peak = Math.max(peak, Math.abs(samples[i]));\n return peak;\n });\n parent.postMessage({ type: 'ad-qf-waveform', value: peaks }, '*');\n AUDIO.play();\n trackName.textContent = file.name.length > 25 ? file.name.slice(0, 22) + '...' : file.name;\n updateUI();\n status.innerHTML = 'SYSTEM: <span>PLAYING</span>';\n }\n\n async function startMic(preferredLabel = null) {\n console.log('Global startMic requested:', preferredLabel);\n try {\n activate();\n await AUDIO.startMic(preferredLabel);\n trackName.textContent = AUDIO.currentTrackName;\n updateUI();\n // status update handled in AUDIO.startMic\n } catch (e) {\n console.error(\"Global startMic failed:\", e);\n const status = document.getElementById('status-bar');\n status.innerHTML = `ERROR: <span style=\"color:#ff6b6b\">${e.message}</span>`;\n }\n }\n\n // Using optional chaining/checks because one input was replaced by a button\n const enterBtn = document.getElementById('enter-btn');\n if (enterBtn) enterBtn.onclick = () => startMic(null);\n\n const fileInput = document.getElementById('file-input');\n if (fileInput) fileInput.onchange = e => e.target.files[0] && loadTrack(e.target.files[0]);\n\n document.getElementById('file-input-2').onchange = e => { if (e.target.files[0]) loadTrack(e.target.files[0]); e.target.value = ''; };\n document.getElementById('mic-btn-2').onclick = () => startMic(null);\n playPauseBtn.onclick = togglePlayPause;\n progressContainer.onclick = e => {\n if (AUDIO.mode !== 'file' || !AUDIO.audioBuffer) return;\n const rect = progressContainer.getBoundingClientRect();\n AUDIO.seek((e.clientX - rect.left) / rect.width * AUDIO.getDuration());\n };\n document.getElementById('screenshot-btn')?.addEventListener('click', () => {\n composer.render();\n const a = document.createElement('a');\n a.download = 'quantum-' + Date.now() + '.png';\n a.href = renderer.domElement.toDataURL('image/png');\n a.click();\n });\n document.getElementById('fullscreen-btn')?.addEventListener('click', () => {\n document.fullscreenElement ? document.exitFullscreen() : document.documentElement.requestFullscreen();\n });\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550 ANIMATION LOOP \u2550\u2550\u2550\u2550\u2550\u2550\u2550\n let renderVisible = true;\n let connectionElapsed = 1 / 20;\n let idleConnectionElapsed = 2;\n let frameRequest = 0;\n let nextFrameAt = 0;\n let lastRenderedAt = -1;\n let elapsed = 0;\n\n function startFrames() {\n if (!frameRequest && renderVisible && !document.hidden) frameRequest = requestAnimationFrame(animate);\n }\n\n function stopFrames() {\n if (frameRequest) cancelAnimationFrame(frameRequest);\n frameRequest = 0;\n nextFrameAt = 0;\n lastRenderedAt = -1;\n }\n\n document.addEventListener('visibilitychange', () => {\n if (document.hidden) stopFrames();\n else startFrames();\n });\n\n function animate(now) {\n frameRequest = 0;\n if (!renderVisible || document.hidden) return;\n startFrames();\n const frameInterval = 1000 / Math.max(1, STATE.targetFPS);\n if (now < nextFrameAt) return;\n nextFrameAt = Math.max(nextFrameAt + frameInterval, now);\n const dt = lastRenderedAt < 0 ? 0 : Math.min((now - lastRenderedAt) / 1000, 0.05);\n lastRenderedAt = now;\n elapsed += dt;\n\n material.uniforms.uTime.value += dt * STATE.timeScale;\n grainPass.uniforms.uTime.value = elapsed;\n lensFlarePass.uniforms.uTime.value = elapsed;\n\n material.uniforms.uZoomFactor.value = camera.position.length() / CONFIG.defaultZoom;\n material.uniforms.uMousePos.value.lerp(mouseWorldPos, 0.1);\n material.uniforms.uMouseVelocity.value.lerp(mouseVelocity, 0.08);\n\n AUDIO.update();\n\n const s = STATE.sensitivity, lf = 0.16;\n material.uniforms.uSubBass.value = THREE.MathUtils.lerp(material.uniforms.uSubBass.value, AUDIO.gatedBands.subBass * s, lf);\n material.uniforms.uBass.value = THREE.MathUtils.lerp(material.uniforms.uBass.value, AUDIO.gatedBands.bass * s, lf);\n material.uniforms.uLowMid.value = THREE.MathUtils.lerp(material.uniforms.uLowMid.value, AUDIO.gatedBands.lowMid * s, lf);\n material.uniforms.uMid.value = THREE.MathUtils.lerp(material.uniforms.uMid.value, AUDIO.gatedBands.mid * s, lf);\n material.uniforms.uHighMid.value = THREE.MathUtils.lerp(material.uniforms.uHighMid.value, AUDIO.gatedBands.highMid * s, lf);\n material.uniforms.uHigh.value = THREE.MathUtils.lerp(material.uniforms.uHigh.value, AUDIO.gatedBands.high * s, lf);\n material.uniforms.uUltraHigh.value = THREE.MathUtils.lerp(material.uniforms.uUltraHigh.value, AUDIO.gatedBands.ultraHigh * s, lf);\n material.uniforms.uBeatEnergy.value = THREE.MathUtils.lerp(material.uniforms.uBeatEnergy.value, AUDIO.beatEnergy, 0.22);\n material.uniforms.uSpectralCentroid.value = THREE.MathUtils.lerp(material.uniforms.uSpectralCentroid.value, AUDIO.spectralCentroid, lf);\n material.uniforms.uSpectralFlux.value = THREE.MathUtils.lerp(material.uniforms.uSpectralFlux.value, AUDIO.spectralFlux * s, lf);\n\n // === ENHANCEMENT: Update new uniforms ===\n material.uniforms.uOnsetEnergy.value = THREE.MathUtils.lerp(material.uniforms.uOnsetEnergy.value, AUDIO.onsetEnergy, 0.25);\n material.uniforms.uTerrainMode.value = FIELD_TYPES[STATE.field]?.terrain ? 1.0 : 0.0;\n material.uniforms.uTerrainHeight.value = STATE.waveformTerrainHeight;\n\n // Update secondary particles\n secondaryPoints.visible = STATE.secondaryEnabled;\n secondaryMaterial.uniforms.uTime.value = material.uniforms.uTime.value;\n secondaryMaterial.uniforms.uSubBass.value = material.uniforms.uSubBass.value;\n secondaryMaterial.uniforms.uBass.value = material.uniforms.uBass.value;\n secondaryMaterial.uniforms.uLowMid.value = material.uniforms.uLowMid.value;\n secondaryMaterial.uniforms.uMid.value = material.uniforms.uMid.value;\n secondaryMaterial.uniforms.uHighMid.value = material.uniforms.uHighMid.value;\n secondaryMaterial.uniforms.uHigh.value = material.uniforms.uHigh.value;\n secondaryMaterial.uniforms.uUltraHigh.value = material.uniforms.uUltraHigh.value;\n secondaryMaterial.uniforms.uBeatEnergy.value = material.uniforms.uBeatEnergy.value;\n secondaryMaterial.uniforms.uSpectralCentroid.value = material.uniforms.uSpectralCentroid.value;\n secondaryMaterial.uniforms.uSpectralFlux.value = material.uniforms.uSpectralFlux.value;\n secondaryMaterial.uniforms.uOnsetEnergy.value = material.uniforms.uOnsetEnergy.value;\n secondaryMaterial.uniforms.uVortexStrength.value = STATE.vortexStrength;\n\n // Update nebula background\n nebulaSphere.visible = STATE.nebulaEnabled;\n if (STATE.nebulaEnabled) {\n nebulaMaterial.uniforms.uTime.value = material.uniforms.uTime.value;\n nebulaMaterial.uniforms.uBass.value = material.uniforms.uBass.value;\n nebulaMaterial.uniforms.uMid.value = material.uniforms.uMid.value;\n nebulaMaterial.uniforms.uHigh.value = material.uniforms.uHigh.value;\n nebulaMaterial.uniforms.uBeatEnergy.value = material.uniforms.uBeatEnergy.value;\n }\n\n // Update particle connections\n connectionMesh.visible = STATE.connectionsEnabled;\n if (STATE.connectionsEnabled) {\n const activeAudio = AUDIO.mode === 'mic' || AUDIO.isPlaying || AUDIO.externalActive;\n const connectionState = [\n STATE.field, STATE.connectionThreshold, geometry.drawRange.count,\n material.uniforms.uViewportAspect.value, material.uniforms.uViewportHalfHeight.value, STATE.fieldSize,\n STATE.qualityLevel, activeAudio\n ];\n connectionElapsed = Math.min(1 / 20, connectionElapsed + dt);\n idleConnectionElapsed = Math.min(2, idleConnectionElapsed + dt);\n if (connectionElapsed >= 1 / 20 && shouldRebuildConnections(activeAudio, connectionState, lastConnectionState, idleConnectionElapsed)) {\n const refreshSelection = !lastConnectionState || connectionState[2] !== lastConnectionState[2]\n || connectionState[6] !== lastConnectionState[6] || (!activeAudio && idleConnectionElapsed >= 2);\n updateConnections(\n STATE.connectionThreshold,\n activeAudio ? material.uniforms.uBass.value : 0,\n activeAudio ? material.uniforms.uHighMid.value : 0,\n activeAudio ? material.uniforms.uMid.value : 0,\n refreshSelection\n );\n lastConnectionState = connectionState;\n connectionElapsed = 0;\n idleConnectionElapsed = 0;\n }\n connectionMaterial.uniforms.uBeatEnergy.value = material.uniforms.uBeatEnergy.value;\n connectionMaterial.uniforms.uHighMid.value = material.uniforms.uHighMid.value;\n connectionMaterial.uniforms.uSpectralCentroid.value = material.uniforms.uSpectralCentroid.value;\n connectionMaterial.uniforms.uOpacity.value = STATE.connectionOpacity;\n } else {\n connectionElapsed = 1 / 20;\n idleConnectionElapsed = 2;\n lastConnectionState = null;\n }\n\n // Update crawlers\n crawlerMesh.visible = STATE.crawlersEnabled && STATE.connectionsEnabled && (AUDIO.externalActive || AUDIO.active && (AUDIO.isPlaying || AUDIO.mode === 'mic'));\n if (crawlerMesh.visible) {\n updateCrawlers(\n dt,\n material.uniforms.uHighMid.value,\n material.uniforms.uBeatEnergy.value,\n material.uniforms.uSpectralCentroid.value\n );\n crawlerTarget.lerpColors(crawlerLow, crawlerHigh, material.uniforms.uSpectralCentroid.value);\n crawlerMaterial.uniforms.uColor.value.lerp(crawlerTarget, 0.05);\n crawlerMaterial.uniforms.uBeatEnergy.value = material.uniforms.uBeatEnergy.value;\n }\n\n // Update post-processing\n chromaticPass.uniforms.uBeatEnergy.value = material.uniforms.uBeatEnergy.value;\n godRaysPass.uniforms.uBeatEnergy.value = material.uniforms.uBeatEnergy.value;\n lensFlarePass.uniforms.uBeatEnergy.value = material.uniforms.uBeatEnergy.value;\n vignettePass.uniforms.uBeatEnergy.value = material.uniforms.uBeatEnergy.value;\n customDOFPass.uniforms.uBeatEnergy.value = material.uniforms.uBeatEnergy.value;\n\n\n\n // Update trails\n trailSystem.update(elapsed, {\n bass: material.uniforms.uBass.value,\n mid: material.uniforms.uMid.value,\n high: material.uniforms.uHigh.value,\n beat: material.uniforms.uBeatEnergy.value\n }, material.uniforms.uColor1.value);\n\n // === ENHANCEMENT: Adaptive Quality System ===\n QUALITY_SYSTEM.update(dt * 1000);\n\n // === ENHANCEMENT: Audio-Reactive Camera ===\n AUDIO_CAMERA.update(dt * 1000);\n\n // Camera shake\n if (STATE.cameraShake > 0 && AUDIO.beatEnergy > 0.1) {\n const shake = AUDIO.beatEnergy * STATE.cameraShake;\n cameraShakeOffset.set((Math.random() - 0.5) * shake * 0.6, (Math.random() - 0.5) * shake * 0.4, (Math.random() - 0.5) * shake * 0.25);\n camera.position.copy(baseCameraPos).add(cameraShakeOffset);\n }\n\n controls.autoRotateSpeed = 0.35 + material.uniforms.uMid.value * 2.0 + material.uniforms.uBeatEnergy.value * 2.0;\n bloomPass.strength = STATE.bloom + (AUDIO.gatedBands.bass + AUDIO.gatedBands.mid + AUDIO.beatEnergy) / 6;\n bloomPass.enabled = bloomPass.strength > 0;\n anamorphicPass.uniforms.uIntensity.value = STATE.anamorphicStretch + AUDIO.gatedBands.bass * 0.4;\n anamorphicPass.enabled = anamorphicPass.uniforms.uIntensity.value > 0;\n\n controls.update();\n composer.render();\n }\n\n window.addEventListener('resize', () => {\n camera.aspect = window.innerWidth / window.innerHeight;\n camera.updateProjectionMatrix();\n material.uniforms.uViewportAspect.value = window.innerWidth / window.innerHeight;\n secondaryMaterial.uniforms.uViewportAspect.value = material.uniforms.uViewportAspect.value;\n material.uniforms.uViewportHalfHeight.value = Math.tan(camera.fov * Math.PI / 360) * CONFIG.defaultZoom;\n secondaryMaterial.uniforms.uViewportHalfHeight.value = material.uniforms.uViewportHalfHeight.value;\n renderer.setSize(window.innerWidth, window.innerHeight);\n composer.setSize(window.innerWidth, window.innerHeight);\n STATE.pixelRatio = fitPixelRatio();\n QUALITY_SYSTEM.applyQuality();\n customDOFPass.uniforms.uResolution.value.set(window.innerWidth, window.innerHeight);\n baseCameraPos.copy(camera.position);\n });\n\n let pausedFromKit = false;\n let requestedTimeScale = STATE.timeScale;\n const sideEffects = {\n trails: value => afterimagePass.uniforms['damp'].value = value,\n chromaticAberration: value => { chromaticPass.uniforms.uIntensity.value = value; chromaticPass.enabled = value > 0; },\n filmGrain: value => { grainPass.uniforms.uIntensity.value = value; grainPass.enabled = value > 0; },\n nebulaIntensity: value => nebulaMaterial.uniforms.uIntensity.value = value,\n lensFlareEnabled: value => lensFlarePass.enabled = value && STATE.lensFlareIntensity > 0,\n lensFlareIntensity: value => { lensFlarePass.uniforms.uIntensity.value = value; lensFlarePass.enabled = STATE.lensFlareEnabled && value > 0; },\n dofEnabled: value => customDOFPass.enabled = value,\n dofFocus: value => customDOFPass.uniforms.uFocus.value = value,\n dofFocalLength: value => customDOFPass.uniforms.uFocalLength.value = value,\n dofBokehStrength: value => customDOFPass.uniforms.uBokehStrength.value = value,\n godRaysEnabled: value => godRaysPass.enabled = value && STATE.godRaysIntensity > 0,\n godRaysIntensity: value => { godRaysPass.uniforms.uIntensity.value = value; godRaysPass.enabled = STATE.godRaysEnabled && value > 0; },\n vortexStrength: value => material.uniforms.uVortexStrength.value = value,\n pulseIntensity: value => material.uniforms.uPulseIntensity.value = value,\n fieldSize: value => { material.uniforms.uFieldFill.value = value; secondaryMaterial.uniforms.uFieldFill.value = value; },\n };\n window.addEventListener('message', ({ source, data }) => {\n if (source !== parent || data?.type !== 'ad-qf-control') return;\n const { key, value } = data;\n if (key === 'field' && FIELD_TYPES[value]) {\n STATE.field = value;\n const selected = FIELD_TYPES[value];\n material.uniforms.uNoiseScale.value = selected.s;\n material.uniforms.uCurlStrength.value = selected.curl;\n material.uniforms.uSizeBase.value = selected.sz;\n } else if (key === 'visible') {\n renderVisible = !!value;\n if (renderVisible) startFrames();\n else stopFrames();\n }\n else if (key === 'interactive') controls.enabled = !!value;\n else if (key === 'spectrum') EXTERNAL.set(value?.bands, value?.range);\n else if (key === 'particleCount') {\n particleCount = Number.isFinite(value) ? Math.max(0, Math.floor(value)) : null;\n QUALITY_SYSTEM.applyQuality();\n }\n else if (key === 'particleBudget') {\n particleBudget = Number.isFinite(value) ? Math.max(0, Math.floor(value)) : Infinity;\n QUALITY_SYSTEM.applyQuality();\n }\n else if (key === 'timeScale') { requestedTimeScale = value; if (!pausedFromKit) STATE.timeScale = value; }\n else if (key === 'paused') { pausedFromKit = !!value; STATE.timeScale = pausedFromKit ? 0 : requestedTimeScale; }\n else if (key === 'play') togglePlayPause();\n else if (key === 'file' && value) loadTrack(value);\n else if (key === 'mic') {\n if (AUDIO.mode === 'mic') { AUDIO.stopCurrentSource(); AUDIO.mode = null; }\n else startMic(null);\n }\n else if (key === 'seek') { AUDIO.seek(value); updateUI(); }\n else if (key === 'screenshot') document.getElementById('screenshot-btn').click();\n else if (key === 'palette' && Array.isArray(value)) {\n const main = new THREE.Color(value[0]), light = new THREE.Color(value[1]);\n applyCssTheme(main);\n material.uniforms.uColor1.value.copy(main);\n material.uniforms.uColor2.value.copy(light);\n material.uniforms.uColor3.value.copy(light).lerp(new THREE.Color('#ffffff'), 0.32);\n secondaryMaterial.uniforms.uColor1.value.copy(main);\n secondaryMaterial.uniforms.uColor2.value.copy(light);\n nebulaMaterial.uniforms.uColor1.value.copy(main);\n nebulaMaterial.uniforms.uColor2.value.copy(light);\n nebulaMaterial.uniforms.uColor3.value.copy(main);\n connectionMaterial.uniforms.uColor.value.copy(light);\n connectionMaterial.uniforms.uColor2.value.copy(main);\n const white = new THREE.Color('#ffffff');\n const tint = glowColor(light.clone().lerp(white, 0.35), 0.75);\n lensFlarePass.uniforms.uTint.value.copy(tint);\n anamorphicPass.uniforms.uTint.value.copy(tint);\n // Reaction lines stand out from the field like the presets' contrasting colours did:\n // a luminous tint of the theme's light colour with a near-white core on bright passages.\n glowColor(crawlerLow.copy(light).lerp(white, 0.2), 0.75);\n glowColor(crawlerHigh.copy(light).lerp(white, 0.5), 0.95);\n crawlerMaterial.uniforms.uColor.value.copy(crawlerLow);\n trailSystem.trails.forEach((trail, i) => glowColor(trail.color.copy(main).lerp(light, i / Math.max(1, trailSystem.trails.length - 1)).lerp(white, 0.15), 0.6));\n } else if (Object.prototype.hasOwnProperty.call(STATE, key)) {\n STATE[key] = value;\n if (key === 'density') QUALITY_SYSTEM.applyQuality();\n sideEffects[key]?.(value);\n }\n });\n let lastTransport = '';\n setInterval(() => {\n if (!renderVisible || document.hidden) return;\n const value = {\n mode: AUDIO.mode, playing: AUDIO.isPlaying,\n current: AUDIO.mode === 'file' ? AUDIO.getCurrentTime() : 0,\n duration: AUDIO.mode === 'file' ? AUDIO.getDuration() : 0,\n track: AUDIO.mode ? AUDIO.currentTrackName : ''\n };\n const snapshot = JSON.stringify(value);\n if (snapshot !== lastTransport) {\n lastTransport = snapshot;\n parent.postMessage({ type: 'ad-qf-transport', value }, '*');\n }\n }, 250);\n parent.postMessage({ type: 'ad-qf-ready' }, '*');\n\n QUALITY_SYSTEM.applyQuality();\n startFrames();\n <\/script>\n</body>\n\n</html>\n";
4801
+ var reference_default = "<!DOCTYPE html>\n<!-- Quantum Fields Contributors, MIT License. Full notice: UPSTREAM-LICENSE.txt in this package. -->\n<html lang=\"en\">\n\n<head>\n <meta charset=\"UTF-8\">\n <title>Quantum Field: Interactive</title>\n <style>\n /* The kit hides this page's own interface, so it loads no web fonts: the scene makes no requests beyond three.js. */\n\n * {\n box-sizing: border-box;\n }\n\n :root {\n --ar: 167; --ag: 139; --ab: 250;\n }\n\n html, body {\n margin: 0;\n overflow: hidden;\n background: transparent !important;\n color-scheme: dark;\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n }\n\n canvas {\n display: block;\n }\n\n #scanlines {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: radial-gradient(ellipse at center, transparent 0%, transparent 50%, rgba(0, 0, 0, 0.5) 100%);\n pointer-events: none;\n z-index: 10;\n }\n\n #overlay {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n background: linear-gradient(180deg, rgba(15, 10, 25, 0.97) 0%, rgba(10, 10, 20, 0.98) 100%);\n z-index: 100;\n backdrop-filter: blur(20px);\n transition: opacity 1s ease;\n pointer-events: none;\n /* Allow clicks to pass through to canvas */\n }\n\n .hud-panel {\n background: linear-gradient(165deg, rgba(255, 255, 255, 0.04) 0%, rgba(255, 255, 255, 0.01) 100%);\n border: 1px solid rgba(255, 255, 255, 0.07);\n padding: 55px 75px;\n text-align: center;\n border-radius: 28px;\n box-shadow: 0 30px 80px rgba(0, 0, 0, 0.5);\n pointer-events: auto;\n /* Re-enable clicks for buttons */\n }\n\n h1 {\n font-family: 'Space Grotesk', sans-serif;\n font-weight: 500;\n letter-spacing: 1px;\n font-size: 34px;\n margin: 0 0 8px 0;\n background: linear-gradient(135deg, #ffffff 0%, rgba(var(--ar),var(--ag),var(--ab),0.75) 40%, rgb(var(--ar),var(--ag),var(--ab)) 100%);\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent;\n background-clip: text;\n }\n\n .hud-panel p {\n color: rgba(255, 255, 255, 0.35);\n font-size: 13px;\n margin-bottom: 40px;\n letter-spacing: 0.5px;\n font-weight: 300;\n }\n\n .btn-group {\n display: flex;\n gap: 14px;\n justify-content: center;\n }\n\n button,\n .file-upload {\n background: linear-gradient(135deg, rgba(var(--ar),var(--ag),var(--ab),0.12) 0%, rgba(var(--ar),var(--ag),var(--ab),0.06) 100%);\n border: 1px solid rgba(var(--ar),var(--ag),var(--ab),0.25);\n color: rgba(255, 255, 255, 0.85);\n padding: 15px 30px;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n letter-spacing: 0.3px;\n border-radius: 14px;\n font-family: 'Inter', sans-serif;\n }\n\n button:hover,\n .file-upload:hover {\n background: linear-gradient(135deg, rgba(var(--ar),var(--ag),var(--ab),0.25) 0%, rgba(var(--ar),var(--ag),var(--ab),0.18) 100%);\n border-color: rgba(var(--ar),var(--ag),var(--ab),0.45);\n transform: translateY(-2px);\n box-shadow: 0 12px 35px rgba(139, 92, 246, 0.2);\n }\n\n input[type=\"file\"] {\n display: none;\n }\n\n #status-bar {\n position: absolute;\n bottom: 80px;\n left: 28px;\n color: rgba(255, 255, 255, 0.3);\n font-size: 10px;\n letter-spacing: 0.5px;\n z-index: 200;\n pointer-events: none;\n }\n\n #status-bar span {\n color: rgba(196, 181, 253, 0.7);\n font-weight: 500;\n }\n\n #interaction-hint {\n position: absolute;\n top: 28px;\n width: 100%;\n text-align: center;\n color: rgba(255, 255, 255, 0.5);\n font-size: 11px;\n letter-spacing: 2px;\n pointer-events: none;\n z-index: 9;\n text-transform: lowercase;\n }\n\n #zoom-indicator {\n position: absolute;\n bottom: 80px;\n right: 28px;\n color: rgba(255, 255, 255, 0.3);\n font-size: 10px;\n z-index: 20;\n pointer-events: none;\n }\n\n #zoom-indicator span {\n color: rgba(196, 181, 253, 0.7);\n }\n\n #audio-controls {\n position: fixed;\n bottom: 24px;\n left: 50%;\n transform: translateX(-50%);\n display: flex;\n gap: 10px;\n align-items: center;\n padding: 12px 20px;\n background: linear-gradient(180deg, rgba(35, 28, 50, 0.88) 0%, rgba(22, 20, 38, 0.92) 100%);\n border: 1px solid rgba(255, 255, 255, 0.05);\n border-radius: 18px;\n z-index: 200;\n opacity: 0;\n pointer-events: none;\n transition: all 0.4s ease;\n backdrop-filter: blur(25px);\n }\n\n #audio-controls.visible {\n opacity: 1;\n pointer-events: auto;\n }\n\n #audio-controls button,\n #audio-controls .file-upload {\n padding: 9px 14px;\n font-size: 11px;\n background: rgba(255, 255, 255, 0.03);\n border: 1px solid rgba(255, 255, 255, 0.07);\n color: rgba(255, 255, 255, 0.65);\n border-radius: 10px;\n }\n\n #play-pause-btn {\n min-width: 78px;\n background: linear-gradient(135deg, rgba(var(--ar),var(--ag),var(--ab),0.18) 0%, rgba(var(--ar),var(--ag),var(--ab),0.10) 100%) !important;\n border-color: rgba(var(--ar),var(--ag),var(--ab),0.28) !important;\n color: #e9d5ff !important;\n }\n\n #play-pause-btn.playing {\n background: linear-gradient(135deg, rgba(251, 113, 133, 0.18) 0%, rgba(244, 114, 182, 0.12) 100%) !important;\n border-color: rgba(251, 113, 133, 0.35) !important;\n }\n\n #progress-container {\n width: 85px;\n height: 4px;\n background: rgba(255, 255, 255, 0.06);\n border-radius: 4px;\n cursor: pointer;\n }\n\n #progress-bar {\n height: 100%;\n background: linear-gradient(90deg, rgb(var(--ar),var(--ag),var(--ab)), rgba(var(--ar),var(--ag),var(--ab),0.6), rgba(var(--ar),var(--ag),var(--ab),0.3));\n border-radius: 4px;\n width: 0%;\n }\n\n #time-display {\n color: rgba(255, 255, 255, 0.35);\n font-size: 10px;\n min-width: 62px;\n text-align: center;\n }\n\n #track-name {\n color: rgba(255, 255, 255, 0.3);\n font-size: 10px;\n max-width: 85px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* --- Center Panel Refinement --- */\n .hud-panel {\n background: rgba(15, 10, 25, 0.85);\n border: 1px solid rgba(255, 255, 255, 0.08);\n /* Darkened container */\n padding: 24px;\n /* Reduced padding */\n border-radius: 24px;\n box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);\n backdrop-filter: blur(40px);\n min-width: 420px;\n /* Wider for buttons */\n }\n\n .hud-panel h1,\n .hud-panel p {\n display: none;\n /* Hide title as per screenshot showing only buttons */\n }\n\n .btn-group {\n display: flex;\n gap: 16px;\n justify-content: center;\n width: 100%;\n }\n\n .btn-group button {\n flex: 1;\n background: transparent;\n border: 1px solid rgba(var(--ar),var(--ag),var(--ab),0.3);\n color: rgb(var(--ar),var(--ag),var(--ab));\n padding: 14px 0;\n border-radius: 12px;\n font-size: 12px;\n letter-spacing: 1px;\n text-transform: uppercase;\n font-weight: 600;\n transition: all 0.3s ease;\n }\n\n .btn-group button:hover {\n background: rgba(var(--ar),var(--ag),var(--ab),0.1);\n border-color: rgb(var(--ar),var(--ag),var(--ab));\n box-shadow: 0 0 20px rgba(var(--ar),var(--ag),var(--ab),0.2);\n }\n #interaction-hint, #audio-controls, #status-bar, #zoom-indicator { display: none !important; }\n </style>\n</head>\n\n<body>\n <div id=\"scanlines\" style=\"display:none\"></div>\n <div id=\"interaction-hint\">space \xB7 play/pause &nbsp;\xB7&nbsp; scroll \xB7 zoom &nbsp;\xB7&nbsp; drag \xB7 rotate &nbsp;\xB7&nbsp; F \xB7 focus mode</div>\n <div id=\"overlay\" style=\"display:none\">\n <div class=\"hud-panel\">\n <h1>Quantum Fields</h1>\n <p>Audio-reactive particle simulation</p>\n <div class=\"btn-group\">\n <button id=\"enter-btn\">Enter</button>\n </div>\n </div>\n </div>\n <div id=\"audio-controls\">\n <button id=\"play-pause-btn\" class=\"playing\">\u23F8 pause</button>\n <div id=\"progress-container\">\n <div id=\"progress-bar\"></div>\n </div>\n <span id=\"time-display\">0:00 / 0:00</span>\n <span id=\"track-name\">no track</span>\n <label class=\"file-upload\" title=\"Load audio\">\u{1F4C1}<input type=\"file\" id=\"file-input-2\" accept=\"audio/*\"></label>\n <button id=\"mic-btn-2\" title=\"Microphone\">\u{1F399}</button>\n <button id=\"screenshot-btn\" title=\"Screenshot\">\u{1F4F7}</button>\n <button id=\"fullscreen-btn\" title=\"Fullscreen\">\u26F6</button>\n </div>\n <div id=\"status-bar\"><span>ready</span> \xB7 awaiting input</div>\n <div id=\"zoom-indicator\">zoom <span id=\"zoom-level\">100%</span></div>\n\n <!-- Global Error Handler (Must be before module imports) -->\n <script>\n window.onerror = function (msg, url, lineNo, columnNo, error) {\n const status = document.getElementById('status-bar');\n if (status) {\n status.innerHTML = `ERROR: <span style=\"color:#ff6b6b\">${msg}</span>`;\n status.style.opacity = 1;\n }\n console.error('Global Error:', msg, url, lineNo, error);\n return false;\n };\n // Catch unhandled promise rejections\n window.onunhandledrejection = function (event) {\n const status = document.getElementById('status-bar');\n if (status) {\n status.innerHTML = `ERROR: <span style=\"color:#ff6b6b\">${event.reason}</span>`;\n status.style.opacity = 1;\n }\n console.error('Unhandled Rejection:', event.reason);\n };\n <\/script>\n\n <script type=\"importmap\">\n{\n \"imports\": {\n \"three\": \"https://unpkg.com/three@0.182.0/build/three.module.js\",\n \"three/addons/\": \"https://unpkg.com/three@0.182.0/examples/jsm/\"\n }\n}\n<\/script>\n\n <script type=\"module\">\n import * as THREE from 'three';\n import { OrbitControls } from 'three/addons/controls/OrbitControls.js';\n import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';\n import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';\n import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';\n import { AfterimagePass } from 'three/addons/postprocessing/AfterimagePass.js';\n import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';\n // BokehPass removed - using custom DOF shader\n import { OutputPass } from 'three/addons/postprocessing/OutputPass.js';\n\n function fitPixelRatio() {\n return Math.min(window.devicePixelRatio || 1, 1.5,\n Math.sqrt(2_100_000 / Math.max(1, window.innerWidth * window.innerHeight)));\n }\n\n const CONFIG = {\n maxParticles: 250000,\n maxDensity: 0.14,\n secondaryParticles: 50000,\n initialDensity: 0.043,\n fieldRadius: 90,\n minZoom: 1,\n maxZoom: 250,\n defaultZoom: 200\n };\n let particleBudget = Number(window.frameElement?.getAttribute('data-particle-budget') ?? Infinity);\n particleBudget = Number.isFinite(particleBudget) ? Math.max(0, Math.floor(particleBudget)) : Infinity;\n // Requested number of particles across both layers; null follows the density setting.\n let particleCount = window.frameElement?.getAttribute('data-particle-count');\n particleCount = particleCount != null && Number.isFinite(Number(particleCount)) ? Math.max(0, Math.floor(Number(particleCount))) : null;\n\n const STATE = {\n field: 'Gluon (Strong)',\n density: CONFIG.initialDensity,\n fieldSize: 0.78,\n particleDepth: 0,\n particleScale: 1,\n particleSharpness: 0,\n sensitivity: 1.1,\n trails: 0.66144,\n bloom: 0.3,\n timeScale: 1,\n pixelRatio: fitPixelRatio(),\n chromaticAberration: 0.004,\n filmGrain: 0.001,\n cameraShake: 0.65,\n vortexStrength: 0.73,\n pulseIntensity: 0.15,\n anamorphicStretch: 0.1,\n bassGateThreshold: 0.12,\n bassGateAttack: 0.3,\n bassGateRelease: 0.136,\n bassGateRatio: 2.142,\n bassGateEnabled: false,\n // NEW EFFECTS\n godRaysEnabled: false,\n godRaysIntensity: 0.078,\n godRaysDecay: 0.92,\n dofEnabled: false,\n dofFocus: 0.04,\n dofFocalLength: 0.40,\n dofBokehStrength: 0.36,\n lensFlareEnabled: true,\n lensFlareIntensity: 0.1,\n particleTrailsEnabled: true,\n particleTrailOpacity: 1,\n nebulaEnabled: false,\n nebulaIntensity: 0.3,\n connectionsEnabled: true,\n connectionThreshold: 20.0,\n connectionOpacity: 0.75,\n // === ENHANCEMENT PACK v2.0 ===\n // 1. Motion Blur\n motionBlurEnabled: true,\n motionBlurStrength: 0.1,\n // 2. Adaptive Quality\n adaptiveQualityEnabled: true,\n targetFPS: 45,\n qualityLevel: 1,\n // 3. Onset Detection\n onsetSensitivity: 1,\n onsetDecay: 0.92,\n // 4. Audio-Reactive Camera\n audioCameraEnabled: true,\n audioCameraSmoothing: 0.2,\n audioCameraIntensity: 0.3,\n // 5. Waveform Terrain\n waveformTerrainScale: 1.0,\n waveformTerrainHeight: 20.0\n };\n\n // --- AUDIO SYSTEM ---\n // Spectrum supplied by the host app (e.g. a native audio engine the iframe cannot hear):\n // band levels 0..1, log-spaced from `low` to `high` Hz. It is turned into the same frequency\n // array an analyser would give, so bands, beats, flux, onsets and the terrain all follow it.\n const EXTERNAL = {\n target: null, low: 40, high: 12000,\n sampleRate: 48000, data: new Uint8Array(2048), smoothed: new Float32Array(2048), prev: new Float32Array(2048),\n fresh() { return this.target !== null; },\n set(bands, range) {\n this.target = Array.isArray(bands) && bands.length ? bands.map((v) => Math.max(0, Math.min(1, Number(v) || 0))) : null;\n if (Array.isArray(range) && range[0] > 0 && range[1] > range[0]) [this.low, this.high] = range;\n },\n // Host frames arrive at their own pace (often 20 Hz); easing toward them every rendered\n // frame keeps the flux continuous, like an analyser's smoothing does.\n fill() {\n const bands = this.target, n = bands.length, len = this.data.length, nyquist = this.sampleRate / 2;\n const span = Math.log(this.high / this.low);\n for (let i = 0; i < len; i++) {\n const freq = Math.max(1, (i + 0.5) / len * nyquist);\n const pos = Math.log(freq / this.low) / span * (n - 1);\n let level;\n if (pos <= 0) level = bands[0];\n else if (pos >= n - 1) level = bands[n - 1] * Math.max(0, 1 - Math.log(freq / this.high) / Math.log(nyquist / this.high));\n else { const k = Math.floor(pos), t = pos - k; level = bands[k] * (1 - t) + bands[k + 1] * t; }\n this.smoothed[i] += (level - this.smoothed[i]) * 0.35;\n this.data[i] = Math.min(255, Math.round(this.smoothed[i] * 255));\n }\n }\n };\n\n const AUDIO = {\n ctx: null, analyser: null, source: null, gainNode: null, data: null,\n audioBuffer: null, isPlaying: false, isPaused: false,\n startTime: 0, pauseTime: 0, currentTrackName: 'No Track',\n mode: null, micStream: null,\n bands: { subBass: 0, bass: 0, lowMid: 0, mid: 0, highMid: 0, high: 0, ultraHigh: 0 },\n gatedBands: { subBass: 0, bass: 0, lowMid: 0, mid: 0, highMid: 0, high: 0, ultraHigh: 0 },\n beatThreshold: 0.15, beatDecay: 0.93, beatEnergy: 0,\n lastBeatTime: 0, beatDetected: false, peakHistory: [],\n spectralCentroid: 0, spectralFlux: 0, prevSpectrum: null, active: false, idleCleared: false,\n // === ENHANCEMENT: Onset Detection ===\n onsetEnergy: 0, onsetDetected: false, onsetHistory: [], onsetThreshold: 0,\n // === ENHANCEMENT: Waveform Data ===\n waveformData: new Float32Array(128), waveformSmoothed: new Float32Array(128),\n gateState: {\n isOpen: false, envelope: 0, holdTime: 0, noiseFloor: 0.05,\n calibrationSamples: [], isCalibrating: true, calibrationFrames: 30\n },\n\n init() {\n if (this.ctx) return;\n this.ctx = new (window.AudioContext || window.webkitAudioContext)();\n this.analyser = this.ctx.createAnalyser();\n this.analyser.fftSize = 4096;\n this.analyser.smoothingTimeConstant = 0.65;\n this.data = new Uint8Array(this.analyser.frequencyBinCount);\n this.prevSpectrum = new Float32Array(this.analyser.frequencyBinCount);\n this.active = true;\n },\n\n loadTrack(arrayBuffer, fileName) {\n return new Promise((resolve, reject) => {\n this.ctx.decodeAudioData(arrayBuffer, (buffer) => {\n this.stopCurrentSource();\n this.audioBuffer = buffer;\n this.currentTrackName = fileName || 'Unknown';\n this.mode = 'file';\n this.pauseTime = 0;\n this.resetCalibration();\n resolve(buffer);\n }, reject);\n });\n },\n\n play() {\n if (!this.audioBuffer || this.mode !== 'file' || this.isPlaying) return;\n this.source = this.ctx.createBufferSource();\n this.source.buffer = this.audioBuffer;\n this.source.loop = true;\n this.source.connect(this.analyser);\n this.analyser.connect(this.ctx.destination);\n this.source.start(0, this.pauseTime);\n this.startTime = this.ctx.currentTime - this.pauseTime;\n this.isPlaying = true;\n this.isPaused = false;\n },\n\n pause() {\n if (!this.isPlaying || this.mode !== 'file') return;\n this.pauseTime = this.getCurrentTime();\n this.isPaused = true;\n this.isPlaying = false;\n if (this.source) { try { this.source.stop(); } catch (e) { } this.source.disconnect(); this.source = null; }\n },\n\n togglePlayPause() {\n if (this.mode === 'mic') return false;\n if (this.isPlaying) { this.pause(); return false; }\n else { this.play(); return true; }\n },\n\n getCurrentTime() {\n if (!this.isPlaying) return this.pauseTime;\n if (!this.audioBuffer) return 0;\n return (this.ctx.currentTime - this.startTime) % this.audioBuffer.duration;\n },\n\n getDuration() { return this.audioBuffer ? this.audioBuffer.duration : 0; },\n\n seek(time) {\n if (this.mode !== 'file' || !this.audioBuffer) return;\n const wasPlaying = this.isPlaying;\n if (wasPlaying) this.pause();\n this.pauseTime = Math.max(0, Math.min(time, this.audioBuffer.duration));\n if (wasPlaying) this.play();\n },\n\n stopCurrentSource() {\n if (this.source) { try { this.source.stop(); this.source.disconnect(); } catch (e) { } this.source = null; }\n if (this.gainNode) { try { this.gainNode.disconnect(); } catch (e) { } this.gainNode = null; }\n if (this.micStream) { this.micStream.getTracks().forEach(t => t.stop()); this.micStream = null; }\n this.isPlaying = false; this.isPaused = false; this.pauseTime = 0;\n },\n\n async startMic(preferredLabel = null) {\n console.log('startMic called with preferredLabel:', preferredLabel);\n const status = document.getElementById('status-bar');\n if (status) status.innerHTML = 'SYSTEM: <span>REQUESTING ACCESS...</span>';\n\n try {\n // Ensure context is running\n if (this.ctx.state === 'suspended') {\n console.log('Resuming AudioContext...');\n await this.ctx.resume();\n }\n\n // 1. Get initial stream (activates permissions so labels are visible)\n console.log('Requesting initial getUserMedia...');\n let stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n console.log('Initial stream obtained:', stream.id);\n\n // 2. Search for preferred device (loopback)\n let target = null;\n if (preferredLabel) {\n const devices = await navigator.mediaDevices.enumerateDevices();\n console.log(\"Audio Devices Found:\", devices.length, devices.map(d => `${d.label} (${d.kind})`));\n\n target = devices.find(d => d.kind === 'audioinput' &&\n d.label.toLowerCase().includes(preferredLabel.toLowerCase()));\n }\n\n if (target) {\n console.log(`Switching audio input to: ${target.label} [${target.deviceId}]`);\n stream.getTracks().forEach(t => t.stop()); // Stop generic stream\n\n // Request specific device - being explicit with constraints\n stream = await navigator.mediaDevices.getUserMedia({\n audio: {\n deviceId: { exact: target.deviceId },\n echoCancellation: false,\n noiseSuppression: false,\n autoGainControl: false,\n channelCount: 2\n }\n });\n this.currentTrackName = target.label;\n } else {\n if (preferredLabel) console.warn(`Preferred device '${preferredLabel}' not found. Using default.`);\n // Keep the initial stream\n this.currentTrackName = stream.getAudioTracks()[0]?.label || 'Live Microphone';\n }\n\n this.stopCurrentSource();\n this.micStream = stream;\n this.source = this.ctx.createMediaStreamSource(stream);\n this.gainNode = this.ctx.createGain();\n this.gainNode.gain.value = 3.0; // Boost loopback/mic signal before FFT \u2014 tune via sensitivity slider\n this.source.connect(this.gainNode);\n this.gainNode.connect(this.analyser);\n this.mode = 'mic';\n this.isPlaying = true;\n this.resetCalibration();\n\n if (status) status.innerHTML = 'SYSTEM: <span>LISTENING (' + (preferredLabel ? 'Loopback' : 'Mic') + ')</span>';\n updateUI();\n\n return stream;\n } catch (e) {\n console.error(\"AUDIO.startMic failed:\", e);\n throw e;\n }\n },\n\n resetCalibration() { this.gateState.isCalibrating = true; this.gateState.calibrationSamples = []; },\n\n applyBassGate(rawValue) {\n if (!STATE.bassGateEnabled) return rawValue;\n const { bassGateThreshold: threshold, bassGateAttack: attack, bassGateRelease: release, bassGateRatio: ratio } = STATE;\n if (this.gateState.isCalibrating) {\n this.gateState.calibrationSamples.push(rawValue);\n if (this.gateState.calibrationSamples.length >= this.gateState.calibrationFrames) {\n const sorted = [...this.gateState.calibrationSamples].sort((a, b) => a - b);\n this.gateState.noiseFloor = sorted.slice(0, Math.floor(sorted.length * 0.2)).reduce((a, b) => a + b, 0) / Math.floor(sorted.length * 0.2);\n this.gateState.isCalibrating = false;\n }\n }\n const effectiveThreshold = Math.max(threshold, this.gateState.noiseFloor * 1.5);\n if (rawValue > effectiveThreshold) { this.gateState.isOpen = true; this.gateState.holdTime = 10; }\n else if (rawValue < effectiveThreshold * 0.7 && this.gateState.holdTime <= 0) { this.gateState.isOpen = false; }\n if (this.gateState.holdTime > 0) this.gateState.holdTime--;\n this.gateState.envelope += ((this.gateState.isOpen ? 1.0 : 0.0) - this.gateState.envelope) * (this.gateState.isOpen ? attack : release);\n let output = rawValue;\n if (this.gateState.isOpen && rawValue > effectiveThreshold) {\n output = effectiveThreshold + (rawValue - effectiveThreshold) / ratio;\n }\n return output * this.gateState.envelope;\n },\n\n update() {\n const own = this.active && this.isPlaying && this.source;\n this.externalActive = !own && EXTERNAL.fresh();\n if (!own && !this.externalActive) {\n if (!this.idleCleared) {\n for (const band in this.bands) this.bands[band] = this.gatedBands[band] = 0;\n this.spectralCentroid = this.spectralFlux = 0;\n this.beatDetected = this.onsetDetected = false;\n this.idleCleared = true;\n }\n this.beatEnergy *= this.beatDecay;\n this.onsetEnergy *= STATE.onsetDecay;\n return;\n }\n this.idleCleared = false;\n let data = this.data, prevSpectrum = this.prevSpectrum, nyquist;\n if (this.externalActive) {\n EXTERNAL.fill();\n data = EXTERNAL.data; prevSpectrum = EXTERNAL.prev; nyquist = EXTERNAL.sampleRate / 2;\n } else {\n this.analyser.getByteFrequencyData(this.data);\n nyquist = this.ctx.sampleRate / 2;\n }\n const len = data.length;\n const getFreqIndex = (freq) => Math.round(freq / nyquist * len);\n const ranges = { subBass: [50, 120], bass: [120, 200], lowMid: [200, 450], mid: [450, 2000], highMid: [2000, 3500], high: [3500, 12000], ultraHigh: [12000, 20000] };\n let totalEnergy = 0, weightedFreqSum = 0;\n for (const [band, [low, high]] of Object.entries(ranges)) {\n const lowIdx = getFreqIndex(low), highIdx = getFreqIndex(high);\n let sum = 0;\n for (let i = lowIdx; i < highIdx && i < len; i++) {\n const v = data[i] / 255.0;\n sum += v; totalEnergy += v; weightedFreqSum += v * i;\n }\n const rawBand = sum / (highIdx - lowIdx);\n this.bands[band] = rawBand;\n this.gatedBands[band] = (band === 'subBass' || band === 'bass' || band === 'lowMid') ? this.applyBassGate(rawBand) : rawBand;\n }\n this.spectralCentroid = totalEnergy > 0 ? weightedFreqSum / totalEnergy / len : 0;\n let flux = 0;\n for (let i = 0; i < len; i++) {\n const current = data[i] / 255.0, diff = current - prevSpectrum[i];\n flux += diff > 0 ? diff * diff : 0;\n prevSpectrum[i] = current;\n }\n this.spectralFlux = Math.sqrt(flux / len);\n const currentEnergy = this.gatedBands.bass * 0.6 + this.gatedBands.subBass * 0.4;\n this.peakHistory.push(currentEnergy);\n if (this.peakHistory.length > 50) this.peakHistory.shift();\n const avgEnergy = this.peakHistory.reduce((a, b) => a + b, 0) / this.peakHistory.length;\n const variance = this.peakHistory.reduce((a, b) => a + Math.pow(b - avgEnergy, 2), 0) / this.peakHistory.length;\n const dynamicThreshold = avgEnergy + Math.sqrt(variance) * 1.8;\n const now = performance.now();\n if (currentEnergy > dynamicThreshold && currentEnergy > this.beatThreshold && now - this.lastBeatTime > 120) {\n this.beatDetected = true; this.beatEnergy = 1.0; this.lastBeatTime = now;\n } else { this.beatDetected = false; }\n this.beatEnergy *= this.beatDecay;\n\n // === ENHANCEMENT: Improved Onset Detection ===\n // Uses spectral flux derivative for sharper transient response\n const onsetSens = STATE.onsetSensitivity;\n this.onsetHistory.push(this.spectralFlux);\n if (this.onsetHistory.length > 8) this.onsetHistory.shift();\n const recentAvg = this.onsetHistory.slice(-4).reduce((a, b) => a + b, 0) / 4;\n const olderAvg = this.onsetHistory.slice(0, 4).reduce((a, b) => a + b, 0) / Math.max(1, this.onsetHistory.slice(0, 4).length);\n const onsetDelta = (recentAvg - olderAvg) * onsetSens;\n this.onsetDetected = onsetDelta > 0.015 && now - this.lastBeatTime > 50;\n this.onsetEnergy = this.onsetDetected ? 1.0 : this.onsetEnergy * STATE.onsetDecay;\n\n // === ENHANCEMENT: Waveform Data for Terrain Field ===\n const waveformBins = 128;\n const binSize = Math.floor(len / waveformBins);\n for (let i = 0; i < waveformBins; i++) {\n let sum = 0;\n for (let j = 0; j < binSize; j++) {\n sum += data[i * binSize + j] / 255.0;\n }\n const target = sum / binSize;\n this.waveformSmoothed[i] += (target - this.waveformSmoothed[i]) * 0.3;\n this.waveformData[i] = this.waveformSmoothed[i];\n }\n }\n };\n\n // --- SCENE SETUP ---\n const scene = new THREE.Scene();\n scene.fog = new THREE.FogExp2(0x000508, 0.005);\n const camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 0.1, 800);\n camera.position.set(0, 12, CONFIG.defaultZoom);\n const cameraShakeOffset = new THREE.Vector3();\n const baseCameraPos = camera.position.clone();\n const zoomIndicator = document.getElementById('zoom-level');\n\n const renderer = new THREE.WebGLRenderer({ powerPreference: \"high-performance\", antialias: true, alpha: true });\n renderer.setClearColor(0x000000, 0);\n renderer.setSize(window.innerWidth, window.innerHeight);\n renderer.setPixelRatio(STATE.pixelRatio);\n renderer.toneMapping = THREE.ACESFilmicToneMapping;\n renderer.toneMappingExposure = 2.1;\n document.body.appendChild(renderer.domElement);\n\n const controls = new OrbitControls(camera, renderer.domElement);\n controls.enableDamping = true;\n controls.dampingFactor = 0.06;\n controls.autoRotate = false;\n controls.autoRotateSpeed = 0.3;\n controls.minDistance = CONFIG.minZoom;\n controls.maxDistance = CONFIG.maxZoom;\n controls.maxPolarAngle = Math.PI * 0.85;\n controls.minPolarAngle = Math.PI * 0.15;\n\n controls.addEventListener('change', () => {\n const dist = camera.position.length();\n const zoomPercent = Math.round((1 - (dist - CONFIG.minZoom) / (CONFIG.maxZoom - CONFIG.minZoom)) * 100);\n zoomIndicator.textContent = Math.max(0, Math.min(100, zoomPercent)) + '%';\n baseCameraPos.copy(camera.position);\n });\n\n const raycaster = new THREE.Raycaster();\n const mouse = new THREE.Vector2(9999, 9999);\n const plane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0);\n const mouseWorldPos = new THREE.Vector3();\n let mouseVelocity = new THREE.Vector2();\n let prevMouse = new THREE.Vector2();\n\n window.addEventListener('mousemove', (e) => {\n const newMouse = new THREE.Vector2((e.clientX / window.innerWidth) * 2 - 1, -(e.clientY / window.innerHeight) * 2 + 1);\n mouseVelocity.subVectors(newMouse, prevMouse);\n prevMouse.copy(newMouse);\n mouse.copy(newMouse);\n raycaster.setFromCamera(mouse, camera);\n const target = new THREE.Vector3();\n raycaster.ray.intersectPlane(plane, target);\n if (target) mouseWorldPos.copy(target);\n });\n\n window.addEventListener('keydown', (e) => {\n if (e.key === '+' || e.key === '=') { camera.position.multiplyScalar(0.9); controls.update(); }\n else if (e.key === '-' || e.key === '_') { camera.position.multiplyScalar(1.1); controls.update(); }\n else if (e.key === '0') { camera.position.normalize().multiplyScalar(CONFIG.defaultZoom); controls.update(); }\n else if (e.key === ' ') { e.preventDefault(); togglePlayPause(); }\n else if (e.key === 'f' || e.key === 'F') { STATE.dofEnabled = !STATE.dofEnabled; customDOFPass.enabled = STATE.dofEnabled; }\n });\n\n // Every field type shares one screen-fitted footprint; switching presets never reallocates particles.\n // uFieldFill: share of the viewport the field spans; above 1.0 it runs past the edges.\n const shapeFunction = `\nuniform float uFieldFill, uParticleDepth, uParticleScale;\nfloat particleProximity(vec3 seed) {\n // Most points stay distant; only a small share enters the foreground.\n return pow(seed.z, 4.0);\n}\nfloat particleTime(float time, float proximity) {\n return time * mix(1.0, 0.25 + proximity * 1.35, uParticleDepth);\n}\nfloat particleProjection(float proximity) {\n return 1.0 - mix(-100.0, 125.0, proximity) * uParticleDepth / ${CONFIG.defaultZoom.toFixed(1)};\n}\nvec3 shapedPosition(vec3 seed, float proximity, float time) {\n vec2 plane = seed.xy * 2.0 - 1.0;\n plane += vec2(sin(time * 0.16 + seed.x * 6.28), cos(time * 0.12 + seed.y * 6.28))\n * mix(0.006, 0.045, proximity) * uParticleDepth;\n float projection = particleProjection(proximity);\n // Compensate the footprint for perspective, so nearby points also fill the viewport.\n return vec3(plane * vec2(uViewportAspect, 1.0) * uViewportHalfHeight * uFieldFill * projection,\n (seed.z - 0.5) * 12.0 + mix(-100.0, 125.0, proximity) * uParticleDepth);\n}\nfloat particleEdge(vec3 position, float proximity) {\n vec2 extent = vec2(uViewportAspect, 1.0) * uViewportHalfHeight * uFieldFill * 1.05 * particleProjection(proximity);\n return 1.0 - smoothstep(0.92, 1.05, max(abs(position.x) / extent.x, abs(position.y) / extent.y));\n}`;\n\n const particleLight = `\nuniform float uParticleSharpness;\nvec4 crispParticle(vec3 tint, float radius, float alpha) {\n float core = 1.0 - smoothstep(0.05, 0.2, radius);\n float halo = exp(-radius * radius * 45.0) * 0.22;\n float edge = 1.0 - smoothstep(0.42, 0.5, radius);\n return vec4(mix(tint, vec3(1.0), core * 0.65), clamp(alpha * (core + halo) * edge, 0.0, 1.0));\n}`;\n\n // --- PARTICLE VERTEX SHADER ---\n const vertexShader = `\nuniform float uTime, uPixelRatio, uSizeBase, uNoiseScale, uCurlStrength;\nuniform float uViewportAspect, uViewportHalfHeight;\nuniform vec3 uColor1, uColor2, uColor3;\nuniform float uSubBass, uBass, uLowMid, uMid, uHighMid, uHigh, uUltraHigh;\nuniform float uBeatEnergy, uSpectralCentroid, uSpectralFlux;\nuniform float uOnsetEnergy, uTerrainMode, uTerrainHeight;\nuniform float uVortexStrength, uPulseIntensity, uZoomFactor;\nuniform vec3 uMousePos;\nuniform vec2 uMouseVelocity;\nattribute vec3 aRandom;\nattribute float aPhase, aLayer;\nvarying vec3 vColor;\nvarying float vAlpha, vEnergy, vDepth, vRimLight, vGlint;\n\nvec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }\nvec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }\nvec4 permute(vec4 x) { return mod289(((x*34.0)+1.0)*x); }\nvec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }\n\nfloat snoise(vec3 v) {\n const vec2 C = vec2(1.0/6.0, 1.0/3.0);\n const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);\n vec3 i = floor(v + dot(v, C.yyy));\n vec3 x0 = v - i + dot(i, C.xxx);\n vec3 g = step(x0.yzx, x0.xyz);\n vec3 l = 1.0 - g;\n vec3 i1 = min(g.xyz, l.zxy);\n vec3 i2 = max(g.xyz, l.zxy);\n vec3 x1 = x0 - i1 + C.xxx;\n vec3 x2 = x0 - i2 + C.yyy;\n vec3 x3 = x0 - D.yyy;\n i = mod289(i);\n vec4 p = permute(permute(permute(i.z + vec4(0.0, i1.z, i2.z, 1.0)) + i.y + vec4(0.0, i1.y, i2.y, 1.0)) + i.x + vec4(0.0, i1.x, i2.x, 1.0));\n float n_ = 0.142857142857;\n vec3 ns = n_ * D.wyz - D.xzx;\n vec4 j = p - 49.0 * floor(p * ns.z * ns.z);\n vec4 x_ = floor(j * ns.z);\n vec4 y_ = floor(j - 7.0 * x_);\n vec4 x = x_ * ns.x + ns.yyyy;\n vec4 y = y_ * ns.x + ns.yyyy;\n vec4 h = 1.0 - abs(x) - abs(y);\n vec4 b0 = vec4(x.xy, y.xy);\n vec4 b1 = vec4(x.zw, y.zw);\n vec4 s0 = floor(b0) * 2.0 + 1.0;\n vec4 s1 = floor(b1) * 2.0 + 1.0;\n vec4 sh = -step(h, vec4(0.0));\n vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;\n vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww;\n vec3 p0 = vec3(a0.xy, h.x);\n vec3 p1 = vec3(a0.zw, h.y);\n vec3 p2 = vec3(a1.xy, h.z);\n vec3 p3 = vec3(a1.zw, h.w);\n vec4 norm = taylorInvSqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));\n p0 *= norm.x; p1 *= norm.y; p2 *= norm.z; p3 *= norm.w;\n vec4 m = max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0);\n m = m * m;\n return 42.0 * dot(m * m, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3)));\n}\n\nvec3 curl(float x, float y, float z) {\n float eps = 0.08;\n return vec3(\n snoise(vec3(x, y+eps, z)) - snoise(vec3(x, y-eps, z)),\n snoise(vec3(x, y, z+eps)) - snoise(vec3(x, y, z-eps)),\n snoise(vec3(x+eps, y, z)) - snoise(vec3(x-eps, y, z))\n );\n}\n\n${shapeFunction}\n\nvoid main() {\n float proximity = particleProximity(aRandom);\n float depthTime = particleTime(uTime, proximity);\n float t = depthTime * (1.0 + uMid * 0.5);\n vec3 noisePos = position * 0.035 + aRandom;\n float layerMod = 1.0 + aLayer * 0.5;\n\n vec3 flow1 = curl(noisePos.x * uNoiseScale + t*0.1, noisePos.y * uNoiseScale, noisePos.z * uNoiseScale);\n vec3 flow2 = curl(noisePos.x * uNoiseScale * 2.2 + t*0.07, noisePos.y * uNoiseScale * 2.2 + t*0.08, noisePos.z * uNoiseScale * 2.2) * 0.4;\n vec3 flow3 = curl(noisePos.x * uNoiseScale * 0.5 - t*0.05, noisePos.y * uNoiseScale * 0.5, noisePos.z * uNoiseScale * 0.5) * 0.6 * uLowMid;\n vec3 flow = flow1 + flow2 * uHigh + flow3;\n\n vec3 newPos = shapedPosition(aRandom, proximity, depthTime) + (flow * uCurlStrength * layerMod * (1.0 + uSpectralFlux * 2.5));\n\n float vortexAngle = uVortexStrength * uBass * 0.45 * (0.75 + aRandom.z * 0.5);\n float cosA = cos(vortexAngle), sinA = sin(vortexAngle);\n vec2 rotated = vec2(newPos.x * cosA - newPos.z * sinA, newPos.x * sinA + newPos.z * cosA);\n newPos.xz = mix(newPos.xz, rotated, uBass * uVortexStrength);\n\n float yVortex = sin(newPos.y * 0.1 + depthTime) * uMid * uVortexStrength * 2.0;\n newPos.x += cos(newPos.y * 0.15) * yVortex;\n newPos.z += sin(newPos.y * 0.15) * yVortex;\n\n float distToMouse = distance(newPos, uMousePos);\n float repulsion = smoothstep(25.0, 0.0, distToMouse);\n vec3 repelDir = normalize(newPos - uMousePos + vec3(0.001));\n newPos += repelDir * repulsion * (12.0 + length(uMouseVelocity) * 360.0);\n\n // Audio changes depth throughout the plane, without evacuating its centre.\n float beatWave = sin(aRandom.x * 12.0 + aRandom.y * 7.0 - uTime * 6.0);\n float bassDepth = (beatWave * uBeatEnergy * uPulseIntensity * 3.5\n + sin(uTime * 1.8 + aPhase) * uSubBass * 4.0\n + smoothstep(0.3, 0.8, uBass) * 6.0\n + uOnsetEnergy * 8.0) * (0.6 + aRandom.z * 0.4);\n newPos.z += bassDepth;\n float fieldWave = sin(aRandom.x * 11.0 - depthTime * 0.8 + sin(aRandom.y * 7.0 + depthTime * 0.35));\n newPos.z += fieldWave * (1.8 + uBass * 2.4 + uBeatEnergy * 1.6);\n\n // Waveform relief moves toward the camera while retaining the shared rectangular footprint.\n if (uTerrainMode > 0.5) {\n float freqBin = aRandom.x;\n float waveHeight = uBass * sin(freqBin * 6.28 * 8.0 + uTime * 3.0) * 0.5;\n waveHeight += uMid * sin(freqBin * 6.28 * 16.0 + uTime * 5.0) * 0.3;\n waveHeight += uHigh * sin(freqBin * 6.28 * 32.0 + uTime * 8.0) * 0.2;\n waveHeight *= (1.0 + uSpectralFlux * 3.0);\n newPos.z += waveHeight * uTerrainHeight;\n newPos.z += sin(aRandom.y * 6.28 * 4.0 + uTime) * uSubBass * 5.0;\n }\n\n vec4 mvPosition = modelViewMatrix * vec4(newPos, 1.0);\n gl_Position = projectionMatrix * mvPosition;\n\n float size = uSizeBase * (0.85 + uBass * 0.5 + uBeatEnergy * 0.5) * (1.0 + (uZoomFactor - 1.0) * 0.3);\n size *= 1.0 + sin(uTime * 10.0 + aPhase * 6.28) * uHigh * 0.35;\n vGlint = smoothstep(mix(0.985, 0.993, uParticleDepth), mix(0.998, 0.999, uParticleDepth), aRandom.z) * (0.7 + 0.3 * sin(uTime * 3.0 + aPhase * 6.28)) * (0.75 + uHigh * 0.15 + uOnsetEnergy * 0.1);\n size *= 0.85 + vGlint * mix(2.1, 0.7, uParticleDepth);\n size = max(size * uPixelRatio * (90.0 / -mvPosition.z) * layerMod, vGlint * 4.5 * uPixelRatio);\n gl_PointSize = clamp(size * uParticleScale * mix(1.0, 0.7 + proximity * 1.8, uParticleDepth), 0.5, 120.0);\n\n float colorMix = smoothstep(0.0, 2.8, length(flow) + uHighMid * 3.0);\n vec3 baseColor = mix(uColor1, uColor2, colorMix);\n baseColor = mix(baseColor, uColor3, uSpectralCentroid * uHigh * 2.5);\n\n float energyLevel = uBeatEnergy * 0.35 + repulsion * uMid * 2.0 + uSpectralFlux * 0.8;\n vColor = mix(baseColor, vec3(1.0), energyLevel * 0.4);\n vColor += vec3(0.08, 0.04, 0.12) * uUltraHigh * 2.5;\n\n vRimLight = pow(1.0 - max(0.0, dot(normalize(-mvPosition.xyz), normalize(newPos))), 3.0);\n vEnergy = energyLevel;\n vDepth = mix(clamp(-mvPosition.z / 120.0, 0.0, 1.0), 1.0 - proximity, uParticleDepth);\n\n float alpha = particleEdge(newPos, proximity);\n float sparkle = 1.0 + (aRandom.x > 0.8 ? sin(uTime * 20.0 + aRandom.y * 150.0) * uHigh * 2.5 : 0.0);\n vAlpha = alpha * sparkle * (0.7 + uMid * 0.35) * layerMod * mix(1.0, 0.5 + proximity * 0.5, uParticleDepth);\n}`;\n\n const fragmentShader = `\nvarying vec3 vColor;\nvarying float vAlpha, vEnergy, vDepth, vRimLight, vGlint;\n\n${particleLight}\n\nvoid main() {\n vec2 xy = gl_PointCoord.xy - vec2(0.5);\n float r = length(xy);\n if(r > 0.5) discard;\n\n float core = smoothstep(0.16 - vEnergy * 0.04, 0.12 - vEnergy * 0.04, r);\n float glow = exp(-r * r * (20.0 - vEnergy * 10.0));\n float halo = exp(-r * r * 5.0) * 0.35 + exp(-r * r * 2.5) * 0.15;\n float glint = vGlint * (1.0 - smoothstep(0.012, 0.045, min(abs(xy.x), abs(xy.y)))) * (1.0 - smoothstep(0.16, 0.48, r));\n\n vec3 finalColor = mix(vColor, vec3(1.0), core * 0.58 + glint * 0.38);\n finalColor += vColor * vRimLight * 0.1;\n finalColor = mix(finalColor, vec3(0.02, 0.03, 0.05), vDepth * 0.4);\n\n float finalAlpha = clamp(vAlpha * (glow * 0.85 + halo * 0.55 + pow(1.0 - r * 2.0, 2.5) * 0.55 + glint * 0.9), 0.0, 1.0);\n gl_FragColor = mix(vec4(finalColor, finalAlpha), crispParticle(vColor, r, vAlpha), uParticleSharpness);\n}`;\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550 POST-PROCESSING SHADERS \u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n // GOD RAYS SHADER (Radial Light Scattering)\n const GodRaysShader = {\n uniforms: {\n tDiffuse: { value: null },\n uCenter: { value: new THREE.Vector2(0.5, 0.5) },\n uIntensity: { value: 0.35 },\n uDecay: { value: 0.94 },\n uBeatEnergy: { value: 0.0 }\n },\n vertexShader: `varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`,\n fragmentShader: `\n uniform sampler2D tDiffuse;\n uniform vec2 uCenter;\n uniform float uIntensity, uDecay, uBeatEnergy;\n varying vec2 vUv;\n\n void main() {\n vec4 color = texture2D(tDiffuse, vUv);\n vec2 dir = vUv - uCenter;\n float dist = length(dir);\n dir = normalize(dir);\n\n vec3 rays = vec3(0.0);\n float decay = 1.0;\n\n for(int i = 0; i < 32; i++) {\n float t = float(i) / 32.0;\n vec2 samplePos = vUv - dir * t * 0.15 * (1.0 + uBeatEnergy);\n vec4 s = texture2D(tDiffuse, samplePos);\n float lum = dot(s.rgb, vec3(0.3, 0.59, 0.11));\n rays += s.rgb * decay * step(0.35, lum);\n decay *= uDecay;\n }\n rays /= 16.0;\n rays *= (1.0 - smoothstep(0.3, 0.9, dist));\n\n gl_FragColor = vec4(color.rgb + rays * uIntensity * (1.0 + uBeatEnergy * 0.8), color.a);\n }`\n };\n\n // LENS FLARE POST-PROCESS SHADER (Cinematic)\n const LensFlareShader = {\n uniforms: {\n tDiffuse: { value: null },\n uIntensity: { value: 1.0 },\n uBeatEnergy: { value: 0.0 },\n uTime: { value: 0.0 },\n uTint: { value: new THREE.Color(0.6, 0.7, 1.0) }\n },\n vertexShader: `varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`,\n fragmentShader: `\n uniform sampler2D tDiffuse;\n uniform float uIntensity, uBeatEnergy, uTime;\n uniform vec3 uTint;\n varying vec2 vUv;\n\n float sdHex(vec2 p) {\n p = abs(p);\n return max(p.x * 0.866 + p.y * 0.5, p.y) - 1.0;\n }\n\n void main() {\n vec4 color = texture2D(tDiffuse, vUv);\n vec2 center = vec2(0.5);\n vec2 toCenter = center - vUv;\n float dist = length(toCenter);\n vec2 dir = normalize(toCenter + 0.0001);\n\n vec3 flare = vec3(0.0);\n\n // Sample bright areas for flare source\n float brightness = dot(color.rgb, vec3(0.299, 0.587, 0.114));\n float threshold = smoothstep(0.6, 0.9, brightness);\n\n // Anamorphic horizontal streak\n float streak = 0.0;\n for(float i = -8.0; i <= 8.0; i += 1.0) {\n vec2 streakUV = vUv + vec2(i * 0.012, 0.0);\n vec4 streakSample = texture2D(tDiffuse, streakUV);\n float streakBright = dot(streakSample.rgb, vec3(0.299, 0.587, 0.114));\n float falloff = 1.0 - abs(i) / 10.0;\n streak += smoothstep(0.7, 1.0, streakBright) * falloff * falloff;\n }\n flare += uTint * streak * 0.08;\n\n // Subtle ghosts (reflections through lens elements)\n for(int i = 1; i <= 3; i++) {\n float scale = 1.0 - float(i) * 0.25;\n vec2 ghostUV = center + (center - vUv) * scale * 0.8;\n if(ghostUV.x > 0.0 && ghostUV.x < 1.0 && ghostUV.y > 0.0 && ghostUV.y < 1.0) {\n vec4 ghostSample = texture2D(tDiffuse, ghostUV);\n float ghostBright = dot(ghostSample.rgb, vec3(0.299, 0.587, 0.114));\n float ghostAlpha = smoothstep(0.5, 0.8, ghostBright) * (0.25 / float(i));\n vec3 ghostTint = mix(vec3(1.0), uTint, 0.6) * (1.0 - float(i) * 0.2);\n flare += ghostSample.rgb * ghostTint * ghostAlpha;\n }\n }\n\n // Soft circular halo around bright center\n float halo = smoothstep(0.25, 0.15, abs(dist - 0.2)) * threshold * 0.25;\n flare += uTint * 0.85 * halo;\n\n // Subtle vignette-edge glow\n float edgeGlow = smoothstep(0.5, 0.7, dist) * (1.0 - smoothstep(0.7, 0.85, dist));\n flare += uTint * 0.55 * edgeGlow * brightness * 0.15;\n\n gl_FragColor = vec4(color.rgb + flare * uIntensity * (1.0 + uBeatEnergy * 0.3), color.a);\n }`\n };\n\n // ANAMORPHIC FLARE SHADER\n const AnamorphicShader = {\n uniforms: {\n tDiffuse: { value: null },\n uStretch: { value: 0.1 },\n uThreshold: { value: 0.7 },\n uIntensity: { value: 0.4 },\n uTint: { value: new THREE.Color(0.6, 0.8, 1.0) }\n },\n vertexShader: `varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`,\n fragmentShader: `\n uniform sampler2D tDiffuse;\n uniform float uStretch, uThreshold, uIntensity;\n uniform vec3 uTint;\n varying vec2 vUv;\n void main() {\n vec4 color = texture2D(tDiffuse, vUv);\n vec3 streak = vec3(0.0);\n for(float i = -15.0; i <= 15.0; i++) {\n vec4 s = texture2D(tDiffuse, vUv + vec2(i * uStretch * 0.01, 0.0));\n float bright = dot(s.rgb, vec3(0.299, 0.587, 0.114));\n if(bright > uThreshold) {\n float w = 1.0 - abs(i) / 15.0;\n streak += s.rgb * w * w;\n }\n }\n gl_FragColor = vec4(color.rgb + streak / 7.5 * uTint * uIntensity, color.a);\n }`\n };\n\n // CHROMATIC ABERRATION SHADER\n const ChromaticShader = {\n uniforms: {\n tDiffuse: { value: null },\n uIntensity: { value: 0.004 },\n uBeatEnergy: { value: 0.0 }\n },\n vertexShader: `varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`,\n fragmentShader: `\n uniform sampler2D tDiffuse;\n uniform float uIntensity, uBeatEnergy;\n varying vec2 vUv;\n void main() {\n vec2 dir = vUv - vec2(0.5);\n float dist = length(dir);\n vec2 offset = dir * dist * uIntensity * (1.0 + uBeatEnergy * 1.5);\n gl_FragColor = vec4(\n texture2D(tDiffuse, vUv + offset * 1.2).r,\n texture2D(tDiffuse, vUv).g,\n texture2D(tDiffuse, vUv - offset * 1.2).b,\n texture2D(tDiffuse, vUv).a\n );\n }`\n };\n\n // FILM GRAIN SHADER\n const GrainShader = {\n uniforms: {\n tDiffuse: { value: null },\n uTime: { value: 0.0 },\n uIntensity: { value: 0.0 }\n },\n vertexShader: `varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`,\n fragmentShader: `\n uniform sampler2D tDiffuse;\n uniform float uTime, uIntensity;\n varying vec2 vUv;\n float rand(vec2 co) { return fract(sin(dot(co, vec2(12.9898,78.233))) * 43758.5453); }\n void main() {\n vec4 color = texture2D(tDiffuse, vUv);\n float grain = (rand(vUv + fract(uTime)) * 2.0 - 1.0) * uIntensity;\n gl_FragColor = vec4(color.rgb + grain * (1.0 - dot(color.rgb, vec3(0.299, 0.587, 0.114)) * 0.5), color.a);\n }`\n };\n\n // CUSTOM DEPTH OF FIELD SHADER (Radial + Edge blur)\n const CustomDOFShader = {\n uniforms: {\n tDiffuse: { value: null },\n uFocus: { value: 0.05 },\n uFocalLength: { value: 0.25 },\n uBokehStrength: { value: 0.5 },\n uResolution: { value: new THREE.Vector2() },\n uBeatEnergy: { value: 0.0 }\n },\n vertexShader: `\n varying vec2 vUv;\n void main() {\n vUv = uv;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n }\n `,\n fragmentShader: `\n uniform sampler2D tDiffuse;\n uniform float uFocus;\n uniform float uFocalLength;\n uniform float uBokehStrength;\n uniform vec2 uResolution;\n uniform float uBeatEnergy;\n varying vec2 vUv;\n\n void main() {\n vec2 center = vec2(0.5);\n float dist = length(vUv - center);\n\n // Focus ring - sharp in a ring around center, blurry inside and outside\n float focusRing = abs(dist - uFocus);\n float blur = smoothstep(0.0, uFocalLength, focusRing) * uBokehStrength;\n\n vec3 color = vec3(0.0);\n float total = 0.0;\n vec2 texel = 1.0 / uResolution;\n\n if(blur < 0.01) {\n color = texture2D(tDiffuse, vUv).rgb;\n total = 1.0;\n } else {\n // Golden angle spiral sampling\n const float GOLDEN_ANGLE = 2.39996323;\n const int SAMPLES = 24;\n float radius = blur * 20.0 * (1.0 + uBeatEnergy * 0.1);\n\n for(int i = 0; i < SAMPLES; i++) {\n float angle = float(i) * GOLDEN_ANGLE;\n float r = sqrt(float(i) / float(SAMPLES)) * radius;\n vec2 offset = vec2(cos(angle), sin(angle)) * r * texel;\n vec3 s = texture2D(tDiffuse, vUv + offset).rgb;\n\n // Subtle highlight boost\n float lum = dot(s, vec3(0.299, 0.587, 0.114));\n s *= 1.0 + smoothstep(0.7, 1.0, lum) * blur * 0.3;\n\n color += s;\n total += 1.0;\n }\n }\n\n gl_FragColor = vec4(color / total, texture2D(tDiffuse, vUv).a);\n }\n `\n };\n\n // VIGNETTE + GLOW SHADER\n const VignetteShader = {\n uniforms: {\n tDiffuse: { value: null },\n uBeatEnergy: { value: 0.0 }\n },\n vertexShader: `varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`,\n fragmentShader: `\n uniform sampler2D tDiffuse;\n uniform float uBeatEnergy;\n varying vec2 vUv;\n void main() {\n vec4 color = texture2D(tDiffuse, vUv);\n vec2 uv = vUv * (1.0 - vUv.yx);\n float vig = pow(uv.x * uv.y * 15.0, 0.38 * (1.0 - uBeatEnergy * 0.15));\n float lum = dot(color.rgb, vec3(0.299, 0.587, 0.114));\n float glow = smoothstep(0.5, 0.9, lum) * 0.1 * (1.0 + uBeatEnergy * 0.5);\n gl_FragColor = vec4(color.rgb * vig * (1.0 + glow), color.a);\n }`\n };\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550 GEOMETRY \u2550\u2550\u2550\u2550\u2550\u2550\u2550\n const geometry = new THREE.BufferGeometry();\n const mainCapacity = Math.ceil(CONFIG.maxParticles * CONFIG.maxDensity);\n const pos = new Float32Array(mainCapacity * 3);\n const rnd = new Float32Array(mainCapacity * 3);\n const phase = new Float32Array(mainCapacity);\n const layer = new Float32Array(mainCapacity);\n const vec = new THREE.Vector3();\n\n for (let i = 0; i < mainCapacity; i++) {\n vec.setFromSphericalCoords(\n CONFIG.fieldRadius * Math.pow(Math.random(), 0.33),\n Math.acos(2 * Math.random() - 1),\n Math.random() * Math.PI * 2\n );\n pos[i * 3] = vec.x;\n pos[i * 3 + 1] = vec.y;\n pos[i * 3 + 2] = vec.z;\n rnd[i * 3] = Math.random();\n rnd[i * 3 + 1] = Math.random();\n rnd[i * 3 + 2] = Math.random();\n phase[i] = Math.random() * Math.PI * 2;\n }\n\n // Secondary particles now use separate instanced system below\n\n geometry.setAttribute('position', new THREE.BufferAttribute(pos, 3));\n geometry.setAttribute('aRandom', new THREE.BufferAttribute(rnd, 3));\n geometry.setAttribute('aPhase', new THREE.BufferAttribute(phase, 1));\n geometry.setAttribute('aLayer', new THREE.BufferAttribute(layer, 1));\n geometry.setDrawRange(0, Math.floor(CONFIG.maxParticles * STATE.density));\n\n const FIELD_TYPES = {\n 'Photon (EM)': { c1: '#00f2ff', c2: '#0051ff', c3: '#ffffff', s: 1.0, curl: 1.0, sz: 3.0, sc1: '#66d9ff', sc2: '#99eeff' },\n 'Gluon (Strong)': { c1: '#ff0055', c2: '#7000ff', c3: '#ff8800', s: 2.5, curl: 2.4, sz: 3.4, sc1: '#cc66ff', sc2: '#ff6699' },\n 'Higgs (Mass)': { c1: '#ffae00', c2: '#ff4800', c3: '#ffff00', s: 0.6, curl: 0.3, sz: 4.0, sc1: '#ffcc66', sc2: '#ffdd99' },\n 'Gravity': { c1: '#ffffff', c2: '#4466ff', c3: '#8844ff', s: 0.75, curl: 0.18, sz: 2.8, sc1: '#aabbff', sc2: '#ccaaff' },\n 'Dark Energy': { c1: '#1a0033', c2: '#6600ff', c3: '#ff00ff', s: 1.9, curl: 1.6, sz: 3.1, sc1: '#9933ff', sc2: '#cc66ff' },\n 'Neutrino': { c1: '#00ff88', c2: '#00ffcc', c3: '#88ffff', s: 3.0, curl: 3.0, sz: 2.6, sc1: '#66ffbb', sc2: '#99ffdd' },\n // === ENHANCEMENT: Waveform Terrain Field ===\n 'Waveform Terrain': { c1: '#ff3366', c2: '#33ff99', c3: '#6633ff', s: 0.4, curl: 0.15, sz: 2.4, sc1: '#ff6699', sc2: '#66ffcc', terrain: true }\n };\n\n // === ENHANCEMENT: Adaptive Quality System ===\n const QUALITY_SYSTEM = {\n frameCount: 0,\n frameTimes: [],\n lastAdjustTime: 0,\n currentFPS: 60,\n\n update(deltaTime) {\n this.frameCount++;\n this.frameTimes.push(deltaTime);\n if (this.frameTimes.length > 30) this.frameTimes.shift();\n\n if (this.frameCount % 30 === 0) {\n const avgDelta = this.frameTimes.reduce((a, b) => a + b, 0) / this.frameTimes.length;\n this.currentFPS = 1000 / avgDelta;\n\n if (STATE.adaptiveQualityEnabled && performance.now() - this.lastAdjustTime > 1000) {\n if (this.currentFPS < STATE.targetFPS - 10 && STATE.qualityLevel > 0.3) {\n STATE.qualityLevel = Math.max(0.3, STATE.qualityLevel - (this.currentFPS < STATE.targetFPS * 0.6 ? 0.2 : 0.1));\n this.applyQuality();\n this.lastAdjustTime = performance.now();\n } else if (this.currentFPS > STATE.targetFPS + 5 && STATE.qualityLevel < 1.0) {\n STATE.qualityLevel = Math.min(1.0, STATE.qualityLevel + 0.05);\n this.applyQuality();\n this.lastAdjustTime = performance.now();\n }\n }\n }\n },\n\n applyQuality() {\n const q = STATE.qualityLevel;\n const pixelRatio = Math.min(STATE.pixelRatio, Math.max(0.35, STATE.pixelRatio * q));\n const requestedMain = Math.floor(CONFIG.maxParticles * Math.min(STATE.density, CONFIG.maxDensity));\n const densityTotal = requestedMain + CONFIG.secondaryParticles;\n const requestedTotal = particleCount === null ? densityTotal : Math.min(particleCount, mainCapacity + CONFIG.secondaryParticles);\n const visibleParticles = Math.floor(Math.min(requestedTotal, particleBudget) * q);\n // Both layers keep the density's mix; whatever one layer cannot hold goes to the other.\n let mainCount = Math.min(mainCapacity, Math.round(visibleParticles * requestedMain / densityTotal));\n mainCount = Math.min(mainCapacity, Math.max(mainCount, visibleParticles - CONFIG.secondaryParticles));\n geometry.setDrawRange(0, mainCount);\n secondaryGeometry.instanceCount = visibleParticles - mainCount;\n if (renderer.getPixelRatio() !== pixelRatio) {\n renderer.setPixelRatio(pixelRatio);\n composer.setPixelRatio(pixelRatio);\n }\n material.uniforms.uPixelRatio.value = pixelRatio;\n secondaryMaterial.uniforms.uPixelRatio.value = pixelRatio;\n }\n };\n\n // === ENHANCEMENT: Audio-Reactive Camera System ===\n const AUDIO_CAMERA = {\n targetOffset: new THREE.Vector3(),\n currentOffset: new THREE.Vector3(),\n targetFOV: 55,\n baseFOV: 55,\n breathPhase: 0,\n\n update(deltaTime) {\n if (!STATE.audioCameraEnabled || !(AUDIO.active || AUDIO.externalActive)) return;\n\n const bass = AUDIO.gatedBands.bass;\n const mid = AUDIO.gatedBands.mid;\n const high = AUDIO.gatedBands.high;\n const intensity = STATE.audioCameraIntensity;\n const smooth = STATE.audioCameraSmoothing;\n\n // Bass-driven zoom pulse\n this.targetFOV = this.baseFOV - bass * 8 * intensity + AUDIO.beatEnergy * 5 * intensity;\n camera.fov += (this.targetFOV - camera.fov) * smooth;\n camera.updateProjectionMatrix();\n\n // Subtle sway based on mid frequencies\n this.breathPhase += deltaTime * 0.001 * (1 + mid * 2);\n this.targetOffset.x = Math.sin(this.breathPhase * 0.7) * mid * 3 * intensity;\n this.targetOffset.y = Math.cos(this.breathPhase * 0.5) * high * 2 * intensity;\n this.targetOffset.z = Math.sin(this.breathPhase * 0.3) * bass * 4 * intensity;\n\n // Smooth interpolation\n this.currentOffset.lerp(this.targetOffset, smooth);\n\n // Apply onset punch\n if (AUDIO.onsetDetected) {\n this.currentOffset.z -= AUDIO.onsetEnergy * 2 * intensity;\n }\n }\n };\n\n const def = FIELD_TYPES[STATE.field];\n // Both particle layers share the same controls and light profile.\n const particleUniforms = {\n uParticleDepth: { value: STATE.particleDepth },\n uParticleScale: { value: STATE.particleScale },\n uParticleSharpness: { value: STATE.particleSharpness }\n };\n const material = new THREE.ShaderMaterial({\n vertexShader, fragmentShader,\n uniforms: {\n ...particleUniforms,\n uTime: { value: 0 },\n uPixelRatio: { value: STATE.pixelRatio },\n uSizeBase: { value: def.sz },\n uNoiseScale: { value: def.s },\n uCurlStrength: { value: def.curl },\n uViewportAspect: { value: window.innerWidth / window.innerHeight },\n uViewportHalfHeight: { value: Math.tan(camera.fov * Math.PI / 360) * CONFIG.defaultZoom },\n uFieldFill: { value: STATE.fieldSize },\n uColor1: { value: new THREE.Color(def.c1) },\n uColor2: { value: new THREE.Color(def.c2) },\n uColor3: { value: new THREE.Color(def.c3) },\n uSubBass: { value: 0 }, uBass: { value: 0 }, uLowMid: { value: 0 },\n uMid: { value: 0 }, uHighMid: { value: 0 }, uHigh: { value: 0 }, uUltraHigh: { value: 0 },\n uBeatEnergy: { value: 0 }, uSpectralCentroid: { value: 0 }, uSpectralFlux: { value: 0 },\n uOnsetEnergy: { value: 0 }, uTerrainMode: { value: 0 }, uTerrainHeight: { value: 15.0 },\n uVortexStrength: { value: STATE.vortexStrength },\n uPulseIntensity: { value: STATE.pulseIntensity },\n uZoomFactor: { value: 1.0 },\n uMousePos: { value: new THREE.Vector3(999, 999, 999) },\n uMouseVelocity: { value: new THREE.Vector2(0, 0) }\n },\n transparent: true, depthWrite: false, blending: THREE.AdditiveBlending\n });\n\n const points = new THREE.Points(geometry, material);\n scene.add(points);\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550 INSTANCED SECONDARY PARTICLES \u2550\u2550\u2550\u2550\u2550\u2550\u2550\n const secondaryVertexShader = `\nuniform float uTime, uPixelRatio;\nuniform float uViewportAspect, uViewportHalfHeight;\nuniform float uSubBass, uBass, uLowMid, uMid, uHighMid, uHigh, uUltraHigh;\nuniform float uBeatEnergy, uSpectralCentroid, uSpectralFlux, uOnsetEnergy;\nuniform vec3 uColor1, uColor2;\nuniform float uVortexStrength;\n\nattribute vec3 instancePosition;\nattribute vec3 instanceRandom;\nattribute float instancePhase;\n\nvarying vec3 vColor;\nvarying float vAlpha;\nvarying float vEnergy;\nvarying float vDepth;\n\nvec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }\nvec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }\nvec4 permute(vec4 x) { return mod289(((x*34.0)+1.0)*x); }\nvec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }\n\nfloat snoise(vec3 v) {\n const vec2 C = vec2(1.0/6.0, 1.0/3.0);\n const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);\n vec3 i = floor(v + dot(v, C.yyy));\n vec3 x0 = v - i + dot(i, C.xxx);\n vec3 g = step(x0.yzx, x0.xyz);\n vec3 l = 1.0 - g;\n vec3 i1 = min(g.xyz, l.zxy);\n vec3 i2 = max(g.xyz, l.zxy);\n vec3 x1 = x0 - i1 + C.xxx;\n vec3 x2 = x0 - i2 + C.yyy;\n vec3 x3 = x0 - D.yyy;\n i = mod289(i);\n vec4 p = permute(permute(permute(i.z + vec4(0.0, i1.z, i2.z, 1.0)) + i.y + vec4(0.0, i1.y, i2.y, 1.0)) + i.x + vec4(0.0, i1.x, i2.x, 1.0));\n float n_ = 0.142857142857;\n vec3 ns = n_ * D.wyz - D.xzx;\n vec4 j = p - 49.0 * floor(p * ns.z * ns.z);\n vec4 x_ = floor(j * ns.z);\n vec4 y_ = floor(j - 7.0 * x_);\n vec4 x = x_ * ns.x + ns.yyyy;\n vec4 y = y_ * ns.x + ns.yyyy;\n vec4 h = 1.0 - abs(x) - abs(y);\n vec4 b0 = vec4(x.xy, y.xy);\n vec4 b1 = vec4(x.zw, y.zw);\n vec4 s0 = floor(b0) * 2.0 + 1.0;\n vec4 s1 = floor(b1) * 2.0 + 1.0;\n vec4 sh = -step(h, vec4(0.0));\n vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;\n vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww;\n vec3 p0 = vec3(a0.xy, h.x);\n vec3 p1 = vec3(a0.zw, h.y);\n vec3 p2 = vec3(a1.xy, h.z);\n vec3 p3 = vec3(a1.zw, h.w);\n vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2,p2), dot(p3,p3)));\n p0 *= norm.x; p1 *= norm.y; p2 *= norm.z; p3 *= norm.w;\n vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);\n m = m * m;\n return 42.0 * dot(m*m, vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3)));\n}\n\n${shapeFunction}\n\nvoid main() {\n // Secondary particles run at a slower timescale \u2014 they are the atmosphere, not the core\n float proximity = particleProximity(instanceRandom) * 0.22;\n float depthTime = particleTime(uTime, proximity);\n float t = depthTime * 0.45;\n\n // Multi-scale noise flow \u2014 low freqs drive large drift, high freqs add fine turbulence\n vec3 noisePos = instancePosition * 0.035 + instanceRandom;\n vec3 drift = vec3(\n snoise(noisePos + t * 0.06),\n snoise(noisePos * 0.9 - t * 0.05),\n snoise(noisePos * 1.1 + t * 0.04)\n ) * (5.0 + uSubBass * 10.0 + uLowMid * 6.0);\n\n vec3 turbulence = vec3(\n snoise(noisePos * 2.5 + t * 0.18),\n snoise(noisePos * 2.5 - t * 0.14),\n snoise(noisePos * 2.5 + t * 0.12)\n ) * (uHigh * 5.0 + uSpectralFlux * 8.0);\n\n vec3 offset = drift + turbulence;\n\n // Orbital drift \u2014 vortex-aware, modulated by bass\n float orbitSpeed = 0.12 + instanceRandom.x * 0.15 + uBass * 0.08;\n float angle = depthTime * orbitSpeed + instancePhase;\n float orbitRadius = length(instancePosition.xz) * (1.0 + uSubBass * 0.25);\n // Vortex coupling \u2014 secondary shell rotates with the main field vortex\n float vortexInfluence = uVortexStrength * uBass * 0.4;\n float vAngle = angle + vortexInfluence;\n vec3 orbit = vec3(\n cos(vAngle) * orbitRadius - instancePosition.x,\n sin(uTime * 0.5 + instancePhase) * (uMid * 4.0 + uLowMid * 2.0),\n sin(vAngle) * orbitRadius - instancePosition.z\n ) * 0.07;\n\n vec3 newPos = shapedPosition(instanceRandom, proximity, depthTime) + offset + orbit;\n\n // Transients and sub-bass move the atmosphere in depth, not away from the centre.\n newPos.z += uOnsetEnergy * 14.0 * (0.6 + instanceRandom.z * 0.4);\n newPos.z += sin(uTime * 1.2 + instancePhase) * uSubBass * 5.0;\n\n vec4 mvPosition = modelViewMatrix * vec4(newPos, 1.0);\n gl_Position = projectionMatrix * mvPosition;\n\n // Size: driven by different bands than the main system\n // Sparkle on ultraHigh, swell on subBass, punch on beatEnergy\n float sparkle = 1.0 + (instanceRandom.x > 0.85 ? sin(uTime * 15.0 + instanceRandom.y * 200.0) * uUltraHigh * 3.0 : 0.0);\n float swell = 1.0 + uSubBass * 0.6 + uLowMid * 0.3;\n float size = (1.1 + uBeatEnergy * 0.7) * swell * sparkle;\n gl_PointSize = clamp(size * uPixelRatio * (55.0 / -mvPosition.z) * uParticleScale, 0.5, 55.0);\n\n // Colour: spectral centroid shifts hue, highMid brightens toward white\n float centroidMix = clamp(uSpectralCentroid * 2.2 + uHighMid * 0.4, 0.0, 1.0);\n vColor = mix(uColor1, uColor2, centroidMix);\n // SubBass warmth \u2014 hint of deep colour saturation on heavy lows\n vColor = mix(vColor, vColor * 1.3, uSubBass * 0.4);\n // Beat flash \u2014 brief white-hot core\n vColor = mix(vColor, vec3(1.0), uBeatEnergy * 0.25);\n\n // Alpha: outer shell fades at edge, mid-frequency presence sustains visibility\n float edgeFade = particleEdge(newPos, proximity);\n vAlpha = edgeFade * (0.45 + uMid * 0.35 + uLowMid * 0.2) * sparkle * mix(1.0, 0.6, uParticleDepth);\n\n vEnergy = uBeatEnergy * 0.4 + uHighMid * 0.3 + uOnsetEnergy * 0.3;\n vDepth = clamp(-mvPosition.z / 160.0, 0.0, 1.0);\n}\n`;\n\n const secondaryFragmentShader = `\nvarying vec3 vColor;\nvarying float vAlpha;\nvarying float vEnergy;\nvarying float vDepth;\n\n${particleLight}\n\nvoid main() {\n vec2 xy = gl_PointCoord.xy - vec2(0.5);\n float r = length(xy);\n if (r > 0.5) discard;\n\n // Wider, softer glow than main particles \u2014 atmospheric haze\n float glow = exp(-r * r * 8.0);\n float softGlow = exp(-r * r * 3.0) * 0.4;\n float core = smoothstep(0.18 - vEnergy * 0.06, 0.10, r);\n\n vec3 finalColor = mix(vColor, vec3(1.0, 0.97, 0.92), core * 0.55);\n // Depth haze \u2014 far particles shift slightly cooler/dimmer\n finalColor = mix(finalColor, vec3(0.02, 0.03, 0.06), vDepth * 0.35);\n\n float finalAlpha = clamp(vAlpha * (glow * 0.65 + softGlow + core * 0.35), 0.0, 1.0);\n gl_FragColor = mix(vec4(finalColor, finalAlpha), crispParticle(vColor, r, vAlpha), uParticleSharpness);\n}\n`;\n\n // Create instanced geometry for secondary particles\n const secondaryGeometry = new THREE.InstancedBufferGeometry();\n\n // Base geometry (single point)\n const basePositions = new Float32Array([0, 0, 0]);\n secondaryGeometry.setAttribute('position', new THREE.BufferAttribute(basePositions, 3));\n\n // Instance attributes\n const instancePositions = new Float32Array(CONFIG.secondaryParticles * 3);\n const instanceRandoms = new Float32Array(CONFIG.secondaryParticles * 3);\n const instancePhases = new Float32Array(CONFIG.secondaryParticles);\n\n for (let i = 0; i < CONFIG.secondaryParticles; i++) {\n // Distribute in outer shell\n const r = CONFIG.fieldRadius * (0.65 + Math.random() * 0.45);\n const theta = Math.acos(2 * Math.random() - 1);\n const phi = Math.random() * Math.PI * 2;\n\n instancePositions[i * 3] = r * Math.sin(theta) * Math.cos(phi);\n instancePositions[i * 3 + 1] = r * Math.sin(theta) * Math.sin(phi);\n instancePositions[i * 3 + 2] = r * Math.cos(theta);\n\n instanceRandoms[i * 3] = Math.random();\n instanceRandoms[i * 3 + 1] = Math.random();\n instanceRandoms[i * 3 + 2] = Math.random();\n\n instancePhases[i] = Math.random() * Math.PI * 2;\n }\n\n secondaryGeometry.setAttribute('instancePosition', new THREE.InstancedBufferAttribute(instancePositions, 3));\n secondaryGeometry.setAttribute('instanceRandom', new THREE.InstancedBufferAttribute(instanceRandoms, 3));\n secondaryGeometry.setAttribute('instancePhase', new THREE.InstancedBufferAttribute(instancePhases, 1));\n\n const secondaryMaterial = new THREE.ShaderMaterial({\n vertexShader: secondaryVertexShader,\n fragmentShader: secondaryFragmentShader,\n uniforms: {\n ...particleUniforms,\n uTime: { value: 0 },\n uPixelRatio: { value: STATE.pixelRatio },\n uSubBass: { value: 0 },\n uBass: { value: 0 },\n uLowMid: { value: 0 },\n uMid: { value: 0 },\n uHighMid: { value: 0 },\n uHigh: { value: 0 },\n uUltraHigh: { value: 0 },\n uBeatEnergy: { value: 0 },\n uSpectralCentroid: { value: 0 },\n uSpectralFlux: { value: 0 },\n uOnsetEnergy: { value: 0 },\n uVortexStrength: { value: STATE.vortexStrength },\n uColor1: { value: new THREE.Color(def.sc1) },\n uColor2: { value: new THREE.Color(def.sc2) },\n uViewportAspect: { value: window.innerWidth / window.innerHeight },\n uViewportHalfHeight: { value: Math.tan(camera.fov * Math.PI / 360) * CONFIG.defaultZoom },\n uFieldFill: { value: STATE.fieldSize }\n },\n transparent: true,\n depthWrite: false,\n blending: THREE.AdditiveBlending\n });\n\n const secondaryPoints = new THREE.Points(secondaryGeometry, secondaryMaterial);\n scene.add(secondaryPoints);\n\n // Expose secondary-layer controls to the host component.\n STATE.secondaryEnabled = true;\n STATE.secondaryOpacity = 1.0;\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550 PARTICLE TRAIL SYSTEM \u2550\u2550\u2550\u2550\u2550\u2550\u2550\n const particleSeeds = geometry.getAttribute('aRandom');\n function fieldParticlePosition(index, target) {\n const halfHeight = material.uniforms.uViewportHalfHeight.value * STATE.fieldSize;\n const halfWidth = halfHeight * material.uniforms.uViewportAspect.value;\n const x = particleSeeds.getX(index), y = particleSeeds.getY(index), z = particleSeeds.getZ(index);\n const proximity = z ** 4, depth = particleUniforms.uParticleDepth.value;\n const offset = (-100 + proximity * 225) * depth;\n const projection = 1 - offset / CONFIG.defaultZoom;\n const time = material.uniforms.uTime.value * (1 + (proximity * 1.35 - 0.75) * depth);\n const drift = (0.006 + proximity * 0.039) * depth;\n return target.set(\n (x * 2 - 1 + Math.sin(time * 0.16 + x * 6.28) * drift) * halfWidth * projection,\n (y * 2 - 1 + Math.cos(time * 0.12 + y * 6.28) * drift) * halfHeight * projection,\n (z - 0.5) * 12 + offset\n );\n }\n\n class TrailSystem {\n constructor(count, length) {\n this.trails = [];\n this.meshes = [];\n this.nextPosition = new THREE.Vector3();\n\n for (let i = 0; i < count; i++) {\n const anchor = new THREE.Vector2(Math.random() * 1.5 - 0.75, Math.random() * 1.5 - 0.75);\n const start = new THREE.Vector3(\n anchor.x * material.uniforms.uViewportAspect.value * material.uniforms.uViewportHalfHeight.value * STATE.fieldSize,\n anchor.y * material.uniforms.uViewportHalfHeight.value * STATE.fieldSize,\n 0\n );\n const trail = {\n positions: Array.from({ length }, () => start.clone()),\n anchor,\n phase: Math.random() * Math.PI * 2,\n speed: 0.3 + Math.random() * 0.7,\n color: new THREE.Color(def.c1).lerp(new THREE.Color(def.c2), i / Math.max(1, count - 1))\n };\n this.trails.push(trail);\n\n const geom = new THREE.BufferGeometry();\n const positions = new Float32Array(length * 3);\n const alphas = new Float32Array(length);\n for (let j = 0; j < length; j++) alphas[j] = 1.0 - j / length;\n\n geom.setAttribute('position', new THREE.BufferAttribute(positions, 3));\n geom.setAttribute('alpha', new THREE.BufferAttribute(alphas, 1));\n\n const mat = new THREE.ShaderMaterial({\n vertexShader: `attribute float alpha; varying float vAlpha; void main() { vAlpha = alpha; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`,\n fragmentShader: `uniform vec3 uColor; uniform float uOpacity, uBeat; varying float vAlpha; void main() { gl_FragColor = vec4(uColor * (1.0 + uBeat * 0.3), vAlpha * uOpacity * 0.6 * (1.0 + uBeat * 0.5)); }`,\n uniforms: { uColor: { value: trail.color }, uOpacity: { value: 0.4 }, uBeat: { value: 0 } },\n transparent: true, depthWrite: false, blending: THREE.AdditiveBlending\n });\n\n const line = new THREE.Line(geom, mat);\n this.meshes.push(line);\n scene.add(line);\n }\n }\n\n update(time, audio, fieldColor) {\n const halfHeight = material.uniforms.uViewportHalfHeight.value * STATE.fieldSize;\n const halfWidth = halfHeight * material.uniforms.uViewportAspect.value;\n for (let i = 0; i < this.trails.length; i++) {\n const trail = this.trails[i], mesh = this.meshes[i];\n mesh.visible = STATE.particleTrailsEnabled && i < this.trails.length * STATE.qualityLevel;\n if (!mesh.visible) continue;\n\n const angle = time * 0.5 * trail.speed + trail.phase;\n const newPos = this.nextPosition.set(\n trail.anchor.x * halfWidth + Math.cos(angle) * (4 + audio.bass * 8),\n trail.anchor.y * halfHeight + Math.sin(time * 0.3 + trail.phase) * (4 + audio.mid * 8),\n Math.sin(angle) * (4 + audio.high * 7)\n );\n newPos.x = THREE.MathUtils.clamp(newPos.x, -halfWidth * 0.94, halfWidth * 0.94);\n newPos.y = THREE.MathUtils.clamp(newPos.y, -halfHeight * 0.94, halfHeight * 0.94);\n\n for (let j = trail.positions.length - 1; j > 0; j--) trail.positions[j].copy(trail.positions[j - 1]);\n trail.positions[0].copy(newPos);\n\n const posAttr = mesh.geometry.getAttribute('position');\n for (let j = 0; j < trail.positions.length; j++) {\n posAttr.setXYZ(j, trail.positions[j].x, trail.positions[j].y, trail.positions[j].z);\n }\n posAttr.needsUpdate = true;\n\n mesh.material.uniforms.uBeat.value = audio.beat;\n mesh.material.uniforms.uOpacity.value = STATE.particleTrailOpacity;\n }\n }\n }\n\n const trailSystem = new TrailSystem(50, 12);\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550 PARTICLE CONNECTIONS (Force Network) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\n const connectionCount = 500; // Number of particles to check for connections\n const maxConnections = 800; // Maximum line segments\n const connectionGeometry = new THREE.BufferGeometry();\n const connectionPositions = new Float32Array(maxConnections * 6); // 2 points per line, 3 coords each\n const connectionAlphas = new Float32Array(maxConnections * 2); // Alpha per vertex\n connectionGeometry.setAttribute('position', new THREE.BufferAttribute(connectionPositions, 3));\n connectionGeometry.setAttribute('alpha', new THREE.BufferAttribute(connectionAlphas, 1));\n connectionGeometry.setDrawRange(0, 0);\n\n const connectionMaterial = new THREE.ShaderMaterial({\n vertexShader: `\n attribute float alpha;\n varying float vAlpha;\n void main() {\n vAlpha = alpha;\n vec4 mvPos = modelViewMatrix * vec4(position, 1.0);\n gl_Position = projectionMatrix * mvPos;\n }\n `,\n fragmentShader: `\n uniform vec3 uColor;\n uniform vec3 uColor2;\n uniform float uOpacity;\n uniform float uBeatEnergy;\n uniform float uHighMid;\n uniform float uSpectralCentroid;\n varying float vAlpha;\n void main() {\n // Blend hue with spectral centroid (brighter/higher = more c2)\n vec3 color = mix(uColor, uColor2, clamp(uSpectralCentroid * 2.0, 0.0, 1.0));\n // Beat pulse flash\n float beatFlash = 1.0 + uBeatEnergy * 1.2;\n color *= beatFlash;\n // Per-line alpha reacts to highMid (attack/presence energy)\n float lineAlpha = vAlpha * uOpacity * (0.4 + uHighMid * 0.5 + uBeatEnergy * 0.4);\n gl_FragColor = vec4(color, clamp(lineAlpha, 0.0, 1.0));\n }\n `,\n uniforms: {\n uColor: { value: new THREE.Color(def.c2) },\n uColor2: { value: new THREE.Color(def.c1) },\n uOpacity: { value: 1.0 },\n uBeatEnergy: { value: 0 },\n uHighMid: { value: 0 },\n uSpectralCentroid: { value: 0 }\n },\n transparent: true,\n depthWrite: false,\n blending: THREE.AdditiveBlending\n });\n\n const connectionMesh = new THREE.LineSegments(connectionGeometry, connectionMaterial);\n scene.add(connectionMesh);\n\n // Particle indices to track for connections \u2014 refreshed periodically\n const trackedParticles = [];\n const positions = Array.from({ length: connectionCount }, () => new THREE.Vector3());\n let connectionRefreshCounter = 0;\n const CONNECTION_REFRESH_INTERVAL = 40; // 20 Hz updates preserve the original ~2 s selection cycle\n\n function refreshTrackedParticles() {\n trackedParticles.length = 0;\n const visibleParticles = Math.max(1, geometry.drawRange.count);\n const visibleFraction = Math.sqrt(visibleParticles / (CONFIG.maxParticles * CONFIG.initialDensity));\n const trackedCount = Math.max(2, Math.floor(connectionCount * Math.min(1, STATE.qualityLevel, visibleFraction)));\n for (let i = 0; i < trackedCount; i++) {\n trackedParticles.push(Math.floor(Math.random() * visibleParticles));\n }\n }\n refreshTrackedParticles();\n let lastConnectionState = null;\n\n function shouldRebuildConnections(moving, current, previous, idleElapsed) {\n return moving || idleElapsed >= 2 || !previous || current.some((value, index) => value !== previous[index]);\n }\n\n function updateConnections(threshold, bassBoost, highMid, mid, forceRefresh = false) {\n // Refresh particle selection every N frames \u2014 network evolves over time\n connectionRefreshCounter++;\n if (forceRefresh || connectionRefreshCounter >= CONNECTION_REFRESH_INTERVAL) {\n refreshTrackedParticles();\n connectionRefreshCounter = 0;\n }\n\n // Mirror the shader's screen-fitted seed mapping for the line systems.\n for (let i = 0; i < trackedParticles.length; i++) {\n fieldParticlePosition(trackedParticles[i], positions[i]);\n }\n\n // Threshold breathes with bass, tightens on high energy\n const effectiveThreshold = threshold * (1.0 + bassBoost * 0.6) * (1.0 - highMid * 0.15);\n const thresholdSq = effectiveThreshold * effectiveThreshold;\n\n // Dynamic connection cap \u2014 expands with mid+highMid energy\n const dynamicMax = Math.floor(maxConnections * (0.3 + (mid + highMid) * 0.7));\n\n let connIdx = 0;\n for (let i = 0; i < trackedParticles.length && connIdx < dynamicMax; i++) {\n for (let j = i + 1; j < trackedParticles.length && connIdx < dynamicMax; j++) {\n const distSq = positions[i].distanceToSquared(positions[j]);\n if (distSq < thresholdSq && distSq > 0.1) {\n const dist = Math.sqrt(distSq);\n const alpha = (1.0 - dist / effectiveThreshold) * 0.8;\n\n connectionPositions[connIdx * 6 + 0] = positions[i].x;\n connectionPositions[connIdx * 6 + 1] = positions[i].y;\n connectionPositions[connIdx * 6 + 2] = positions[i].z;\n connectionPositions[connIdx * 6 + 3] = positions[j].x;\n connectionPositions[connIdx * 6 + 4] = positions[j].y;\n connectionPositions[connIdx * 6 + 5] = positions[j].z;\n\n connectionAlphas[connIdx * 2 + 0] = alpha;\n connectionAlphas[connIdx * 2 + 1] = alpha;\n\n connIdx++;\n }\n }\n }\n\n connectionGeometry.attributes.position.needsUpdate = true;\n connectionGeometry.attributes.alpha.needsUpdate = true;\n connectionGeometry.setDrawRange(0, connIdx * 2);\n }\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550 NETWORK CRAWLERS \u2550\u2550\u2550\u2550\u2550\u2550\u2550\n const CRAWLER_COUNT = 24; // concurrent crawlers\n const CRAWLER_TAIL = 10; // trail positions per crawler\n const crawlerMaxSegs = CRAWLER_COUNT * (CRAWLER_TAIL - 1);\n\n const crawlerGeo = new THREE.BufferGeometry();\n const crawlerPos = new Float32Array(crawlerMaxSegs * 6);\n const crawlerAlp = new Float32Array(crawlerMaxSegs * 2);\n crawlerGeo.setAttribute('position', new THREE.BufferAttribute(crawlerPos, 3));\n crawlerGeo.setAttribute('alpha', new THREE.BufferAttribute(crawlerAlp, 1));\n crawlerGeo.setDrawRange(0, 0);\n\n // Bloom only lights pixels above 0.85 luminance. Preset greens and cyans got there on their own;\n // a theme's reds and violets carry the same energy at a fraction of the luminance, so reaction\n // colours keep the theme's hue but are lifted (past 1.0 if needed) to the brightness of the originals.\n function glowColor(color, luminance) {\n const current = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;\n return color.multiplyScalar(Math.min(4, Math.max(1, luminance / Math.max(current, 0.02))));\n }\n const crawlerLow = new THREE.Color(def.c1), crawlerHigh = new THREE.Color(def.c3);\n const crawlerTarget = new THREE.Color();\n const crawlerMaterial = new THREE.ShaderMaterial({\n vertexShader: `\n attribute float alpha;\n varying float vAlpha;\n void main() {\n vAlpha = alpha;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n }\n `,\n fragmentShader: `\n uniform vec3 uColor;\n uniform float uBeatEnergy;\n varying float vAlpha;\n void main() {\n vec3 c = uColor * (1.2 + uBeatEnergy * 1.5);\n gl_FragColor = vec4(c, vAlpha);\n }\n `,\n uniforms: {\n uColor: { value: new THREE.Color(def.c1) },\n uBeatEnergy: { value: 0 }\n },\n transparent: true,\n depthWrite: false,\n blending: THREE.AdditiveBlending\n });\n\n const crawlerMesh = new THREE.LineSegments(crawlerGeo, crawlerMaterial);\n scene.add(crawlerMesh);\n\n // Initialise crawler agents\n const crawlers = [];\n const CRAWLER_MIN_STEP = 1.8; // min world-space distance before adding a tail point\n for (let i = 0; i < CRAWLER_COUNT; i++) {\n crawlers.push({\n fromIdx: Math.floor(Math.random() * connectionCount),\n toIdx: Math.floor(Math.random() * connectionCount),\n t: Math.random(),\n speed: 0.0015 + Math.random() * 0.002,\n tail: [],\n lastTailPos: null, // last world position a tail point was recorded\n hue: Math.random()\n });\n }\n\n function updateCrawlers(dt, highMid, beatEnergy, spectralCentroid) {\n // Helper: world position of a tracked particle\n function particlePos(idx) {\n return positions[idx % trackedParticles.length];\n }\n\n // Helper: find nearest neighbour within threshold\n function nearestNeighbour(fromIdx, excludeIdx) {\n const origin = particlePos(fromIdx);\n const threshold = STATE.connectionThreshold * (1.2 + highMid * 0.4);\n const threshSq = threshold * threshold;\n let bestDist = Infinity, bestIdx = -1;\n const check = Math.min(trackedParticles.length, 80); // sample subset for perf\n const start = Math.floor(Math.random() * (trackedParticles.length - check));\n for (let k = start; k < start + check; k++) {\n const ki = k % trackedParticles.length;\n if (ki === fromIdx || ki === excludeIdx) continue;\n const d = origin.distanceToSquared(particlePos(ki));\n if (d < threshSq && d < bestDist) { bestDist = d; bestIdx = ki; }\n }\n // fallback: pick random if no neighbour in range\n return bestIdx === -1 ? Math.floor(Math.random() * trackedParticles.length) : bestIdx;\n }\n\n let segIdx = 0;\n\n for (let i = 0; i < crawlers.length; i++) {\n const c = crawlers[i];\n\n // Speed reacts to highMid (attack energy) and beat\n const spd = c.speed * (1.0 + highMid * 0.8 + beatEnergy * 0.5);\n c.t += spd;\n\n // Reached target \u2014 advance to next node\n if (c.t >= 1.0) {\n c.t -= 1.0;\n c.fromIdx = c.toIdx;\n c.toIdx = nearestNeighbour(c.fromIdx, c.fromIdx);\n // Occasionally teleport to keep crawlers spread across the field\n if (Math.random() < 0.04) {\n c.fromIdx = Math.floor(Math.random() * trackedParticles.length);\n c.toIdx = nearestNeighbour(c.fromIdx, c.fromIdx);\n c.tail = [];\n c.lastTailPos = null;\n }\n }\n\n // Current head position\n const from = particlePos(c.fromIdx);\n const to = particlePos(c.toIdx);\n const head = new THREE.Vector3().lerpVectors(from, to, c.t);\n\n // Only record a tail point when head has moved far enough in world space\n const distMoved = c.lastTailPos ? head.distanceTo(c.lastTailPos) : Infinity;\n if (distMoved >= CRAWLER_MIN_STEP) {\n c.tail.push(head.clone());\n if (c.tail.length > CRAWLER_TAIL) c.tail.shift();\n c.lastTailPos = head.clone();\n }\n\n // Write tail segments\n const tailLen = c.tail.length;\n for (let s = 0; s < tailLen - 1 && segIdx < crawlerMaxSegs; s++) {\n const p0 = c.tail[s];\n const p1 = c.tail[s + 1];\n // Alpha: head is brightest, tail fades\n const baseAlpha = ((s + 1) / (tailLen - 1));\n const alpha = baseAlpha * (0.25 + spectralCentroid * 0.3 + beatEnergy * 0.4);\n\n crawlerPos[segIdx * 6 + 0] = p0.x; crawlerPos[segIdx * 6 + 1] = p0.y; crawlerPos[segIdx * 6 + 2] = p0.z;\n crawlerPos[segIdx * 6 + 3] = p1.x; crawlerPos[segIdx * 6 + 4] = p1.y; crawlerPos[segIdx * 6 + 5] = p1.z;\n crawlerAlp[segIdx * 2 + 0] = alpha;\n crawlerAlp[segIdx * 2 + 1] = alpha;\n segIdx++;\n }\n }\n\n crawlerGeo.attributes.position.needsUpdate = true;\n crawlerGeo.attributes.alpha.needsUpdate = true;\n crawlerGeo.setDrawRange(0, segIdx * 2);\n }\n\n STATE.crawlersEnabled = true;\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550 NEBULA BACKGROUND \u2550\u2550\u2550\u2550\u2550\u2550\u2550\n const nebulaVertexShader = `\nvarying vec3 vWorldPosition;\nvarying vec2 vUv;\nvoid main() {\n vUv = uv;\n vec4 worldPos = modelMatrix * vec4(position, 1.0);\n vWorldPosition = worldPos.xyz;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}\n`;\n\n const nebulaFragmentShader = `\nuniform float uTime;\nuniform vec3 uColor1;\nuniform vec3 uColor2;\nuniform vec3 uColor3;\nuniform float uBass;\nuniform float uMid;\nuniform float uHigh;\nuniform float uBeatEnergy;\nuniform float uIntensity;\nvarying vec3 vWorldPosition;\nvarying vec2 vUv;\n\n// Simplex 3D noise\nvec4 permute(vec4 x) { return mod(((x*34.0)+1.0)*x, 289.0); }\nvec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }\n\nfloat snoise(vec3 v) {\n const vec2 C = vec2(1.0/6.0, 1.0/3.0);\n const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);\n vec3 i = floor(v + dot(v, C.yyy));\n vec3 x0 = v - i + dot(i, C.xxx);\n vec3 g = step(x0.yzx, x0.xyz);\n vec3 l = 1.0 - g;\n vec3 i1 = min(g.xyz, l.zxy);\n vec3 i2 = max(g.xyz, l.zxy);\n vec3 x1 = x0 - i1 + C.xxx;\n vec3 x2 = x0 - i2 + C.yyy;\n vec3 x3 = x0 - D.yyy;\n i = mod(i, 289.0);\n vec4 p = permute(permute(permute(i.z + vec4(0.0, i1.z, i2.z, 1.0)) + i.y + vec4(0.0, i1.y, i2.y, 1.0)) + i.x + vec4(0.0, i1.x, i2.x, 1.0));\n float n_ = 1.0/7.0;\n vec3 ns = n_ * D.wyz - D.xzx;\n vec4 j = p - 49.0 * floor(p * ns.z * ns.z);\n vec4 x_ = floor(j * ns.z);\n vec4 y_ = floor(j - 7.0 * x_);\n vec4 x = x_ *ns.x + ns.yyyy;\n vec4 y = y_ *ns.x + ns.yyyy;\n vec4 h = 1.0 - abs(x) - abs(y);\n vec4 b0 = vec4(x.xy, y.xy);\n vec4 b1 = vec4(x.zw, y.zw);\n vec4 s0 = floor(b0)*2.0 + 1.0;\n vec4 s1 = floor(b1)*2.0 + 1.0;\n vec4 sh = -step(h, vec4(0.0));\n vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy;\n vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww;\n vec3 p0 = vec3(a0.xy, h.x);\n vec3 p1 = vec3(a0.zw, h.y);\n vec3 p2 = vec3(a1.xy, h.z);\n vec3 p3 = vec3(a1.zw, h.w);\n vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2,p2), dot(p3,p3)));\n p0 *= norm.x; p1 *= norm.y; p2 *= norm.z; p3 *= norm.w;\n vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);\n return 42.0 * dot(m*m*m*m, vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3)));\n}\n\nfloat fbm(vec3 p) {\n float value = 0.0;\n float amplitude = 0.5;\n float frequency = 1.0;\n for(int i = 0; i < 3; i++) {\n value += amplitude * snoise(p * frequency);\n amplitude *= 0.5;\n frequency *= 2.0;\n }\n return value;\n}\n\nvoid main() {\n vec3 dir = normalize(vWorldPosition);\n float t = uTime * 0.02;\n\n // Layered nebula clouds\n vec3 p1 = dir * 2.0 + t * 0.5;\n vec3 p2 = dir * 4.0 - t * 0.3;\n vec3 p3 = dir * 1.5 + vec3(t * 0.2, -t * 0.1, t * 0.15);\n\n float n1 = fbm(p1) * 0.5 + 0.5;\n float n2 = fbm(p2) * 0.5 + 0.5;\n float n3 = fbm(p3) * 0.5 + 0.5;\n\n // Create cloud density\n float density = n1 * n2;\n density = pow(density, 1.5) * 1.5;\n\n // Wispy tendrils\n float tendrils = pow(n3, 3.0) * 0.8;\n\n // Color mixing based on field colors\n vec3 nebulaColor = mix(uColor1 * 0.3, uColor2 * 0.5, n1);\n nebulaColor = mix(nebulaColor, uColor3 * 0.4, n2 * 0.5);\n\n // Add some complementary deep space colors\n vec3 deepSpace = vec3(0.02, 0.01, 0.05);\n vec3 starGlow = vec3(0.1, 0.08, 0.15);\n\n // Audio reactivity\n float bassGlow = uBass * 0.3;\n float midShimmer = uMid * 0.2 * sin(uTime * 2.0 + n1 * 10.0);\n float beatPulse = uBeatEnergy * 0.4;\n\n // Combine layers\n vec3 color = deepSpace;\n color += nebulaColor * density * (0.4 + bassGlow + beatPulse);\n color += uColor2 * 0.15 * tendrils * (1.0 + uHigh * 0.5);\n color += starGlow * pow(n1 * n2, 4.0) * (1.0 + midShimmer);\n\n // Scattered stars\n float stars = pow(snoise(dir * 50.0) * 0.5 + 0.5, 20.0);\n stars += pow(snoise(dir * 80.0 + 100.0) * 0.5 + 0.5, 25.0) * 0.5;\n color += vec3(1.0, 0.95, 0.9) * stars * (0.5 + uHigh * 0.5);\n\n // Vignette toward horizon\n float horizonFade = 1.0 - pow(abs(dir.y), 0.5) * 0.3;\n color *= horizonFade;\n\n // Apply intensity\n color *= uIntensity;\n\n gl_FragColor = vec4(color, 1.0);\n}\n`;\n\n const nebulaMaterial = new THREE.ShaderMaterial({\n vertexShader: nebulaVertexShader,\n fragmentShader: nebulaFragmentShader,\n uniforms: {\n uTime: { value: 0 },\n uColor1: { value: new THREE.Color(def.c1) },\n uColor2: { value: new THREE.Color(def.c2) },\n uColor3: { value: new THREE.Color(def.c3) },\n uBass: { value: 0 },\n uMid: { value: 0 },\n uHigh: { value: 0 },\n uBeatEnergy: { value: 0 },\n uIntensity: { value: STATE.nebulaIntensity }\n },\n side: THREE.BackSide,\n depthWrite: false\n });\n\n const nebulaSphere = new THREE.Mesh(\n new THREE.SphereGeometry(500, 64, 64),\n nebulaMaterial\n );\n scene.add(nebulaSphere);\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550 POST-PROCESSING COMPOSER \u2550\u2550\u2550\u2550\u2550\u2550\u2550\n const composer = new EffectComposer(renderer);\n composer.addPass(new RenderPass(scene, camera));\n\n const afterimagePass = new AfterimagePass();\n afterimagePass.uniforms['damp'].value = STATE.trails;\n composer.addPass(afterimagePass);\n\n const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85);\n bloomPass.threshold = 0.25;\n bloomPass.strength = STATE.bloom;\n bloomPass.radius = 0.45;\n composer.addPass(bloomPass);\n\n const godRaysPass = new ShaderPass(GodRaysShader);\n godRaysPass.enabled = false;\n composer.addPass(godRaysPass);\n\n const lensFlarePass = new ShaderPass(LensFlareShader);\n lensFlarePass.enabled = STATE.lensFlareEnabled && STATE.lensFlareIntensity > 0;\n composer.addPass(lensFlarePass);\n\n const anamorphicPass = new ShaderPass(AnamorphicShader);\n composer.addPass(anamorphicPass);\n\n const chromaticPass = new ShaderPass(ChromaticShader);\n chromaticPass.enabled = STATE.chromaticAberration > 0;\n composer.addPass(chromaticPass);\n\n // Custom DOF pass (radial focus)\n const customDOFPass = new ShaderPass(CustomDOFShader);\n customDOFPass.uniforms.uFocus.value = STATE.dofFocus;\n customDOFPass.uniforms.uFocalLength.value = STATE.dofFocalLength;\n customDOFPass.uniforms.uBokehStrength.value = STATE.dofBokehStrength;\n customDOFPass.uniforms.uResolution.value.set(window.innerWidth, window.innerHeight);\n customDOFPass.enabled = STATE.dofEnabled;\n composer.addPass(customDOFPass);\n\n const vignettePass = new ShaderPass(VignetteShader);\n composer.addPass(vignettePass);\n\n const grainPass = new ShaderPass(GrainShader);\n grainPass.enabled = STATE.filmGrain > 0;\n composer.addPass(grainPass);\n\n const AlphaFromLumaShader = {\n uniforms: { tDiffuse: { value: null } },\n vertexShader: `varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`,\n fragmentShader: `uniform sampler2D tDiffuse; varying vec2 vUv;\n void main() {\n vec3 rgb = texture2D(tDiffuse, vUv).rgb;\n float luminance = max(max(rgb.r, rgb.g), rgb.b);\n float alpha = smoothstep(0.004, 0.06, luminance);\n gl_FragColor = vec4(rgb, alpha);\n }`\n };\n composer.addPass(new OutputPass());\n composer.addPass(new ShaderPass(AlphaFromLumaShader));\n\n // Keep the embedded CSS accents aligned with the kit palette.\n function applyCssTheme(accent) {\n const luminance = accent.r * 0.299 + accent.g * 0.587 + accent.b * 0.114;\n const boost = luminance < 0.35 ? 0.35 / Math.max(luminance, 0.001) : 1;\n for (const [channel, value] of Object.entries({ ar: accent.r, ag: accent.g, ab: accent.b })) {\n document.documentElement.style.setProperty(`--${channel}`, Math.min(Math.round(value * boost * 255), 255));\n }\n }\n applyCssTheme(new THREE.Color(def.c1));\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550 UI & EVENTS \u2550\u2550\u2550\u2550\u2550\u2550\u2550\n const overlay = document.getElementById('overlay');\n const audioControls = document.getElementById('audio-controls');\n const playPauseBtn = document.getElementById('play-pause-btn');\n const progressBar = document.getElementById('progress-bar');\n const progressContainer = document.getElementById('progress-container');\n const timeDisplay = document.getElementById('time-display');\n const trackName = document.getElementById('track-name');\n const status = document.getElementById('status-bar');\n\n const formatTime = s => `${Math.floor(s / 60)}:${Math.floor(s % 60).toString().padStart(2, '0')}`;\n\n function updateUI() {\n if (AUDIO.mode === 'mic') {\n playPauseBtn.textContent = '\u{1F399} live';\n playPauseBtn.disabled = true;\n } else {\n playPauseBtn.textContent = AUDIO.isPlaying ? '\u23F8 pause' : '\u25B6 play';\n playPauseBtn.classList.toggle('playing', AUDIO.isPlaying);\n playPauseBtn.disabled = false;\n }\n if (AUDIO.mode === 'file' && AUDIO.audioBuffer) {\n const cur = AUDIO.getCurrentTime(), dur = AUDIO.getDuration();\n progressBar.style.width = (cur / dur * 100) + '%';\n timeDisplay.textContent = `${formatTime(cur)} / ${formatTime(dur)}`;\n } else {\n progressBar.style.width = '0%';\n timeDisplay.textContent = AUDIO.mode === 'mic' ? 'LIVE' : '0:00 / 0:00';\n }\n }\n\n function togglePlayPause() {\n if (AUDIO.mode !== 'file') return;\n AUDIO.togglePlayPause();\n updateUI();\n status.innerHTML = AUDIO.isPlaying ? 'SYSTEM: <span>PLAYING</span>' : 'SYSTEM: <span>PAUSED</span>';\n }\n\n function activate() {\n AUDIO.init();\n if (AUDIO.ctx.state === 'suspended') AUDIO.ctx.resume();\n overlay.style.opacity = 0;\n setTimeout(() => overlay.style.display = 'none', 800);\n audioControls.classList.add('visible');\n }\n\n async function loadTrack(file) {\n activate();\n const buffer = await AUDIO.loadTrack(await file.arrayBuffer(), file.name);\n const samples = buffer.getChannelData(0);\n const bars = 160;\n const peaks = Array.from({ length: bars }, (_, bar) => {\n const from = Math.floor(bar * samples.length / bars);\n const to = Math.floor((bar + 1) * samples.length / bars);\n const stride = Math.max(1, Math.floor((to - from) / 64));\n let peak = 0;\n for (let i = from; i < to; i += stride) peak = Math.max(peak, Math.abs(samples[i]));\n return peak;\n });\n parent.postMessage({ type: 'ad-qf-waveform', value: peaks }, '*');\n AUDIO.play();\n trackName.textContent = file.name.length > 25 ? file.name.slice(0, 22) + '...' : file.name;\n updateUI();\n status.innerHTML = 'SYSTEM: <span>PLAYING</span>';\n }\n\n async function startMic(preferredLabel = null) {\n console.log('Global startMic requested:', preferredLabel);\n try {\n activate();\n await AUDIO.startMic(preferredLabel);\n trackName.textContent = AUDIO.currentTrackName;\n updateUI();\n // status update handled in AUDIO.startMic\n } catch (e) {\n console.error(\"Global startMic failed:\", e);\n const status = document.getElementById('status-bar');\n status.innerHTML = `ERROR: <span style=\"color:#ff6b6b\">${e.message}</span>`;\n }\n }\n\n // Using optional chaining/checks because one input was replaced by a button\n const enterBtn = document.getElementById('enter-btn');\n if (enterBtn) enterBtn.onclick = () => startMic(null);\n\n const fileInput = document.getElementById('file-input');\n if (fileInput) fileInput.onchange = e => e.target.files[0] && loadTrack(e.target.files[0]);\n\n document.getElementById('file-input-2').onchange = e => { if (e.target.files[0]) loadTrack(e.target.files[0]); e.target.value = ''; };\n document.getElementById('mic-btn-2').onclick = () => startMic(null);\n playPauseBtn.onclick = togglePlayPause;\n progressContainer.onclick = e => {\n if (AUDIO.mode !== 'file' || !AUDIO.audioBuffer) return;\n const rect = progressContainer.getBoundingClientRect();\n AUDIO.seek((e.clientX - rect.left) / rect.width * AUDIO.getDuration());\n };\n document.getElementById('screenshot-btn')?.addEventListener('click', () => {\n composer.render();\n const a = document.createElement('a');\n a.download = 'quantum-' + Date.now() + '.png';\n a.href = renderer.domElement.toDataURL('image/png');\n a.click();\n });\n document.getElementById('fullscreen-btn')?.addEventListener('click', () => {\n document.fullscreenElement ? document.exitFullscreen() : document.documentElement.requestFullscreen();\n });\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550 ANIMATION LOOP \u2550\u2550\u2550\u2550\u2550\u2550\u2550\n let renderVisible = true;\n let connectionElapsed = 1 / 20;\n let idleConnectionElapsed = 2;\n let frameRequest = 0;\n let nextFrameAt = 0;\n let lastRenderedAt = -1;\n let elapsed = 0;\n\n function startFrames() {\n if (!frameRequest && renderVisible && !document.hidden) frameRequest = requestAnimationFrame(animate);\n }\n\n function stopFrames() {\n if (frameRequest) cancelAnimationFrame(frameRequest);\n frameRequest = 0;\n nextFrameAt = 0;\n lastRenderedAt = -1;\n }\n\n document.addEventListener('visibilitychange', () => {\n if (document.hidden) stopFrames();\n else startFrames();\n });\n\n function animate(now) {\n frameRequest = 0;\n if (!renderVisible || document.hidden) return;\n startFrames();\n const frameInterval = 1000 / Math.max(1, STATE.targetFPS);\n if (now < nextFrameAt) return;\n nextFrameAt = Math.max(nextFrameAt + frameInterval, now);\n const dt = lastRenderedAt < 0 ? 0 : Math.min((now - lastRenderedAt) / 1000, 0.05);\n lastRenderedAt = now;\n elapsed += dt;\n\n material.uniforms.uTime.value += dt * STATE.timeScale;\n grainPass.uniforms.uTime.value = elapsed;\n lensFlarePass.uniforms.uTime.value = elapsed;\n\n material.uniforms.uZoomFactor.value = camera.position.length() / CONFIG.defaultZoom;\n material.uniforms.uMousePos.value.lerp(mouseWorldPos, 0.1);\n material.uniforms.uMouseVelocity.value.lerp(mouseVelocity, 0.08);\n\n AUDIO.update();\n\n const s = STATE.sensitivity, lf = 0.16;\n material.uniforms.uSubBass.value = THREE.MathUtils.lerp(material.uniforms.uSubBass.value, AUDIO.gatedBands.subBass * s, lf);\n material.uniforms.uBass.value = THREE.MathUtils.lerp(material.uniforms.uBass.value, AUDIO.gatedBands.bass * s, lf);\n material.uniforms.uLowMid.value = THREE.MathUtils.lerp(material.uniforms.uLowMid.value, AUDIO.gatedBands.lowMid * s, lf);\n material.uniforms.uMid.value = THREE.MathUtils.lerp(material.uniforms.uMid.value, AUDIO.gatedBands.mid * s, lf);\n material.uniforms.uHighMid.value = THREE.MathUtils.lerp(material.uniforms.uHighMid.value, AUDIO.gatedBands.highMid * s, lf);\n material.uniforms.uHigh.value = THREE.MathUtils.lerp(material.uniforms.uHigh.value, AUDIO.gatedBands.high * s, lf);\n material.uniforms.uUltraHigh.value = THREE.MathUtils.lerp(material.uniforms.uUltraHigh.value, AUDIO.gatedBands.ultraHigh * s, lf);\n material.uniforms.uBeatEnergy.value = THREE.MathUtils.lerp(material.uniforms.uBeatEnergy.value, AUDIO.beatEnergy, 0.22);\n material.uniforms.uSpectralCentroid.value = THREE.MathUtils.lerp(material.uniforms.uSpectralCentroid.value, AUDIO.spectralCentroid, lf);\n material.uniforms.uSpectralFlux.value = THREE.MathUtils.lerp(material.uniforms.uSpectralFlux.value, AUDIO.spectralFlux * s, lf);\n\n // === ENHANCEMENT: Update new uniforms ===\n material.uniforms.uOnsetEnergy.value = THREE.MathUtils.lerp(material.uniforms.uOnsetEnergy.value, AUDIO.onsetEnergy, 0.25);\n material.uniforms.uTerrainMode.value = FIELD_TYPES[STATE.field]?.terrain ? 1.0 : 0.0;\n material.uniforms.uTerrainHeight.value = STATE.waveformTerrainHeight;\n\n // Update secondary particles\n secondaryPoints.visible = STATE.secondaryEnabled;\n secondaryMaterial.uniforms.uTime.value = material.uniforms.uTime.value;\n secondaryMaterial.uniforms.uSubBass.value = material.uniforms.uSubBass.value;\n secondaryMaterial.uniforms.uBass.value = material.uniforms.uBass.value;\n secondaryMaterial.uniforms.uLowMid.value = material.uniforms.uLowMid.value;\n secondaryMaterial.uniforms.uMid.value = material.uniforms.uMid.value;\n secondaryMaterial.uniforms.uHighMid.value = material.uniforms.uHighMid.value;\n secondaryMaterial.uniforms.uHigh.value = material.uniforms.uHigh.value;\n secondaryMaterial.uniforms.uUltraHigh.value = material.uniforms.uUltraHigh.value;\n secondaryMaterial.uniforms.uBeatEnergy.value = material.uniforms.uBeatEnergy.value;\n secondaryMaterial.uniforms.uSpectralCentroid.value = material.uniforms.uSpectralCentroid.value;\n secondaryMaterial.uniforms.uSpectralFlux.value = material.uniforms.uSpectralFlux.value;\n secondaryMaterial.uniforms.uOnsetEnergy.value = material.uniforms.uOnsetEnergy.value;\n secondaryMaterial.uniforms.uVortexStrength.value = STATE.vortexStrength;\n\n // Update nebula background\n nebulaSphere.visible = STATE.nebulaEnabled;\n if (STATE.nebulaEnabled) {\n nebulaMaterial.uniforms.uTime.value = material.uniforms.uTime.value;\n nebulaMaterial.uniforms.uBass.value = material.uniforms.uBass.value;\n nebulaMaterial.uniforms.uMid.value = material.uniforms.uMid.value;\n nebulaMaterial.uniforms.uHigh.value = material.uniforms.uHigh.value;\n nebulaMaterial.uniforms.uBeatEnergy.value = material.uniforms.uBeatEnergy.value;\n }\n\n // Update particle connections\n connectionMesh.visible = STATE.connectionsEnabled;\n if (STATE.connectionsEnabled) {\n const activeAudio = AUDIO.mode === 'mic' || AUDIO.isPlaying || AUDIO.externalActive;\n const moving = activeAudio || particleUniforms.uParticleDepth.value > 0;\n const connectionState = [\n STATE.field, STATE.connectionThreshold, geometry.drawRange.count,\n material.uniforms.uViewportAspect.value, material.uniforms.uViewportHalfHeight.value, STATE.fieldSize,\n STATE.qualityLevel, activeAudio, particleUniforms.uParticleDepth.value\n ];\n connectionElapsed = Math.min(1 / 20, connectionElapsed + dt);\n idleConnectionElapsed = Math.min(2, idleConnectionElapsed + dt);\n if (connectionElapsed >= 1 / 20 && shouldRebuildConnections(moving, connectionState, lastConnectionState, idleConnectionElapsed)) {\n const refreshSelection = !lastConnectionState || connectionState[2] !== lastConnectionState[2]\n || connectionState[6] !== lastConnectionState[6] || (!activeAudio && idleConnectionElapsed >= 2);\n updateConnections(\n STATE.connectionThreshold,\n activeAudio ? material.uniforms.uBass.value : 0,\n activeAudio ? material.uniforms.uHighMid.value : 0,\n activeAudio ? material.uniforms.uMid.value : 0,\n refreshSelection\n );\n lastConnectionState = connectionState;\n connectionElapsed = 0;\n idleConnectionElapsed = 0;\n }\n connectionMaterial.uniforms.uBeatEnergy.value = material.uniforms.uBeatEnergy.value;\n connectionMaterial.uniforms.uHighMid.value = material.uniforms.uHighMid.value;\n connectionMaterial.uniforms.uSpectralCentroid.value = material.uniforms.uSpectralCentroid.value;\n connectionMaterial.uniforms.uOpacity.value = STATE.connectionOpacity;\n } else {\n connectionElapsed = 1 / 20;\n idleConnectionElapsed = 2;\n lastConnectionState = null;\n }\n\n // Update crawlers\n crawlerMesh.visible = STATE.crawlersEnabled && STATE.connectionsEnabled && (AUDIO.externalActive || AUDIO.active && (AUDIO.isPlaying || AUDIO.mode === 'mic'));\n if (crawlerMesh.visible) {\n updateCrawlers(\n dt,\n material.uniforms.uHighMid.value,\n material.uniforms.uBeatEnergy.value,\n material.uniforms.uSpectralCentroid.value\n );\n crawlerTarget.lerpColors(crawlerLow, crawlerHigh, material.uniforms.uSpectralCentroid.value);\n crawlerMaterial.uniforms.uColor.value.lerp(crawlerTarget, 0.05);\n crawlerMaterial.uniforms.uBeatEnergy.value = material.uniforms.uBeatEnergy.value;\n }\n\n // Update post-processing\n chromaticPass.uniforms.uBeatEnergy.value = material.uniforms.uBeatEnergy.value;\n godRaysPass.uniforms.uBeatEnergy.value = material.uniforms.uBeatEnergy.value;\n lensFlarePass.uniforms.uBeatEnergy.value = material.uniforms.uBeatEnergy.value;\n vignettePass.uniforms.uBeatEnergy.value = material.uniforms.uBeatEnergy.value;\n customDOFPass.uniforms.uBeatEnergy.value = material.uniforms.uBeatEnergy.value;\n\n\n\n // Update trails\n trailSystem.update(elapsed, {\n bass: material.uniforms.uBass.value,\n mid: material.uniforms.uMid.value,\n high: material.uniforms.uHigh.value,\n beat: material.uniforms.uBeatEnergy.value\n }, material.uniforms.uColor1.value);\n\n // === ENHANCEMENT: Adaptive Quality System ===\n QUALITY_SYSTEM.update(dt * 1000);\n\n // === ENHANCEMENT: Audio-Reactive Camera ===\n AUDIO_CAMERA.update(dt * 1000);\n\n // Camera shake\n if (STATE.cameraShake > 0 && AUDIO.beatEnergy > 0.1) {\n const shake = AUDIO.beatEnergy * STATE.cameraShake;\n cameraShakeOffset.set((Math.random() - 0.5) * shake * 0.6, (Math.random() - 0.5) * shake * 0.4, (Math.random() - 0.5) * shake * 0.25);\n camera.position.copy(baseCameraPos).add(cameraShakeOffset);\n }\n\n controls.autoRotateSpeed = 0.35 + material.uniforms.uMid.value * 2.0 + material.uniforms.uBeatEnergy.value * 2.0;\n bloomPass.strength = STATE.bloom + (AUDIO.gatedBands.bass + AUDIO.gatedBands.mid + AUDIO.beatEnergy) / 6;\n bloomPass.enabled = bloomPass.strength > 0;\n anamorphicPass.uniforms.uIntensity.value = STATE.anamorphicStretch + AUDIO.gatedBands.bass * 0.4;\n anamorphicPass.enabled = anamorphicPass.uniforms.uIntensity.value > 0;\n\n controls.update();\n composer.render();\n }\n\n window.addEventListener('resize', () => {\n camera.aspect = window.innerWidth / window.innerHeight;\n camera.updateProjectionMatrix();\n material.uniforms.uViewportAspect.value = window.innerWidth / window.innerHeight;\n secondaryMaterial.uniforms.uViewportAspect.value = material.uniforms.uViewportAspect.value;\n material.uniforms.uViewportHalfHeight.value = Math.tan(camera.fov * Math.PI / 360) * CONFIG.defaultZoom;\n secondaryMaterial.uniforms.uViewportHalfHeight.value = material.uniforms.uViewportHalfHeight.value;\n renderer.setSize(window.innerWidth, window.innerHeight);\n composer.setSize(window.innerWidth, window.innerHeight);\n STATE.pixelRatio = fitPixelRatio();\n QUALITY_SYSTEM.applyQuality();\n customDOFPass.uniforms.uResolution.value.set(window.innerWidth, window.innerHeight);\n baseCameraPos.copy(camera.position);\n });\n\n let pausedFromKit = false;\n let requestedTimeScale = STATE.timeScale;\n const sideEffects = {\n particleDepth: value => particleUniforms.uParticleDepth.value = THREE.MathUtils.clamp(value, 0, 1),\n particleScale: value => particleUniforms.uParticleScale.value = THREE.MathUtils.clamp(value, 0.5, 3),\n particleSharpness: value => particleUniforms.uParticleSharpness.value = THREE.MathUtils.clamp(value, 0, 1),\n trails: value => afterimagePass.uniforms['damp'].value = value,\n chromaticAberration: value => { chromaticPass.uniforms.uIntensity.value = value; chromaticPass.enabled = value > 0; },\n filmGrain: value => { grainPass.uniforms.uIntensity.value = value; grainPass.enabled = value > 0; },\n nebulaIntensity: value => nebulaMaterial.uniforms.uIntensity.value = value,\n lensFlareEnabled: value => lensFlarePass.enabled = value && STATE.lensFlareIntensity > 0,\n lensFlareIntensity: value => { lensFlarePass.uniforms.uIntensity.value = value; lensFlarePass.enabled = STATE.lensFlareEnabled && value > 0; },\n dofEnabled: value => customDOFPass.enabled = value,\n dofFocus: value => customDOFPass.uniforms.uFocus.value = value,\n dofFocalLength: value => customDOFPass.uniforms.uFocalLength.value = value,\n dofBokehStrength: value => customDOFPass.uniforms.uBokehStrength.value = value,\n godRaysEnabled: value => godRaysPass.enabled = value && STATE.godRaysIntensity > 0,\n godRaysIntensity: value => { godRaysPass.uniforms.uIntensity.value = value; godRaysPass.enabled = STATE.godRaysEnabled && value > 0; },\n vortexStrength: value => material.uniforms.uVortexStrength.value = value,\n pulseIntensity: value => material.uniforms.uPulseIntensity.value = value,\n fieldSize: value => { material.uniforms.uFieldFill.value = value; secondaryMaterial.uniforms.uFieldFill.value = value; },\n };\n window.addEventListener('message', ({ source, data }) => {\n if (source !== parent || data?.type !== 'ad-qf-control') return;\n const { key, value } = data;\n if (key === 'field' && FIELD_TYPES[value]) {\n STATE.field = value;\n const selected = FIELD_TYPES[value];\n material.uniforms.uNoiseScale.value = selected.s;\n material.uniforms.uCurlStrength.value = selected.curl;\n material.uniforms.uSizeBase.value = selected.sz;\n } else if (key === 'visible') {\n renderVisible = !!value;\n if (renderVisible) startFrames();\n else stopFrames();\n }\n else if (key === 'interactive') controls.enabled = !!value;\n else if (key === 'spectrum') EXTERNAL.set(value?.bands, value?.range);\n else if (key === 'particleCount') {\n particleCount = Number.isFinite(value) ? Math.max(0, Math.floor(value)) : null;\n QUALITY_SYSTEM.applyQuality();\n }\n else if (key === 'particleBudget') {\n particleBudget = Number.isFinite(value) ? Math.max(0, Math.floor(value)) : Infinity;\n QUALITY_SYSTEM.applyQuality();\n }\n else if (key === 'timeScale') { requestedTimeScale = value; if (!pausedFromKit) STATE.timeScale = value; }\n else if (key === 'paused') { pausedFromKit = !!value; STATE.timeScale = pausedFromKit ? 0 : requestedTimeScale; }\n else if (key === 'play') togglePlayPause();\n else if (key === 'file' && value) loadTrack(value);\n else if (key === 'mic') {\n if (AUDIO.mode === 'mic') { AUDIO.stopCurrentSource(); AUDIO.mode = null; }\n else startMic(null);\n }\n else if (key === 'seek') { AUDIO.seek(value); updateUI(); }\n else if (key === 'screenshot') document.getElementById('screenshot-btn').click();\n else if (key === 'palette' && Array.isArray(value)) {\n const main = new THREE.Color(value[0]), light = new THREE.Color(value[1]);\n applyCssTheme(main);\n material.uniforms.uColor1.value.copy(main);\n material.uniforms.uColor2.value.copy(light);\n material.uniforms.uColor3.value.copy(light).lerp(new THREE.Color('#ffffff'), 0.32);\n secondaryMaterial.uniforms.uColor1.value.copy(main);\n secondaryMaterial.uniforms.uColor2.value.copy(light);\n nebulaMaterial.uniforms.uColor1.value.copy(main);\n nebulaMaterial.uniforms.uColor2.value.copy(light);\n nebulaMaterial.uniforms.uColor3.value.copy(main);\n connectionMaterial.uniforms.uColor.value.copy(light);\n connectionMaterial.uniforms.uColor2.value.copy(main);\n const white = new THREE.Color('#ffffff');\n const tint = glowColor(light.clone().lerp(white, 0.35), 0.75);\n lensFlarePass.uniforms.uTint.value.copy(tint);\n anamorphicPass.uniforms.uTint.value.copy(tint);\n // Reaction lines stand out from the field like the presets' contrasting colours did:\n // a luminous tint of the theme's light colour with a near-white core on bright passages.\n glowColor(crawlerLow.copy(light).lerp(white, 0.2), 0.75);\n glowColor(crawlerHigh.copy(light).lerp(white, 0.5), 0.95);\n crawlerMaterial.uniforms.uColor.value.copy(crawlerLow);\n trailSystem.trails.forEach((trail, i) => glowColor(trail.color.copy(main).lerp(light, i / Math.max(1, trailSystem.trails.length - 1)).lerp(white, 0.15), 0.6));\n } else if (Object.prototype.hasOwnProperty.call(STATE, key)) {\n STATE[key] = value;\n if (key === 'density') QUALITY_SYSTEM.applyQuality();\n sideEffects[key]?.(value);\n }\n });\n let lastTransport = '';\n setInterval(() => {\n if (!renderVisible || document.hidden) return;\n const value = {\n mode: AUDIO.mode, playing: AUDIO.isPlaying,\n current: AUDIO.mode === 'file' ? AUDIO.getCurrentTime() : 0,\n duration: AUDIO.mode === 'file' ? AUDIO.getDuration() : 0,\n track: AUDIO.mode ? AUDIO.currentTrackName : ''\n };\n const snapshot = JSON.stringify(value);\n if (snapshot !== lastTransport) {\n lastTransport = snapshot;\n parent.postMessage({ type: 'ad-qf-transport', value }, '*');\n }\n }, 250);\n parent.postMessage({ type: 'ad-qf-ready' }, '*');\n\n QUALITY_SYSTEM.applyQuality();\n startFrames();\n <\/script>\n</body>\n\n</html>\n";
5080
4802
 
5081
4803
  // src/components/artwork/QuantumField/QuantumFieldExperience.tsx
5082
- import { jsx as jsx46, jsxs as jsxs37 } from "react/jsx-runtime";
4804
+ import { jsx as jsx41, jsxs as jsxs33 } from "react/jsx-runtime";
5083
4805
  var quantumBackgroundThreeUrl = "https://unpkg.com/three@0.182.0/";
5084
4806
  var sceneDocument = (threeUrl) => {
5085
4807
  const base = threeUrl.endsWith("/") ? threeUrl : `${threeUrl}/`;
@@ -5092,7 +4814,10 @@ var quantumBackgroundControls = [
5092
4814
  { key: "sensitivity", label: "\u0427\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043B\u044C\u043D\u043E\u0441\u0442\u044C", value: 1.1, min: 0.1, max: 3, step: 0.01 },
5093
4815
  { key: "density", label: "\u041F\u043B\u043E\u0442\u043D\u043E\u0441\u0442\u044C", value: 0.043, min: 0.017, max: 0.14, step: 1e-3 },
5094
4816
  { key: "timeScale", label: "\u0421\u043A\u043E\u0440\u043E\u0441\u0442\u044C", value: 1, min: 0, max: 3, step: 0.01 },
5095
- { key: "fieldSize", label: "\u0420\u0430\u0437\u043C\u0435\u0440 \u043F\u043E\u043B\u044F", value: 0.78, min: 0.3, max: 1.2, step: 0.01 }
4817
+ { key: "fieldSize", label: "\u0420\u0430\u0437\u043C\u0435\u0440 \u043F\u043E\u043B\u044F", value: 0.78, min: 0.3, max: 1.2, step: 0.01 },
4818
+ { key: "particleDepth", label: "\u0413\u043B\u0443\u0431\u0438\u043D\u0430 \u0447\u0430\u0441\u0442\u0438\u0446", value: 0, min: 0, max: 1, step: 0.01 },
4819
+ { key: "particleScale", label: "\u0420\u0430\u0437\u043C\u0435\u0440 \u0447\u0430\u0441\u0442\u0438\u0446", value: 1, min: 0.5, max: 3, step: 0.01 },
4820
+ { key: "particleSharpness", label: "\u0427\u0451\u0442\u043A\u043E\u0441\u0442\u044C \u0447\u0430\u0441\u0442\u0438\u0446", value: 0, min: 0, max: 1, step: 0.01 }
5096
4821
  ] },
5097
4822
  { id: "environment", label: "\u0421\u0440\u0435\u0434\u0430", controls: [
5098
4823
  { key: "nebulaEnabled", label: "\u0422\u0443\u043C\u0430\u043D\u043D\u043E\u0441\u0442\u044C", value: false },
@@ -5149,7 +4874,7 @@ var quantumBackgroundDefaults = Object.freeze(Object.fromEntries(
5149
4874
  var settingKeys = Object.keys(quantumBackgroundDefaults);
5150
4875
  var quantumBackgroundFullscreenFieldSize = 1.1;
5151
4876
  var quantumBackgroundMaxParticles = 85e3;
5152
- var QuantumBackground = /* @__PURE__ */ forwardRef3(function QuantumBackground2(props, ref) {
4877
+ var QuantumBackground = /* @__PURE__ */ forwardRef(function QuantumBackground2(props, ref) {
5153
4878
  const {
5154
4879
  className = "",
5155
4880
  particleCount,
@@ -5172,13 +4897,13 @@ var QuantumBackground = /* @__PURE__ */ forwardRef3(function QuantumBackground2(
5172
4897
  }
5173
4898
  if (fullscreen) settings.fieldSize ??= quantumBackgroundFullscreenFieldSize;
5174
4899
  for (const key of settingKeys) settings[key] ??= quantumBackgroundDefaults[key];
5175
- const frame = useRef26(null);
5176
- const root = useRef26(null);
4900
+ const frame = useRef24(null);
4901
+ const root = useRef24(null);
5177
4902
  const theme = useThemePalette();
5178
- const [mounted, setMounted] = useState10(typeof IntersectionObserver === "undefined");
5179
- const [ready, setReady] = useState10(false);
5180
- const sent = useRef26({});
5181
- const callbacks = useRef26({ onTransportChange, onWaveformChange, onFullscreenChange });
4903
+ const [mounted, setMounted] = useState9(typeof IntersectionObserver === "undefined");
4904
+ const [ready, setReady] = useState9(false);
4905
+ const sent = useRef24({});
4906
+ const callbacks = useRef24({ onTransportChange, onWaveformChange, onFullscreenChange });
5182
4907
  callbacks.current = { onTransportChange, onWaveformChange, onFullscreenChange };
5183
4908
  const normalizedBudget = typeof particleBudget === "number" && Number.isFinite(particleBudget) ? Math.max(0, Math.floor(particleBudget)) : void 0;
5184
4909
  const normalizedCount = typeof particleCount === "number" && Number.isFinite(particleCount) ? Math.min(quantumBackgroundMaxParticles, Math.max(0, Math.floor(particleCount))) : void 0;
@@ -5231,7 +4956,7 @@ var QuantumBackground = /* @__PURE__ */ forwardRef3(function QuantumBackground2(
5231
4956
  useEffect9(() => {
5232
4957
  if (ready) send("spectrum", { bands: spectrum ? [...spectrum] : null, range: [low, high] });
5233
4958
  }, [ready, spectrum, low, high]);
5234
- useLayoutEffect5(() => {
4959
+ useLayoutEffect4(() => {
5235
4960
  const node = root.current;
5236
4961
  if (!fullscreen || !node?.showPopover) return;
5237
4962
  node.showPopover();
@@ -5259,7 +4984,7 @@ var QuantumBackground = /* @__PURE__ */ forwardRef3(function QuantumBackground2(
5259
4984
  if (!ready) return;
5260
4985
  send("palette", [theme.primary, theme.secondary]);
5261
4986
  }, [ready, theme.primary, theme.secondary]);
5262
- return /* @__PURE__ */ jsxs37(
4987
+ return /* @__PURE__ */ jsxs33(
5263
4988
  "div",
5264
4989
  {
5265
4990
  "data-ad-component": "QuantumBackground",
@@ -5269,7 +4994,7 @@ var QuantumBackground = /* @__PURE__ */ forwardRef3(function QuantumBackground2(
5269
4994
  popover: fullscreen ? "manual" : void 0,
5270
4995
  ...attributes,
5271
4996
  children: [
5272
- mounted && /* @__PURE__ */ jsx46(
4997
+ mounted && /* @__PURE__ */ jsx41(
5273
4998
  "iframe",
5274
4999
  {
5275
5000
  ref: frame,
@@ -5280,7 +5005,7 @@ var QuantumBackground = /* @__PURE__ */ forwardRef3(function QuantumBackground2(
5280
5005
  "data-particle-count": normalizedCount
5281
5006
  }
5282
5007
  ),
5283
- children != null && /* @__PURE__ */ jsx46("div", { className: "ad-qb-content", children })
5008
+ children != null && /* @__PURE__ */ jsx41("div", { className: "ad-qb-content", children })
5284
5009
  ]
5285
5010
  }
5286
5011
  );
@@ -5371,8 +5096,8 @@ function createQuantumFieldAudio() {
5371
5096
  }
5372
5097
 
5373
5098
  // src/components/artwork/Spectrum/Spectrum.tsx
5374
- import { useRef as useRef27 } from "react";
5375
- import { jsx as jsx47, jsxs as jsxs38 } from "react/jsx-runtime";
5099
+ import { useRef as useRef25 } from "react";
5100
+ import { jsx as jsx42, jsxs as jsxs34 } from "react/jsx-runtime";
5376
5101
  var COLUMNS = 27;
5377
5102
  var ROWS = 29;
5378
5103
  var BARS = 23;
@@ -5384,11 +5109,11 @@ var barHeight = (i, time) => {
5384
5109
  return 9 + 100 * envelope * (0.53 + 0.47 * rhythm) + 15 * Math.sin(i * 0.67 + time * 0.58) ** 2;
5385
5110
  };
5386
5111
  function Spectrum({ variant = "segmented", ...p }) {
5387
- return variant === "segmented" ? /* @__PURE__ */ jsx47(Segmented, { ...p }) : /* @__PURE__ */ jsx47(Bars, { ...p });
5112
+ return variant === "segmented" ? /* @__PURE__ */ jsx42(Segmented, { ...p }) : /* @__PURE__ */ jsx42(Bars, { ...p });
5388
5113
  }
5389
5114
  function Segmented(p) {
5390
- const ref = useRef27(null);
5391
- const columns2 = useRef27(null);
5115
+ const ref = useRef25(null);
5116
+ const columns2 = useRef25(null);
5392
5117
  usePauseOffscreen(ref);
5393
5118
  useDecoration(ref, (time) => {
5394
5119
  if (!ref.current) return;
@@ -5399,19 +5124,19 @@ function Segmented(p) {
5399
5124
  column.firstElementChild?.style.setProperty("transform", `translateY(${(-hidden).toFixed(2)}%)`);
5400
5125
  });
5401
5126
  });
5402
- return /* @__PURE__ */ jsx47("div", { ...mark("Spectrum", p), ref, "data-variant": "segmented", "aria-hidden": "true", children: Array.from({ length: COLUMNS }, (_, i) => /* @__PURE__ */ jsx47(
5127
+ return /* @__PURE__ */ jsx42("div", { ...mark("Spectrum", p), ref, "data-variant": "segmented", "aria-hidden": "true", children: Array.from({ length: COLUMNS }, (_, i) => /* @__PURE__ */ jsx42(
5403
5128
  "span",
5404
5129
  {
5405
5130
  className: "ad-spectrum-column",
5406
5131
  style: { left: `${(i * 12 + 3) / 325 * 100}%`, width: `${8 / 325 * 100}%` },
5407
- children: /* @__PURE__ */ jsx47("svg", { viewBox: "0 0 8 232", preserveAspectRatio: "none", children: Array.from({ length: ROWS }, (__, row) => /* @__PURE__ */ jsx47("rect", { y: 224 - row * 7, width: "8", height: "5", rx: ".35", opacity: 0.17 + row / 35 }, row)) })
5132
+ children: /* @__PURE__ */ jsx42("svg", { viewBox: "0 0 8 232", preserveAspectRatio: "none", children: Array.from({ length: ROWS }, (__, row) => /* @__PURE__ */ jsx42("rect", { y: 224 - row * 7, width: "8", height: "5", rx: ".35", opacity: 0.17 + row / 35 }, row)) })
5408
5133
  },
5409
5134
  i
5410
5135
  )) });
5411
5136
  }
5412
5137
  function Bars(p) {
5413
- const ref = useRef27(null);
5414
- const layers = useRef27(null);
5138
+ const ref = useRef25(null);
5139
+ const layers = useRef25(null);
5415
5140
  const id = useSvgId();
5416
5141
  usePauseOffscreen(ref);
5417
5142
  useDecoration(ref, (time) => {
@@ -5429,7 +5154,7 @@ function Bars(p) {
5429
5154
  });
5430
5155
  });
5431
5156
  });
5432
- return /* @__PURE__ */ jsxs38(
5157
+ return /* @__PURE__ */ jsxs34(
5433
5158
  "svg",
5434
5159
  {
5435
5160
  ...mark("Spectrum", p),
@@ -5439,23 +5164,23 @@ function Bars(p) {
5439
5164
  preserveAspectRatio: "none",
5440
5165
  "aria-hidden": "true",
5441
5166
  children: [
5442
- /* @__PURE__ */ jsxs38("defs", { children: [
5443
- /* @__PURE__ */ jsxs38("linearGradient", { id: `${id}-bar`, x1: "0", x2: "0", y1: "0", y2: "1", children: [
5444
- /* @__PURE__ */ jsx47("stop", { stopColor: "var(--ad-secondary-200)" }),
5445
- /* @__PURE__ */ jsx47("stop", { offset: ".24", stopColor: "var(--ad-red)" }),
5446
- /* @__PURE__ */ jsx47("stop", { offset: "1", stopColor: "var(--ad-primary-600)", stopOpacity: "0" })
5167
+ /* @__PURE__ */ jsxs34("defs", { children: [
5168
+ /* @__PURE__ */ jsxs34("linearGradient", { id: `${id}-bar`, x1: "0", x2: "0", y1: "0", y2: "1", children: [
5169
+ /* @__PURE__ */ jsx42("stop", { stopColor: "var(--ad-secondary-200)" }),
5170
+ /* @__PURE__ */ jsx42("stop", { offset: ".24", stopColor: "var(--ad-red)" }),
5171
+ /* @__PURE__ */ jsx42("stop", { offset: "1", stopColor: "var(--ad-primary-600)", stopOpacity: "0" })
5447
5172
  ] }),
5448
- /* @__PURE__ */ jsxs38("linearGradient", { id: `${id}-halo`, x1: "0", x2: "0", y1: "0", y2: "1", children: [
5449
- /* @__PURE__ */ jsx47("stop", { stopColor: "var(--ad-red)", stopOpacity: ".6" }),
5450
- /* @__PURE__ */ jsx47("stop", { offset: ".24", stopColor: "var(--ad-red)", stopOpacity: ".6" }),
5451
- /* @__PURE__ */ jsx47("stop", { offset: "1", stopColor: "var(--ad-red)", stopOpacity: "0" })
5173
+ /* @__PURE__ */ jsxs34("linearGradient", { id: `${id}-halo`, x1: "0", x2: "0", y1: "0", y2: "1", children: [
5174
+ /* @__PURE__ */ jsx42("stop", { stopColor: "var(--ad-red)", stopOpacity: ".6" }),
5175
+ /* @__PURE__ */ jsx42("stop", { offset: ".24", stopColor: "var(--ad-red)", stopOpacity: ".6" }),
5176
+ /* @__PURE__ */ jsx42("stop", { offset: "1", stopColor: "var(--ad-red)", stopOpacity: "0" })
5452
5177
  ] })
5453
5178
  ] }),
5454
5179
  Array.from({ length: BARS }, (_, i) => {
5455
5180
  const x = 4 + i * 6.75;
5456
5181
  const opacity = 0.55 + i / 55;
5457
- return /* @__PURE__ */ jsxs38("g", { "data-bar": true, children: [
5458
- HALO.map(([grow, halo]) => /* @__PURE__ */ jsx47(
5182
+ return /* @__PURE__ */ jsxs34("g", { "data-bar": true, children: [
5183
+ HALO.map(([grow, halo]) => /* @__PURE__ */ jsx42(
5459
5184
  "rect",
5460
5185
  {
5461
5186
  "data-grow": grow,
@@ -5469,7 +5194,7 @@ function Bars(p) {
5469
5194
  },
5470
5195
  grow
5471
5196
  )),
5472
- /* @__PURE__ */ jsx47("rect", { x, y: "40", width: "2.8", height: "100", rx: "1.3", fill: `url(#${id}-bar)`, opacity })
5197
+ /* @__PURE__ */ jsx42("rect", { x, y: "40", width: "2.8", height: "100", rx: "1.3", fill: `url(#${id}-bar)`, opacity })
5473
5198
  ] }, i);
5474
5199
  })
5475
5200
  ]
@@ -5478,7 +5203,7 @@ function Bars(p) {
5478
5203
  }
5479
5204
 
5480
5205
  // src/components/artwork/layers.tsx
5481
- import { jsx as jsx48 } from "react/jsx-runtime";
5206
+ import { jsx as jsx43 } from "react/jsx-runtime";
5482
5207
  function ArtLayer({
5483
5208
  viewBox,
5484
5209
  className,
@@ -5487,7 +5212,7 @@ function ArtLayer({
5487
5212
  children
5488
5213
  }) {
5489
5214
  const [, , width, height] = viewBox.split(" ").map(Number);
5490
- return /* @__PURE__ */ jsx48(
5215
+ return /* @__PURE__ */ jsx43(
5491
5216
  "svg",
5492
5217
  {
5493
5218
  className: className ? `ad-art-layer ${className}` : "ad-art-layer",
@@ -5501,7 +5226,7 @@ function ArtLayer({
5501
5226
  }
5502
5227
 
5503
5228
  // src/components/artwork/DatabaseArt/DatabaseArt.tsx
5504
- import { jsx as jsx49, jsxs as jsxs39 } from "react/jsx-runtime";
5229
+ import { jsx as jsx44, jsxs as jsxs35 } from "react/jsx-runtime";
5505
5230
  var SPARKS = [
5506
5231
  [42, 84, 1, -1],
5507
5232
  [44, 111, 1, -2.7],
@@ -5515,7 +5240,7 @@ function DatabaseArt({ label, ...p }) {
5515
5240
  const ref = (name) => `url(#${id}-${name})`;
5516
5241
  const rings = "M39 83C39 106 134 106 134 83M39 108C39 132 134 132 134 108M39 133C39 155 134 155 134 133";
5517
5242
  const box = "0 0 170 179";
5518
- return /* @__PURE__ */ jsxs39(
5243
+ return /* @__PURE__ */ jsxs35(
5519
5244
  "div",
5520
5245
  {
5521
5246
  ...mark("DatabaseArt", p),
@@ -5523,42 +5248,42 @@ function DatabaseArt({ label, ...p }) {
5523
5248
  "aria-label": label,
5524
5249
  "aria-hidden": !label,
5525
5250
  children: [
5526
- /* @__PURE__ */ jsx49(ArtLayer, { viewBox: box, children: /* @__PURE__ */ jsxs39("defs", { children: [
5527
- /* @__PURE__ */ jsx49("filter", { id: `${id}-bloom`, x: "-50%", y: "-50%", width: "200%", height: "200%", children: /* @__PURE__ */ jsx49("feGaussianBlur", { stdDeviation: "3" }) }),
5528
- /* @__PURE__ */ jsxs39("linearGradient", { id: `${id}-body`, children: [
5529
- /* @__PURE__ */ jsx49("stop", { stopColor: "var(--ad-primary-600)" }),
5530
- /* @__PURE__ */ jsx49("stop", { offset: ".12", stopColor: "var(--ad-primary-800)" }),
5531
- /* @__PURE__ */ jsx49("stop", { offset: ".3", stopColor: "var(--ad-primary-900)" }),
5532
- /* @__PURE__ */ jsx49("stop", { offset: ".72", stopColor: "var(--ad-neutral-950)" }),
5533
- /* @__PURE__ */ jsx49("stop", { offset: "1", stopColor: "var(--ad-primary-800)" })
5251
+ /* @__PURE__ */ jsx44(ArtLayer, { viewBox: box, children: /* @__PURE__ */ jsxs35("defs", { children: [
5252
+ /* @__PURE__ */ jsx44("filter", { id: `${id}-bloom`, x: "-50%", y: "-50%", width: "200%", height: "200%", children: /* @__PURE__ */ jsx44("feGaussianBlur", { stdDeviation: "3" }) }),
5253
+ /* @__PURE__ */ jsxs35("linearGradient", { id: `${id}-body`, children: [
5254
+ /* @__PURE__ */ jsx44("stop", { stopColor: "var(--ad-primary-600)" }),
5255
+ /* @__PURE__ */ jsx44("stop", { offset: ".12", stopColor: "var(--ad-primary-800)" }),
5256
+ /* @__PURE__ */ jsx44("stop", { offset: ".3", stopColor: "var(--ad-primary-900)" }),
5257
+ /* @__PURE__ */ jsx44("stop", { offset: ".72", stopColor: "var(--ad-neutral-950)" }),
5258
+ /* @__PURE__ */ jsx44("stop", { offset: "1", stopColor: "var(--ad-primary-800)" })
5534
5259
  ] }),
5535
- /* @__PURE__ */ jsxs39("radialGradient", { id: `${id}-top`, cx: ".3", cy: ".27", r: ".9", children: [
5536
- /* @__PURE__ */ jsx49("stop", { stopColor: "var(--ad-primary-600)" }),
5537
- /* @__PURE__ */ jsx49("stop", { offset: ".23", stopColor: "var(--ad-primary-800)" }),
5538
- /* @__PURE__ */ jsx49("stop", { offset: ".66", stopColor: "var(--ad-neutral-900)" }),
5539
- /* @__PURE__ */ jsx49("stop", { offset: "1", stopColor: "var(--ad-primary-900)" })
5260
+ /* @__PURE__ */ jsxs35("radialGradient", { id: `${id}-top`, cx: ".3", cy: ".27", r: ".9", children: [
5261
+ /* @__PURE__ */ jsx44("stop", { stopColor: "var(--ad-primary-600)" }),
5262
+ /* @__PURE__ */ jsx44("stop", { offset: ".23", stopColor: "var(--ad-primary-800)" }),
5263
+ /* @__PURE__ */ jsx44("stop", { offset: ".66", stopColor: "var(--ad-neutral-900)" }),
5264
+ /* @__PURE__ */ jsx44("stop", { offset: "1", stopColor: "var(--ad-primary-900)" })
5540
5265
  ] }),
5541
- /* @__PURE__ */ jsxs39("linearGradient", { id: `${id}-edge`, children: [
5542
- /* @__PURE__ */ jsx49("stop", { stopColor: "var(--ad-neutral-200)" }),
5543
- /* @__PURE__ */ jsx49("stop", { offset: ".16", stopColor: "var(--ad-secondary)" }),
5544
- /* @__PURE__ */ jsx49("stop", { offset: ".47", stopColor: "var(--ad-primary-700)" }),
5545
- /* @__PURE__ */ jsx49("stop", { offset: ".8", stopColor: "var(--ad-primary)" }),
5546
- /* @__PURE__ */ jsx49("stop", { offset: "1", stopColor: "var(--ad-secondary-200)" })
5266
+ /* @__PURE__ */ jsxs35("linearGradient", { id: `${id}-edge`, children: [
5267
+ /* @__PURE__ */ jsx44("stop", { stopColor: "var(--ad-neutral-200)" }),
5268
+ /* @__PURE__ */ jsx44("stop", { offset: ".16", stopColor: "var(--ad-secondary)" }),
5269
+ /* @__PURE__ */ jsx44("stop", { offset: ".47", stopColor: "var(--ad-primary-700)" }),
5270
+ /* @__PURE__ */ jsx44("stop", { offset: ".8", stopColor: "var(--ad-primary)" }),
5271
+ /* @__PURE__ */ jsx44("stop", { offset: "1", stopColor: "var(--ad-secondary-200)" })
5547
5272
  ] }),
5548
- /* @__PURE__ */ jsxs39("radialGradient", { id: `${id}-aura`, children: [
5549
- /* @__PURE__ */ jsx49("stop", { stopColor: "var(--ad-primary)", stopOpacity: ".3" }),
5550
- /* @__PURE__ */ jsx49("stop", { offset: ".5", stopColor: "var(--ad-primary)", stopOpacity: ".12" }),
5551
- /* @__PURE__ */ jsx49("stop", { offset: "1", stopColor: "var(--ad-primary)", stopOpacity: "0" })
5273
+ /* @__PURE__ */ jsxs35("radialGradient", { id: `${id}-aura`, children: [
5274
+ /* @__PURE__ */ jsx44("stop", { stopColor: "var(--ad-primary)", stopOpacity: ".3" }),
5275
+ /* @__PURE__ */ jsx44("stop", { offset: ".5", stopColor: "var(--ad-primary)", stopOpacity: ".12" }),
5276
+ /* @__PURE__ */ jsx44("stop", { offset: "1", stopColor: "var(--ad-primary)", stopOpacity: "0" })
5552
5277
  ] }),
5553
- /* @__PURE__ */ jsxs39("g", { id: `${id}-spark`, children: [
5554
- /* @__PURE__ */ jsx49("path", { d: "M-7 0H7M0-8V8", stroke: "var(--ad-secondary-200)", strokeWidth: ".7" }),
5555
- /* @__PURE__ */ jsx49("circle", { r: "3.4", fill: "var(--ad-primary)", filter: ref("bloom") }),
5556
- /* @__PURE__ */ jsx49("circle", { r: "1.4", fill: "var(--ad-neutral-200)" })
5278
+ /* @__PURE__ */ jsxs35("g", { id: `${id}-spark`, children: [
5279
+ /* @__PURE__ */ jsx44("path", { d: "M-7 0H7M0-8V8", stroke: "var(--ad-secondary-200)", strokeWidth: ".7" }),
5280
+ /* @__PURE__ */ jsx44("circle", { r: "3.4", fill: "var(--ad-primary)", filter: ref("bloom") }),
5281
+ /* @__PURE__ */ jsx44("circle", { r: "1.4", fill: "var(--ad-neutral-200)" })
5557
5282
  ] })
5558
5283
  ] }) }),
5559
- /* @__PURE__ */ jsx49(ArtLayer, { viewBox: box, className: "ad-art-aura-layer", origin: [85, 120], children: /* @__PURE__ */ jsx49("ellipse", { cx: "85", cy: "120", rx: "83", ry: "71", fill: ref("aura") }) }),
5560
- /* @__PURE__ */ jsx49(ArtLayer, { viewBox: box, children: /* @__PURE__ */ jsxs39("g", { transform: "translate(0 2)", children: [
5561
- /* @__PURE__ */ jsx49(
5284
+ /* @__PURE__ */ jsx44(ArtLayer, { viewBox: box, className: "ad-art-aura-layer", origin: [85, 120], children: /* @__PURE__ */ jsx44("ellipse", { cx: "85", cy: "120", rx: "83", ry: "71", fill: ref("aura") }) }),
5285
+ /* @__PURE__ */ jsx44(ArtLayer, { viewBox: box, children: /* @__PURE__ */ jsxs35("g", { transform: "translate(0 2)", children: [
5286
+ /* @__PURE__ */ jsx44(
5562
5287
  "path",
5563
5288
  {
5564
5289
  d: "M39 57V133C39 156 134 156 134 133V57Z",
@@ -5567,7 +5292,7 @@ function DatabaseArt({ label, ...p }) {
5567
5292
  strokeWidth: ".75"
5568
5293
  }
5569
5294
  ),
5570
- /* @__PURE__ */ jsx49(
5295
+ /* @__PURE__ */ jsx44(
5571
5296
  "path",
5572
5297
  {
5573
5298
  d: rings,
@@ -5577,8 +5302,8 @@ function DatabaseArt({ label, ...p }) {
5577
5302
  filter: ref("bloom")
5578
5303
  }
5579
5304
  ),
5580
- /* @__PURE__ */ jsx49("path", { d: rings, stroke: ref("edge"), strokeWidth: "1.4" }),
5581
- /* @__PURE__ */ jsx49(
5305
+ /* @__PURE__ */ jsx44("path", { d: rings, stroke: ref("edge"), strokeWidth: "1.4" }),
5306
+ /* @__PURE__ */ jsx44(
5582
5307
  "ellipse",
5583
5308
  {
5584
5309
  cx: "86.5",
@@ -5590,7 +5315,7 @@ function DatabaseArt({ label, ...p }) {
5590
5315
  strokeWidth: "1.1"
5591
5316
  }
5592
5317
  ),
5593
- /* @__PURE__ */ jsx49(
5318
+ /* @__PURE__ */ jsx44(
5594
5319
  "ellipse",
5595
5320
  {
5596
5321
  cx: "86.5",
@@ -5603,7 +5328,7 @@ function DatabaseArt({ label, ...p }) {
5603
5328
  filter: ref("bloom")
5604
5329
  }
5605
5330
  ),
5606
- /* @__PURE__ */ jsx49(
5331
+ /* @__PURE__ */ jsx44(
5607
5332
  "ellipse",
5608
5333
  {
5609
5334
  cx: "86.5",
@@ -5615,7 +5340,7 @@ function DatabaseArt({ label, ...p }) {
5615
5340
  opacity: ".8"
5616
5341
  }
5617
5342
  ),
5618
- [81, 106, 132].map((cy) => /* @__PURE__ */ jsx49(
5343
+ [81, 106, 132].map((cy) => /* @__PURE__ */ jsx44(
5619
5344
  "ellipse",
5620
5345
  {
5621
5346
  cx: "86.5",
@@ -5629,7 +5354,7 @@ function DatabaseArt({ label, ...p }) {
5629
5354
  cy
5630
5355
  ))
5631
5356
  ] }) }),
5632
- /* @__PURE__ */ jsx49(ArtLayer, { viewBox: box, className: "ad-art-orbits", children: /* @__PURE__ */ jsx49("g", { transform: "translate(0 2)", stroke: "var(--ad-neutral-200)", strokeWidth: "1.45", children: [57, 106, 132].map((cy, i) => /* @__PURE__ */ jsx49(
5357
+ /* @__PURE__ */ jsx44(ArtLayer, { viewBox: box, className: "ad-art-orbits", children: /* @__PURE__ */ jsx44("g", { transform: "translate(0 2)", stroke: "var(--ad-neutral-200)", strokeWidth: "1.45", children: [57, 106, 132].map((cy, i) => /* @__PURE__ */ jsx44(
5633
5358
  "ellipse",
5634
5359
  {
5635
5360
  className: "ad-art-orbit",
@@ -5642,8 +5367,8 @@ function DatabaseArt({ label, ...p }) {
5642
5367
  },
5643
5368
  cy
5644
5369
  )) }) }),
5645
- /* @__PURE__ */ jsx49(ArtLayer, { viewBox: box, children: /* @__PURE__ */ jsxs39("g", { transform: "translate(0 2)", children: [
5646
- /* @__PURE__ */ jsx49(
5370
+ /* @__PURE__ */ jsx44(ArtLayer, { viewBox: box, children: /* @__PURE__ */ jsxs35("g", { transform: "translate(0 2)", children: [
5371
+ /* @__PURE__ */ jsx44(
5647
5372
  "ellipse",
5648
5373
  {
5649
5374
  cx: "86.5",
@@ -5654,16 +5379,16 @@ function DatabaseArt({ label, ...p }) {
5654
5379
  filter: ref("bloom")
5655
5380
  }
5656
5381
  ),
5657
- /* @__PURE__ */ jsx49("ellipse", { cx: "86.5", cy: "53.5", rx: "3.4", ry: ".8", fill: "var(--ad-secondary-200)" })
5382
+ /* @__PURE__ */ jsx44("ellipse", { cx: "86.5", cy: "53.5", rx: "3.4", ry: ".8", fill: "var(--ad-secondary-200)" })
5658
5383
  ] }) }),
5659
- SPARKS.map(([x, y, scale, delay]) => /* @__PURE__ */ jsx49(
5384
+ SPARKS.map(([x, y, scale, delay]) => /* @__PURE__ */ jsx44(
5660
5385
  ArtLayer,
5661
5386
  {
5662
5387
  viewBox: box,
5663
5388
  className: "ad-art-spark-layer",
5664
5389
  origin: [x, y],
5665
5390
  style: { "--ad-delay": `${delay}s` },
5666
- children: /* @__PURE__ */ jsx49("use", { href: `#${id}-spark`, transform: `translate(${x} ${y}) scale(${scale})` })
5391
+ children: /* @__PURE__ */ jsx44("use", { href: `#${id}-spark`, transform: `translate(${x} ${y}) scale(${scale})` })
5667
5392
  },
5668
5393
  `${x}-${y}`
5669
5394
  ))
@@ -5673,7 +5398,7 @@ function DatabaseArt({ label, ...p }) {
5673
5398
  }
5674
5399
 
5675
5400
  // src/components/artwork/ServerArt/ServerArt.tsx
5676
- import { jsx as jsx50, jsxs as jsxs40 } from "react/jsx-runtime";
5401
+ import { jsx as jsx45, jsxs as jsxs36 } from "react/jsx-runtime";
5677
5402
  var rackRows = Array.from({ length: 15 }, (_, row) => row);
5678
5403
  var rackLines = rackRows.map((row) => `M33 ${50 + row * 3.35} 90 ${38.8 + row * 3.35}`).join(" ");
5679
5404
  var rackHatches = rackRows.flatMap((row) => Array.from({ length: 9 }, (_, col) => `M${34 + col * 6.15} ${49.8 + row * 3.35 - col * 1.205}l2.6-.51`)).join(" ");
@@ -5683,7 +5408,7 @@ function ServerArt({ upload = false, label, ...p }) {
5683
5408
  const box = "0 0 180 170";
5684
5409
  const lift = upload ? "translate(0 15)" : void 0;
5685
5410
  const dy = upload ? 15 : 0;
5686
- return /* @__PURE__ */ jsxs40(
5411
+ return /* @__PURE__ */ jsxs36(
5687
5412
  "div",
5688
5413
  {
5689
5414
  ...mark("ServerArt", p),
@@ -5691,40 +5416,40 @@ function ServerArt({ upload = false, label, ...p }) {
5691
5416
  "aria-label": label,
5692
5417
  "aria-hidden": !label,
5693
5418
  children: [
5694
- /* @__PURE__ */ jsx50(ArtLayer, { viewBox: box, children: /* @__PURE__ */ jsxs40("defs", { children: [
5695
- /* @__PURE__ */ jsxs40("linearGradient", { id: `${id}-front`, x1: "0", y1: "0", x2: "1", y2: "1", children: [
5696
- /* @__PURE__ */ jsx50("stop", { stopColor: "var(--ad-primary-700)" }),
5697
- /* @__PURE__ */ jsx50("stop", { offset: ".22", stopColor: "var(--ad-primary-900)" }),
5698
- /* @__PURE__ */ jsx50("stop", { offset: ".72", stopColor: "var(--ad-neutral-900)" }),
5699
- /* @__PURE__ */ jsx50("stop", { offset: "1", stopColor: "var(--ad-primary-800)" })
5419
+ /* @__PURE__ */ jsx45(ArtLayer, { viewBox: box, children: /* @__PURE__ */ jsxs36("defs", { children: [
5420
+ /* @__PURE__ */ jsxs36("linearGradient", { id: `${id}-front`, x1: "0", y1: "0", x2: "1", y2: "1", children: [
5421
+ /* @__PURE__ */ jsx45("stop", { stopColor: "var(--ad-primary-700)" }),
5422
+ /* @__PURE__ */ jsx45("stop", { offset: ".22", stopColor: "var(--ad-primary-900)" }),
5423
+ /* @__PURE__ */ jsx45("stop", { offset: ".72", stopColor: "var(--ad-neutral-900)" }),
5424
+ /* @__PURE__ */ jsx45("stop", { offset: "1", stopColor: "var(--ad-primary-800)" })
5700
5425
  ] }),
5701
- /* @__PURE__ */ jsxs40("linearGradient", { id: `${id}-side`, x1: "0", y1: "0", x2: ".9", y2: "1", children: [
5702
- /* @__PURE__ */ jsx50("stop", { stopColor: "var(--ad-primary-800)" }),
5703
- /* @__PURE__ */ jsx50("stop", { offset: ".27", stopColor: "var(--ad-primary-900)" }),
5704
- /* @__PURE__ */ jsx50("stop", { offset: "1", stopColor: "var(--ad-neutral-950)" })
5426
+ /* @__PURE__ */ jsxs36("linearGradient", { id: `${id}-side`, x1: "0", y1: "0", x2: ".9", y2: "1", children: [
5427
+ /* @__PURE__ */ jsx45("stop", { stopColor: "var(--ad-primary-800)" }),
5428
+ /* @__PURE__ */ jsx45("stop", { offset: ".27", stopColor: "var(--ad-primary-900)" }),
5429
+ /* @__PURE__ */ jsx45("stop", { offset: "1", stopColor: "var(--ad-neutral-950)" })
5705
5430
  ] }),
5706
- /* @__PURE__ */ jsxs40("linearGradient", { id: `${id}-top`, x1: "0", y1: "0", x2: ".7", y2: "1", children: [
5707
- /* @__PURE__ */ jsx50("stop", { stopColor: "var(--ad-secondary-200)" }),
5708
- /* @__PURE__ */ jsx50("stop", { offset: ".23", stopColor: "var(--ad-primary-600)" }),
5709
- /* @__PURE__ */ jsx50("stop", { offset: ".57", stopColor: "var(--ad-primary-800)" }),
5710
- /* @__PURE__ */ jsx50("stop", { offset: "1", stopColor: "var(--ad-neutral-900)" })
5431
+ /* @__PURE__ */ jsxs36("linearGradient", { id: `${id}-top`, x1: "0", y1: "0", x2: ".7", y2: "1", children: [
5432
+ /* @__PURE__ */ jsx45("stop", { stopColor: "var(--ad-secondary-200)" }),
5433
+ /* @__PURE__ */ jsx45("stop", { offset: ".23", stopColor: "var(--ad-primary-600)" }),
5434
+ /* @__PURE__ */ jsx45("stop", { offset: ".57", stopColor: "var(--ad-primary-800)" }),
5435
+ /* @__PURE__ */ jsx45("stop", { offset: "1", stopColor: "var(--ad-neutral-900)" })
5711
5436
  ] }),
5712
- /* @__PURE__ */ jsxs40("linearGradient", { id: `${id}-edge`, x1: "0", y1: "0", x2: "1", y2: "1", children: [
5713
- /* @__PURE__ */ jsx50("stop", { stopColor: "var(--ad-secondary-200)" }),
5714
- /* @__PURE__ */ jsx50("stop", { offset: ".29", stopColor: "var(--ad-primary)" }),
5715
- /* @__PURE__ */ jsx50("stop", { offset: ".52", stopColor: "var(--ad-primary-700)" }),
5716
- /* @__PURE__ */ jsx50("stop", { offset: ".8", stopColor: "var(--ad-primary)" }),
5717
- /* @__PURE__ */ jsx50("stop", { offset: "1", stopColor: "var(--ad-primary-700)" })
5437
+ /* @__PURE__ */ jsxs36("linearGradient", { id: `${id}-edge`, x1: "0", y1: "0", x2: "1", y2: "1", children: [
5438
+ /* @__PURE__ */ jsx45("stop", { stopColor: "var(--ad-secondary-200)" }),
5439
+ /* @__PURE__ */ jsx45("stop", { offset: ".29", stopColor: "var(--ad-primary)" }),
5440
+ /* @__PURE__ */ jsx45("stop", { offset: ".52", stopColor: "var(--ad-primary-700)" }),
5441
+ /* @__PURE__ */ jsx45("stop", { offset: ".8", stopColor: "var(--ad-primary)" }),
5442
+ /* @__PURE__ */ jsx45("stop", { offset: "1", stopColor: "var(--ad-primary-700)" })
5718
5443
  ] }),
5719
- /* @__PURE__ */ jsxs40("radialGradient", { id: `${id}-aura`, children: [
5720
- /* @__PURE__ */ jsx50("stop", { stopColor: "var(--ad-primary)", stopOpacity: ".28" }),
5721
- /* @__PURE__ */ jsx50("stop", { offset: ".6", stopColor: "var(--ad-primary)", stopOpacity: ".06" }),
5722
- /* @__PURE__ */ jsx50("stop", { offset: "1", stopColor: "var(--ad-primary)", stopOpacity: "0" })
5444
+ /* @__PURE__ */ jsxs36("radialGradient", { id: `${id}-aura`, children: [
5445
+ /* @__PURE__ */ jsx45("stop", { stopColor: "var(--ad-primary)", stopOpacity: ".28" }),
5446
+ /* @__PURE__ */ jsx45("stop", { offset: ".6", stopColor: "var(--ad-primary)", stopOpacity: ".06" }),
5447
+ /* @__PURE__ */ jsx45("stop", { offset: "1", stopColor: "var(--ad-primary)", stopOpacity: "0" })
5723
5448
  ] }),
5724
- /* @__PURE__ */ jsx50("filter", { id: `${id}-bloom`, x: "-35%", y: "-35%", width: "170%", height: "170%", children: /* @__PURE__ */ jsx50("feGaussianBlur", { stdDeviation: "2.4" }) })
5449
+ /* @__PURE__ */ jsx45("filter", { id: `${id}-bloom`, x: "-35%", y: "-35%", width: "170%", height: "170%", children: /* @__PURE__ */ jsx45("feGaussianBlur", { stdDeviation: "2.4" }) })
5725
5450
  ] }) }),
5726
- upload && /* @__PURE__ */ jsxs40(ArtLayer, { viewBox: box, className: "ad-server-cloud", children: [
5727
- /* @__PURE__ */ jsx50(
5451
+ upload && /* @__PURE__ */ jsxs36(ArtLayer, { viewBox: box, className: "ad-server-cloud", children: [
5452
+ /* @__PURE__ */ jsx45(
5728
5453
  "path",
5729
5454
  {
5730
5455
  d: "M57 40C37 42 40 16 58 20 63-5 96-4 102 18 120 13 132 32 117 42Z",
@@ -5734,11 +5459,11 @@ function ServerArt({ upload = false, label, ...p }) {
5734
5459
  strokeWidth: ".7"
5735
5460
  }
5736
5461
  ),
5737
- /* @__PURE__ */ jsx50("path", { d: "M80 37V18m-7 7 7-7 7 7", stroke: "var(--ad-secondary)", strokeWidth: "1.5" })
5462
+ /* @__PURE__ */ jsx45("path", { d: "M80 37V18m-7 7 7-7 7 7", stroke: "var(--ad-secondary)", strokeWidth: "1.5" })
5738
5463
  ] }),
5739
- /* @__PURE__ */ jsx50(ArtLayer, { viewBox: box, className: "ad-art-aura-layer", origin: [89, 124 + dy], children: /* @__PURE__ */ jsx50("ellipse", { cx: "89", cy: 124 + dy, rx: "76", ry: "22", fill: url("aura") }) }),
5740
- /* @__PURE__ */ jsx50(ArtLayer, { viewBox: box, children: /* @__PURE__ */ jsxs40("g", { transform: lift, children: [
5741
- /* @__PURE__ */ jsx50(
5464
+ /* @__PURE__ */ jsx45(ArtLayer, { viewBox: box, className: "ad-art-aura-layer", origin: [89, 124 + dy], children: /* @__PURE__ */ jsx45("ellipse", { cx: "89", cy: 124 + dy, rx: "76", ry: "22", fill: url("aura") }) }),
5465
+ /* @__PURE__ */ jsx45(ArtLayer, { viewBox: box, children: /* @__PURE__ */ jsxs36("g", { transform: lift, children: [
5466
+ /* @__PURE__ */ jsx45(
5742
5467
  "path",
5743
5468
  {
5744
5469
  d: "M26 43 98 29 150 45 75 61Z",
@@ -5747,7 +5472,7 @@ function ServerArt({ upload = false, label, ...p }) {
5747
5472
  strokeWidth: ".75"
5748
5473
  }
5749
5474
  ),
5750
- /* @__PURE__ */ jsx50(
5475
+ /* @__PURE__ */ jsx45(
5751
5476
  "path",
5752
5477
  {
5753
5478
  d: "M98 29 150 45 150 119 98 107Z",
@@ -5756,7 +5481,7 @@ function ServerArt({ upload = false, label, ...p }) {
5756
5481
  strokeWidth: ".7"
5757
5482
  }
5758
5483
  ),
5759
- /* @__PURE__ */ jsx50(
5484
+ /* @__PURE__ */ jsx45(
5760
5485
  "path",
5761
5486
  {
5762
5487
  d: "M26 43 98 29 98 107 26 121Z",
@@ -5765,7 +5490,7 @@ function ServerArt({ upload = false, label, ...p }) {
5765
5490
  strokeWidth: "1"
5766
5491
  }
5767
5492
  ),
5768
- /* @__PURE__ */ jsx50(
5493
+ /* @__PURE__ */ jsx45(
5769
5494
  "path",
5770
5495
  {
5771
5496
  d: "M29 46 94 33 94 105 29 117Z",
@@ -5775,9 +5500,9 @@ function ServerArt({ upload = false, label, ...p }) {
5775
5500
  strokeWidth: ".65"
5776
5501
  }
5777
5502
  ),
5778
- /* @__PURE__ */ jsx50("path", { d: rackLines, stroke: "var(--ad-primary-700)", strokeOpacity: ".58" }),
5779
- /* @__PURE__ */ jsx50("path", { d: rackHatches, stroke: "var(--ad-neutral-950)", strokeWidth: "1.65" }),
5780
- /* @__PURE__ */ jsx50(
5503
+ /* @__PURE__ */ jsx45("path", { d: rackLines, stroke: "var(--ad-primary-700)", strokeOpacity: ".58" }),
5504
+ /* @__PURE__ */ jsx45("path", { d: rackHatches, stroke: "var(--ad-neutral-950)", strokeWidth: "1.65" }),
5505
+ /* @__PURE__ */ jsx45(
5781
5506
  "path",
5782
5507
  {
5783
5508
  d: "M27 44 98 30 147 45",
@@ -5787,8 +5512,8 @@ function ServerArt({ upload = false, label, ...p }) {
5787
5512
  filter: url("bloom")
5788
5513
  }
5789
5514
  ),
5790
- /* @__PURE__ */ jsx50("path", { d: "M27 44 98 30 147 45", stroke: "var(--ad-secondary-200)", strokeWidth: ".75" }),
5791
- Array.from({ length: 8 }, (_, i) => /* @__PURE__ */ jsx50(
5515
+ /* @__PURE__ */ jsx45("path", { d: "M27 44 98 30 147 45", stroke: "var(--ad-secondary-200)", strokeWidth: ".75" }),
5516
+ Array.from({ length: 8 }, (_, i) => /* @__PURE__ */ jsx45(
5792
5517
  "path",
5793
5518
  {
5794
5519
  d: `M107 ${52 + i * 7.5}l34 9v4l-34-9Z`,
@@ -5798,7 +5523,7 @@ function ServerArt({ upload = false, label, ...p }) {
5798
5523
  },
5799
5524
  i
5800
5525
  )),
5801
- /* @__PURE__ */ jsx50(
5526
+ /* @__PURE__ */ jsx45(
5802
5527
  "path",
5803
5528
  {
5804
5529
  d: "M31 108 93 96v8l-62 12Z",
@@ -5807,7 +5532,7 @@ function ServerArt({ upload = false, label, ...p }) {
5807
5532
  strokeWidth: ".55"
5808
5533
  }
5809
5534
  ),
5810
- /* @__PURE__ */ jsx50(
5535
+ /* @__PURE__ */ jsx45(
5811
5536
  "path",
5812
5537
  {
5813
5538
  d: "M26 125 98 112 150 126v14l-73 10-51-10Z",
@@ -5816,7 +5541,7 @@ function ServerArt({ upload = false, label, ...p }) {
5816
5541
  strokeWidth: ".6"
5817
5542
  }
5818
5543
  ),
5819
- /* @__PURE__ */ jsx50(
5544
+ /* @__PURE__ */ jsx45(
5820
5545
  "path",
5821
5546
  {
5822
5547
  d: "M26 125 98 112v15l-72 13Z",
@@ -5825,7 +5550,7 @@ function ServerArt({ upload = false, label, ...p }) {
5825
5550
  strokeWidth: ".65"
5826
5551
  }
5827
5552
  ),
5828
- /* @__PURE__ */ jsx50(
5553
+ /* @__PURE__ */ jsx45(
5829
5554
  "path",
5830
5555
  {
5831
5556
  d: "M33 130 83 121m-50 13 41-7",
@@ -5834,8 +5559,8 @@ function ServerArt({ upload = false, label, ...p }) {
5834
5559
  }
5835
5560
  )
5836
5561
  ] }) }),
5837
- /* @__PURE__ */ jsx50(ArtLayer, { viewBox: box, children: /* @__PURE__ */ jsxs40("g", { transform: lift, children: [
5838
- Array.from({ length: 8 }, (_, i) => /* @__PURE__ */ jsx50(
5562
+ /* @__PURE__ */ jsx45(ArtLayer, { viewBox: box, children: /* @__PURE__ */ jsxs36("g", { transform: lift, children: [
5563
+ Array.from({ length: 8 }, (_, i) => /* @__PURE__ */ jsx45(
5839
5564
  "path",
5840
5565
  {
5841
5566
  d: `M109 ${54 + i * 7.5}l3 .8`,
@@ -5845,8 +5570,8 @@ function ServerArt({ upload = false, label, ...p }) {
5845
5570
  },
5846
5571
  i
5847
5572
  )),
5848
- /* @__PURE__ */ jsx50("path", { d: "M35 110l20-4", className: "ad-server-glow", strokeWidth: "1.1" }),
5849
- /* @__PURE__ */ jsx50(
5573
+ /* @__PURE__ */ jsx45("path", { d: "M35 110l20-4", className: "ad-server-glow", strokeWidth: "1.1" }),
5574
+ /* @__PURE__ */ jsx45(
5850
5575
  "path",
5851
5576
  {
5852
5577
  d: "M27 43 98 29 98 107 27 121Z",
@@ -5855,10 +5580,10 @@ function ServerArt({ upload = false, label, ...p }) {
5855
5580
  }
5856
5581
  )
5857
5582
  ] }) }),
5858
- [[85, 103.5, 1.5, 0], [91, 121.5, 1.3, -1.7]].map(([cx, cy, r, delay]) => /* @__PURE__ */ jsx50(ArtLayer, { viewBox: box, className: "ad-server-dot", style: { animationDelay: `${delay}s` }, children: /* @__PURE__ */ jsx50("circle", { cx, cy: cy + dy, r }) }, cx)),
5859
- /* @__PURE__ */ jsxs40(ArtLayer, { viewBox: box, className: "ad-art-spark-layer", origin: [121, 27], children: [
5860
- /* @__PURE__ */ jsx50("path", { d: "M114 27h14m-7-10v20", stroke: "var(--ad-secondary-100)", strokeWidth: ".65" }),
5861
- /* @__PURE__ */ jsx50("circle", { cx: "121", cy: "27", r: "2.6", fill: "var(--ad-neutral-200)" })
5583
+ [[85, 103.5, 1.5, 0], [91, 121.5, 1.3, -1.7]].map(([cx, cy, r, delay]) => /* @__PURE__ */ jsx45(ArtLayer, { viewBox: box, className: "ad-server-dot", style: { animationDelay: `${delay}s` }, children: /* @__PURE__ */ jsx45("circle", { cx, cy: cy + dy, r }) }, cx)),
5584
+ /* @__PURE__ */ jsxs36(ArtLayer, { viewBox: box, className: "ad-art-spark-layer", origin: [121, 27], children: [
5585
+ /* @__PURE__ */ jsx45("path", { d: "M114 27h14m-7-10v20", stroke: "var(--ad-secondary-100)", strokeWidth: ".65" }),
5586
+ /* @__PURE__ */ jsx45("circle", { cx: "121", cy: "27", r: "2.6", fill: "var(--ad-neutral-200)" })
5862
5587
  ] })
5863
5588
  ]
5864
5589
  }
@@ -5866,13 +5591,13 @@ function ServerArt({ upload = false, label, ...p }) {
5866
5591
  }
5867
5592
 
5868
5593
  // src/components/media/AudioPlayer/AudioPlayer.tsx
5869
- import { useEffect as useEffect12, useRef as useRef29, useState as useState13 } from "react";
5594
+ import { useEffect as useEffect12, useRef as useRef27, useState as useState12 } from "react";
5870
5595
 
5871
5596
  // src/components/media/Waveform/Waveform.tsx
5872
- import { useEffect as useEffect11, useMemo as useMemo4, useRef as useRef28, useState as useState12 } from "react";
5597
+ import { useEffect as useEffect11, useMemo as useMemo4, useRef as useRef26, useState as useState11 } from "react";
5873
5598
 
5874
5599
  // src/components/media/Waveform/useWaveformPeaks.ts
5875
- import { useEffect as useEffect10, useState as useState11 } from "react";
5600
+ import { useEffect as useEffect10, useState as useState10 } from "react";
5876
5601
  async function decode(src, bins, signal) {
5877
5602
  const data = typeof src === "string" ? await (await fetch(src, { signal })).arrayBuffer() : await src.arrayBuffer();
5878
5603
  signal.throwIfAborted();
@@ -5915,7 +5640,7 @@ async function decode(src, bins, signal) {
5915
5640
  };
5916
5641
  }
5917
5642
  function useWaveformPeaks(src, bins = 600) {
5918
- const [data, setData] = useState11(null);
5643
+ const [data, setData] = useState10(null);
5919
5644
  useEffect10(() => {
5920
5645
  setData(null);
5921
5646
  if (!src) return;
@@ -5932,7 +5657,7 @@ function useWaveformPeaks(src, bins = 600) {
5932
5657
  }
5933
5658
 
5934
5659
  // src/components/media/Waveform/Waveform.tsx
5935
- import { jsx as jsx51, jsxs as jsxs41 } from "react/jsx-runtime";
5660
+ import { jsx as jsx46, jsxs as jsxs37 } from "react/jsx-runtime";
5936
5661
  var W4 = 1e3;
5937
5662
  var H4 = 100;
5938
5663
  var MID = H4 / 2;
@@ -6000,9 +5725,9 @@ function Waveform({
6000
5725
  }) {
6001
5726
  const tr2 = useTr();
6002
5727
  const id = useSvgId();
6003
- const surface = useRef28(null);
6004
- const drag = useRef28(null);
6005
- const [hover, setHover] = useState12(null);
5728
+ const surface = useRef26(null);
5729
+ const drag = useRef26(null);
5730
+ const [hover, setHover] = useState11(null);
6006
5731
  const decoded = useWaveformPeaks(points ? null : src, bins);
6007
5732
  const loading = !points && !!src && decoded === null;
6008
5733
  const { peaksPath, corePath } = useMemo4(() => {
@@ -6023,8 +5748,8 @@ function Waveform({
6023
5748
  const progress = clamp(position / duration, 0, 1);
6024
5749
  const played = progress * W4;
6025
5750
  const ahead = Math.max(0, (hover ?? 0) * W4 - played);
6026
- const [playing, setPlaying] = useState12(false);
6027
- const previous = useRef28(position);
5751
+ const [playing, setPlaying] = useState11(false);
5752
+ const previous = useRef26(position);
6028
5753
  useEffect11(() => {
6029
5754
  const step = position - previous.current;
6030
5755
  previous.current = position;
@@ -6047,7 +5772,7 @@ function Waveform({
6047
5772
  if (cancelled && !disabled) seek(clamp(current.position, 0, duration));
6048
5773
  setHover(null);
6049
5774
  };
6050
- return /* @__PURE__ */ jsxs41(
5775
+ return /* @__PURE__ */ jsxs37(
6051
5776
  "div",
6052
5777
  {
6053
5778
  ...mark("Waveform", p),
@@ -6064,8 +5789,6 @@ function Waveform({
6064
5789
  "data-playing": playing || void 0,
6065
5790
  style: {
6066
5791
  ...p.style,
6067
- "--ad-wave-played": `${progress * 100}%`,
6068
- "--ad-wave-progress": progress,
6069
5792
  "--ad-wave-hover": hover === null ? void 0 : `${hover * 100}%`,
6070
5793
  ...color3 ? { "--ad-wave-color": color3 } : {}
6071
5794
  },
@@ -6111,8 +5834,8 @@ function Waveform({
6111
5834
  seek(clamp(next, 0, duration));
6112
5835
  },
6113
5836
  children: [
6114
- /* @__PURE__ */ jsx51("span", { className: "ad-waveform-floor", "aria-hidden": true }),
6115
- /* @__PURE__ */ jsxs41(
5837
+ /* @__PURE__ */ jsx46("span", { className: "ad-waveform-floor", style: { transform: `scaleX(${progress})` }, "aria-hidden": true }),
5838
+ /* @__PURE__ */ jsxs37(
6116
5839
  "svg",
6117
5840
  {
6118
5841
  className: "ad-waveform-base",
@@ -6120,63 +5843,63 @@ function Waveform({
6120
5843
  preserveAspectRatio: "none",
6121
5844
  "aria-hidden": "true",
6122
5845
  children: [
6123
- /* @__PURE__ */ jsxs41("defs", { children: [
6124
- /* @__PURE__ */ jsx51("clipPath", { id: `${id}-track`, children: /* @__PURE__ */ jsx51("path", { d: peaksPath }) }),
6125
- /* @__PURE__ */ jsxs41("linearGradient", { id: `${id}-silver`, x1: "0", x2: "0", y1: "0", y2: "1", children: [
6126
- /* @__PURE__ */ jsx51("stop", { offset: "0", stopColor: "var(--ad-neutral-300)", stopOpacity: "0.35" }),
6127
- /* @__PURE__ */ jsx51("stop", { offset: "0.5", stopColor: "#fff", stopOpacity: "0.95" }),
6128
- /* @__PURE__ */ jsx51("stop", { offset: "1", stopColor: "var(--ad-neutral-300)", stopOpacity: "0.35" })
5846
+ /* @__PURE__ */ jsxs37("defs", { children: [
5847
+ /* @__PURE__ */ jsx46("clipPath", { id: `${id}-track`, children: /* @__PURE__ */ jsx46("path", { d: peaksPath }) }),
5848
+ /* @__PURE__ */ jsxs37("linearGradient", { id: `${id}-silver`, x1: "0", x2: "0", y1: "0", y2: "1", children: [
5849
+ /* @__PURE__ */ jsx46("stop", { offset: "0", stopColor: "var(--ad-neutral-300)", stopOpacity: "0.35" }),
5850
+ /* @__PURE__ */ jsx46("stop", { offset: "0.5", stopColor: "#fff", stopOpacity: "0.95" }),
5851
+ /* @__PURE__ */ jsx46("stop", { offset: "1", stopColor: "var(--ad-neutral-300)", stopOpacity: "0.35" })
6129
5852
  ] }),
6130
- /* @__PURE__ */ jsxs41("linearGradient", { id: `${id}-solid`, x1: "0", x2: "0", y1: "0", y2: "1", children: [
6131
- /* @__PURE__ */ jsx51("stop", { offset: "0", stopColor: "var(--ad-neutral-200)", stopOpacity: "0.8" }),
6132
- /* @__PURE__ */ jsx51("stop", { offset: "0.5", stopColor: "#fff" }),
6133
- /* @__PURE__ */ jsx51("stop", { offset: "1", stopColor: "var(--ad-neutral-200)", stopOpacity: "0.8" })
5853
+ /* @__PURE__ */ jsxs37("linearGradient", { id: `${id}-solid`, x1: "0", x2: "0", y1: "0", y2: "1", children: [
5854
+ /* @__PURE__ */ jsx46("stop", { offset: "0", stopColor: "var(--ad-neutral-200)", stopOpacity: "0.8" }),
5855
+ /* @__PURE__ */ jsx46("stop", { offset: "0.5", stopColor: "#fff" }),
5856
+ /* @__PURE__ */ jsx46("stop", { offset: "1", stopColor: "var(--ad-neutral-200)", stopOpacity: "0.8" })
6134
5857
  ] }),
6135
- /* @__PURE__ */ jsxs41("linearGradient", { id: `${id}-depth`, x1: "0", x2: "0", y1: "0", y2: "1", children: [
6136
- /* @__PURE__ */ jsx51("stop", { offset: "0", stopColor: "#fff", stopOpacity: "0.16" }),
6137
- /* @__PURE__ */ jsx51("stop", { offset: "0.42", stopColor: "#fff", stopOpacity: "0" }),
6138
- /* @__PURE__ */ jsx51("stop", { offset: "0.56", stopColor: "#000", stopOpacity: "0" }),
6139
- /* @__PURE__ */ jsx51("stop", { offset: "1", stopColor: "#000", stopOpacity: "0.6" })
5858
+ /* @__PURE__ */ jsxs37("linearGradient", { id: `${id}-depth`, x1: "0", x2: "0", y1: "0", y2: "1", children: [
5859
+ /* @__PURE__ */ jsx46("stop", { offset: "0", stopColor: "#fff", stopOpacity: "0.16" }),
5860
+ /* @__PURE__ */ jsx46("stop", { offset: "0.42", stopColor: "#fff", stopOpacity: "0" }),
5861
+ /* @__PURE__ */ jsx46("stop", { offset: "0.56", stopColor: "#000", stopOpacity: "0" }),
5862
+ /* @__PURE__ */ jsx46("stop", { offset: "1", stopColor: "#000", stopOpacity: "0.6" })
6140
5863
  ] })
6141
5864
  ] }),
6142
- /* @__PURE__ */ jsx51("line", { className: "ad-waveform-axis", x2: W4, y1: MID, y2: MID }),
6143
- /* @__PURE__ */ jsx51("path", { className: "ad-waveform-peaks", d: peaksPath, fill: `url(#${id}-silver)` }),
6144
- /* @__PURE__ */ jsx51("path", { className: "ad-waveform-core", d: corePath, fill: `url(#${id}-solid)` }),
6145
- /* @__PURE__ */ jsx51("path", { className: "ad-waveform-edge", d: peaksPath })
5865
+ /* @__PURE__ */ jsx46("line", { className: "ad-waveform-axis", x2: W4, y1: MID, y2: MID }),
5866
+ /* @__PURE__ */ jsx46("path", { className: "ad-waveform-peaks", d: peaksPath, fill: `url(#${id}-silver)` }),
5867
+ /* @__PURE__ */ jsx46("path", { className: "ad-waveform-core", d: corePath, fill: `url(#${id}-solid)` }),
5868
+ /* @__PURE__ */ jsx46("path", { className: "ad-waveform-edge", d: peaksPath })
6146
5869
  ]
6147
5870
  },
6148
5871
  shape
6149
5872
  ),
6150
- ahead > 0 && /* @__PURE__ */ jsxs41("svg", { className: "ad-waveform-overlay", viewBox: `0 0 ${W4} ${H4}`, preserveAspectRatio: "none", "aria-hidden": "true", children: [
6151
- /* @__PURE__ */ jsx51("clipPath", { id: `${id}-ahead`, children: /* @__PURE__ */ jsx51("rect", { x: played, width: ahead, height: H4 }) }),
6152
- /* @__PURE__ */ jsxs41("g", { clipPath: `url(#${id}-ahead)`, className: "ad-waveform-ahead", children: [
6153
- /* @__PURE__ */ jsx51("path", { d: peaksPath }),
6154
- /* @__PURE__ */ jsx51("path", { d: corePath })
5873
+ ahead > 0 && /* @__PURE__ */ jsxs37("svg", { className: "ad-waveform-overlay", viewBox: `0 0 ${W4} ${H4}`, preserveAspectRatio: "none", "aria-hidden": "true", children: [
5874
+ /* @__PURE__ */ jsx46("clipPath", { id: `${id}-ahead`, children: /* @__PURE__ */ jsx46("rect", { x: played, width: ahead, height: H4 }) }),
5875
+ /* @__PURE__ */ jsxs37("g", { clipPath: `url(#${id}-ahead)`, className: "ad-waveform-ahead", children: [
5876
+ /* @__PURE__ */ jsx46("path", { d: peaksPath }),
5877
+ /* @__PURE__ */ jsx46("path", { d: corePath })
6155
5878
  ] })
6156
5879
  ] }),
6157
- /* @__PURE__ */ jsx51("span", { className: "ad-waveform-layer ad-waveform-bloom", style: { maskImage: masks.bloom, WebkitMaskImage: masks.bloom }, "aria-hidden": true, children: /* @__PURE__ */ jsx51("i", {}) }),
6158
- /* @__PURE__ */ jsx51("span", { className: "ad-waveform-layer ad-waveform-lit", style: { maskImage: masks.peaks, WebkitMaskImage: masks.peaks }, "aria-hidden": true, children: /* @__PURE__ */ jsx51("i", {}) }),
6159
- /* @__PURE__ */ jsx51("span", { className: "ad-waveform-layer ad-waveform-lit ad-waveform-lit-core", style: { maskImage: masks.core, WebkitMaskImage: masks.core }, "aria-hidden": true, children: /* @__PURE__ */ jsx51("i", {}) }),
6160
- /* @__PURE__ */ jsx51("span", { className: "ad-waveform-layer ad-waveform-sheen-clip", style: { maskImage: masks.peaks, WebkitMaskImage: masks.peaks }, "aria-hidden": true, children: /* @__PURE__ */ jsx51("span", { className: "ad-waveform-window", children: /* @__PURE__ */ jsx51("span", { className: "ad-waveform-window-inner", children: /* @__PURE__ */ jsx51("i", { className: "ad-waveform-sheen" }) }) }) }),
6161
- /* @__PURE__ */ jsx51("span", { className: "ad-waveform-layer ad-waveform-depth", style: { maskImage: masks.peaks, WebkitMaskImage: masks.peaks }, "aria-hidden": true }),
6162
- /* @__PURE__ */ jsx51("span", { className: "ad-waveform-layer ad-waveform-shade", style: { maskImage: masks.peaks, WebkitMaskImage: masks.peaks }, "aria-hidden": true, children: /* @__PURE__ */ jsx51("span", { className: "ad-waveform-follow", children: /* @__PURE__ */ jsx51("i", { className: "ad-waveform-spot" }) }) }),
6163
- /* @__PURE__ */ jsx51("span", { className: "ad-waveform-follow", "aria-hidden": true, children: /* @__PURE__ */ jsx51("span", { className: "ad-waveform-cursor", children: /* @__PURE__ */ jsx51("span", { className: "ad-waveform-sparks", children: Array.from({ length: 8 }, (_, i) => /* @__PURE__ */ jsx51("i", {}, i)) }) }) }),
6164
- hover !== null && !disabled && /* @__PURE__ */ jsx51("span", { className: "ad-waveform-hover", "aria-hidden": true, children: /* @__PURE__ */ jsx51("span", { className: "ad-waveform-tip", children: timeText(hover * duration) }) })
5880
+ /* @__PURE__ */ jsx46("span", { className: "ad-waveform-layer ad-waveform-bloom", style: { maskImage: masks.bloom, WebkitMaskImage: masks.bloom }, "aria-hidden": true, children: /* @__PURE__ */ jsx46("i", { style: { transform: `scaleX(${progress})` } }) }),
5881
+ /* @__PURE__ */ jsx46("span", { className: "ad-waveform-layer ad-waveform-lit", style: { maskImage: masks.peaks, WebkitMaskImage: masks.peaks }, "aria-hidden": true, children: /* @__PURE__ */ jsx46("i", { style: { transform: `scaleX(${progress})` } }) }),
5882
+ /* @__PURE__ */ jsx46("span", { className: "ad-waveform-layer ad-waveform-lit ad-waveform-lit-core", style: { maskImage: masks.core, WebkitMaskImage: masks.core }, "aria-hidden": true, children: /* @__PURE__ */ jsx46("i", { style: { transform: `scaleX(${progress})` } }) }),
5883
+ /* @__PURE__ */ jsx46("span", { className: "ad-waveform-layer ad-waveform-sheen-clip", "data-ad-paused": progress === 0 ? "" : void 0, style: { maskImage: masks.peaks, WebkitMaskImage: masks.peaks }, "aria-hidden": true, children: /* @__PURE__ */ jsx46("span", { className: "ad-waveform-window", style: { transform: `translateX(calc(${progress * 100}% - 100%))` }, children: /* @__PURE__ */ jsx46("span", { className: "ad-waveform-window-inner", style: { transform: `translateX(calc(100% - ${progress * 100}%))` }, children: /* @__PURE__ */ jsx46("i", { className: "ad-waveform-sheen" }) }) }) }),
5884
+ /* @__PURE__ */ jsx46("span", { className: "ad-waveform-layer ad-waveform-depth", style: { maskImage: masks.peaks, WebkitMaskImage: masks.peaks }, "aria-hidden": true }),
5885
+ /* @__PURE__ */ jsx46("span", { className: "ad-waveform-layer ad-waveform-shade", style: { maskImage: masks.peaks, WebkitMaskImage: masks.peaks }, "aria-hidden": true, children: /* @__PURE__ */ jsx46("span", { className: "ad-waveform-follow", style: { transform: `translateX(${progress * 100}%)` }, children: /* @__PURE__ */ jsx46("i", { className: "ad-waveform-spot" }) }) }),
5886
+ /* @__PURE__ */ jsx46("span", { className: "ad-waveform-follow", style: { transform: `translateX(${progress * 100}%)` }, "aria-hidden": true, children: /* @__PURE__ */ jsx46("span", { className: "ad-waveform-cursor", children: /* @__PURE__ */ jsx46("span", { className: "ad-waveform-sparks", "data-ad-paused": playing ? void 0 : "", children: Array.from({ length: 8 }, (_, i) => /* @__PURE__ */ jsx46("i", {}, i)) }) }) }),
5887
+ hover !== null && !disabled && /* @__PURE__ */ jsx46("span", { className: "ad-waveform-hover", "aria-hidden": true, children: /* @__PURE__ */ jsx46("span", { className: "ad-waveform-tip", children: timeText(hover * duration) }) })
6165
5888
  ]
6166
5889
  }
6167
5890
  );
6168
5891
  }
6169
5892
 
6170
5893
  // src/components/media/AudioPlayer/AudioPlayer.tsx
6171
- import { jsx as jsx52, jsxs as jsxs42 } from "react/jsx-runtime";
5894
+ import { jsx as jsx47, jsxs as jsxs38 } from "react/jsx-runtime";
6172
5895
  var AudioPlayer = (p) => {
6173
5896
  const tr2 = useTr();
6174
- const audio = useRef29(null);
6175
- const [playing, setPlaying] = useState13(false), [muted, setMuted] = useState13(false);
5897
+ const audio = useRef27(null);
5898
+ const [playing, setPlaying] = useState12(false), [muted, setMuted] = useState12(false);
6176
5899
  const [volume, setVolume] = useControllable(p.volume, p.defaultVolume ?? 0.7, p.onVolumeChange);
6177
- const onPlayingChange = useRef29(p.onPlayingChange);
5900
+ const onPlayingChange = useRef27(p.onPlayingChange);
6178
5901
  onPlayingChange.current = p.onPlayingChange;
6179
- const [fileDuration, setFileDuration] = useState13();
5902
+ const [fileDuration, setFileDuration] = useState12();
6180
5903
  const duration = p.duration ?? fileDuration ?? 51;
6181
5904
  const controlled = p.playing !== void 0;
6182
5905
  const active = p.playing ?? playing;
@@ -6225,8 +5948,8 @@ var AudioPlayer = (p) => {
6225
5948
  else audio.current.pause();
6226
5949
  }
6227
5950
  };
6228
- return /* @__PURE__ */ jsxs42("div", { ...mark("AudioPlayer", p), "data-playing": active || void 0, children: [
6229
- /* @__PURE__ */ jsx52("span", { className: "ad-player-play", children: /* @__PURE__ */ jsx52(
5951
+ return /* @__PURE__ */ jsxs38("div", { ...mark("AudioPlayer", p), "data-playing": active || void 0, children: [
5952
+ /* @__PURE__ */ jsx47("span", { className: "ad-player-play", children: /* @__PURE__ */ jsx47(
6230
5953
  IconButton,
6231
5954
  {
6232
5955
  variant: "primary",
@@ -6237,7 +5960,7 @@ var AudioPlayer = (p) => {
6237
5960
  onClick: toggle
6238
5961
  }
6239
5962
  ) }),
6240
- /* @__PURE__ */ jsx52(
5963
+ /* @__PURE__ */ jsx47(
6241
5964
  PlaybackTrack,
6242
5965
  {
6243
5966
  audio,
@@ -6255,8 +5978,8 @@ var AudioPlayer = (p) => {
6255
5978
  },
6256
5979
  p.src
6257
5980
  ),
6258
- p.showVolume !== false && /* @__PURE__ */ jsxs42("div", { className: "ad-player-volume", children: [
6259
- /* @__PURE__ */ jsx52(
5981
+ p.showVolume !== false && /* @__PURE__ */ jsxs38("div", { className: "ad-player-volume", children: [
5982
+ /* @__PURE__ */ jsx47(
6260
5983
  IconButton,
6261
5984
  {
6262
5985
  variant: "ghost",
@@ -6268,7 +5991,7 @@ var AudioPlayer = (p) => {
6268
5991
  onClick: () => setMuted((v) => !v)
6269
5992
  }
6270
5993
  ),
6271
- /* @__PURE__ */ jsx52(
5994
+ /* @__PURE__ */ jsx47(
6272
5995
  Slider,
6273
5996
  {
6274
5997
  size: "sm",
@@ -6288,10 +6011,10 @@ var AudioPlayer = (p) => {
6288
6011
  ] });
6289
6012
  };
6290
6013
  function PlaybackTrack({ audio, playing, position: controlledPosition, disabled, duration, points, src, onTimeChange, onEnded }) {
6291
- const [localPosition, setPosition] = useState13(0);
6014
+ const [localPosition, setPosition] = useState12(0);
6292
6015
  const position = controlledPosition ?? localPosition;
6293
- const current = useRef29(0);
6294
- const lastTick = useRef29(void 0);
6016
+ const current = useRef27(0);
6017
+ const lastTick = useRef27(void 0);
6295
6018
  useTick((now) => {
6296
6019
  const media = audio.current;
6297
6020
  const next = media?.currentTime ?? current.current + (now - (lastTick.current ?? now)) / 1e3;
@@ -6314,11 +6037,11 @@ function PlaybackTrack({ audio, playing, position: controlledPosition, disabled,
6314
6037
  if (audio.current) audio.current.currentTime = next;
6315
6038
  onTimeChange?.(next);
6316
6039
  };
6317
- return /* @__PURE__ */ jsxs42("div", { className: "ad-player-track", children: [
6318
- /* @__PURE__ */ jsx52(Waveform, { duration, position, onSeek: seek, points, src: points ? void 0 : src, disabled }),
6319
- /* @__PURE__ */ jsxs42("div", { className: "ad-player-times", children: [
6320
- /* @__PURE__ */ jsx52("span", { className: "ad-time", children: timeText(position) }),
6321
- /* @__PURE__ */ jsxs42("span", { className: "ad-time", children: [
6040
+ return /* @__PURE__ */ jsxs38("div", { className: "ad-player-track", children: [
6041
+ /* @__PURE__ */ jsx47(Waveform, { duration, position, onSeek: seek, points, src: points ? void 0 : src, disabled }),
6042
+ /* @__PURE__ */ jsxs38("div", { className: "ad-player-times", children: [
6043
+ /* @__PURE__ */ jsx47("span", { className: "ad-time", children: timeText(position) }),
6044
+ /* @__PURE__ */ jsxs38("span", { className: "ad-time", children: [
6322
6045
  "\u2212",
6323
6046
  timeText(duration - position)
6324
6047
  ] })
@@ -6327,8 +6050,8 @@ function PlaybackTrack({ audio, playing, position: controlledPosition, disabled,
6327
6050
  }
6328
6051
 
6329
6052
  // src/components/media/LevelMeter/LevelMeter.tsx
6330
- import { useEffect as useEffect13, useRef as useRef30 } from "react";
6331
- import { jsx as jsx53, jsxs as jsxs43 } from "react/jsx-runtime";
6053
+ import { useEffect as useEffect13, useRef as useRef28 } from "react";
6054
+ import { jsx as jsx48, jsxs as jsxs39 } from "react/jsx-runtime";
6332
6055
  var SAMPLES2 = 64;
6333
6056
  var WIDTH = 256;
6334
6057
  var HEIGHT = 24;
@@ -6357,9 +6080,9 @@ function LevelMeter({
6357
6080
  }) {
6358
6081
  const tr2 = useTr();
6359
6082
  const id = useSvgId();
6360
- const path = useRef30(null);
6361
- const target = useRef30(0);
6362
- const wake = useRef30(() => void 0);
6083
+ const path = useRef28(null);
6084
+ const target = useRef28(0);
6085
+ const wake = useRef28(() => void 0);
6363
6086
  const level = clamp(value) / 100;
6364
6087
  useEffect13(() => {
6365
6088
  target.current = active ? level : 0;
@@ -6424,7 +6147,7 @@ function LevelMeter({
6424
6147
  void context?.close();
6425
6148
  };
6426
6149
  }, [active, stream]);
6427
- return /* @__PURE__ */ jsx53(
6150
+ return /* @__PURE__ */ jsx48(
6428
6151
  "div",
6429
6152
  {
6430
6153
  ...mark("LevelMeter", p),
@@ -6435,20 +6158,20 @@ function LevelMeter({
6435
6158
  "aria-valuenow": stream ? void 0 : Math.round(active ? level * 100 : 0),
6436
6159
  "data-active": active,
6437
6160
  "data-compact": compact || void 0,
6438
- children: /* @__PURE__ */ jsxs43(
6161
+ children: /* @__PURE__ */ jsxs39(
6439
6162
  "svg",
6440
6163
  {
6441
6164
  viewBox: `0 0 ${WIDTH} ${HEIGHT}`,
6442
6165
  preserveAspectRatio: "none",
6443
6166
  "aria-hidden": "true",
6444
6167
  children: [
6445
- /* @__PURE__ */ jsx53("defs", { children: /* @__PURE__ */ jsxs43("linearGradient", { id: `${id}-wave`, children: [
6446
- /* @__PURE__ */ jsx53("stop", { stopColor: "var(--ad-primary-700)" }),
6447
- /* @__PURE__ */ jsx53("stop", { offset: "0.52", stopColor: "var(--ad-red)" }),
6448
- /* @__PURE__ */ jsx53("stop", { offset: "1", stopColor: "var(--ad-secondary-100)" })
6168
+ /* @__PURE__ */ jsx48("defs", { children: /* @__PURE__ */ jsxs39("linearGradient", { id: `${id}-wave`, children: [
6169
+ /* @__PURE__ */ jsx48("stop", { stopColor: "var(--ad-primary-700)" }),
6170
+ /* @__PURE__ */ jsx48("stop", { offset: "0.52", stopColor: "var(--ad-red)" }),
6171
+ /* @__PURE__ */ jsx48("stop", { offset: "1", stopColor: "var(--ad-secondary-100)" })
6449
6172
  ] }) }),
6450
- /* @__PURE__ */ jsx53("line", { className: "ad-level-meter-axis", x2: WIDTH, y1: MID2, y2: MID2 }),
6451
- /* @__PURE__ */ jsx53(
6173
+ /* @__PURE__ */ jsx48("line", { className: "ad-level-meter-axis", x2: WIDTH, y1: MID2, y2: MID2 }),
6174
+ /* @__PURE__ */ jsx48(
6452
6175
  "path",
6453
6176
  {
6454
6177
  ref: path,
@@ -6464,13 +6187,13 @@ function LevelMeter({
6464
6187
  }
6465
6188
 
6466
6189
  // src/components/media/KaraokeLyrics/KaraokeLyrics.tsx
6467
- import { jsx as jsx54, jsxs as jsxs44 } from "react/jsx-runtime";
6190
+ import { jsx as jsx49, jsxs as jsxs40 } from "react/jsx-runtime";
6468
6191
  var DEMO = [
6469
6192
  { id: "1", text: "\u041D\u043E\u0447\u044C", progress: 1 },
6470
6193
  { id: "2", text: "\u0433\u043E\u0440\u0438\u0442", progress: 0.55 },
6471
6194
  { id: "3", text: "\u043E\u0433\u043D\u044F\u043C\u0438", progress: 0 }
6472
6195
  ];
6473
- var line = (words, sung) => words.map((word) => /* @__PURE__ */ jsxs44(
6196
+ var line = (words, sung) => words.map((word) => /* @__PURE__ */ jsxs40(
6474
6197
  "span",
6475
6198
  {
6476
6199
  className: "ad-lyric-word",
@@ -6492,22 +6215,22 @@ var KaraokeLyrics = ({
6492
6215
  snare = 0,
6493
6216
  pulse = 0,
6494
6217
  ...p
6495
- }) => /* @__PURE__ */ jsxs44(
6218
+ }) => /* @__PURE__ */ jsxs40(
6496
6219
  "div",
6497
6220
  {
6498
6221
  ...mark("KaraokeLyrics", p),
6499
6222
  "aria-live": "off",
6500
6223
  style: { ...p.style, "--ad-lyric-kick": kick, "--ad-lyric-snare": snare, "--ad-lyric-pulse": pulse },
6501
6224
  children: [
6502
- /* @__PURE__ */ jsx54("p", { className: "ad-lyrics-current", "data-message": message !== void 0 || void 0, children: message ?? line(current, true) }, currentKey),
6503
- /* @__PURE__ */ jsx54("p", { className: "ad-lyrics-next", "aria-hidden": message !== void 0 || void 0, children: message === void 0 && line(next, false) }, nextKey)
6225
+ /* @__PURE__ */ jsx49("p", { className: "ad-lyrics-current", "data-message": message !== void 0 || void 0, children: message ?? line(current, true) }, currentKey),
6226
+ /* @__PURE__ */ jsx49("p", { className: "ad-lyrics-next", "aria-hidden": message !== void 0 || void 0, children: message === void 0 && line(next, false) }, nextKey)
6504
6227
  ]
6505
6228
  }
6506
6229
  );
6507
6230
 
6508
6231
  // src/components/media/MelodyRoll/MelodyRoll.tsx
6509
- import { useRef as useRef31 } from "react";
6510
- import { jsx as jsx55, jsxs as jsxs45 } from "react/jsx-runtime";
6232
+ import { useRef as useRef29 } from "react";
6233
+ import { jsx as jsx50, jsxs as jsxs41 } from "react/jsx-runtime";
6511
6234
  var DEMO2 = [
6512
6235
  { id: "1", start: 0.4, end: 1.1, pitch: 64, lyric: "\u041D\u043E\u0447\u044C" },
6513
6236
  { id: "2", start: 1.2, end: 1.6, pitch: 67, lyric: "\u0433\u043E" },
@@ -6549,7 +6272,7 @@ var MelodyRoll = ({
6549
6272
  const x = (seconds) => (seconds - from) / span * 100;
6550
6273
  const y = (pitch) => (high - pitch) / rows * 100;
6551
6274
  const visible = notes.filter((note) => note.end >= from && note.start <= from + span);
6552
- const trail = useRef31([]);
6275
+ const trail = useRef29([]);
6553
6276
  if (trail.current.at(-1)?.t !== position) {
6554
6277
  const kept = trail.current.filter((point) => point.t > position - TRAIL && point.t < position);
6555
6278
  trail.current = [...kept, { t: position, pitch: livePitch }];
@@ -6566,7 +6289,7 @@ var MelodyRoll = ({
6566
6289
  if (!event.deltaY) return;
6567
6290
  setSpan(clamp(span * (event.deltaY > 0 ? 1.12 : 1 / 1.12), 2, 20));
6568
6291
  };
6569
- return /* @__PURE__ */ jsxs45(
6292
+ return /* @__PURE__ */ jsxs41(
6570
6293
  "div",
6571
6294
  {
6572
6295
  ...mark("MelodyRoll", p),
@@ -6583,7 +6306,7 @@ var MelodyRoll = ({
6583
6306
  "--ad-roll-shift": `${-((from % 1 + 1) % 1) / span * 100}cqw`
6584
6307
  },
6585
6308
  children: [
6586
- /* @__PURE__ */ jsx55(
6309
+ /* @__PURE__ */ jsx50(
6587
6310
  PianoKeyboard,
6588
6311
  {
6589
6312
  className: "ad-melody-roll-keys",
@@ -6595,9 +6318,9 @@ var MelodyRoll = ({
6595
6318
  onKeyPress
6596
6319
  }
6597
6320
  ),
6598
- /* @__PURE__ */ jsxs45("div", { className: "ad-melody-roll-lane", "aria-hidden": "true", children: [
6599
- /* @__PURE__ */ jsx55("span", { className: "ad-melody-roll-seconds" }),
6600
- /* @__PURE__ */ jsx55("div", { className: "ad-melody-roll-strip", style: { transform: `translateX(${(-from / span * 100).toFixed(3)}%)` }, children: visible.map((note) => /* @__PURE__ */ jsx55(
6321
+ /* @__PURE__ */ jsxs41("div", { className: "ad-melody-roll-lane", "aria-hidden": "true", children: [
6322
+ /* @__PURE__ */ jsx50("span", { className: "ad-melody-roll-seconds" }),
6323
+ /* @__PURE__ */ jsx50("div", { className: "ad-melody-roll-strip", style: { transform: `translateX(${(-from / span * 100).toFixed(3)}%)` }, children: visible.map((note) => /* @__PURE__ */ jsx50(
6601
6324
  "span",
6602
6325
  {
6603
6326
  className: "ad-melody-note",
@@ -6609,17 +6332,17 @@ var MelodyRoll = ({
6609
6332
  width: `${Math.max(0.6, (note.end - note.start) / span * 100)}%`,
6610
6333
  top: `${y(note.pitch)}%`
6611
6334
  },
6612
- children: showLyrics && note.lyric && /* @__PURE__ */ jsx55("span", { children: note.lyric })
6335
+ children: showLyrics && note.lyric && /* @__PURE__ */ jsx50("span", { children: note.lyric })
6613
6336
  },
6614
6337
  note.id
6615
6338
  )) }),
6616
- /* @__PURE__ */ jsx55("svg", { className: "ad-melody-trail", viewBox: "0 0 100 100", preserveAspectRatio: "none", children: segments.flatMap((d, i) => [
6617
- /* @__PURE__ */ jsx55("path", { "data-halo": "wide", d, vectorEffect: "non-scaling-stroke" }, `${i}-wide`),
6618
- /* @__PURE__ */ jsx55("path", { "data-halo": "near", d, vectorEffect: "non-scaling-stroke" }, `${i}-near`),
6619
- /* @__PURE__ */ jsx55("path", { d, vectorEffect: "non-scaling-stroke" }, i)
6339
+ /* @__PURE__ */ jsx50("svg", { className: "ad-melody-trail", viewBox: "0 0 100 100", preserveAspectRatio: "none", children: segments.flatMap((d, i) => [
6340
+ /* @__PURE__ */ jsx50("path", { "data-halo": "wide", d, vectorEffect: "non-scaling-stroke" }, `${i}-wide`),
6341
+ /* @__PURE__ */ jsx50("path", { "data-halo": "near", d, vectorEffect: "non-scaling-stroke" }, `${i}-near`),
6342
+ /* @__PURE__ */ jsx50("path", { d, vectorEffect: "non-scaling-stroke" }, i)
6620
6343
  ]) }),
6621
- /* @__PURE__ */ jsx55("span", { className: "ad-melody-playhead", style: { left: `${lead * 100}%` } }),
6622
- livePitch !== void 0 && /* @__PURE__ */ jsx55(
6344
+ /* @__PURE__ */ jsx50("span", { className: "ad-melody-playhead", style: { left: `${lead * 100}%` } }),
6345
+ livePitch !== void 0 && /* @__PURE__ */ jsx50(
6623
6346
  "span",
6624
6347
  {
6625
6348
  className: "ad-melody-voice",
@@ -6634,26 +6357,26 @@ var MelodyRoll = ({
6634
6357
  };
6635
6358
 
6636
6359
  // src/components/layout/FloatingPanel/FloatingPanel.tsx
6637
- import { useRef as useRef33 } from "react";
6360
+ import { useRef as useRef31 } from "react";
6638
6361
 
6639
6362
  // src/components/layout/FloatingPanel/useFloatingPanel.ts
6640
- import { useCallback as useCallback2, useEffect as useEffect14, useLayoutEffect as useLayoutEffect6, useRef as useRef32, useState as useState14 } from "react";
6363
+ import { useCallback as useCallback2, useEffect as useEffect14, useLayoutEffect as useLayoutEffect5, useRef as useRef30, useState as useState13 } from "react";
6641
6364
  var resizeEdges = ["n", "s", "e", "w", "ne", "nw", "se", "sw"];
6642
6365
  var threshold = 3;
6643
6366
  var panelControls = "button, a, input, select, textarea, [role=slider], [role=button], [role=switch], [role=listbox], [contenteditable=true], .ad-rotary-knob";
6644
6367
  var useFloatingPanel = (frameRef, options = {}) => {
6645
- const [active, setActive] = useState14(false);
6646
- const [live, setLive] = useState14(null);
6647
- const drag = useRef32(null);
6648
- const moved = useRef32(false);
6649
- const latest = useRef32(options);
6368
+ const [active, setActive] = useState13(false);
6369
+ const [live, setLive] = useState13(null);
6370
+ const drag = useRef30(null);
6371
+ const moved = useRef30(false);
6372
+ const latest = useRef30(options);
6650
6373
  latest.current = options;
6651
6374
  const saved = options.layout;
6652
- useLayoutEffect6(() => {
6375
+ useLayoutEffect5(() => {
6653
6376
  drag.current = null;
6654
6377
  setLive(null);
6655
6378
  }, [saved?.left, saved?.top, saved?.width, saved?.height]);
6656
- const [, setViewport] = useState14(0);
6379
+ const [, setViewport] = useState13(0);
6657
6380
  useEffect14(() => {
6658
6381
  const onResize = () => setViewport((n) => n + 1);
6659
6382
  window.addEventListener("resize", onResize);
@@ -6776,9 +6499,9 @@ var useFloatingPanel = (frameRef, options = {}) => {
6776
6499
  };
6777
6500
 
6778
6501
  // src/components/layout/FloatingPanel/FloatingPanel.tsx
6779
- import { jsx as jsx56, jsxs as jsxs46 } from "react/jsx-runtime";
6502
+ import { jsx as jsx51, jsxs as jsxs42 } from "react/jsx-runtime";
6780
6503
  var FloatingPanel = ({ layout: saved, onLayoutChange, defaultSize, limits, onDragOutside, ignore, resizable = false, label, children, ...p }) => {
6781
- const frame = useRef33(null);
6504
+ const frame = useRef31(null);
6782
6505
  const { layout, active, beginMove, beginResize, handleMove, handleUp, handleCancel, handleKeyDown } = useFloatingPanel(frame, {
6783
6506
  layout: saved,
6784
6507
  onLayoutChange,
@@ -6787,7 +6510,7 @@ var FloatingPanel = ({ layout: saved, onLayoutChange, defaultSize, limits, onDra
6787
6510
  onDragOutside,
6788
6511
  ignore
6789
6512
  });
6790
- return /* @__PURE__ */ jsxs46(
6513
+ return /* @__PURE__ */ jsxs42(
6791
6514
  "div",
6792
6515
  {
6793
6516
  ...mark("FloatingPanel", p),
@@ -6804,20 +6527,20 @@ var FloatingPanel = ({ layout: saved, onLayoutChange, defaultSize, limits, onDra
6804
6527
  onKeyDown: (event) => handleKeyDown(event, resizable),
6805
6528
  children: [
6806
6529
  children,
6807
- resizable && active && resizeEdges.map((edge) => /* @__PURE__ */ jsx56("span", { className: "ad-floating-panel-handle", "data-edge": edge, "aria-hidden": "true", onPointerDown: beginResize(edge) }, edge))
6530
+ resizable && active && resizeEdges.map((edge) => /* @__PURE__ */ jsx51("span", { className: "ad-floating-panel-handle", "data-edge": edge, "aria-hidden": "true", onPointerDown: beginResize(edge) }, edge))
6808
6531
  ]
6809
6532
  }
6810
6533
  );
6811
6534
  };
6812
6535
 
6813
6536
  // src/components/media/Sparkline/Sparkline.tsx
6814
- import { useMemo as useMemo5, useRef as useRef34 } from "react";
6815
- import { Fragment as Fragment5, jsx as jsx57, jsxs as jsxs47 } from "react/jsx-runtime";
6537
+ import { useMemo as useMemo5, useRef as useRef32 } from "react";
6538
+ import { Fragment as Fragment4, jsx as jsx52, jsxs as jsxs43 } from "react/jsx-runtime";
6816
6539
  var DEMO3 = [12, 23, 17, 31, 43, 24, 28, 20, 41, 29, 51, 34, 38, 22, 31, 16, 23];
6817
6540
  var Sparkline = (p) => {
6818
6541
  const values = p.values ?? DEMO3;
6819
6542
  const id = useSvgId();
6820
- const ref = useRef34(null);
6543
+ const ref = useRef32(null);
6821
6544
  usePauseOffscreen(ref);
6822
6545
  const { line: line2, lastX, lastY } = useMemo5(() => {
6823
6546
  if (!values.length) return { line: "", lastX: 0, lastY: 0 };
@@ -6854,7 +6577,7 @@ var Sparkline = (p) => {
6854
6577
  };
6855
6578
  }, [values, p.fit]);
6856
6579
  const box = { viewBox: "0 0 240 70", preserveAspectRatio: "none", "aria-hidden": true };
6857
- return /* @__PURE__ */ jsx57(
6580
+ return /* @__PURE__ */ jsx52(
6858
6581
  "span",
6859
6582
  {
6860
6583
  ...mark("Sparkline", p),
@@ -6863,13 +6586,13 @@ var Sparkline = (p) => {
6863
6586
  "aria-label": p.label,
6864
6587
  "aria-hidden": !p.label,
6865
6588
  style: { ...p.style, "--ad-spark": p.color ?? "var(--ad-primary)" },
6866
- children: line2 && /* @__PURE__ */ jsxs47(Fragment5, { children: [
6867
- /* @__PURE__ */ jsxs47("svg", { ...box, children: [
6868
- /* @__PURE__ */ jsx57("defs", { children: /* @__PURE__ */ jsxs47("linearGradient", { id: `${id}-area`, x1: "0", y1: "0", x2: "0", y2: "1", children: [
6869
- /* @__PURE__ */ jsx57("stop", { offset: "0", stopColor: "var(--ad-spark)", stopOpacity: "0.45" }),
6870
- /* @__PURE__ */ jsx57("stop", { offset: "1", stopColor: "var(--ad-spark)", stopOpacity: "0" })
6589
+ children: line2 && /* @__PURE__ */ jsxs43(Fragment4, { children: [
6590
+ /* @__PURE__ */ jsxs43("svg", { ...box, children: [
6591
+ /* @__PURE__ */ jsx52("defs", { children: /* @__PURE__ */ jsxs43("linearGradient", { id: `${id}-area`, x1: "0", y1: "0", x2: "0", y2: "1", children: [
6592
+ /* @__PURE__ */ jsx52("stop", { offset: "0", stopColor: "var(--ad-spark)", stopOpacity: "0.45" }),
6593
+ /* @__PURE__ */ jsx52("stop", { offset: "1", stopColor: "var(--ad-spark)", stopOpacity: "0" })
6871
6594
  ] }) }),
6872
- /* @__PURE__ */ jsx57(
6595
+ /* @__PURE__ */ jsx52(
6873
6596
  "path",
6874
6597
  {
6875
6598
  className: "ad-sparkline-area",
@@ -6877,18 +6600,18 @@ var Sparkline = (p) => {
6877
6600
  fill: `url(#${id}-area)`
6878
6601
  }
6879
6602
  ),
6880
- /* @__PURE__ */ jsx57("path", { className: "ad-sparkline-line", d: line2, pathLength: 1 })
6603
+ /* @__PURE__ */ jsx52("path", { className: "ad-sparkline-line", d: line2, pathLength: 1 })
6881
6604
  ] }),
6882
- /* @__PURE__ */ jsx57("svg", { ...box, children: /* @__PURE__ */ jsx57("circle", { className: "ad-sparkline-ping", cx: lastX, cy: lastY, r: "3" }) }),
6883
- /* @__PURE__ */ jsx57("svg", { ...box, children: /* @__PURE__ */ jsx57("circle", { className: "ad-sparkline-dot", cx: lastX, cy: lastY, r: "3" }) })
6605
+ /* @__PURE__ */ jsx52("svg", { ...box, children: /* @__PURE__ */ jsx52("circle", { className: "ad-sparkline-ping", cx: lastX, cy: lastY, r: "3" }) }),
6606
+ /* @__PURE__ */ jsx52("svg", { ...box, children: /* @__PURE__ */ jsx52("circle", { className: "ad-sparkline-dot", cx: lastX, cy: lastY, r: "3" }) })
6884
6607
  ] })
6885
6608
  }
6886
6609
  );
6887
6610
  };
6888
6611
 
6889
6612
  // src/components/media/WaveDecoration/WaveDecoration.tsx
6890
- import { useRef as useRef35 } from "react";
6891
- import { jsx as jsx58, jsxs as jsxs48 } from "react/jsx-runtime";
6613
+ import { useRef as useRef33 } from "react";
6614
+ import { jsx as jsx53, jsxs as jsxs44 } from "react/jsx-runtime";
6892
6615
  var POINTS = 23;
6893
6616
  var STEP2 = 65 / (POINTS - 1);
6894
6617
  var X = Array.from({ length: POINTS }, (_, k) => k * STEP2 / 65 * 600);
@@ -6906,8 +6629,8 @@ var strand = (j, t) => {
6906
6629
  return d;
6907
6630
  };
6908
6631
  var WaveDecoration = (p) => {
6909
- const ref = useRef35(null);
6910
- const paths = useRef35(null);
6632
+ const ref = useRef33(null);
6633
+ const paths = useRef33(null);
6911
6634
  const uid = useSvgId();
6912
6635
  const paint = (t) => {
6913
6636
  if (!ref.current) return;
@@ -6916,7 +6639,7 @@ var WaveDecoration = (p) => {
6916
6639
  };
6917
6640
  usePauseOffscreen(ref);
6918
6641
  useDecoration(ref, paint);
6919
- return /* @__PURE__ */ jsxs48(
6642
+ return /* @__PURE__ */ jsxs44(
6920
6643
  "svg",
6921
6644
  {
6922
6645
  ...mark("WaveDecoration", p),
@@ -6924,12 +6647,12 @@ var WaveDecoration = (p) => {
6924
6647
  viewBox: "0 0 600 130",
6925
6648
  "aria-hidden": "true",
6926
6649
  children: [
6927
- /* @__PURE__ */ jsx58("defs", { children: /* @__PURE__ */ jsx58("linearGradient", { id: `wave-${uid}`, children: [
6650
+ /* @__PURE__ */ jsx53("defs", { children: /* @__PURE__ */ jsx53("linearGradient", { id: `wave-${uid}`, children: [
6928
6651
  [0, 0],
6929
6652
  [0.2, 0.3],
6930
6653
  [0.7, 1],
6931
6654
  [1, 0.35]
6932
- ].map(([offset, opacity]) => /* @__PURE__ */ jsx58(
6655
+ ].map(([offset, opacity]) => /* @__PURE__ */ jsx53(
6933
6656
  "stop",
6934
6657
  {
6935
6658
  offset,
@@ -6938,7 +6661,7 @@ var WaveDecoration = (p) => {
6938
6661
  },
6939
6662
  offset
6940
6663
  )) }) }),
6941
- Array.from({ length: 22 }, (_, j) => /* @__PURE__ */ jsx58(
6664
+ Array.from({ length: 22 }, (_, j) => /* @__PURE__ */ jsx53(
6942
6665
  "path",
6943
6666
  {
6944
6667
  d: "M0 65H600",
@@ -7100,6 +6823,7 @@ export {
7100
6823
  quantumFieldExperienceControls,
7101
6824
  quantumFieldModes,
7102
6825
  quantumFieldResolution,
6826
+ refreshMotion,
7103
6827
  resizeEdges,
7104
6828
  resolveToken,
7105
6829
  resolveTokens,