@wave3d/core 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/config/model.d.ts +209 -6
  2. package/dist/config/model.js +85 -3
  3. package/dist/config/model.js.map +1 -1
  4. package/dist/index.d.ts +3 -2
  5. package/dist/index.js +3 -2
  6. package/dist/presets.js +295 -0
  7. package/dist/presets.js.map +1 -1
  8. package/dist/renderer/WaveGeometry.js +21 -0
  9. package/dist/renderer/WaveGeometry.js.map +1 -1
  10. package/dist/renderer/WaveRenderer.d.ts +62 -0
  11. package/dist/renderer/WaveRenderer.js +349 -10
  12. package/dist/renderer/WaveRenderer.js.map +1 -1
  13. package/dist/renderer/WaveRendererGPU.js +32 -2
  14. package/dist/renderer/WaveRendererGPU.js.map +1 -1
  15. package/dist/renderer/interaction.js +12 -0
  16. package/dist/renderer/interaction.js.map +1 -1
  17. package/dist/renderer/particleField.js +26 -2
  18. package/dist/renderer/particleField.js.map +1 -1
  19. package/dist/renderer/particleFieldGPU.js +6 -0
  20. package/dist/renderer/particleFieldGPU.js.map +1 -1
  21. package/dist/renderer/shaders.js +748 -13
  22. package/dist/renderer/shaders.js.map +1 -1
  23. package/dist/renderer/tsl/dissolve.js +56 -0
  24. package/dist/renderer/tsl/dissolve.js.map +1 -0
  25. package/dist/renderer/tsl/particleMaterial.js +46 -8
  26. package/dist/renderer/tsl/particleMaterial.js.map +1 -1
  27. package/dist/renderer/tsl/uniforms.js +40 -1
  28. package/dist/renderer/tsl/uniforms.js.map +1 -1
  29. package/dist/renderer/tsl/waveMaterial.js +275 -26
  30. package/dist/renderer/tsl/waveMaterial.js.map +1 -1
  31. package/dist/renderer/tsl/waveShape.js +31 -10
  32. package/dist/renderer/tsl/waveShape.js.map +1 -1
  33. package/dist/renderer/wavePath.js +189 -0
  34. package/dist/renderer/wavePath.js.map +1 -0
  35. package/dist/shell/createWave.d.ts +23 -3
  36. package/dist/shell/createWave.js +5 -4
  37. package/dist/shell/createWave.js.map +1 -1
  38. package/dist/shell/probe.d.ts +28 -0
  39. package/dist/shell/probe.js +58 -11
  40. package/dist/shell/probe.js.map +1 -1
  41. package/dist/standalone/wave3d.standalone.js +3401 -2108
  42. package/dist/standalone/wave3d.standalone.webgpu.js +7372 -5848
  43. package/dist/standalone.d.ts +2 -2
  44. package/dist/standalone.js +2 -2
  45. package/dist/studio/StudioWaveRenderer.d.ts +115 -8
  46. package/dist/studio/StudioWaveRenderer.js +588 -14
  47. package/dist/studio/StudioWaveRenderer.js.map +1 -1
  48. package/dist/studio/index.d.ts +2 -2
  49. package/dist/studio/index.js.map +1 -1
  50. package/dist/studio/randomize.js +0 -1
  51. package/dist/studio/randomize.js.map +1 -1
  52. package/package.json +1 -1
  53. package/skills/wave3d/SKILL.md +142 -5
@@ -1 +1 @@
1
- {"version":3,"file":"WaveRenderer.js","names":[],"sources":["../../src/renderer/WaveRenderer.ts"],"sourcesContent":["import * as THREE from \"three\";\nimport { EffectComposer } from \"three/addons/postprocessing/EffectComposer.js\";\nimport { RenderPass } from \"three/addons/postprocessing/RenderPass.js\";\nimport { OutputPass } from \"three/addons/postprocessing/OutputPass.js\";\nimport { ShaderPass } from \"three/addons/postprocessing/ShaderPass.js\";\nimport { UnrealBloomPass } from \"three/addons/postprocessing/UnrealBloomPass.js\";\nimport {\n vertexShader,\n fragmentShader,\n lineFragmentShader,\n postVertexShader,\n postFragmentShader,\n ditherFragmentShader,\n innerLightFragmentShader,\n halftoneFragmentShader,\n heatmapFragmentShader,\n paperTextureFragmentShader,\n halftoneCmykFragmentShader,\n} from \"./shaders\";\nimport { WaveGeometry } from \"./WaveGeometry\";\nimport { ParticleField, type ParticleFrame } from \"./particleField\";\n// Gates only — the interactivity RUNTIME (controller, applier tables, tilt sensor) is reached\n// through a dynamic import in loadInteraction(), so a scene that never interacts never fetches it.\n// Anything added here that pulls ./interaction statically undoes that; see interactionGates.ts.\nimport {\n interactionActive,\n anyPointerFxActive,\n wavePointerFxActive,\n waveRipplesActive,\n RIPPLE_SLOTS,\n} from \"./interactionGates\";\nimport type { InteractionController } from \"./interaction\";\nimport type { TiltStatus } from \"./tilt\";\n\n/** The lazily-fetched interactivity runtime. Held alongside the controller so the per-frame binding\n * writes can reach the applier tables without importing them eagerly. */\ntype InteractionModule = typeof import(\"./interaction\");\nimport {\n buildPaletteTexture,\n configurePaletteTexture,\n paletteSignature,\n PALETTE_MAPS,\n paletteMapCanvas,\n canvasToTexture,\n loadPaletteImage,\n buildBackgroundGradientCanvas,\n buildBackgroundMeshCanvas,\n buildBackgroundImageCanvas,\n drawBackgroundMediaFrame,\n} from \"./palette\";\nimport { buildHeroPaletteCanvas, buildHeroPaletteTexture } from \"./heroPalette\";\nimport {\n MAX_COLORS,\n MAX_LIGHTS,\n MAX_MESH_POINTS,\n MAX_NOISE_BANDS,\n ensureStudioConfig,\n} from \"../config/model\";\nimport type { StudioConfig, WaveConfig, BlendMode, CameraFit } from \"../config/model\";\n\nconst BASE_SEGMENTS = 220; // base segment count along the ribbon; denser = smoother (scaled down per wave — see get segments)\n\n/** Reference frame (world units) the orthographic camera fills at cameraZoom 1. The wave is\n * framed by mapping this FRAME_W × FRAME_H rectangle (centred on cameraTarget) onto the canvas,\n * so a given cameraZoom / cameraTarget frames the wave the SAME at any canvas size or aspect\n * (only the margin differs). FRAME_H = FRAME_W / (16/9) makes the reference a 16:9 rectangle.\n * This is what makes a saved preset reproduce on anyone's screen. See {@link frameZoom} for how\n * a canvas of a different aspect is reconciled against it. */\nexport const FRAME_W = 1333;\nexport const FRAME_H = 750;\n\n/**\n * The responsive base zoom: how many device pixels one world unit occupies so the FRAME_W × FRAME_H\n * reference lands on a `dw × dh` (device px) canvas under `fit`, then clamped so at least\n * `minVisibleWidth` of the frame's width survives.\n *\n * Shared by the renderer (which applies it) and the studio (which inverts it to persist a\n * scroll-zoom back into config.cameraZoom) — one implementation, so the two cannot drift.\n *\n * The clamp is a pure zoom CEILING layered on top of the fit, which is what lets both knobs\n * coexist: it can only widen the view, never tighten it, so it is inert for `contain`/`width`\n * (already at or below that zoom) and bites exactly where the crop hurts — `cover`/`height` on a\n * canvas narrower than 16:9. `minVisibleWidth` 0 disables it and restores pure-fit behaviour.\n */\nexport function frameZoom(dw: number, dh: number, fit: CameraFit, minVisibleWidth = 0): number {\n const byWidth = dw / FRAME_W;\n const byHeight = dh / FRAME_H;\n let zoom: number;\n switch (fit) {\n case \"contain\": // fit the whole frame — reveals world beyond it on the long axis\n zoom = Math.min(byWidth, byHeight);\n break;\n case \"width\": // horizontal composition identical at every aspect\n zoom = byWidth;\n break;\n case \"height\":\n zoom = byHeight;\n break;\n default: // \"cover\" — fill both axes, crop the overflow\n zoom = Math.max(byWidth, byHeight);\n }\n if (minVisibleWidth > 0) {\n // Visible world width = dw / zoom; hold it at ≥ FRAME_W × minVisibleWidth.\n zoom = Math.min(zoom, dw / (FRAME_W * minVisibleWidth));\n }\n return zoom;\n}\n\nexport interface WaveRendererOptions {\n /** Honor prefers-reduced-motion by freezing animation. Default true. */\n respectReducedMotion?: boolean;\n /**\n * Skip the intro time-ramp (the ~1s ease-in of animation on load), rendering at full speed\n * immediately. The studio passes `import.meta.env.DEV` here so a fresh renderer on every HMR\n * hot-swap doesn't replay the ease-in (which reads as a \"speed up\" when you tab back). Default\n * false — production embeds keep the ease-in. Ignored while paused (a paused frame is always the\n * full frame). Default false.\n */\n skipIntroRamp?: boolean;\n}\n\n/**\n * Per-wave 2D palette texture (+ optional looping video). One instance per wave, so each\n * wave carries its own palette. Guarded by a signature so it only rebuilds when that wave's\n * palette actually changes (not every refresh).\n */\nclass WavePalette {\n texture?: THREE.Texture;\n private sig = \"\";\n private video?: HTMLVideoElement;\n private videoUrl = \"\";\n private failedUrl = \"\";\n\n constructor(\n private readonly makeVideo: (url: string) => HTMLVideoElement,\n private readonly onVideoReady: () => void,\n ) {}\n\n /** Rebuild this wave's palette texture from its config (video / custom image / stops /\n * built-in map / hero LUT) and point the wave's palette uniforms at it. */\n apply(cfg: WaveConfig, uniforms: Record<string, THREE.IUniform>): void {\n const videoUrl = cfg.paletteVideoUrl;\n const url = cfg.paletteImageUrl;\n const source = cfg.paletteSource ?? \"hero\";\n let sig: string;\n let build: () => THREE.Texture;\n if (videoUrl) {\n this.ensureVideo(videoUrl);\n if (this.video?.readyState && this.video.readyState >= 2) {\n sig = \"video|\" + videoUrl;\n build = () => configurePaletteTexture(new THREE.VideoTexture(this.video!));\n } else {\n sig = \"video-loading|\" + videoUrl;\n build = () => buildHeroPaletteTexture();\n }\n } else if (url) {\n this.clearVideo();\n sig = \"url|\" + url;\n build = () => loadPaletteImage(url);\n } else if (source === \"stops\") {\n this.clearVideo();\n const opts = {\n stops: cfg.palette,\n edgeColor: cfg.paletteEdgeColor ?? \"#8e9dff\",\n edgeAmount: cfg.paletteEdgeAmount ?? 0.3,\n };\n sig = \"stops|\" + paletteSignature(opts);\n build = () => buildPaletteTexture(opts);\n } else if (PALETTE_MAPS[source]) {\n this.clearVideo();\n sig = \"map|\" + source;\n build = () => canvasToTexture(paletteMapCanvas(PALETTE_MAPS[source]));\n } else {\n this.clearVideo();\n sig = \"hero\";\n build = () => buildHeroPaletteTexture();\n }\n if (sig !== this.sig || !this.texture) {\n this.texture?.dispose();\n this.texture = build();\n this.sig = sig;\n }\n uniforms.uPalette.value = this.texture;\n uniforms.uUsePalette.value = cfg.usePaletteTexture === false ? 0 : 1;\n uniforms.uPaletteRaw.value = 1;\n }\n\n /** Play/pause this wave's palette video with the render loop. */\n syncPlayback(cfg: WaveConfig, running: boolean): void {\n const v = this.video;\n if (!v) return;\n const active =\n running &&\n cfg.gradientType !== \"mesh\" &&\n cfg.usePaletteTexture !== false &&\n !!cfg.paletteVideoUrl;\n if (active) void v.play().catch(() => {});\n else v.pause();\n }\n\n private ensureVideo(url: string): void {\n if (this.videoUrl === url || this.failedUrl === url) return;\n this.clearVideo();\n this.videoUrl = url;\n const video = this.makeVideo(url);\n video.addEventListener(\n \"loadeddata\",\n () => {\n this.video = video;\n this.failedUrl = \"\";\n this.sig = \"\";\n this.onVideoReady();\n },\n { once: true },\n );\n video.addEventListener(\n \"error\",\n () => {\n this.failedUrl = url;\n this.sig = \"\";\n this.onVideoReady();\n },\n { once: true },\n );\n this.video = video;\n video.load();\n }\n\n private clearVideo(): void {\n if (this.video) {\n this.video.pause();\n this.video.removeAttribute(\"src\");\n this.video.load();\n }\n this.video = undefined;\n this.videoUrl = \"\";\n }\n\n dispose(): void {\n this.texture?.dispose();\n this.texture = undefined;\n this.clearVideo();\n this.failedUrl = \"\";\n }\n}\n\n/**\n * The uniform surface both backends expose. The GLSL path holds `THREE.IUniform`s; the TSL path\n * holds TSL uniform nodes and array wrappers. Both are written the same way — `u.uFoo.value = x`,\n * `u.uColors.value[i].set(...)` — which is what lets the ~116 config-sync writes in `refresh()`\n * stay backend-agnostic.\n */\nexport type WaveUniforms = Record<string, { value: unknown }>;\n\n/** A wave material, whichever backend built it. */\nexport type WaveMaterial = THREE.Material & { uniforms: WaveUniforms };\n\n/**\n * A wave's particle field, whichever backend built it.\n *\n * The TSL field is a `THREE.Sprite` where the GLSL one is a `THREE.Points` (WebGPU has no point\n * size), so the scene node is reached through `object` rather than either concrete type.\n */\nexport interface WaveParticleField {\n readonly object: THREE.Object3D;\n sync(cfg: NonNullable<WaveConfig[\"particles\"]>, loopSeconds: number): void;\n frame(f: ParticleFrame, pixelRatio: number): void;\n configure(shape: {\n defines?: Record<string, string>;\n uniforms?: Record<string, { value: unknown }>;\n matrixWorld: THREE.Matrix4;\n speed: number;\n seed: number;\n }): void;\n setTime(t: number): void;\n dispose(): void;\n}\n\ntype Wave = {\n mesh: THREE.Mesh;\n material: WaveMaterial;\n geometry: WaveGeometry;\n /** This wave's own 2D palette texture + optional video. */\n palette: WavePalette;\n /** This wave's own particle / dust field — created when its `particles.count` first goes >0,\n * disposed at 0 / absent (the WavePalette lifecycle pattern). Undefined = no dust for this wave. */\n particleField?: WaveParticleField;\n};\n\n// Parse scratch: refresh() converts ~25 hex colours per wave per call (i.e. per slider input),\n// so reuse one Color instead of allocating each time.\nconst HEX_SCRATCH = new THREE.Color();\n\n/** Convert an sRGB hex string to a linear-space RGB vector (three's ColorManagement does the\n * sRGB→linear conversion on parse). Exported for the studio subclass's live light-uniform push. */\nexport function hexToLinearVec3(hex: string, target: THREE.Vector3): THREE.Vector3 {\n // three's ColorManagement (on by default in r169) already converts the sRGB hex to\n // LINEAR when parsing the hex — its .r/.g/.b are linear. Calling\n // convertSRGBToLinear() again would double-linearize (crushing greens → everything\n // turns red), so we read the components directly.\n const c = HEX_SCRATCH.set(hex);\n return target.set(c.r, c.g, c.b);\n}\n\n/**\n * Renders a gradient \"wave of light\" from a {@link StudioConfig}. Framework-agnostic:\n * it needs only a DOM container and a config. The studio mutates the config in\n * place and calls `refresh()` / `rebuild()`.\n */\nexport class WaveRenderer {\n readonly renderer: THREE.WebGLRenderer;\n /**\n * Whether this renderer can draw yet. The WebGL backend is ready the moment it is constructed;\n * the WebGPU subclass has to `await renderer.init()` first, and `renderOnce()` throws before that.\n * The base class is always ready, so the WebGL path is unaffected.\n */\n protected ready = true;\n protected readonly scene = new THREE.Scene();\n protected readonly camera: THREE.OrthographicCamera;\n protected readonly group = new THREE.Group();\n protected composer: EffectComposer;\n protected postPass: ShaderPass;\n /** Optional bloom pass — created lazily when bloomStrength first goes >0, removed at 0. */\n private bloomPass?: UnrealBloomPass;\n private ditherPass?: ShaderPass;\n private innerLightPass?: ShaderPass;\n private halftonePass?: ShaderPass;\n private heatmapPass?: ShaderPass;\n private paperTexturePass?: ShaderPass;\n private halftoneCmykPass?: ShaderPass;\n // Reused scratch for the per-frame per-wave particle placement (updateSceneFx) — no allocation.\n private readonly fxCenter = new THREE.Vector3();\n private readonly fxRight = new THREE.Vector3();\n private readonly fxUp = new THREE.Vector3();\n protected readonly container: HTMLElement;\n private readonly respectReducedMotion: boolean;\n private readonly skipIntroRamp: boolean;\n\n protected config: StudioConfig;\n protected waves: Wave[] = [];\n\n // 2D palette textures (+ any palette videos) live per-wave — see WavePalette on each Wave.\n private backgroundTexture?: THREE.Texture;\n private backgroundSig = \"\";\n private backgroundImage?: HTMLImageElement;\n private backgroundImageUrl = \"\";\n private failedBackgroundImageUrl = \"\";\n private backgroundVideo?: HTMLVideoElement;\n private backgroundVideoUrl = \"\";\n private failedBackgroundVideoUrl = \"\";\n private backgroundVideoCanvas?: HTMLCanvasElement;\n /** Authored default camera pose, for \"Reset camera\". */\n protected readonly homeCamPos = new THREE.Vector3();\n protected readonly homeCamTarget = new THREE.Vector3();\n\n // Reused scratch for per-frame clip-plane fitting (see updateClipPlanes) — hoisted so the\n // render loop allocates nothing.\n private readonly clipBox = new THREE.Box3();\n private readonly clipSphere = new THREE.Sphere();\n private readonly clipTmpA = new THREE.Vector3();\n private readonly clipTmpB = new THREE.Vector3();\n\n // ---- Interaction layer (optional; fetched only when config.interaction is active) ----\n /** Created by loadInteraction() once the runtime chunk lands, disposed when interaction turns off.\n * Stays undefined for the frames between the config turning it on and the chunk arriving. */\n protected interaction?: InteractionController;\n /** The chunk itself, kept for the applier tables. Set and cleared with `interaction`. */\n private interactionModule?: InteractionModule;\n /** A fetch already in flight, so a per-frame refresh can't start a second one. */\n private interactionLoading = false;\n /** setInteractionInput() calls made while the chunk was in flight, replayed once it lands. */\n private stagedInputs?: Map<string, number>;\n /** Set by dispose(), so an in-flight chunk can't attach listeners to a dead renderer. */\n private disposed = false;\n /** Extra ortho-zoom MULTIPLIER from a cameraZoom binding (1 = none); applied in applyZoom().\n * Protected so the studio's writeCameraToConfig() can divide it back out (keep it out of config). */\n protected interactionZoom = 1;\n /** Extra time-offset DELTA from a timeOffset binding (0 = none); added in updateTime(). */\n private interactionTimeOffset = 0;\n /** Scene-binding out-params: appliers write into this, applyBindings() reads it back. */\n private readonly interactionSceneOut = { timeOffset: 0, zoom: 1 };\n\n private readonly timer = new THREE.Timer();\n private time = 0;\n private rafId = 0;\n protected running = false;\n private started = false;\n\n private visible = true;\n private pageVisible = true;\n private reducedMotion = false;\n /** Intro ramp: eases animation time 0→1 over ~1s on load (when config.introRamp). */\n private introTimeRamp = 0;\n\n private readonly resizeObserver: ResizeObserver;\n private readonly intersectionObserver: IntersectionObserver;\n private readonly motionQuery: MediaQueryList;\n /** Re-armed at the live devicePixelRatio on every change — see watchDpr(). */\n private dprQuery?: MediaQueryList;\n /** Pending coalesced resize (0 = none), and the metrics the last resize() actually applied. */\n private resizeRaf = 0;\n private lastResize?: { w: number; h: number; dpr: number };\n\n protected capturing = false;\n /** Fixed backing-buffer dimensions used by the studio's visible export frame. Embeds leave\n * this unset and continue to resize responsively with their container and device DPR. */\n private outputSize?: { width: number; height: number };\n\n constructor(container: HTMLElement, config: StudioConfig, options: WaveRendererOptions = {}) {\n this.container = container;\n this.config = ensureStudioConfig(config);\n this.respectReducedMotion = options.respectReducedMotion ?? true;\n this.skipIntroRamp = options.skipIntroRamp ?? false;\n\n this.renderer = this.createRenderer();\n this.renderer.outputColorSpace = THREE.SRGBColorSpace;\n this.renderer.setClearColor(0x000000, 0);\n container.appendChild(this.renderer.domElement);\n\n // Resilience: if the GPU drops the context (memory pressure, sleep/wake), don't\n // let the browser hard-crash the page — prevent the default and rebuild on restore.\n this.renderer.domElement.addEventListener(\"webglcontextlost\", this.onContextLost, false);\n this.renderer.domElement.addEventListener(\n \"webglcontextrestored\",\n this.onContextRestored,\n false,\n );\n\n // Orthographic, framed in device pixels: resize() sets the frustum to the canvas size, and\n // the mesh is scaled up so the wave overflows the frame, leaving only the twist on screen.\n // The left/right/top/bottom bounds here are placeholders overwritten by the first resize();\n // near/far are placeholders too — updateClipPlanes() refits them to the scene every frame so\n // no camera angle ever clips the wave (a fixed slab does — see updateClipPlanes).\n this.camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 1, 10000);\n this.camera.position.set(\n config.cameraPosition.x,\n config.cameraPosition.y,\n config.cameraPosition.z,\n );\n this.camera.zoom = config.cameraZoom ?? 1;\n this.camera.lookAt(config.cameraTarget.x, config.cameraTarget.y, config.cameraTarget.z);\n this.camera.updateProjectionMatrix();\n // Remember the authored default pose so \"Reset camera\" returns to it (orbit mutates\n // config.cameraPosition, so we can't read it back from config later).\n this.homeCamPos.copy(this.camera.position);\n this.homeCamTarget.set(config.cameraTarget.x, config.cameraTarget.y, config.cameraTarget.z);\n this.scene.add(this.group);\n\n this.composer = new EffectComposer(this.renderer);\n this.composer.addPass(new RenderPass(this.scene, this.camera));\n this.postPass = new ShaderPass({\n uniforms: {\n tDiffuse: { value: null },\n uResolution: { value: new THREE.Vector2(1, 1) },\n uBlurAmount: { value: config.blur },\n uBlurSamples: { value: 6 },\n uGrainAmount: { value: config.grain },\n uTime: { value: 0 },\n },\n vertexShader: postVertexShader,\n fragmentShader: postFragmentShader,\n });\n this.composer.addPass(this.postPass);\n this.composer.addPass(new OutputPass());\n\n this.motionQuery = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n this.reducedMotion = this.respectReducedMotion && this.motionQuery.matches;\n this.motionQuery.addEventListener(\"change\", this.onMotionChange);\n document.addEventListener(\"visibilitychange\", this.onVisibilityChange);\n\n this.intersectionObserver = new IntersectionObserver(\n (entries) => {\n this.visible = entries[0]?.isIntersecting ?? true;\n this.updateRunning();\n },\n { rootMargin: \"100px\" },\n );\n this.intersectionObserver.observe(container);\n\n this.resizeObserver = new ResizeObserver(this.onResize);\n this.resizeObserver.observe(container);\n this.watchDpr(); // the CSS box can stay put while devicePixelRatio moves under it\n\n this.applyBackground();\n this.buildWaves();\n this.resize();\n }\n\n /**\n * Build the backing three renderer. Overridden by the WebGPU subclass; called from the\n * constructor, so it must not depend on subclass FIELDS (prototype methods are available during\n * `super()`, field initialisers are not).\n */\n protected createRenderer(): THREE.WebGLRenderer {\n return new THREE.WebGLRenderer({\n // antialias smooths edges; preserveDrawingBuffer keeps the drawing buffer readable so we\n // can export PNG/WebM. Both cost a little performance, but this authoring tool needs them.\n antialias: true,\n alpha: true,\n preserveDrawingBuffer: true,\n powerPreference: \"high-performance\",\n });\n }\n\n /**\n * Await any asynchronous backend startup. A no-op on WebGL; the WebGPU subclass overrides it to\n * `await renderer.init()` and then draw the first frame. Safe to call more than once.\n */\n async init(): Promise<void> {}\n\n private get segments(): number {\n // Scale detail down as waves multiply, so total geometry stays bounded.\n const q = this.config.quality / Math.sqrt(Math.max(1, this.config.waves.length));\n return THREE.MathUtils.clamp(Math.round(BASE_SEGMENTS * q), 24, 360);\n }\n\n private makeUniforms(): Record<string, THREE.IUniform> {\n const colors: THREE.Vector3[] = [];\n const colorPos: number[] = [];\n for (let i = 0; i < MAX_COLORS; i++) {\n colors.push(new THREE.Vector3(1, 1, 1));\n colorPos.push(MAX_COLORS > 1 ? i / (MAX_COLORS - 1) : 0);\n }\n const meshPointPos: THREE.Vector2[] = [];\n const meshPointColor: THREE.Vector3[] = [];\n const meshPointInfluence: number[] = [];\n for (let i = 0; i < MAX_MESH_POINTS; i++) {\n meshPointPos.push(new THREE.Vector2(0.5, 0.5));\n meshPointColor.push(new THREE.Vector3(1, 1, 1));\n meshPointInfluence.push(0.65);\n }\n const lightPos: THREE.Vector3[] = [];\n const lightColor: THREE.Vector3[] = [];\n const lightIntensity: number[] = [];\n for (let i = 0; i < MAX_LIGHTS; i++) {\n lightPos.push(new THREE.Vector3());\n lightColor.push(new THREE.Vector3(1, 1, 1));\n lightIntensity.push(0);\n }\n const bandBounds: THREE.Vector4[] = [];\n const bandParams: THREE.Vector4[] = [];\n const bandParaPow: number[] = [];\n for (let i = 0; i < MAX_NOISE_BANDS; i++) {\n bandBounds.push(new THREE.Vector4());\n bandParams.push(new THREE.Vector4());\n bandParaPow.push(0);\n }\n // Click-ripple ring buffer (read only under POINTER_RIPPLES). Sized to RIPPLE_SLOTS.\n const rippleOrigin: THREE.Vector2[] = [];\n const rippleAge: number[] = [];\n const rippleAmp: number[] = [];\n for (let i = 0; i < RIPPLE_SLOTS; i++) {\n rippleOrigin.push(new THREE.Vector2());\n rippleAge.push(0);\n rippleAmp.push(0);\n }\n return {\n // Deformation (vertex)\n uTime: { value: 0 },\n uSpeed: { value: 0.05 },\n uSeed: { value: 0 },\n uDispFreqX: { value: 0.003234 },\n uDispFreqZ: { value: 0.00799 },\n uDispAmount: { value: 6.051 },\n uDetailFreq: { value: 0.04 },\n uDetailAmount: { value: 0 }, // 2nd displacement octave (read only under DETAIL_OCTAVE)\n uTwFreqX: { value: -0.055 },\n uTwFreqY: { value: 0.077 },\n uTwFreqZ: { value: -0.518 },\n uTwPowX: { value: 3.95 },\n uTwPowY: { value: 5.85 },\n uTwPowZ: { value: 6.33 },\n uLoopSeconds: { value: 0 }, // seamless-loop period (read only under the LOOP_MOTION define)\n // Colour + light (fragment)\n uColors: { value: colors },\n uColorPos: { value: colorPos },\n uColorCount: { value: 2 },\n uGradType: { value: 0 },\n uGradAngle: { value: 0 },\n uGradShift: { value: 0.15 },\n uMeshPointPos: { value: meshPointPos },\n uMeshPointColor: { value: meshPointColor },\n uMeshPointInfluence: { value: meshPointInfluence },\n uMeshPointCount: { value: 0 },\n uMeshSoftness: { value: 0.62 },\n uPalette: { value: null },\n uUsePalette: { value: 1 },\n uPaletteRaw: { value: 1 },\n uPaletteScale: { value: new THREE.Vector2(1, 1) },\n uPaletteOffset: { value: new THREE.Vector2(0, 0) },\n uPaletteRotation: { value: 0 },\n uDebug: { value: 0 },\n uSheen: { value: 1 },\n uRoundness: { value: 0.35 },\n uIridescence: { value: 0 },\n uDepthTint: { value: 0 }, // solid-theme depth tint (read only under DEPTH_TINT)\n uDepthTintColor: { value: new THREE.Vector3() },\n uHueShift: { value: 0 },\n uContrast: { value: 1 },\n uSaturation: { value: 1 },\n uFiberCount: { value: 90 },\n uFiberStrength: { value: 0.25 },\n uTexture: { value: 0 },\n uCreaseLight: { value: 0.15 },\n uCreaseSharpness: { value: 2.0 },\n uCreaseSoftness: { value: 1.0 },\n uEdgeFade: { value: 0.06 },\n uEdgeFeather: { value: 0.1 }, // ribbon-edge softness (read only under EDGE_FEATHER)\n uOpacity: { value: 1 },\n uSquared: { value: 1 }, // \"squared\" deep-colour mode: square the colour in-shader (see applyBlendMode)\n // Seed from the CURRENT drawing buffer, not (1,1): resize() is the only other writer, so a wave\n // added afterwards (wave count raised, multi-wave preset/share link loaded) would keep (1,1)\n // until the next resize — and the edgeFade vignette divides gl_FragCoord by it, so\n // `1 - smoothstep(1-edgeFade, 1, sc)` collapses to 0 and the whole wave renders invisible.\n uResolution: { value: this.renderer.getDrawingBufferSize(new THREE.Vector2()) },\n uAmbient: { value: 0.45 },\n uNumLights: { value: 1 },\n uLightPos: { value: lightPos },\n uLightColor: { value: lightColor },\n uLightIntensity: { value: lightIntensity },\n uNumNoiseBands: { value: 0 },\n uNoiseBandBounds: { value: bandBounds },\n uNoiseBandParams: { value: bandParams },\n uNoiseBandParaPow: { value: bandParaPow },\n // Wireframe thin-line theme (used only by lineFragmentShader)\n uLineAmount: { value: 425 },\n uLineThickness: { value: 1 },\n uLineDerivativePower: { value: 0.95 },\n uMaxWidth: { value: 1232 },\n uClearColor: { value: new THREE.Vector3(1, 1, 1) },\n // Interaction / pointer field. ALWAYS present in JS (read only under POINTER_FX /\n // POINTER_RIPPLES); three uploads them only when the compiled program declares them, so their\n // presence never affects a non-interactive wave (byte-identity precedent: uDetailAmount).\n // Helix (vertex, under HELIX) + wireframe rungs (line fragment, under RUNGS). Always present\n // JS-side; three uploads them only when the compiled program declares them, so a wave with no\n // helix / no rungs is untouched (byte-identity precedent: uDetailAmount).\n uHelixTurns: { value: 0 },\n uHelixRadius: { value: 0 },\n uHelixRoll: { value: 0 },\n uHelixPhase: { value: 0 },\n // Radial fan (vertex, under RADIAL). Always present JS-side; three uploads them only when the\n // compiled program declares them, so a non-radial wave is untouched (precedent: uDetailAmount).\n uRadialAmount: { value: 0 },\n uRadialArc: { value: 160 },\n uRadialSpread: { value: 1 },\n uRadialRadius: { value: 40 },\n uRadialCenter: { value: 0 },\n uRungAmount: { value: 0 },\n uRungThickness: { value: 1 },\n uPointer: { value: new THREE.Vector2(0, 0) }, // smoothed pointer NDC\n uPointerActive: { value: 0 }, // presence ramp × per-wave influence\n uPointerRadius: { value: 0.6 }, // falloff radius in NDC-y (config radius × 2)\n uPointerAspect: { value: 1 }, // drawing-buffer dw/dh\n uPointerAgitate: { value: 0 },\n uPointerPush: { value: 0 }, // signed membrane dome at the cursor (+ repel / − attract)\n uPointerWake: { value: 0 }, // drag-wake trough amplitude\n uPointerVel: { value: new THREE.Vector2(0, 0) }, // smoothed pointer velocity, NDC/s (wake dir)\n uShapeFlow: { value: 0 }, // ribbon-flow footprint stretch (0 = plain circle)\n uPointerThin: { value: 0 },\n uPointerHue: { value: 0 },\n uPointerLighten: { value: 0 },\n uPointerRipple: { value: 0 }, // this wave's ripple amplitude (scales the shared envelope)\n uRippleOrigin: { value: rippleOrigin },\n uRippleAge: { value: rippleAge },\n uRippleAmp: { value: rippleAmp },\n };\n }\n\n /** Vertex-shader #defines for a wave: TWIST_MOTION (per-wave animated twist wobble) and\n * LOOP_MOTION (scene-level seamless loop). Both select #ifdef-gated code paths; an empty\n * object compiles the default (linear-time) program. */\n private waveDefines(sc: WaveConfig | undefined): Record<string, string> {\n const defines: Record<string, string> = {};\n if (sc?.twistMotion) defines.TWIST_MOTION = \"\";\n if ((this.config.loopSeconds ?? 0) > 0) defines.LOOP_MOTION = \"\";\n // A detailAmount binding on THIS wave also needs the octave compiled (else it no-ops on a wave\n // authored at 0). Only this wave's bindings matter now (bindings are per wave).\n const bindsDetail =\n sc?.interaction?.bindings?.some((b) => b.target === \"detailAmount\") ?? false;\n if ((sc?.detailAmount ?? 0) !== 0 || bindsDetail) defines.DETAIL_OCTAVE = \"\";\n if ((sc?.depthTint ?? 0) > 0) defines.DEPTH_TINT = \"\";\n if ((sc?.edgeFeather ?? 0.1) !== 0.1) defines.EDGE_FEATHER = \"\";\n // Helix: turns/phase alone move nothing — the block only displaces via radius or roll, so those\n // two are what decide whether it's compiled at all. A helix binding counts too: driving\n // helixRadius up from an authored 0 has to have somewhere to land (as with detailAmount above).\n const bindsHelix =\n sc?.interaction?.bindings?.some((b) => b.target.startsWith(\"helix\")) ?? false;\n if ((sc?.helixRadius ?? 0) !== 0 || (sc?.helixRoll ?? 0) !== 0 || bindsHelix) {\n defines.HELIX = \"\";\n }\n // Radial fan: amount 0 is the identity mix, so it alone decides whether the block is compiled.\n // Not binding-driveable in v1 (not in WAVE_TARGET_NAMES), so no bindsRadial term is needed.\n if ((sc?.radialAmount ?? 0) !== 0) defines.RADIAL = \"\";\n // Rungs live in the wireframe fragment shader only; setting the define on a solid wave would\n // key a second, identical program for nothing.\n if (sc?.theme === \"wireframe\" && (sc.rungAmount ?? 0) > 0) defines.RUNGS = \"\";\n // Pointer field (per wave, config-only, so input never triggers a recompile). Ripples nest inside.\n if (sc && wavePointerFxActive(this.config, sc)) {\n defines.POINTER_FX = \"\";\n if (waveRipplesActive(this.config, sc)) defines.POINTER_RIPPLES = \"\";\n }\n return defines;\n }\n\n private addWave(): void {\n const geo = new WaveGeometry(this.segments);\n // Initialise defines/fragment/blend from the wave this material will represent, so the\n // first refresh() doesn't force a needless program recompile. Falls back to the first wave.\n const sc = this.config.waves[this.waves.length] ?? this.config.waves[0];\n const material = this.createWaveMaterial(sc);\n // Blending (incl. the squaring blend) is set from the wave's blendMode — see applyBlendMode —\n // so it survives refresh() instead of being a dead constructor flag.\n this.applyBlendMode(material, sc?.blendMode ?? \"squared\");\n const mesh = new THREE.Mesh(geo.geometry, material);\n mesh.frustumCulled = false;\n this.group.add(mesh);\n const palette = new WavePalette(\n (url) => this.createLoopingVideo(url),\n () => {\n this.updatePaletteTextures();\n this.syncVideoPlayback();\n if (!this.running) this.renderOnce();\n },\n );\n this.waves.push({ mesh, material, geometry: geo, palette });\n }\n\n /**\n * Build the material for one wave. The GLSL backend compiles a ShaderMaterial with `#define`\n * variants; the WebGPU subclass builds a TSL node graph instead. Both expose the same `uniforms`\n * surface, so everything downstream in `refresh()` is shared.\n */\n protected createWaveMaterial(sc: WaveConfig | undefined): WaveMaterial {\n return new THREE.ShaderMaterial({\n uniforms: this.makeUniforms(),\n // TWIST_MOTION / LOOP_MOTION select variant vertex-shader paths. Toggled live in refresh().\n defines: this.waveDefines(sc),\n vertexShader,\n // solid theme = surfaceColor shader; wireframe theme = thin-line shader.\n // Swapped live in refresh() when the wave's theme changes.\n fragmentShader: sc?.theme === \"wireframe\" ? lineFragmentShader : fragmentShader,\n transparent: true,\n depthTest: true,\n depthWrite: true,\n side: THREE.DoubleSide,\n }) as unknown as WaveMaterial;\n }\n\n /**\n * Re-select this wave's shader variant when its config changes shape (theme, twist motion, helix,\n * …). Returns true if the material needs recompiling. The GLSL backend swaps `defines` and the\n * fragment source; the WebGPU subclass rebuilds the node graph, since a TSL variant is a\n * different graph rather than a different define set.\n */\n protected applyWaveVariant(wave: Wave, sc: WaveConfig): boolean {\n const material = wave.material as unknown as THREE.ShaderMaterial;\n let changed = false;\n // Recompile the program when its #define set changes: TWIST_MOTION / DETAIL_OCTAVE / DEPTH_TINT\n // (per wave) and LOOP_MOTION (scene-level). Compare the whole set so any combination is handled.\n const wantDefines = this.waveDefines(sc);\n const curDefines = material.defines ?? {};\n if (Object.keys(wantDefines).sort().join(\",\") !== Object.keys(curDefines).sort().join(\",\")) {\n material.defines = wantDefines;\n changed = true;\n }\n // Swap the fragment shader when this wave's theme changes: solid surfaceColor <->\n // wireframe thin-line. Three recompiles the program on needsUpdate.\n const wantFrag = sc.theme === \"wireframe\" ? lineFragmentShader : fragmentShader;\n if (material.fragmentShader !== wantFrag) {\n material.fragmentShader = wantFrag;\n changed = true;\n }\n return changed;\n }\n\n /**\n * Apply config.blendMode to a material. \"squared\" (the default) is the hero blend:\n * CustomBlending with AddEquation, src = SrcColorFactor, dst = ZeroFactor, so the\n * framebuffer result is fragColor² — the squaring deepens the colours into the vivid\n * hero look (without it the wave reads pastel). \"additive\"/\"normal\"/\"multiply\" are\n * authoring overrides. Multiply uses Three's premultiplied-alpha path; the custom\n * fragment shaders premultiply their output when Three injects PREMULTIPLIED_ALPHA.\n * Returns true if material state changed (caller flags needsUpdate).\n */\n protected applyBlendMode(material: WaveMaterial, mode: BlendMode): boolean {\n // \"squared\" is the deep hero look. It used to be a framebuffer-squaring CustomBlending\n // (src·src, dst×0) — which REPLACES the destination rather than compositing over it, so any\n // semi-transparent pixel (soft ribbon edges, and the large near-edge-on regions at oblique\n // camera angles) wiped the framebuffer's colour AND alpha, punching dark / see-through holes\n // through the background and through other waves. On a transparent page you never saw it; on\n // an opaque background or with overlapping waves it's the \"chunks vanish\" artifact.\n //\n // Fix: do the colour-squaring in the shader (uSquared) and composite it with ordinary\n // premultiplied alpha (NormalBlending). Over an opaque body the result is identical (col²);\n // soft edges now blend into what's behind them instead of erasing it.\n const squared = mode === \"squared\";\n const blending =\n mode === \"additive\"\n ? THREE.AdditiveBlending\n : mode === \"multiply\"\n ? THREE.MultiplyBlending\n : THREE.NormalBlending; // \"squared\" (deep, via uSquared) and \"normal\" both composite\n // Premultiplied so the squared/multiply colour composites correctly (the shaders premultiply\n // their output under the PREMULTIPLIED_ALPHA define Three injects for this).\n const premultipliedAlpha = mode === \"multiply\" || squared;\n material.uniforms.uSquared.value = squared ? 1 : 0;\n if (material.blending === blending && material.premultipliedAlpha === premultipliedAlpha) {\n return false;\n }\n material.blending = blending;\n material.premultipliedAlpha = premultipliedAlpha;\n return true;\n }\n\n private disposeWaves(): void {\n for (const s of this.waves) {\n this.group.remove(s.mesh);\n s.material.dispose();\n s.geometry.dispose();\n s.palette.dispose();\n if (s.particleField) {\n this.scene.remove(s.particleField.object);\n s.particleField.dispose();\n }\n }\n this.waves = [];\n }\n\n /**\n * Reconcile the wave pool to `waveCount` WITHOUT tearing everything down:\n * keep existing waves (so the compiled shader program is never deleted and\n * re-compiled — that churn can crash some GPU drivers), add/remove only the\n * delta, and resize each geometry to the current quality.\n */\n private buildWaves(): void {\n const target = Math.max(1, this.config.waves.length);\n while (this.waves.length > target) {\n const s = this.waves.pop();\n if (!s) break;\n this.group.remove(s.mesh);\n s.material.dispose();\n s.geometry.dispose();\n s.palette.dispose();\n // Also drop this wave's particle field, or its THREE.Points lingers in the scene and sheds stray\n // dust onto later frames — notably the shared thumbnail renderer (preset previews after a\n // particle-heavy preset showed leftover white specks).\n if (s.particleField) {\n this.scene.remove(s.particleField.object);\n s.particleField.dispose();\n }\n }\n while (this.waves.length < target) this.addWave();\n\n const segments = this.segments;\n this.waves.forEach((s, i) => {\n s.geometry.resize(segments);\n s.mesh.renderOrder = i;\n });\n this.refresh();\n }\n\n /** Re-read per-frame-independent values from the (mutated) config. */\n refresh(): void {\n this.applyBackground();\n this.applyPost();\n this.applyParticles();\n this.syncInteraction(); // create/dispose the interaction controller as config toggles it\n // Once an external driver (orbit / edit gizmo) owns the camera, don't fight it here; the shell\n // (no orbit) applies the saved camera position/target so it matches the authored view.\n if (!this.isCameraExternallyDriven()) {\n const p = this.config.cameraPosition;\n const tg = this.config.cameraTarget;\n this.camera.position.set(p.x, p.y, p.z);\n this.camera.lookAt(tg.x, tg.y, tg.z);\n }\n\n this.waves.forEach((wave, i) => {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n const u = wave.material.uniforms;\n if (this.applyBlendMode(wave.material, sc.blendMode)) wave.material.needsUpdate = true;\n if (this.applyWaveVariant(wave, sc)) wave.material.needsUpdate = true;\n\n const stops = [...sc.palette].sort((a, b) => a.pos - b.pos);\n const colorCount = Math.max(1, Math.min(stops.length, MAX_COLORS));\n const colors = u.uColors.value as THREE.Vector3[];\n const colorPos = u.uColorPos.value as number[];\n for (let c = 0; c < MAX_COLORS; c++) {\n const stop = stops[Math.min(c, colorCount - 1)] ?? { color: \"#ffffff\", pos: 0 };\n hexToLinearVec3(stop.color, colors[c]);\n colorPos[c] = stop.pos;\n }\n u.uColorCount.value = colorCount;\n u.uGradType.value =\n sc.gradientType === \"radial\"\n ? 1\n : sc.gradientType === \"conic\"\n ? 2\n : sc.gradientType === \"mesh\"\n ? 3\n : 0;\n u.uGradAngle.value = ((sc.gradientAngle ?? 0) * Math.PI) / 180;\n u.uGradShift.value = sc.gradientShift ?? 0;\n const meshPoints = sc.meshGradientPoints.slice(0, MAX_MESH_POINTS);\n const meshPointPos = u.uMeshPointPos.value as THREE.Vector2[];\n const meshPointColor = u.uMeshPointColor.value as THREE.Vector3[];\n const meshPointInfluence = u.uMeshPointInfluence.value as number[];\n for (let pointIndex = 0; pointIndex < MAX_MESH_POINTS; pointIndex++) {\n const point = meshPoints[pointIndex] ?? meshPoints[meshPoints.length - 1];\n if (!point) continue;\n meshPointPos[pointIndex].set(point.x, point.y);\n hexToLinearVec3(point.color, meshPointColor[pointIndex]);\n meshPointInfluence[pointIndex] = point.influence;\n }\n u.uMeshPointCount.value = meshPoints.length;\n u.uMeshSoftness.value = sc.meshGradientSoftness;\n (u.uPaletteScale.value as THREE.Vector2).set(\n sc.paletteTextureScale?.x ?? 1,\n sc.paletteTextureScale?.y ?? 1,\n );\n (u.uPaletteOffset.value as THREE.Vector2).set(\n sc.paletteTextureOffset?.x ?? 0,\n sc.paletteTextureOffset?.y ?? 0,\n );\n u.uPaletteRotation.value = ((sc.paletteTextureRotation ?? 0) * Math.PI) / 180;\n u.uHueShift.value = sc.hueShift;\n u.uContrast.value = sc.colorContrast;\n u.uSaturation.value = sc.colorSaturation;\n // Wireframe thin-line theme params (used only by lineFragmentShader). uClearColor is the\n // between-line colour = the page background (scene), fed in linear space like the palette.\n u.uLineAmount.value = sc.lineAmount ?? 425;\n u.uLineThickness.value = sc.lineThickness ?? 1;\n u.uLineDerivativePower.value = sc.lineDerivativePower ?? 0.95;\n u.uRungAmount.value = sc.rungAmount ?? 0;\n u.uRungThickness.value = sc.rungThickness ?? 1;\n u.uMaxWidth.value = sc.maxWidth ?? 1232;\n hexToLinearVec3(this.config.background, u.uClearColor.value as THREE.Vector3);\n u.uFiberCount.value = sc.fiberCount;\n u.uFiberStrength.value = sc.fiberStrength;\n u.uTexture.value = sc.texture;\n u.uCreaseLight.value = sc.creaseLight;\n u.uCreaseSharpness.value = sc.creaseSharpness;\n u.uCreaseSoftness.value = sc.creaseSoftness;\n u.uSheen.value = sc.sheen ?? 1;\n u.uRoundness.value = sc.roundness ?? 0.35;\n u.uIridescence.value = sc.iridescence ?? 0;\n u.uDepthTint.value = sc.depthTint ?? 0;\n hexToLinearVec3(sc.depthTintColor ?? \"#0a2540\", u.uDepthTintColor.value as THREE.Vector3);\n u.uEdgeFade.value = sc.edgeFade;\n u.uEdgeFeather.value = sc.edgeFeather ?? 0.1;\n // Lights + ambient are scene-level (shared by every wave).\n const lights = this.config.lights ?? [];\n u.uAmbient.value = this.config.ambient ?? 0.45;\n u.uNumLights.value = Math.min(lights.length, MAX_LIGHTS);\n const lPos = u.uLightPos.value as THREE.Vector3[];\n const lCol = u.uLightColor.value as THREE.Vector3[];\n const lInt = u.uLightIntensity.value as number[];\n for (let li = 0; li < MAX_LIGHTS; li++) {\n const light = lights[li];\n if (light) {\n lPos[li].set(light.position.x, light.position.y, light.position.z);\n hexToLinearVec3(light.color, lCol[li]);\n lInt[li] = light.intensity;\n } else {\n lInt[li] = 0;\n }\n }\n // Noise bands (per-region fiber overrides) — per wave\n const bands = sc.noiseBands ?? [];\n u.uNumNoiseBands.value = Math.min(bands.length, MAX_NOISE_BANDS);\n const bBounds = u.uNoiseBandBounds.value as THREE.Vector4[];\n const bParams = u.uNoiseBandParams.value as THREE.Vector4[];\n const bPara = u.uNoiseBandParaPow.value as number[];\n for (let bi = 0; bi < MAX_NOISE_BANDS; bi++) {\n const band = bands[bi];\n if (band) {\n bBounds[bi].set(band.startX, band.endX, band.startY, band.endY);\n bParams[bi].set(band.feather, band.strength, band.frequency, band.colorAttenuation);\n bPara[bi] = band.parabolaPower;\n }\n }\n // Deformation (absolute per wave)\n u.uSpeed.value = sc.speed;\n u.uSeed.value = sc.seed;\n u.uLoopSeconds.value = this.config.loopSeconds ?? 0; // scene-level; shared by every wave\n u.uDispFreqX.value = sc.displaceFrequency.x;\n u.uDispFreqZ.value = sc.displaceFrequency.y;\n u.uDispAmount.value = sc.displaceAmount;\n u.uDetailFreq.value = sc.detailFrequency ?? 0.04;\n u.uDetailAmount.value = sc.detailAmount ?? 0;\n u.uTwFreqX.value = sc.twistFrequency.x;\n u.uTwFreqY.value = sc.twistFrequency.y;\n u.uTwFreqZ.value = sc.twistFrequency.z;\n u.uTwPowX.value = sc.twistPower.x;\n u.uTwPowY.value = sc.twistPower.y;\n u.uTwPowZ.value = sc.twistPower.z;\n u.uHelixTurns.value = sc.helixTurns ?? 0;\n u.uHelixRadius.value = sc.helixRadius ?? 0;\n u.uHelixRoll.value = sc.helixRoll ?? 0;\n u.uHelixPhase.value = sc.helixPhase ?? 0;\n u.uRadialAmount.value = sc.radialAmount ?? 0;\n u.uRadialArc.value = sc.radialArc ?? 160;\n u.uRadialSpread.value = sc.radialSpread ?? 1;\n u.uRadialRadius.value = sc.radialRadius ?? 40;\n u.uRadialCenter.value = sc.radialCenter ?? 0;\n // Mesh transform — each wave's ABSOLUTE scale / rotation / position, applied via\n // modelMatrix using THREE's Euler XYZ order so the on-screen orientation matches the\n // authored view.\n wave.mesh.scale.set(sc.scale.x, sc.scale.y, sc.scale.z);\n wave.mesh.rotation.set(\n THREE.MathUtils.degToRad(sc.rotation.x),\n THREE.MathUtils.degToRad(sc.rotation.y),\n THREE.MathUtils.degToRad(sc.rotation.z),\n );\n wave.mesh.position.set(sc.position.x, sc.position.y, sc.position.z);\n u.uOpacity.value = sc.opacity;\n });\n\n // Static pointer-field params. The falloff radius and ribbon-flow footprint stretch are SHARED\n // (scene-level); the hover amplitudes and ripple amplitude are PER WAVE. The dynamic pointer state\n // (position/velocity/presence/ripple envelopes) is pushed each frame in applyPointerField().\n const sharedRadius = (this.config.interaction?.radius ?? 0.3) * 2; // radius = fraction of viewport H\n const sharedFlow = this.config.interaction?.ribbonFlow ?? 0.8; // on by default; 0 = plain circle\n this.waves.forEach((wave, i) => {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n if (!wavePointerFxActive(this.config, sc)) return;\n const u = wave.material.uniforms;\n const h = sc.interaction?.hover;\n u.uPointerRadius.value = sharedRadius;\n u.uPointerAgitate.value = h?.agitate ?? 0;\n u.uPointerPush.value = h?.push ?? 0;\n u.uPointerWake.value = h?.wake ?? 0;\n u.uPointerThin.value = h?.thin ?? 0;\n u.uPointerHue.value = h?.hueShift ?? 0;\n u.uPointerLighten.value = h?.lighten ?? 0;\n u.uPointerRipple.value = sc.interaction?.press?.ripple ?? 0;\n u.uShapeFlow.value = sharedFlow;\n });\n\n this.updatePaletteTextures();\n this.syncVideoPlayback();\n\n // Whole-wave mirror (world-space flip ≈ screen flip for the near-frontal camera).\n this.group.scale.set(this.config.mirrorH ? -1 : 1, this.config.mirrorV ? -1 : 1, 1);\n\n this.onAfterRefresh();\n if (!this.running) this.renderOnce();\n }\n\n /** Point every wave's palette sampler at its own WavePalette texture (each rebuilt only\n * when that wave's palette actually changes — see WavePalette.apply). */\n private updatePaletteTextures(): void {\n this.waves.forEach((wave, i) => {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n wave.palette.apply(sc, wave.material.uniforms);\n });\n }\n\n /** Dev: 0 = normal, 1 = visualise the crease value, 2 = visualise derivative normal. */\n setDebug(v: number): void {\n for (const s of this.waves) s.material.uniforms.uDebug.value = v;\n this.renderOnce();\n }\n\n /** Rebuild geometry + waves (call when waveCount or quality changes). */\n rebuild(): void {\n this.buildWaves();\n }\n\n private applyBackground(): void {\n if (this.config.transparentBackground) {\n this.scene.background = null;\n this.renderer.setClearColor(0x000000, 0);\n this.onBackgroundChanged();\n return;\n }\n\n const matte = new THREE.Color(this.config.background);\n this.renderer.setClearColor(matte, 1);\n if (this.config.backgroundMode === \"color\") {\n this.applyColorBackground(matte);\n this.onBackgroundChanged();\n return;\n }\n // Live video is redrawn every frame, so cap its staging canvas near 1080p. Still images\n // and procedural maps retain the full 4K-capable background texture path.\n const { width, height } = this.backgroundCanvasSize(\n this.config.backgroundVideoUrl ? 2048 : 4096,\n );\n if (this.config.backgroundMode === \"gradient\") this.applyGradientBackground(width, height);\n else this.applyImageBackground(matte, width, height);\n this.onBackgroundChanged();\n }\n\n /**\n * Build a wave's particle field. The GLSL backend returns a `THREE.Points` field; the TSL backend\n * returns an instanced-sprite one wired to this wave's own uniform registry.\n */\n protected createParticleField(\n _wave: Wave,\n _sc: WaveConfig,\n onReady: () => void,\n ): WaveParticleField {\n return new ParticleField(onReady);\n }\n\n /**\n * Largest texture edge the backend will accept, used to cap the background canvas. WebGL reports\n * it on `capabilities`; WebGPU has no such object, so the subclass reads the device limit.\n */\n protected maxTextureSize(): number {\n return this.renderer.capabilities.maxTextureSize;\n }\n\n /**\n * Called whenever the scene background changes. A no-op on WebGL, where `EffectComposer`'s\n * RenderPass reads the scene afresh each frame; the WebGPU subclass rebuilds its post chain,\n * because a `pass()` node captures the background state at the point its render context is built.\n */\n protected onBackgroundChanged(): void {}\n\n private applyColorBackground(matte: THREE.Color): void {\n this.clearBackgroundVideo();\n this.backgroundTexture?.dispose();\n this.backgroundTexture = undefined;\n this.backgroundSig = \"\";\n this.scene.background = matte;\n }\n\n private applyGradientBackground(width: number, height: number): void {\n this.clearBackgroundVideo();\n const gradType = this.config.backgroundGradientType;\n if (gradType === \"mesh\") {\n const pts = this.config.backgroundMeshPoints ?? [];\n const softness = this.config.backgroundMeshSoftness ?? 0.62;\n const ptSig = pts\n .map((p) => `${p.color}@${p.x.toFixed(3)},${p.y.toFixed(3)},${p.influence.toFixed(3)}`)\n .join(\",\");\n const sig = [\"mesh\", softness.toFixed(3), ptSig, width, height].join(\"|\");\n if (sig !== this.backgroundSig || !this.backgroundTexture) {\n const canvas = buildBackgroundMeshCanvas(pts, softness, width, height);\n this.backgroundTexture?.dispose();\n this.backgroundTexture = canvasToTexture(canvas);\n this.backgroundSig = sig;\n }\n this.scene.background = this.backgroundTexture;\n return;\n }\n const source = this.config.backgroundGradientSource ?? \"stops\";\n const def = PALETTE_MAPS[source];\n const stops =\n source !== \"stops\" && def?.kind === \"gradient\" && def.stops\n ? def.stops\n : this.config.backgroundPalette;\n const stopSig = stops.map((stop) => `${stop.color}@${stop.pos.toFixed(3)}`).join(\",\");\n const sig = [\n \"gradient\",\n source,\n gradType,\n this.config.backgroundGradientAngle,\n stopSig,\n width,\n height,\n ].join(\"|\");\n if (sig !== this.backgroundSig || !this.backgroundTexture) {\n const canvas = buildBackgroundGradientCanvas({\n stops,\n type: gradType,\n angle: this.config.backgroundGradientAngle,\n width,\n height,\n });\n this.backgroundTexture?.dispose();\n this.backgroundTexture = canvasToTexture(canvas);\n this.backgroundSig = sig;\n }\n this.scene.background = this.backgroundTexture;\n }\n\n /** Image mode: a live video, a user-loaded image, or a built-in map. `matte` shows while an\n * async source is still loading. */\n private applyImageBackground(matte: THREE.Color, width: number, height: number): void {\n const fit = this.config.backgroundImageFit ?? \"cover\";\n const zoom = this.config.backgroundImageZoom ?? 1;\n const position = this.config.backgroundImagePosition ?? { x: 0, y: 0 };\n const videoUrl = this.config.backgroundVideoUrl;\n const customUrl = this.config.backgroundImageUrl;\n let source: CanvasImageSource;\n let sourceWidth: number;\n let sourceHeight: number;\n let sourceSig: string;\n if (videoUrl) {\n this.ensureBackgroundVideo(videoUrl);\n if (!this.backgroundVideo || this.backgroundVideo.readyState < 2) {\n this.scene.background = matte;\n return;\n }\n source = this.backgroundVideo;\n sourceWidth = this.backgroundVideo.videoWidth;\n sourceHeight = this.backgroundVideo.videoHeight;\n sourceSig = `video|${videoUrl}`;\n } else if (customUrl) {\n this.clearBackgroundVideo();\n if (\n !this.backgroundImage ||\n this.backgroundImageUrl !== customUrl ||\n !this.backgroundImage.complete\n ) {\n this.loadBackgroundImage(customUrl);\n this.scene.background = matte;\n return;\n }\n source = this.backgroundImage;\n sourceWidth = this.backgroundImage.naturalWidth;\n sourceHeight = this.backgroundImage.naturalHeight;\n sourceSig = `custom|${customUrl}`;\n } else {\n this.clearBackgroundVideo();\n const imageSource = this.config.backgroundImageSource ?? \"vaporwave\";\n const canvas =\n imageSource === \"hero\"\n ? buildHeroPaletteCanvas()\n : PALETTE_MAPS[imageSource]?.kind === \"image\"\n ? paletteMapCanvas(PALETTE_MAPS[imageSource], Math.max(width, height))\n : null;\n if (!canvas) {\n this.scene.background = matte;\n return;\n }\n source = canvas;\n sourceWidth = canvas.width;\n sourceHeight = canvas.height;\n sourceSig = `map|${imageSource}`;\n }\n\n const sig = [\n \"image\",\n sourceSig,\n fit,\n zoom,\n position.x,\n position.y,\n this.config.background,\n width,\n height,\n ].join(\"|\");\n if (sig !== this.backgroundSig || !this.backgroundTexture) {\n const canvas = buildBackgroundImageCanvas(\n source,\n sourceWidth,\n sourceHeight,\n width,\n height,\n fit,\n this.config.background,\n zoom,\n position.x,\n position.y,\n );\n this.backgroundTexture?.dispose();\n this.backgroundTexture = canvasToTexture(canvas);\n this.backgroundSig = sig;\n this.backgroundVideoCanvas = videoUrl ? canvas : undefined;\n }\n this.scene.background = this.backgroundTexture;\n }\n\n private backgroundCanvasSize(maxRequestedEdge = 4096): { width: number; height: number } {\n const rawWidth = this.outputSize?.width ?? Math.max(1, this.container.clientWidth);\n const rawHeight = this.outputSize?.height ?? Math.max(1, this.container.clientHeight);\n const maxEdge = Math.min(maxRequestedEdge, this.maxTextureSize());\n const scale = Math.min(1, maxEdge / Math.max(rawWidth, rawHeight));\n return {\n width: Math.max(1, Math.round(rawWidth * scale)),\n height: Math.max(1, Math.round(rawHeight * scale)),\n };\n }\n\n private loadBackgroundImage(url: string): void {\n if (this.backgroundImageUrl === url || this.failedBackgroundImageUrl === url) return;\n this.backgroundImageUrl = url;\n this.backgroundImage = undefined;\n const image = new Image();\n image.decoding = \"async\";\n if (!url.startsWith(\"data:\") && !url.startsWith(\"blob:\")) image.crossOrigin = \"anonymous\";\n image.addEventListener(\n \"load\",\n () => {\n if (this.config.backgroundImageUrl !== url) return;\n this.backgroundImage = image;\n this.failedBackgroundImageUrl = \"\";\n this.backgroundSig = \"\";\n this.applyBackground();\n if (!this.running) this.renderOnce();\n },\n { once: true },\n );\n image.addEventListener(\n \"error\",\n () => {\n if (this.config.backgroundImageUrl !== url) return;\n this.failedBackgroundImageUrl = url;\n this.backgroundSig = \"\";\n this.applyBackground();\n if (!this.running) this.renderOnce();\n },\n { once: true },\n );\n image.src = url;\n }\n\n private ensureBackgroundVideo(url: string): void {\n if (this.backgroundVideoUrl === url || this.failedBackgroundVideoUrl === url) return;\n this.clearBackgroundVideo();\n this.backgroundVideoUrl = url;\n const video = this.createLoopingVideo(url);\n video.addEventListener(\n \"loadeddata\",\n () => {\n if (this.config.backgroundVideoUrl !== url) return;\n this.backgroundVideo = video;\n this.failedBackgroundVideoUrl = \"\";\n this.backgroundSig = \"\";\n this.applyBackground();\n this.syncVideoPlayback();\n if (!this.running) this.renderOnce();\n },\n { once: true },\n );\n video.addEventListener(\n \"error\",\n () => {\n if (this.config.backgroundVideoUrl !== url) return;\n this.failedBackgroundVideoUrl = url;\n this.backgroundSig = \"\";\n this.applyBackground();\n if (!this.running) this.renderOnce();\n },\n { once: true },\n );\n this.backgroundVideo = video;\n video.load();\n }\n\n private clearBackgroundVideo(): void {\n if (this.backgroundVideo) {\n this.backgroundVideo.pause();\n this.backgroundVideo.removeAttribute(\"src\");\n this.backgroundVideo.load();\n }\n this.backgroundVideo = undefined;\n this.backgroundVideoUrl = \"\";\n this.failedBackgroundVideoUrl = \"\";\n this.backgroundVideoCanvas = undefined;\n }\n\n private createLoopingVideo(url: string): HTMLVideoElement {\n const video = document.createElement(\"video\");\n video.muted = true;\n video.defaultMuted = true;\n video.loop = true;\n video.playsInline = true;\n video.preload = \"auto\";\n if (!url.startsWith(\"data:\") && !url.startsWith(\"blob:\")) video.crossOrigin = \"anonymous\";\n video.src = url;\n return video;\n }\n\n private syncVideoPlayback(): void {\n const backgroundActive =\n this.running &&\n !this.config.transparentBackground &&\n this.config.backgroundMode === \"image\" &&\n !!this.config.backgroundVideoUrl;\n if (this.backgroundVideo) {\n if (backgroundActive) void this.backgroundVideo.play().catch(() => {});\n else this.backgroundVideo.pause();\n }\n // Palette videos are per wave.\n this.waves.forEach((wave, i) => {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n wave.palette.syncPlayback(sc, this.running);\n });\n }\n\n private updateBackgroundVideoFrame(): void {\n const video = this.backgroundVideo;\n const canvas = this.backgroundVideoCanvas;\n if (!video || !canvas || video.readyState < 2 || !this.backgroundTexture) return;\n const position = this.config.backgroundImagePosition ?? { x: 0, y: 0 };\n drawBackgroundMediaFrame(\n canvas,\n video,\n video.videoWidth,\n video.videoHeight,\n this.config.backgroundImageFit ?? \"cover\",\n this.config.background,\n this.config.backgroundImageZoom ?? 1,\n position.x,\n position.y,\n );\n this.backgroundTexture.needsUpdate = true;\n }\n\n private applyPost(): void {\n const u = this.postPass.uniforms;\n u.uBlurAmount.value = this.config.blur;\n u.uGrainAmount.value = this.config.grain;\n u.uBlurSamples.value = Math.round(this.config.blurSamples ?? 6);\n this.applyBloom();\n this.applyInnerLight();\n this.applyHalftone();\n this.applyHeatmap();\n this.applyHalftoneCmyk();\n this.applyPaperTexture();\n this.applyDither();\n }\n\n /** Insert / tune / remove the bloom pass. strength 0 removes it from the composer entirely, so\n * cost and pixels are identical to bloom-off; the pass (and its mip-chain render targets) is\n * created lazily the first time bloom is enabled and disposed when turned back off. It sits\n * right after the scene RenderPass so it blooms the wave before the grain/blur pass. */\n private applyBloom(): void {\n const strength = this.config.bloomStrength ?? 0;\n if (strength > 0) {\n if (!this.bloomPass) {\n const size = this.renderer.getDrawingBufferSize(new THREE.Vector2());\n this.bloomPass = new UnrealBloomPass(\n size,\n strength,\n this.config.bloomRadius ?? 0.4,\n this.config.bloomThreshold ?? 0.85,\n );\n this.composer.insertPass(this.bloomPass, 1); // after RenderPass, before postPass/OutputPass\n }\n this.bloomPass.strength = strength;\n this.bloomPass.radius = this.config.bloomRadius ?? 0.4;\n this.bloomPass.threshold = this.config.bloomThreshold ?? 0.85;\n } else if (this.bloomPass) {\n this.composer.removePass(this.bloomPass);\n this.bloomPass.dispose();\n this.bloomPass = undefined;\n }\n }\n\n /** Insert / tune / remove the dithering pass — a self-contained \"layered\" post shader (an ordered\n * Bayer dither, in the spirit of paper-design/shaders). Like bloom, dither 0 removes the pass\n * entirely so cost and pixels match dither-off, and it's created lazily on first enable. It is\n * appended AFTER OutputPass so it runs last and quantizes display-space colour (tone-mapped +\n * sRGB) — dithering the linear composer buffer would crush the steps in the shadows. */\n private applyDither(): void {\n const strength = this.config.dither ?? 0;\n if (strength > 0) {\n if (!this.ditherPass) {\n this.ditherPass = new ShaderPass({\n uniforms: {\n tDiffuse: { value: null },\n uResolution: { value: this.renderer.getDrawingBufferSize(new THREE.Vector2()) },\n uDitherStrength: { value: strength },\n uDitherScale: { value: this.config.ditherScale ?? 2 },\n uDitherSteps: { value: this.config.ditherSteps ?? 4 },\n },\n vertexShader: postVertexShader,\n fragmentShader: ditherFragmentShader,\n });\n this.composer.addPass(this.ditherPass); // last pass → renders the dithered image to screen\n }\n const u = this.ditherPass.uniforms;\n u.uDitherStrength.value = strength;\n u.uDitherScale.value = Math.max(1, this.config.ditherScale ?? 2);\n u.uDitherSteps.value = Math.max(2, Math.round(this.config.ditherSteps ?? 4));\n } else if (this.ditherPass) {\n this.composer.removePass(this.ditherPass);\n this.ditherPass.dispose();\n this.ditherPass = undefined;\n }\n }\n\n /** Insert / tune / remove the innerLight pass — volumetric light streaks scattered from the bright\n * wave toward a light point (innerLightX/Y in UV). Scene zone (index 1) so it scatters the raw wave\n * like bloom. innerLight 0 removes the pass entirely; created lazily on first enable. */\n private applyInnerLight(): void {\n const strength = this.config.innerLight ?? 0;\n if (strength > 0) {\n const cx = this.config.innerLightX ?? 0.5;\n const cy = this.config.innerLightY ?? 0.15;\n if (!this.innerLightPass) {\n this.innerLightPass = new ShaderPass({\n uniforms: {\n tDiffuse: { value: null },\n uInnerLight: { value: strength },\n uInnerLightDensity: { value: this.config.innerLightDensity ?? 0.5 },\n uInnerLightDecay: { value: this.config.innerLightDecay ?? 0.95 },\n uInnerLightCenter: { value: new THREE.Vector2(cx, cy) },\n },\n vertexShader: postVertexShader,\n fragmentShader: innerLightFragmentShader,\n });\n this.composer.insertPass(this.innerLightPass, 1); // scene zone: scatter the raw wave\n }\n const u = this.innerLightPass.uniforms;\n u.uInnerLight.value = strength;\n u.uInnerLightDensity.value = this.config.innerLightDensity ?? 0.5;\n u.uInnerLightDecay.value = this.config.innerLightDecay ?? 0.95;\n (u.uInnerLightCenter.value as THREE.Vector2).set(cx, cy);\n } else if (this.innerLightPass) {\n this.composer.removePass(this.innerLightPass);\n this.innerLightPass.dispose();\n this.innerLightPass = undefined;\n }\n }\n\n /** Insert / tune / remove the halftone pass — a rotated dot screen (dot size scales with local\n * brightness) over the finished image. halftone 0 removes the pass; created lazily on enable. */\n private applyHalftone(): void {\n const strength = this.config.halftone ?? 0;\n if (strength > 0) {\n if (!this.halftonePass) {\n this.halftonePass = new ShaderPass({\n uniforms: {\n tDiffuse: { value: null },\n uResolution: { value: this.renderer.getDrawingBufferSize(new THREE.Vector2()) },\n uHalftone: { value: strength },\n uHalftoneCell: { value: this.config.halftoneCell ?? 6 },\n uHalftoneAngle: { value: this.config.halftoneAngle ?? 0.4 },\n },\n vertexShader: postVertexShader,\n fragmentShader: halftoneFragmentShader,\n });\n this.composer.addPass(this.halftonePass); // finish zone: display-space stylization\n }\n const u = this.halftonePass.uniforms;\n u.uHalftone.value = strength;\n u.uHalftoneCell.value = Math.max(2, this.config.halftoneCell ?? 6);\n u.uHalftoneAngle.value = this.config.halftoneAngle ?? 0.4;\n } else if (this.halftonePass) {\n this.composer.removePass(this.halftonePass);\n this.halftonePass.dispose();\n this.halftonePass = undefined;\n }\n }\n\n /** Heatmap: recolour the final image by luminance → thermal palette. Finish zone. */\n private applyHeatmap(): void {\n const strength = this.config.heatmap ?? 0;\n if (strength > 0) {\n if (!this.heatmapPass) {\n this.heatmapPass = new ShaderPass({\n uniforms: { tDiffuse: { value: null }, uHeatmap: { value: strength } },\n vertexShader: postVertexShader,\n fragmentShader: heatmapFragmentShader,\n });\n this.composer.addPass(this.heatmapPass);\n }\n this.heatmapPass.uniforms.uHeatmap.value = strength;\n } else if (this.heatmapPass) {\n this.composer.removePass(this.heatmapPass);\n this.heatmapPass.dispose();\n this.heatmapPass = undefined;\n }\n }\n\n /** Paper texture: fibrous substrate shading multiplied over the image. Finish zone. */\n private applyPaperTexture(): void {\n const strength = this.config.paperTexture ?? 0;\n if (strength > 0) {\n if (!this.paperTexturePass) {\n this.paperTexturePass = new ShaderPass({\n uniforms: {\n tDiffuse: { value: null },\n uPaper: { value: strength },\n uPaperScale: { value: this.config.paperTextureScale ?? 2 },\n },\n vertexShader: postVertexShader,\n fragmentShader: paperTextureFragmentShader,\n });\n this.composer.addPass(this.paperTexturePass);\n }\n const u = this.paperTexturePass.uniforms;\n u.uPaper.value = strength;\n u.uPaperScale.value = Math.max(0.5, this.config.paperTextureScale ?? 2);\n } else if (this.paperTexturePass) {\n this.composer.removePass(this.paperTexturePass);\n this.paperTexturePass.dispose();\n this.paperTexturePass = undefined;\n }\n }\n\n /** CMYK halftone: four rotated dot screens (cyan/magenta/yellow/black). Finish zone. */\n private applyHalftoneCmyk(): void {\n const strength = this.config.halftoneCmyk ?? 0;\n if (strength > 0) {\n if (!this.halftoneCmykPass) {\n this.halftoneCmykPass = new ShaderPass({\n uniforms: {\n tDiffuse: { value: null },\n uHalftoneCmyk: { value: strength },\n uHalftoneCmykCell: { value: this.config.halftoneCmykCell ?? 6 },\n },\n vertexShader: postVertexShader,\n fragmentShader: halftoneCmykFragmentShader,\n });\n this.composer.addPass(this.halftoneCmykPass);\n }\n const u = this.halftoneCmykPass.uniforms;\n u.uHalftoneCmyk.value = strength;\n u.uHalftoneCmykCell.value = Math.max(2, this.config.halftoneCmykCell ?? 6);\n } else if (this.halftoneCmykPass) {\n this.composer.removePass(this.halftoneCmykPass);\n this.halftoneCmykPass.dispose();\n this.halftoneCmykPass = undefined;\n }\n }\n\n /** Coalesce observer-driven resizes to one per frame, and drop any that don't move a device pixel.\n *\n * resize() is expensive — composer.setSize reallocates every pass's render target, and\n * applyBackground() rebuilds a container-sized canvas + texture for gradient/image backgrounds.\n * The old 1:1 `observe → resize()` paid that for observations that changed nothing: the observer\n * reports fractional content-box sizes, so sub-pixel layout shifts (and anything that rounds to\n * the same backing buffer) triggered a full reallocation, as did every observation while an\n * export frame is pinned and the container is not what drives the buffer at all.\n *\n * Genuine per-frame changes — a mobile URL bar collapsing animates the container height — still\n * resize every frame. That work is necessary; the canvas would otherwise stretch. What is\n * removed is the redundant work, not the real work.\n *\n * Only this path is throttled. `resize()` itself stays synchronous and unconditional — context\n * restore and setOutputSize must re-apply immediately, and on a fresh GPU context the metrics\n * are unchanged but the resources are not. */\n private onResize = (): void => {\n if (this.resizeRaf) return;\n this.resizeRaf = requestAnimationFrame(() => {\n this.resizeRaf = 0;\n const next = this.viewportMetrics();\n const last = this.lastResize;\n if (last && next.w === last.w && next.h === last.h && next.dpr === last.dpr) return;\n this.resize();\n });\n };\n\n /** Re-arm the DPR watch and re-render at the new device-pixel ratio.\n *\n * ResizeObserver watches the CSS box only, so browser zoom or dragging the window to a monitor\n * with a different DPR changes devicePixelRatio without changing the box — the backing buffer\n * stayed at the old resolution and the wave went soft until something else forced a resize. */\n private onDprChange = (): void => {\n this.watchDpr();\n this.resize();\n };\n\n /** A `(resolution: Xdppx)` query only fires when we LEAVE the current ratio, so it is re-armed at\n * the new one on every change. */\n private watchDpr(): void {\n this.dprQuery?.removeEventListener(\"change\", this.onDprChange);\n this.dprQuery = window.matchMedia(`(resolution: ${window.devicePixelRatio || 1}dppx)`);\n this.dprQuery.addEventListener(\"change\", this.onDprChange);\n }\n\n /** The backing-buffer metrics resize() will apply: the export frame when one is pinned, else the\n * container box at the (clamped) device-pixel ratio. */\n private viewportMetrics(): { w: number; h: number; dpr: number } {\n return {\n w: this.outputSize?.width ?? Math.max(1, this.container.clientWidth),\n h: this.outputSize?.height ?? Math.max(1, this.container.clientHeight),\n dpr: this.outputSize ? 1 : Math.min(window.devicePixelRatio || 1, this.config.dprMax),\n };\n }\n\n private onContextLost = (e: Event): void => {\n e.preventDefault(); // tell the browser we'll recover → no \"Aw, Snap\" crash\n cancelAnimationFrame(this.rafId);\n this.running = false;\n };\n\n private onContextRestored = (): void => {\n // old GPU resources are invalid on a fresh context — disposeWaves drops each wave's palette AND\n // its particle Points, so refresh() (via buildWaves → applyParticles) rebuilds them fresh.\n this.disposeWaves();\n this.backgroundTexture?.dispose();\n this.backgroundTexture = undefined;\n this.backgroundSig = \"\";\n this.buildWaves();\n this.resize();\n this.updateRunning();\n };\n\n resize(): void {\n const { w, h, dpr } = this.viewportMetrics();\n this.lastResize = { w, h, dpr }; // what onResize compares against to skip no-op observations\n this.renderer.setPixelRatio(dpr);\n this.renderer.setSize(w, h, !this.outputSize);\n if (this.outputSize) {\n // setSize(..., false) preserves the exact backing buffer without writing fixed CSS\n // pixel dimensions. Keep the canvas stretched to the visible aspect-ratio frame.\n this.renderer.domElement.style.width = \"100%\";\n this.renderer.domElement.style.height = \"100%\";\n }\n this.resizePost(w, h, dpr);\n const dw = w * dpr;\n const dh = h * dpr;\n (this.postPass.uniforms.uResolution.value as THREE.Vector2).set(dw, dh);\n if (this.ditherPass) (this.ditherPass.uniforms.uResolution.value as THREE.Vector2).set(dw, dh);\n if (this.halftonePass)\n (this.halftonePass.uniforms.uResolution.value as THREE.Vector2).set(dw, dh);\n for (const s of this.waves) {\n (s.material.uniforms.uResolution.value as THREE.Vector2).set(dw, dh);\n }\n // The responsive ortho framing: the frustum = the canvas in DEVICE pixels (1 world unit =\n // 1px at zoom 1). Combined with the ×10 mesh scale, the wave overflows the frame.\n this.camera.left = -dw / 2;\n this.camera.right = dw / 2;\n this.camera.top = dh / 2;\n this.camera.bottom = -dh / 2;\n this.applyZoom(); // responsive ortho zoom (maps FRAME_W world units onto the canvas)\n this.applyBackground();\n this.onAfterResize();\n if (!this.running) this.renderOnce();\n }\n\n /** Set an exact output buffer while CSS scales the canvas into the on-screen export frame. */\n setOutputSize(width: number, height: number): void {\n this.outputSize = {\n width: THREE.MathUtils.clamp(Math.round(width), 64, 8192),\n height: THREE.MathUtils.clamp(Math.round(height), 64, 8192),\n };\n this.resize();\n }\n\n start(): void {\n this.started = true;\n this.updateRunning();\n }\n\n stop(): void {\n this.started = false;\n this.updateRunning();\n }\n\n private onMotionChange = (e: MediaQueryListEvent): void => {\n this.reducedMotion = this.respectReducedMotion && e.matches;\n this.updateRunning();\n };\n\n private onVisibilityChange = (): void => {\n this.pageVisible = document.visibilityState === \"visible\";\n this.updateRunning();\n };\n\n private updateRunning(): void {\n const shouldAnimate =\n this.started &&\n this.visible &&\n this.pageVisible &&\n !this.config.paused &&\n !this.reducedMotion;\n\n if (shouldAnimate && !this.running) {\n this.running = true;\n // Reset the delta baseline (as Clock.start() + a discarded getDelta() used to) so the first\n // frame advances by ~one frame, not by the whole idle/pause gap that elapsed while stopped.\n this.timer.update();\n this.rafId = requestAnimationFrame(this.loop);\n } else if (!shouldAnimate && this.running) {\n this.running = false;\n cancelAnimationFrame(this.rafId);\n }\n // When not animating (paused / reduced-motion / static export) show the FULL frame, not a\n // frozen mid-ease, by forcing introTimeRamp = 1.\n if (!this.running) {\n this.introTimeRamp = 1;\n this.interaction?.settle(); // collapse pointer/input to rest before the one settled frame\n this.renderOnce();\n }\n this.syncVideoPlayback();\n }\n\n private loop = (): void => {\n if (!this.running) return;\n this.timer.update();\n const dt = this.timer.getDelta();\n this.time += dt;\n this.interaction?.update(dt); // advance smoothed input by the SAME delta (no time-model change)\n if (this.introTimeRamp < 1) this.introTimeRamp = Math.min(1, this.introTimeRamp + 0.016); // ~1s to full at 60fps\n this.renderOnce();\n this.rafId = requestAnimationFrame(this.loop);\n };\n\n /** Advance the per-frame time uniforms (geometry itself is static). Time model:\n * time = elapsed·introTimeRamp + timeOffset — the ramp eases the animation in on load. */\n private updateTime(): void {\n // Skip the ease-in when asked: a fresh renderer (first load, or the studio's HMR hot-swap)\n // starts introTimeRamp at 0, and replaying the ramp on every save reads as a \"speed up\" when\n // you tab back. The studio passes skipIntroRamp in dev; prod builds + the embed keep the ease-in.\n const ramp = this.config.introRamp === false || this.skipIntroRamp ? 1 : this.introTimeRamp;\n const t = this.time * ramp + (this.config.timeOffset ?? 0) + this.interactionTimeOffset;\n // Indexed loop (no per-frame closure) — this runs every frame.\n for (let i = 0; i < this.waves.length; i++) {\n const u = this.waves[i].material.uniforms;\n u.uTime.value = t;\n // Palette drift: flow the colour along the ribbon independently of the geometry by drifting\n // uPaletteOffset over time. Only touch it when nonzero, so refresh()'s static base offset —\n // and every drift-off preset — is left byte-for-byte unchanged.\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n const dx = sc.paletteDriftX ?? 0;\n const dy = sc.paletteDriftY ?? 0;\n if (dx !== 0 || dy !== 0) {\n const base = sc.paletteTextureOffset;\n (u.uPaletteOffset.value as THREE.Vector2).set(base.x + dx * t, base.y + dy * t);\n }\n }\n this.postPass.uniforms.uTime.value = t;\n // same t as the waves → scrub / loop / pause stay in sync\n for (const w of this.waves) w.particleField?.setTime(t);\n }\n\n /** Draw the composed frame. WebGL runs the EffectComposer; WebGPU runs a node post chain. */\n protected renderComposed(): void {\n this.composer.render();\n }\n\n /** Resize the post chain's render targets. */\n protected resizePost(w: number, h: number, dpr: number): void {\n this.composer.setPixelRatio(dpr);\n this.composer.setSize(w, h);\n }\n\n /** Release the post chain's GPU resources. */\n protected disposePost(): void {\n this.composer.dispose();\n }\n\n /** Render exactly one frame at the current time. */\n renderOnce(): void {\n // A backend with asynchronous startup (WebGPU) is not drawable until init() resolves; the base\n // WebGL renderer sets `ready` at construction, so this never trips there.\n if (!this.ready) return;\n this.updateBackgroundVideoFrame();\n this.updateTime();\n this.applyInteraction(); // write pointer + binding uniforms (no-op when off / capturing)\n this.updateSceneFx(); // feed the particle field (before clip fitting)\n this.updateClipPlanes(); // keep near/far bracketing the scene so no camera angle clips the wave\n this.renderComposed();\n // Editor overlays (gizmo/helpers + camera-rig minimap) draw on top of the composed frame —\n // the studio subclass plugs them in here; the base renders nothing extra.\n this.onAfterRenderFrame();\n }\n\n /** Insert / sync / remove EACH wave's particle field — mirrors applyBloom's lazy lifecycle, per wave.\n * An absent block or count 0 means no THREE.Points for that wave (byte-identical). The seeded buffers\n * rebuild only when the (count, seed, edgeBias, bias) signature changes; the rest are live uniforms.\n * configure() (the wave-shape binding) runs later in updateSceneFx, once transforms are current. */\n private applyParticles(): void {\n const loop = this.config.loopSeconds ?? 0;\n this.waves.forEach((wave, i) => {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n const cfg = sc?.particles;\n if (cfg && cfg.count > 0) {\n if (!wave.particleField) {\n // The callback fires when a sprite image finishes rasterizing: a paused / settled\n // renderer has no next frame to pick it up, so it has to be asked to draw one.\n wave.particleField = this.createParticleField(wave, sc, () => {\n if (!this.running) this.renderOnce();\n });\n this.scene.add(wave.particleField.object);\n }\n wave.particleField.sync(cfg, loop);\n } else if (wave.particleField) {\n this.scene.remove(wave.particleField.object);\n wave.particleField.dispose();\n wave.particleField = undefined;\n }\n });\n }\n\n /** Per-frame placement for every wave's particle field: derive the camera's screen basis + each\n * wave's world centre (drift radiates from there) and bind that wave's live shape (#defines +\n * uniforms + matrixWorld) so the dust rides the SAME deform as the ribbon. Runs after refresh()'s\n * per-wave loop so the wave transforms/uniforms are current. No allocation; a no-op when off. */\n private updateSceneFx(): void {\n let anyField = false;\n for (const w of this.waves) {\n if (w.particleField) {\n anyField = true;\n break;\n }\n }\n if (!anyField) return;\n this.camera.updateMatrixWorld();\n this.fxRight.setFromMatrixColumn(this.camera.matrixWorld, 0).normalize();\n this.fxUp.setFromMatrixColumn(this.camera.matrixWorld, 1).normalize();\n const pr = this.renderer.getPixelRatio();\n this.waves.forEach((wave, i) => {\n const field = wave.particleField;\n if (!field) return;\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n wave.mesh.updateWorldMatrix(true, false);\n wave.mesh.getWorldPosition(this.fxCenter); // drift radiates from this wave's world centre\n const frame: ParticleFrame = { center: this.fxCenter, right: this.fxRight, up: this.fxUp };\n field.frame(frame, pr);\n field.configure({\n defines: this.shapeDefines(sc),\n uniforms: wave.material.uniforms,\n matrixWorld: wave.mesh.matrixWorld,\n speed: sc.speed,\n seed: sc.seed,\n });\n });\n }\n\n /** The subset of waveDefines() the particle program shares: the shape gates (what waveShape reads,\n * so the dust matches its wave's deform) plus the pointer gates (what pointerField reads, so the\n * dust reacts to the same cursor). Every one of these is derived from CONFIG alone — live input\n * must never reach here, or each frame would flip the define set and recompile the point program. */\n private shapeDefines(sc: WaveConfig): Record<string, string> {\n const all = this.waveDefines(sc);\n const out: Record<string, string> = {};\n for (const k of [\n \"LOOP_MOTION\",\n \"DETAIL_OCTAVE\",\n \"HELIX\",\n \"TWIST_MOTION\",\n \"RADIAL\",\n \"POINTER_FX\",\n \"POINTER_RIPPLES\",\n ]) {\n if (k in all) out[k] = \"\";\n }\n return out;\n }\n\n /** Create/dispose the interaction controller as config toggles interaction on/off. Called from\n * refresh(); the compiled define set (POINTER_FX etc.) is handled separately by waveDefines(). */\n private syncInteraction(): void {\n const active = interactionActive(this.config);\n if (active && !this.interaction) {\n void this.loadInteraction();\n } else if (!active && this.interaction) {\n this.interaction.dispose();\n this.interaction = undefined;\n this.interactionModule = undefined;\n this.interactionTimeOffset = 0;\n // Clear any live scroll→cameraZoom multiplier left in camera.zoom: with the controller gone\n // applyBindings won't run to reset it, so recompute the zoom here (no-op when already 1).\n if (this.interactionZoom !== 1) {\n this.interactionZoom = 1;\n this.applyZoom();\n }\n }\n }\n\n /**\n * Fetch the interactivity runtime and attach it. Deliberately a DYNAMIC import: the controller,\n * its listeners, the applier tables and the tilt sensor are ~3.8 KB gzipped that a scene with no\n * `interaction` block never runs, and a static import would put all of it in every bundle. The\n * cost is that interaction goes live a chunk-fetch after the first frame instead of on it —\n * invisible in practice, since there is nothing to react to until a reader moves.\n *\n * A failed fetch is not fatal: the wave renders exactly as an inert one does, which is the same\n * thing that happens on a config with no interaction at all.\n */\n private async loadInteraction(): Promise<void> {\n if (this.interactionLoading) return;\n this.interactionLoading = true;\n try {\n const mod = await import(\"./interaction\");\n // The renderer may have been disposed, or the config edited back to inert, in flight.\n if (this.disposed || this.interaction || !interactionActive(this.config)) return;\n this.interactionModule = mod;\n this.interaction = new mod.InteractionController(this.container, () => this.config);\n if (this.stagedInputs) {\n for (const [name, value] of this.stagedInputs) this.interaction.setInput(name, value);\n this.stagedInputs = undefined;\n }\n this.onInteractionReady();\n } catch {\n // Chunk unavailable (offline, a stale deploy): stay inert rather than throwing into the loop.\n } finally {\n this.interactionLoading = false;\n }\n }\n\n /** Hook for subclasses that hold state the controller must be told about once it exists (the\n * studio's scroll preview). No-op in the base renderer. */\n protected onInteractionReady(): void {}\n\n /** Per-frame interaction write: dynamic pointer-field uniforms + bindings. No-op without a\n * controller. While capturing it writes the REST state instead (pointer field zeroed, every bound\n * param at its authored base) — merely skipping the write would freeze whatever live hover/scroll\n * state the previous frame left in the uniforms, so exports wouldn't be deterministic. */\n private applyInteraction(): void {\n const ic = this.interaction;\n const mod = this.interactionModule;\n if (!ic || !mod) return; // set together, so this is \"before the chunk landed\"\n if (this.capturing) {\n this.applyInteractionRest(mod);\n return;\n }\n if (anyPointerFxActive(this.config)) this.applyPointerField(ic);\n this.applyBindings(ic, mod);\n }\n\n /** Write the capture-frame interaction state: exactly what this config renders with no input —\n * pointer presence + ripple envelopes zeroed (vPointerFall gates every hover effect to 0) and each\n * bound param at its authored base. Live controller state is left untouched, so the frame after\n * the capture resumes mid-gesture; the trailing renderOnce() in captureImage restores the preview.\n * interactionZoom is deliberately NOT reset — captureImage strips it from camera.zoom itself, and\n * the post-capture restore depends on it being unchanged. */\n private applyInteractionRest(mod: InteractionModule): void {\n for (let i = 0; i < this.waves.length; i++) {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n if (!wavePointerFxActive(this.config, sc)) continue;\n const u = this.waves[i].material.uniforms;\n u.uPointerActive.value = 0;\n const rAmp = u.uRippleAmp.value as number[];\n for (let r = 0; r < RIPPLE_SLOTS; r++) rAmp[r] = 0;\n }\n // Scene bindings → authored base: blur/grain write straight to the post uniforms; the time\n // offset delta is simply 0 at base. (Live applyBindings recomputes all of these next frame.)\n const sceneBindings = this.config.interaction?.bindings;\n if (sceneBindings) {\n this.interactionSceneOut.timeOffset = this.config.timeOffset ?? 0;\n this.interactionSceneOut.zoom = this.config.cameraZoom ?? 1;\n const sceneArgs = { post: this.postPass.uniforms, out: this.interactionSceneOut };\n for (const b of sceneBindings) {\n const applier = mod.SCENE_APPLIERS[b.target];\n applier.apply(applier.base(this.config), sceneArgs);\n }\n }\n if (this.interactionTimeOffset !== 0) {\n // renderOnce ran updateTime() BEFORE this, so uTime already carries the live offset for the\n // frame about to draw — zero it and re-run updateTime so the capture uses the authored time.\n this.interactionTimeOffset = 0;\n this.updateTime();\n }\n // Per-wave bindings → that wave's authored base.\n for (let i = 0; i < this.waves.length; i++) {\n const sc = this.config.waves[i];\n const bindings = sc?.interaction?.bindings;\n if (!sc || !bindings || bindings.length === 0) continue;\n const wave = this.waves[i];\n for (const b of bindings) {\n const applier = mod.WAVE_APPLIERS[b.target];\n applier.apply(applier.base(sc), { u: wave.material.uniforms, mesh: wave.mesh });\n }\n }\n }\n\n /** Write the dynamic pointer-field uniforms to every wave that HAS a pointer field. Position /\n * presence are PER WAVE (each trails the cursor at its own hover smoothing); ripple origins/ages\n * are shared. Per-wave amplitudes were already pushed statically in refresh(). */\n private applyPointerField(ic: InteractionController): void {\n const dw = this.camera.right - this.camera.left;\n const dh = this.camera.top - this.camera.bottom;\n const aspect = dh !== 0 ? dw / dh : 1;\n const ripples = ic.sample().ripples;\n // Velocity-driven agitation: the configured `agitate` is the full-gesture strength; when the\n // cursor is still the drive drops to a low idle floor, so the churn tracks how you move instead\n // of buzzing at a constant rate the whole time the cursor is merely present.\n const AGITATE_IDLE = 0.2; // fraction of `agitate` still shown when the cursor holds still\n const AGITATE_GAIN = 0.85; // how much cursor speed adds back on top (capped at full strength)\n const agitateDrive = Math.min(1, AGITATE_IDLE + AGITATE_GAIN * ic.pointerFlux());\n const vel = ic.pointerVelocity();\n for (let i = 0; i < this.waves.length; i++) {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n if (!wavePointerFxActive(this.config, sc)) continue;\n const u = this.waves[i].material.uniforms;\n u.uPointerAgitate.value = (sc.interaction?.hover?.agitate ?? 0) * agitateDrive;\n (u.uPointerVel.value as THREE.Vector2).copy(vel);\n const f = ic.fieldFor(i);\n if (f) {\n (u.uPointer.value as THREE.Vector2).copy(f.ndc);\n u.uPointerActive.value = f.presence;\n } else {\n u.uPointerActive.value = 0; // wave not advanced by update() yet → rest (no hover)\n }\n u.uPointerAspect.value = aspect;\n const rOrigin = u.uRippleOrigin.value as THREE.Vector2[];\n const rAge = u.uRippleAge.value as number[];\n const rAmp = u.uRippleAmp.value as number[];\n for (let r = 0; r < RIPPLE_SLOTS; r++) {\n rOrigin[r].copy(ripples[r].origin);\n rAge[r] = ripples[r].age;\n rAmp[r] = ripples[r].amp;\n }\n }\n }\n\n /** Evaluate bindings via the applier tables: value = mix(from ?? base, to, smoothedSource). Scene\n * bindings drive scene params; each wave's bindings drive that wave's uniforms. */\n private applyBindings(ic: InteractionController, mod: InteractionModule): void {\n // Scene bindings → scene params. Seed out-params at base; appliers overwrite only what they drive,\n // so with no scene binding these stay at base → interactionTimeOffset 0 / interactionZoom 1.\n this.interactionSceneOut.timeOffset = this.config.timeOffset ?? 0;\n this.interactionSceneOut.zoom = this.config.cameraZoom ?? 1;\n const sceneArgs = { post: this.postPass.uniforms, out: this.interactionSceneOut };\n for (const b of this.config.interaction?.bindings ?? []) {\n const applier = mod.SCENE_APPLIERS[b.target];\n const value = THREE.MathUtils.lerp(\n b.from ?? applier.base(this.config),\n b.to,\n ic.bindingValue(b),\n );\n applier.apply(value, sceneArgs);\n }\n // Per-wave bindings → that wave's uniforms / mesh.\n for (let i = 0; i < this.waves.length; i++) {\n const sc = this.config.waves[i];\n const bindings = sc?.interaction?.bindings;\n if (!sc || !bindings || bindings.length === 0) continue;\n const wave = this.waves[i];\n for (const b of bindings) {\n const applier = mod.WAVE_APPLIERS[b.target];\n const value = THREE.MathUtils.lerp(b.from ?? applier.base(sc), b.to, ic.bindingValue(b));\n applier.apply(value, { u: wave.material.uniforms, mesh: wave.mesh });\n }\n }\n // interactionTimeOffset is the DELTA over config.timeOffset (updateTime adds both together).\n this.interactionTimeOffset =\n this.interactionSceneOut.timeOffset - (this.config.timeOffset ?? 0);\n // interactionZoom is a MULTIPLIER over config.cameraZoom (applyZoom multiplies both). This runs\n // even in the studio (where orbit owns the camera) so a scroll→cameraZoom reaction previews:\n // applyZoom only re-fires when the multiplier actually CHANGES (i.e. you scrub the scroll\n // preview), so it never fights an idle orbit; writeCameraToConfig divides it back out so it can't\n // bake into the saved/exported config, and captureImage strips it so posters use the authored zoom.\n const nextZoom = this.interactionSceneOut.zoom / (this.config.cameraZoom || 1);\n if (nextZoom !== this.interactionZoom) {\n this.interactionZoom = nextZoom;\n this.applyZoom();\n }\n }\n\n /** Feed a `custom:<name>` interaction input (developer API). No-op when interaction is off.\n * Values fed before the interaction chunk lands are STAGED (last one per name wins) and replayed\n * when it does — otherwise a one-shot input sent right after `onReady` would vanish into the\n * fetch window. */\n setInteractionInput(name: string, value: number): void {\n if (this.interaction) {\n this.interaction.setInput(name, value);\n return;\n }\n if (typeof name !== \"string\" || !Number.isFinite(value)) return;\n if (!interactionActive(this.config)) return; // nothing will ever consume it\n (this.stagedInputs ??= new Map()).set(name, value);\n }\n\n /**\n * Explicitly ask for the device-orientation sensor. OPTIONAL, and on iOS it opens a modal\n * permission dialog — so this belongs to a page where tilt is the point, not to a decorative\n * background, which should simply go without tilt there. Nothing calls this for you.\n *\n * CALL IT FROM A USER GESTURE: iOS 13+ only grants the sensor from inside a tap handler, and\n * awaiting anything before it (a fetch, a timeout) spends the gesture. Resolves true once\n * readings can flow — false when\n * the platform has no sensor, the scene declares no `interaction.tilt`, or the reader refused.\n * Where no permission is required the sensor is already live and this resolves true.\n */\n enableTilt(): Promise<boolean> {\n // NOT awaited before the permission call: iOS only grants the sensor to a synchronous chain\n // from the gesture, so this hands straight to the controller. In the window before the\n // interaction chunk lands there is nothing to hand to — start the fetch and report false, and\n // the reader's next tap works. (That window is the first frames after mount: any tilt binding\n // makes the layer active, so the fetch begins on the first refresh, long before a tap.)\n if (!this.interaction) {\n if (interactionActive(this.config)) void this.loadInteraction();\n return Promise.resolve(false);\n }\n return this.interaction.enableTilt();\n }\n\n /** Where the tilt sensor stands. `\"prompt\"` is exactly when a tap-to-enable affordance helps. */\n tiltStatus(): TiltStatus {\n if (this.interaction) return this.interaction.tiltStatus();\n // Before the chunk lands there is no sensor to ask, but the platform check needs no chunk.\n const supported =\n typeof window !== \"undefined\" && typeof window.DeviceOrientationEvent !== \"undefined\";\n return supported ? \"prompt\" : \"unsupported\";\n }\n\n /** Take the next orientation reading as the neutral pose — for when the reader has changed grip. */\n recenterTilt(): void {\n this.interaction?.recenterTilt();\n }\n\n /** Re-evaluate play/pause after `config.paused` changes. */\n refreshPlayback(): void {\n this.updateRunning();\n }\n\n /** Jump the camera to the config's authored framing (cameraPosition / cameraTarget /\n * cameraZoom). Called on whole-config swaps (preset / reset / randomize / import). The base\n * applies the pose directly; the studio subclass overrides it to also drive the orbit target\n * and sync the panel. Hook ⑤. */\n protected applyCameraFromConfig(): void {\n const p = this.config.cameraPosition;\n const tg = this.config.cameraTarget;\n this.camera.position.set(p.x, p.y, p.z);\n this.camera.up.set(0, 1, 0);\n this.camera.lookAt(tg.x, tg.y, tg.z);\n this.applyZoom();\n if (!this.running) this.renderOnce();\n }\n\n // ---- Editor hook points (no-ops in the base; the studio subclass overrides them) ----\n\n /** Hook ①: true while orbit or an edit gizmo owns the camera, so refresh() won't reset it. */\n protected isCameraExternallyDriven(): boolean {\n return false;\n }\n\n /** Hook ②: called at the end of refresh(), before the trailing renderOnce(). */\n protected onAfterRefresh(): void {}\n\n /** Hook ③: called at the end of renderOnce(), after the composed frame is drawn. */\n protected onAfterRenderFrame(): void {}\n\n /** Hook ④: called at the end of resize(), before the trailing renderOnce(). */\n protected onAfterResize(): void {}\n\n /** Responsive ortho zoom: map the FRAME_W × FRAME_H reference frame onto the canvas (per\n * config.cameraFit / cameraMinVisibleWidth — see {@link frameZoom}) so the wave frames the same\n * at any size/aspect/dpr, times the user's cameraZoom. */\n protected applyZoom(): void {\n const dw = this.camera.right - this.camera.left; // device px (set in resize)\n const dh = this.camera.top - this.camera.bottom;\n // A cameraZoom binding multiplies in here. It stays 1 unless a binding drives it; when one does,\n // writeCameraToConfig divides it back out (so the studio's orbit-persisted config isn't polluted)\n // and captureImage strips it from camera.zoom (so exports use the authored framing).\n this.camera.zoom =\n this.baseFrameZoom(dw, dh) * (this.config.cameraZoom ?? 1) * this.interactionZoom;\n this.camera.updateProjectionMatrix();\n }\n\n /** The responsive base zoom for the current config's framing policy, before the cameraZoom\n * multiplier. Subclasses invert this to recover cameraZoom from a live camera. */\n protected baseFrameZoom(dw: number, dh: number): number {\n return frameZoom(\n dw,\n dh,\n this.config.cameraFit ?? \"cover\",\n this.config.cameraMinVisibleWidth ?? 0,\n );\n }\n\n /** Fit the orthographic near/far planes to the scene before every render, so no part of a wave\n * is ever clipped as the camera orbits / dollies / pans (or when waves are added or scaled).\n *\n * The camera is *constructed* with fixed 1..10000 planes that only suit the authored hero\n * framing; once the view moves, the wave's depth extent along the view axis easily crosses\n * them and the GPU hard-slices the geometry along a flat plane — chunks of the wave vanish.\n *\n * The in-shader twist rotates each vertex about its LOCAL origin, so it can never move a vertex\n * further from that origin than the base geometry already sits. A sphere at each mesh's origin\n * (radius = base extent × the mesh's largest world scale, plus the Y displacement, ×1.2 slack)\n * therefore safely contains the fully-deformed wave. We union those, then bracket the union\n * along the view axis with a margin. Bracketing to the scene (rather than a fixed huge slab)\n * keeps clip-space depth scene-normalised — so the wireframe theme's depth fade, which reads\n * gl_Position.z, looks the same at any camera distance instead of washing out. Runs each frame;\n * it's a handful of vector ops over 1–8 meshes with no allocation. */\n private updateClipPlanes(): void {\n this.clipBox.makeEmpty();\n for (let i = 0; i < this.waves.length; i++) {\n const wave = this.waves[i];\n const mesh = wave.mesh;\n mesh.updateWorldMatrix(true, false);\n const bs = mesh.geometry.boundingSphere;\n if (!bs) continue;\n // The twist pivot (the mesh's local origin) in world space = the safe sphere's centre.\n const center = this.clipTmpA.setFromMatrixPosition(mesh.matrixWorld);\n const disp = Math.abs(Number(wave.material.uniforms.uDispAmount.value) || 0);\n // Extra wave-local displacement THIS wave's pointer field can add (agitation + push/wake +\n // ripple — worst case they all stack at the cursor), so the deformed surface never crosses the\n // fitted near/far planes. 0 when off → byte-identical.\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n let pointerDisp = 0;\n if (wavePointerFxActive(this.config, sc)) {\n const h = sc.interaction?.hover;\n pointerDisp =\n (h?.agitate ?? 0) +\n Math.abs(h?.push ?? 0) +\n (h?.wake ?? 0) +\n (sc.interaction?.press?.ripple ?? 0);\n }\n const localRadius = bs.center.length() + bs.radius + disp + pointerDisp;\n // This wave's dust (if any) spawns on its surface and drifts OUTWARD by up to `drift` world\n // units as it ages — added post-scale so the particles never cross the fitted near/far. No field\n // / drift 0 → byte-identical.\n const drift = wave.particleField ? Math.abs(sc.particles?.drift ?? 0) : 0;\n const radius = localRadius * mesh.matrixWorld.getMaxScaleOnAxis() * 1.2 + drift;\n // Enclose this wave's sphere by adding its axis-aligned bounding cube corners.\n this.clipBox.expandByPoint(this.clipTmpB.copy(center).addScalar(radius));\n this.clipBox.expandByPoint(this.clipTmpB.copy(center).addScalar(-radius));\n }\n if (this.clipBox.isEmpty()) return;\n this.clipBox.getBoundingSphere(this.clipSphere);\n const viewDir = this.camera.getWorldDirection(this.clipTmpA); // normalised view axis\n const centerDepth = this.clipTmpB\n .copy(this.clipSphere.center)\n .sub(this.camera.position)\n .dot(viewDir);\n const radius = this.clipSphere.radius;\n const margin = radius * 0.25 + 10;\n // Orthographic: a negative near is legal — the slab may extend behind the camera origin.\n this.camera.near = centerDepth - radius - margin;\n this.camera.far = centerDepth + radius + margin;\n this.camera.updateProjectionMatrix();\n }\n\n /** A world-space position delta that drops a duplicated wave into open frame space beside the\n * one it was copied from — screen-left and a touch down, sized to the visible frame — instead\n * of hidden exactly on top of it or (for the hero, which fills the right of the frame) pushed\n * off-frame. Camera-relative: it's \"left on screen\" no matter how the view is rotated/zoomed,\n * because it's built from the camera's right/up axes and the frame's visible world size. */\n\n async captureImage(\n mime: string,\n transparent = true,\n quality?: number,\n time?: number,\n ): Promise<Blob> {\n const prevBg = this.config.transparentBackground;\n if (transparent !== prevBg) {\n this.config.transparentBackground = transparent;\n this.applyBackground();\n }\n // Deterministic frame: render at a fixed animation-time (with the ease-in ramp forced full) so\n // a captured poster is reproducible instead of whatever frame happened to be on screen — pass\n // time = 0 for the frame the wave opens on. Restored in `finally`. Undefined = the live frame.\n const fixTime = time !== undefined;\n const prevTime = this.time;\n const prevRamp = this.introTimeRamp;\n if (fixTime) {\n this.time = time;\n this.introTimeRamp = 1;\n }\n let blob: Blob | null = null;\n // Exports use the AUTHORED framing — strip any live scroll→cameraZoom multiplier from camera.zoom\n // for the capture render (config itself is never polluted, so code/embed exports are already\n // clean; this covers the pixel capture). The trailing renderOnce() restores the live preview.\n const prevZoom = this.camera.zoom;\n const strippingZoom = this.interactionZoom !== 1;\n try {\n this.capturing = true;\n if (strippingZoom) {\n this.camera.zoom = prevZoom / this.interactionZoom;\n this.camera.updateProjectionMatrix();\n }\n this.renderOnce();\n blob = await new Promise<Blob | null>((resolve) =>\n this.canvas.toBlob(resolve, mime, quality),\n );\n } finally {\n this.capturing = false;\n if (strippingZoom) {\n this.camera.zoom = prevZoom;\n this.camera.updateProjectionMatrix();\n }\n if (transparent !== prevBg) {\n this.config.transparentBackground = prevBg;\n this.applyBackground();\n }\n if (fixTime) {\n this.time = prevTime;\n this.introTimeRamp = prevRamp;\n }\n this.renderOnce();\n }\n if (!blob || blob.type !== mime) throw new Error(`Failed to capture ${mime}`);\n return blob;\n }\n\n captureStream(fps = 60): MediaStream {\n return this.canvas.captureStream(fps);\n }\n\n get canvas(): HTMLCanvasElement {\n return this.renderer.domElement;\n }\n\n getConfig(): StudioConfig {\n return this.config;\n }\n\n setConfig(config: StudioConfig): void {\n const next = ensureStudioConfig(config);\n const structural =\n next.waves.length !== this.waves.length || next.quality !== this.config.quality;\n this.config = next;\n if (structural) this.rebuild();\n else this.refresh();\n // A whole new config (preset/reset/randomize/import) carries its own authored framing —\n // apply it even in the studio, where refresh() leaves the camera to orbit. Without this,\n // selecting a preset updated the wave but kept the previous camera (wrong framing).\n this.applyCameraFromConfig();\n }\n\n dispose(): void {\n this.disposed = true; // an interaction chunk still in flight must not attach to a dead renderer\n cancelAnimationFrame(this.rafId);\n cancelAnimationFrame(this.resizeRaf); // a coalesced resize may still be queued\n this.running = false;\n this.interaction?.dispose();\n this.interaction = undefined;\n this.interactionModule = undefined;\n this.resizeObserver.disconnect();\n this.intersectionObserver.disconnect();\n this.motionQuery.removeEventListener(\"change\", this.onMotionChange);\n this.dprQuery?.removeEventListener(\"change\", this.onDprChange);\n document.removeEventListener(\"visibilitychange\", this.onVisibilityChange);\n this.renderer.domElement.removeEventListener(\"webglcontextlost\", this.onContextLost);\n this.renderer.domElement.removeEventListener(\"webglcontextrestored\", this.onContextRestored);\n this.clearBackgroundVideo();\n this.backgroundTexture?.dispose();\n for (const s of this.waves) {\n s.material.dispose();\n s.geometry.dispose();\n s.palette.dispose();\n s.particleField?.dispose();\n }\n this.bloomPass?.dispose();\n this.ditherPass?.dispose();\n this.innerLightPass?.dispose();\n this.halftonePass?.dispose();\n this.heatmapPass?.dispose();\n this.paperTexturePass?.dispose();\n this.halftoneCmykPass?.dispose();\n this.disposePost();\n this.renderer.dispose();\n this.renderer.domElement.remove();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"WaveRenderer.js","names":[],"sources":["../../src/renderer/WaveRenderer.ts"],"sourcesContent":["import * as THREE from \"three\";\nimport { EffectComposer } from \"three/addons/postprocessing/EffectComposer.js\";\nimport { RenderPass } from \"three/addons/postprocessing/RenderPass.js\";\nimport { OutputPass } from \"three/addons/postprocessing/OutputPass.js\";\nimport { ShaderPass } from \"three/addons/postprocessing/ShaderPass.js\";\nimport { UnrealBloomPass } from \"three/addons/postprocessing/UnrealBloomPass.js\";\nimport {\n vertexShader,\n fragmentShader,\n lineFragmentShader,\n glassFragmentShader,\n glassLayerFragmentShader,\n postVertexShader,\n postFragmentShader,\n ditherFragmentShader,\n innerLightFragmentShader,\n halftoneFragmentShader,\n heatmapFragmentShader,\n paperTextureFragmentShader,\n halftoneCmykFragmentShader,\n} from \"./shaders\";\nimport { WaveGeometry } from \"./WaveGeometry\";\nimport { bakePathTexture, writePathTexture } from \"./wavePath\";\nimport { ParticleField, type ParticleFrame } from \"./particleField\";\n// Gates only — the interactivity RUNTIME (controller, applier tables, tilt sensor) is reached\n// through a dynamic import in loadInteraction(), so a scene that never interacts never fetches it.\n// Anything added here that pulls ./interaction statically undoes that; see interactionGates.ts.\nimport {\n interactionActive,\n anyPointerFxActive,\n wavePointerFxActive,\n waveRipplesActive,\n RIPPLE_SLOTS,\n} from \"./interactionGates\";\nimport type { InteractionController } from \"./interaction\";\nimport type { TiltStatus } from \"./tilt\";\n\n/** The lazily-fetched interactivity runtime. Held alongside the controller so the per-frame binding\n * writes can reach the applier tables without importing them eagerly. */\ntype InteractionModule = typeof import(\"./interaction\");\nimport {\n buildPaletteTexture,\n configurePaletteTexture,\n paletteSignature,\n PALETTE_MAPS,\n paletteMapCanvas,\n canvasToTexture,\n loadPaletteImage,\n buildBackgroundGradientCanvas,\n buildBackgroundMeshCanvas,\n buildBackgroundImageCanvas,\n drawBackgroundMediaFrame,\n} from \"./palette\";\nimport { buildHeroPaletteCanvas, buildHeroPaletteTexture } from \"./heroPalette\";\nimport {\n MAX_COLORS,\n MAX_LIGHTS,\n MAX_MESH_POINTS,\n MAX_NOISE_BANDS,\n ensureStudioConfig,\n} from \"../config/model\";\nimport type { StudioConfig, WaveConfig, BlendMode, CameraFit, DissolveAxis } from \"../config/model\";\n\n/** Which fragment program a wave's theme wants. */\nfunction fragmentFor(sc: WaveConfig | undefined): string {\n if (sc?.theme === \"wireframe\") return lineFragmentShader;\n if (sc?.theme === \"glass\") return glassFragmentShader;\n return fragmentShader;\n}\n\n/** How opaque a wave's between-strand colour is. Absent is the page background at full strength (the\n * theme as it always drew); `\"transparent\"` or an 8-digit hex carries its own alpha. */\nfunction gapAlpha(color: string | undefined): number {\n if (!color) return 1;\n if (color === \"transparent\") return 0;\n if (/^#[0-9a-f]{8}$/i.test(color)) return parseInt(color.slice(7, 9), 16) / 255;\n return 1;\n}\n\n/** The colour half of the same field — the alpha is stripped, and `\"transparent\"` has no colour of\n * its own, so it falls back to the page background it is standing in for. */\nfunction gapColorHex(color: string | undefined): string | undefined {\n if (!color || color === \"transparent\") return undefined;\n return /^#[0-9a-f]{8}$/i.test(color) ? color.slice(0, 7) : color;\n}\n\n/** Dissolve axis name → the float the shaders branch on (see dissolveChunk's dissolveCoord). */\nconst DISSOLVE_AXIS_INDEX: Record<DissolveAxis, number> = {\n length: 0,\n width: 1,\n screenX: 2,\n screenY: 3,\n};\n\nconst BASE_SEGMENTS = 220; // base segment count along the ribbon; denser = smoother (scaled down per wave — see get segments)\n/** The longest frame the clock will believe, in seconds. Anything past it is a STALL — frames\n * suspended with no `visibilitychange` to say so (an embedding webview that parks rAF, a debugger\n * pause, the machine asleep) — and replaying a stall is a fast-forward, not motion. A ceiling, not\n * a jitter clamp: recordings run on this same wall-clock loop, so a merely slow frame (a 4K export\n * at 10fps) must still advance by its real length or a `loopSeconds` export stops closing. */\nconst MAX_FRAME_SECONDS = 1;\n\n/** Reference frame (world units) the orthographic camera fills at cameraZoom 1. The wave is\n * framed by mapping this FRAME_W × FRAME_H rectangle (centred on cameraTarget) onto the canvas,\n * so a given cameraZoom / cameraTarget frames the wave the SAME at any canvas size or aspect\n * (only the margin differs). FRAME_H = FRAME_W / (16/9) makes the reference a 16:9 rectangle.\n * This is what makes a saved preset reproduce on anyone's screen. See {@link frameZoom} for how\n * a canvas of a different aspect is reconciled against it. */\nexport const FRAME_W = 1333;\nexport const FRAME_H = 750;\n\n/**\n * The responsive base zoom: how many device pixels one world unit occupies so the FRAME_W × FRAME_H\n * reference lands on a `dw × dh` (device px) canvas under `fit`, then clamped so at least\n * `minVisibleWidth` of the frame's width survives.\n *\n * Shared by the renderer (which applies it) and the studio (which inverts it to persist a\n * scroll-zoom back into config.cameraZoom) — one implementation, so the two cannot drift.\n *\n * The clamp is a pure zoom CEILING layered on top of the fit, which is what lets both knobs\n * coexist: it can only widen the view, never tighten it, so it is inert for `contain`/`width`\n * (already at or below that zoom) and bites exactly where the crop hurts — `cover`/`height` on a\n * canvas narrower than 16:9. `minVisibleWidth` 0 disables it and restores pure-fit behaviour.\n */\nexport function frameZoom(dw: number, dh: number, fit: CameraFit, minVisibleWidth = 0): number {\n const byWidth = dw / FRAME_W;\n const byHeight = dh / FRAME_H;\n let zoom: number;\n switch (fit) {\n case \"contain\": // fit the whole frame — reveals world beyond it on the long axis\n zoom = Math.min(byWidth, byHeight);\n break;\n case \"width\": // horizontal composition identical at every aspect\n zoom = byWidth;\n break;\n case \"height\":\n zoom = byHeight;\n break;\n default: // \"cover\" — fill both axes, crop the overflow\n zoom = Math.max(byWidth, byHeight);\n }\n if (minVisibleWidth > 0) {\n // Visible world width = dw / zoom; hold it at ≥ FRAME_W × minVisibleWidth.\n zoom = Math.min(zoom, dw / (FRAME_W * minVisibleWidth));\n }\n return zoom;\n}\n\nexport interface WaveRendererOptions {\n /** Honor prefers-reduced-motion by freezing animation. Default true. */\n respectReducedMotion?: boolean;\n /**\n * Skip the intro time-ramp (the ~1s ease-in of animation on load), rendering at full speed\n * immediately. The studio passes `import.meta.env.DEV` here so a fresh renderer on every HMR\n * hot-swap doesn't replay the ease-in (which reads as a \"speed up\" when you tab back). Default\n * false — production embeds keep the ease-in. Ignored while paused (a paused frame is always the\n * full frame). Default false.\n */\n skipIntroRamp?: boolean;\n}\n\n/**\n * Per-wave 2D palette texture (+ optional looping video). One instance per wave, so each\n * wave carries its own palette. Guarded by a signature so it only rebuilds when that wave's\n * palette actually changes (not every refresh).\n */\nclass WavePalette {\n texture?: THREE.Texture;\n private sig = \"\";\n private video?: HTMLVideoElement;\n private videoUrl = \"\";\n private failedUrl = \"\";\n\n constructor(\n private readonly makeVideo: (url: string) => HTMLVideoElement,\n private readonly onVideoReady: () => void,\n ) {}\n\n /** Rebuild this wave's palette texture from its config (video / custom image / stops /\n * built-in map / hero LUT) and point the wave's palette uniforms at it. */\n apply(cfg: WaveConfig, uniforms: Record<string, THREE.IUniform>): void {\n const videoUrl = cfg.paletteVideoUrl;\n const url = cfg.paletteImageUrl;\n const source = cfg.paletteSource ?? \"hero\";\n let sig: string;\n let build: () => THREE.Texture;\n if (videoUrl) {\n this.ensureVideo(videoUrl);\n if (this.video?.readyState && this.video.readyState >= 2) {\n sig = \"video|\" + videoUrl;\n build = () => configurePaletteTexture(new THREE.VideoTexture(this.video!));\n } else {\n sig = \"video-loading|\" + videoUrl;\n build = () => buildHeroPaletteTexture();\n }\n } else if (url) {\n this.clearVideo();\n sig = \"url|\" + url;\n build = () => loadPaletteImage(url);\n } else if (source === \"stops\") {\n this.clearVideo();\n const opts = {\n stops: cfg.palette,\n edgeColor: cfg.paletteEdgeColor ?? \"#8e9dff\",\n edgeAmount: cfg.paletteEdgeAmount ?? 0.3,\n };\n sig = \"stops|\" + paletteSignature(opts);\n build = () => buildPaletteTexture(opts);\n } else if (PALETTE_MAPS[source]) {\n this.clearVideo();\n sig = \"map|\" + source;\n build = () => canvasToTexture(paletteMapCanvas(PALETTE_MAPS[source]));\n } else {\n this.clearVideo();\n sig = \"hero\";\n build = () => buildHeroPaletteTexture();\n }\n if (sig !== this.sig || !this.texture) {\n this.texture?.dispose();\n this.texture = build();\n this.sig = sig;\n }\n uniforms.uPalette.value = this.texture;\n uniforms.uUsePalette.value = cfg.usePaletteTexture === false ? 0 : 1;\n uniforms.uPaletteRaw.value = 1;\n }\n\n /** Play/pause this wave's palette video with the render loop. */\n syncPlayback(cfg: WaveConfig, running: boolean): void {\n const v = this.video;\n if (!v) return;\n const active =\n running &&\n cfg.gradientType !== \"mesh\" &&\n cfg.usePaletteTexture !== false &&\n !!cfg.paletteVideoUrl;\n if (active) void v.play().catch(() => {});\n else v.pause();\n }\n\n private ensureVideo(url: string): void {\n if (this.videoUrl === url || this.failedUrl === url) return;\n this.clearVideo();\n this.videoUrl = url;\n const video = this.makeVideo(url);\n video.addEventListener(\n \"loadeddata\",\n () => {\n this.video = video;\n this.failedUrl = \"\";\n this.sig = \"\";\n this.onVideoReady();\n },\n { once: true },\n );\n video.addEventListener(\n \"error\",\n () => {\n this.failedUrl = url;\n this.sig = \"\";\n this.onVideoReady();\n },\n { once: true },\n );\n this.video = video;\n video.load();\n }\n\n private clearVideo(): void {\n if (this.video) {\n this.video.pause();\n this.video.removeAttribute(\"src\");\n this.video.load();\n }\n this.video = undefined;\n this.videoUrl = \"\";\n }\n\n dispose(): void {\n this.texture?.dispose();\n this.texture = undefined;\n this.clearVideo();\n this.failedUrl = \"\";\n }\n}\n\n/**\n * The uniform surface both backends expose. The GLSL path holds `THREE.IUniform`s; the TSL path\n * holds TSL uniform nodes and array wrappers. Both are written the same way — `u.uFoo.value = x`,\n * `u.uColors.value[i].set(...)` — which is what lets the ~116 config-sync writes in `refresh()`\n * stay backend-agnostic.\n */\nexport type WaveUniforms = Record<string, { value: unknown }>;\n\n/** A wave material, whichever backend built it. */\nexport type WaveMaterial = THREE.Material & { uniforms: WaveUniforms };\n\n/**\n * A wave's particle field, whichever backend built it.\n *\n * The TSL field is a `THREE.Sprite` where the GLSL one is a `THREE.Points` (WebGPU has no point\n * size), so the scene node is reached through `object` rather than either concrete type.\n */\nexport interface WaveParticleField {\n readonly object: THREE.Object3D;\n sync(cfg: NonNullable<WaveConfig[\"particles\"]>, loopSeconds: number): void;\n frame(f: ParticleFrame, pixelRatio: number): void;\n configure(shape: {\n defines?: Record<string, string>;\n uniforms?: Record<string, { value: unknown }>;\n matrixWorld: THREE.Matrix4;\n speed: number;\n seed: number;\n }): void;\n setTime(t: number): void;\n dispose(): void;\n}\n\ntype Wave = {\n mesh: THREE.Mesh;\n material: WaveMaterial;\n geometry: WaveGeometry;\n /** This wave's own 2D palette texture + optional video. */\n palette: WavePalette;\n /** This wave's own particle / dust field — created when its `particles.count` first goes >0,\n * disposed at 0 / absent (the WavePalette lifecycle pattern). Undefined = no dust for this wave. */\n particleField?: WaveParticleField;\n /** The baked centreline LUT for this wave's `path` (absent when it has none). */\n pathTexture?: THREE.DataTexture;\n /** Signature of the points the LUT was baked from, so a drag only rewrites it when it changed. */\n pathSig?: string;\n /** Glass only: the layer-count companion (this wave's vertex stage + a coverage-only fragment),\n * built lazily by renderGlassPasses() and rebuilt when `layerKey` no longer matches. */\n layerMaterial?: THREE.Material;\n layerKey?: string;\n};\n\n// Parse scratch: refresh() converts ~25 hex colours per wave per call (i.e. per slider input),\n// so reuse one Color instead of allocating each time.\nconst HEX_SCRATCH = new THREE.Color();\n\n/** Convert an sRGB hex string to a linear-space RGB vector (three's ColorManagement does the\n * sRGB→linear conversion on parse). Exported for the studio subclass's live light-uniform push. */\nexport function hexToLinearVec3(hex: string, target: THREE.Vector3): THREE.Vector3 {\n // three's ColorManagement (on by default in r169) already converts the sRGB hex to\n // LINEAR when parsing the hex — its .r/.g/.b are linear. Calling\n // convertSRGBToLinear() again would double-linearize (crushing greens → everything\n // turns red), so we read the components directly.\n const c = HEX_SCRATCH.set(hex);\n return target.set(c.r, c.g, c.b);\n}\n\n/**\n * Renders a gradient \"wave of light\" from a {@link StudioConfig}. Framework-agnostic:\n * it needs only a DOM container and a config. The studio mutates the config in\n * place and calls `refresh()` / `rebuild()`.\n */\nexport class WaveRenderer {\n readonly renderer: THREE.WebGLRenderer;\n /**\n * Whether this renderer can draw yet. The WebGL backend is ready the moment it is constructed;\n * the WebGPU subclass has to `await renderer.init()` first, and `renderOnce()` throws before that.\n * The base class is always ready, so the WebGL path is unaffected.\n */\n protected ready = true;\n protected readonly scene = new THREE.Scene();\n protected readonly camera: THREE.OrthographicCamera;\n protected readonly group = new THREE.Group();\n protected composer: EffectComposer;\n protected postPass: ShaderPass;\n /** Optional bloom pass — created lazily when bloomStrength first goes >0, removed at 0. */\n private bloomPass?: UnrealBloomPass;\n private ditherPass?: ShaderPass;\n private innerLightPass?: ShaderPass;\n private halftonePass?: ShaderPass;\n private heatmapPass?: ShaderPass;\n private paperTexturePass?: ShaderPass;\n private halftoneCmykPass?: ShaderPass;\n // Reused scratch for the per-frame per-wave particle placement (updateSceneFx) — no allocation.\n private readonly fxCenter = new THREE.Vector3();\n private readonly fxRight = new THREE.Vector3();\n private readonly fxUp = new THREE.Vector3();\n protected readonly container: HTMLElement;\n private readonly respectReducedMotion: boolean;\n private readonly skipIntroRamp: boolean;\n\n protected config: StudioConfig;\n protected waves: Wave[] = [];\n\n // 2D palette textures (+ any palette videos) live per-wave — see WavePalette on each Wave.\n private backgroundTexture?: THREE.Texture;\n /** The frame behind the glass waves — allocated only once a wave asks for the glass theme. */\n private backdropTarget?: THREE.WebGLRenderTarget;\n /** How many glass layers cover each pixel. A sheet that folds over itself is thicker there, and\n * thickness is the cue that reads as VOLUME rather than as a tinted film — but glass draws\n * opaque, so the nearest layer wins and the fold behind it is invisible without counting. */\n private layerTarget?: THREE.WebGLRenderTarget;\n private backgroundSig = \"\";\n private backgroundImage?: HTMLImageElement;\n private backgroundImageUrl = \"\";\n private failedBackgroundImageUrl = \"\";\n private backgroundVideo?: HTMLVideoElement;\n private backgroundVideoUrl = \"\";\n private failedBackgroundVideoUrl = \"\";\n private backgroundVideoCanvas?: HTMLCanvasElement;\n /** Authored default camera pose, for \"Reset camera\". */\n protected readonly homeCamPos = new THREE.Vector3();\n protected readonly homeCamTarget = new THREE.Vector3();\n\n // Reused scratch for per-frame clip-plane fitting (see updateClipPlanes) — hoisted so the\n // render loop allocates nothing.\n private readonly clipBox = new THREE.Box3();\n private readonly clipSphere = new THREE.Sphere();\n private readonly clipTmpA = new THREE.Vector3();\n private readonly clipTmpB = new THREE.Vector3();\n\n // ---- Interaction layer (optional; fetched only when config.interaction is active) ----\n /** Created by loadInteraction() once the runtime chunk lands, disposed when interaction turns off.\n * Stays undefined for the frames between the config turning it on and the chunk arriving. */\n protected interaction?: InteractionController;\n /** The chunk itself, kept for the applier tables. Set and cleared with `interaction`. */\n private interactionModule?: InteractionModule;\n /** A fetch already in flight, so a per-frame refresh can't start a second one. */\n private interactionLoading = false;\n /** setInteractionInput() calls made while the chunk was in flight, replayed once it lands. */\n private stagedInputs?: Map<string, number>;\n /** Set by dispose(), so an in-flight chunk can't attach listeners to a dead renderer. */\n private disposed = false;\n /** Extra ortho-zoom MULTIPLIER from a cameraZoom binding (1 = none); applied in applyZoom().\n * Protected so the studio's writeCameraToConfig() can divide it back out (keep it out of config). */\n protected interactionZoom = 1;\n /** Extra time-offset DELTA from a timeOffset binding (0 = none); added in updateTime(). */\n private interactionTimeOffset = 0;\n /** Scene-binding out-params: appliers write into this, applyBindings() reads it back. */\n private readonly interactionSceneOut = { timeOffset: 0, zoom: 1 };\n\n private readonly timer = new THREE.Timer();\n private time = 0;\n private rafId = 0;\n protected running = false;\n private started = false;\n\n private visible = true;\n /** Read from the document, NOT assumed: the shell builds the renderer whenever the engine chunk\n * lands, which can be after the reader has left the tab. Assumed visible, that renderer marked\n * itself running with no frame to run on, and the `visibilitychange` that brought the reader back\n * found nothing to restart — so the delta baseline was never reset and the first frame was handed\n * the whole absence at once (see MAX_FRAME_SECONDS for the same stall without the event). */\n private pageVisible = document.visibilityState === \"visible\";\n private reducedMotion = false;\n /** Intro ramp: eases animation time 0→1 over ~1s on load (when config.introRamp). */\n private introTimeRamp = 0;\n\n private readonly resizeObserver: ResizeObserver;\n private readonly intersectionObserver: IntersectionObserver;\n private readonly motionQuery: MediaQueryList;\n /** Re-armed at the live devicePixelRatio on every change — see watchDpr(). */\n private dprQuery?: MediaQueryList;\n /** Pending coalesced resize (0 = none), and the metrics the last resize() actually applied. */\n private resizeRaf = 0;\n private lastResize?: { w: number; h: number; dpr: number };\n\n protected capturing = false;\n /** Fixed backing-buffer dimensions used by the studio's visible export frame. Embeds leave\n * this unset and continue to resize responsively with their container and device DPR. */\n private outputSize?: { width: number; height: number };\n\n constructor(container: HTMLElement, config: StudioConfig, options: WaveRendererOptions = {}) {\n this.container = container;\n this.config = ensureStudioConfig(config);\n this.respectReducedMotion = options.respectReducedMotion ?? true;\n this.skipIntroRamp = options.skipIntroRamp ?? false;\n\n this.renderer = this.createRenderer();\n this.renderer.outputColorSpace = THREE.SRGBColorSpace;\n this.renderer.setClearColor(0x000000, 0);\n container.appendChild(this.renderer.domElement);\n\n // Resilience: if the GPU drops the context (memory pressure, sleep/wake), don't\n // let the browser hard-crash the page — prevent the default and rebuild on restore.\n this.renderer.domElement.addEventListener(\"webglcontextlost\", this.onContextLost, false);\n this.renderer.domElement.addEventListener(\n \"webglcontextrestored\",\n this.onContextRestored,\n false,\n );\n\n // Orthographic, framed in device pixels: resize() sets the frustum to the canvas size, and\n // the mesh is scaled up so the wave overflows the frame, leaving only the twist on screen.\n // The left/right/top/bottom bounds here are placeholders overwritten by the first resize();\n // near/far are placeholders too — updateClipPlanes() refits them to the scene every frame so\n // no camera angle ever clips the wave (a fixed slab does — see updateClipPlanes).\n this.camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 1, 10000);\n this.camera.position.set(\n config.cameraPosition.x,\n config.cameraPosition.y,\n config.cameraPosition.z,\n );\n this.camera.zoom = config.cameraZoom ?? 1;\n this.camera.lookAt(config.cameraTarget.x, config.cameraTarget.y, config.cameraTarget.z);\n this.camera.updateProjectionMatrix();\n // Remember the authored default pose so \"Reset camera\" returns to it (orbit mutates\n // config.cameraPosition, so we can't read it back from config later).\n this.homeCamPos.copy(this.camera.position);\n this.homeCamTarget.set(config.cameraTarget.x, config.cameraTarget.y, config.cameraTarget.z);\n this.scene.add(this.group);\n\n this.composer = new EffectComposer(this.renderer);\n this.composer.addPass(new RenderPass(this.scene, this.camera));\n this.postPass = new ShaderPass({\n uniforms: {\n tDiffuse: { value: null },\n uResolution: { value: new THREE.Vector2(1, 1) },\n uBlurAmount: { value: config.blur },\n uBlurSamples: { value: 6 },\n uGrainAmount: { value: config.grain },\n uTime: { value: 0 },\n },\n vertexShader: postVertexShader,\n fragmentShader: postFragmentShader,\n });\n this.composer.addPass(this.postPass);\n this.composer.addPass(new OutputPass());\n\n this.motionQuery = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n this.reducedMotion = this.respectReducedMotion && this.motionQuery.matches;\n this.motionQuery.addEventListener(\"change\", this.onMotionChange);\n document.addEventListener(\"visibilitychange\", this.onVisibilityChange);\n\n this.intersectionObserver = new IntersectionObserver(\n (entries) => {\n this.visible = entries[0]?.isIntersecting ?? true;\n this.updateRunning();\n },\n { rootMargin: \"100px\" },\n );\n this.intersectionObserver.observe(container);\n\n this.resizeObserver = new ResizeObserver(this.onResize);\n this.resizeObserver.observe(container);\n this.watchDpr(); // the CSS box can stay put while devicePixelRatio moves under it\n\n this.applyBackground();\n this.buildWaves();\n this.resize();\n }\n\n /**\n * Build the backing three renderer. Overridden by the WebGPU subclass; called from the\n * constructor, so it must not depend on subclass FIELDS (prototype methods are available during\n * `super()`, field initialisers are not).\n */\n protected createRenderer(): THREE.WebGLRenderer {\n return new THREE.WebGLRenderer({\n // antialias smooths edges; preserveDrawingBuffer keeps the drawing buffer readable so we\n // can export PNG/WebM. Both cost a little performance, but this authoring tool needs them.\n antialias: true,\n alpha: true,\n preserveDrawingBuffer: true,\n powerPreference: \"high-performance\",\n });\n }\n\n /**\n * Await any asynchronous backend startup. A no-op on WebGL; the WebGPU subclass overrides it to\n * `await renderer.init()` and then draw the first frame. Safe to call more than once.\n */\n async init(): Promise<void> {}\n\n private get segments(): number {\n // Scale detail down as waves multiply, so total geometry stays bounded.\n const q = this.config.quality / Math.sqrt(Math.max(1, this.config.waves.length));\n return THREE.MathUtils.clamp(Math.round(BASE_SEGMENTS * q), 24, 360);\n }\n\n private makeUniforms(): Record<string, THREE.IUniform> {\n const colors: THREE.Vector3[] = [];\n const colorPos: number[] = [];\n for (let i = 0; i < MAX_COLORS; i++) {\n colors.push(new THREE.Vector3(1, 1, 1));\n colorPos.push(MAX_COLORS > 1 ? i / (MAX_COLORS - 1) : 0);\n }\n const meshPointPos: THREE.Vector2[] = [];\n const meshPointColor: THREE.Vector3[] = [];\n const meshPointInfluence: number[] = [];\n for (let i = 0; i < MAX_MESH_POINTS; i++) {\n meshPointPos.push(new THREE.Vector2(0.5, 0.5));\n meshPointColor.push(new THREE.Vector3(1, 1, 1));\n meshPointInfluence.push(0.65);\n }\n const lightPos: THREE.Vector3[] = [];\n const lightColor: THREE.Vector3[] = [];\n const lightIntensity: number[] = [];\n for (let i = 0; i < MAX_LIGHTS; i++) {\n lightPos.push(new THREE.Vector3());\n lightColor.push(new THREE.Vector3(1, 1, 1));\n lightIntensity.push(0);\n }\n const bandBounds: THREE.Vector4[] = [];\n const bandParams: THREE.Vector4[] = [];\n const bandParaPow: number[] = [];\n for (let i = 0; i < MAX_NOISE_BANDS; i++) {\n bandBounds.push(new THREE.Vector4());\n bandParams.push(new THREE.Vector4());\n bandParaPow.push(0);\n }\n // Click-ripple ring buffer (read only under POINTER_RIPPLES). Sized to RIPPLE_SLOTS.\n const rippleOrigin: THREE.Vector2[] = [];\n const rippleAge: number[] = [];\n const rippleAmp: number[] = [];\n for (let i = 0; i < RIPPLE_SLOTS; i++) {\n rippleOrigin.push(new THREE.Vector2());\n rippleAge.push(0);\n rippleAmp.push(0);\n }\n return {\n // Deformation (vertex)\n uTime: { value: 0 },\n uSpeed: { value: 0.05 },\n uSeed: { value: 0 },\n uDispFreqX: { value: 0.003234 },\n uDispFreqZ: { value: 0.00799 },\n uDispAmount: { value: 6.051 },\n uDetailFreq: { value: 0.04 },\n uDetailAmount: { value: 0 }, // 2nd displacement octave (read only under DETAIL_OCTAVE)\n uTwFreqX: { value: -0.055 },\n uTwFreqY: { value: 0.077 },\n uTwFreqZ: { value: -0.518 },\n uTwPowX: { value: 3.95 },\n uTwPowY: { value: 5.85 },\n uTwPowZ: { value: 6.33 },\n uLoopSeconds: { value: 0 }, // seamless-loop period (read only under the LOOP_MOTION define)\n // Colour + light (fragment)\n uColors: { value: colors },\n uColorPos: { value: colorPos },\n uColorCount: { value: 2 },\n uGradType: { value: 0 },\n uGradAngle: { value: 0 },\n uGradShift: { value: 0.15 },\n uMeshPointPos: { value: meshPointPos },\n uMeshPointColor: { value: meshPointColor },\n uMeshPointInfluence: { value: meshPointInfluence },\n uMeshPointCount: { value: 0 },\n uMeshSoftness: { value: 0.62 },\n uPalette: { value: null },\n uUsePalette: { value: 1 },\n uPaletteRaw: { value: 1 },\n uPaletteScale: { value: new THREE.Vector2(1, 1) },\n uPaletteOffset: { value: new THREE.Vector2(0, 0) },\n uPaletteRotation: { value: 0 },\n uDebug: { value: 0 },\n uSheen: { value: 1 },\n uRoundness: { value: 0.35 },\n uIridescence: { value: 0 },\n uDepthTint: { value: 0 }, // solid-theme depth tint (read only under DEPTH_TINT)\n uDepthTintColor: { value: new THREE.Vector3() },\n uHueShift: { value: 0 },\n uContrast: { value: 1 },\n uSaturation: { value: 1 },\n uFiberCount: { value: 90 },\n uFiberStrength: { value: 0.25 },\n uTexture: { value: 0 },\n uCreaseLight: { value: 0.15 },\n uCreaseSharpness: { value: 2.0 },\n uCreaseSoftness: { value: 1.0 },\n uEdgeFade: { value: 0.06 },\n uEdgeFeather: { value: 0.1 }, // ribbon-edge softness (read only under EDGE_FEATHER)\n // Glass theme. uBackdrop is everything drawn BEHIND this wave; it is null on the backdrop\n // pass itself, because binding a target as a texture while rendering into it is a\n // framebuffer feedback loop and the frame is undefined.\n uBackdrop: { value: null as THREE.Texture | null },\n uGlassStrength: { value: 90 },\n uGlassChroma: { value: 0.7 },\n uGlassFrost: { value: 0.08 },\n uGlassSpec: { value: 1.2 },\n uGlassVibrancy: { value: 0.05 },\n uGlassTint: { value: 0.12 },\n uGlassRimPower: { value: 1.2 },\n uGlassRipple: { value: 0 },\n uGlassRippleScale: { value: 0.012 },\n uGlassFlow: { value: 0.9 },\n uGlassPath: { value: 0.45 },\n uGlassDensity: { value: 1.2 },\n uGlassRim: { value: 0.5 },\n uGlassIrid: { value: 0 },\n uGlassFilmNm: { value: 380 },\n uGlassIor: { value: 1.45 },\n uLayers: { value: null as THREE.Texture | null },\n uGlassLayerGain: { value: 0.6 },\n uGlassFusion: { value: 0 },\n uGlassCaustic: { value: 0.4 },\n uViewAxis: { value: new THREE.Vector3(0, 0, 1) },\n uOpacity: { value: 1 },\n uSquared: { value: 1 }, // \"squared\" deep-colour mode: square the colour in-shader (see applyBlendMode)\n // Seed from the CURRENT drawing buffer, not (1,1): resize() is the only other writer, so a wave\n // added afterwards (wave count raised, multi-wave preset/share link loaded) would keep (1,1)\n // until the next resize — and the edgeFade vignette divides gl_FragCoord by it, so\n // `1 - smoothstep(1-edgeFade, 1, sc)` collapses to 0 and the whole wave renders invisible.\n uResolution: { value: this.renderer.getDrawingBufferSize(new THREE.Vector2()) },\n uAmbient: { value: 0.45 },\n uNumLights: { value: 1 },\n uLightPos: { value: lightPos },\n uLightColor: { value: lightColor },\n uLightIntensity: { value: lightIntensity },\n uNumNoiseBands: { value: 0 },\n uNoiseBandBounds: { value: bandBounds },\n uNoiseBandParams: { value: bandParams },\n uNoiseBandParaPow: { value: bandParaPow },\n // Wireframe thin-line theme (used only by lineFragmentShader)\n uLineAmount: { value: 425 },\n uLineThickness: { value: 1 },\n uLineDerivativePower: { value: 0.95 },\n uLineDepthFade: { value: 1 },\n uLineSharpness: { value: 0 },\n uLineGapOpacity: { value: 1 },\n uLineLight: { value: 0 },\n uClearColor: { value: new THREE.Vector3(1, 1, 1) },\n // Interaction / pointer field. ALWAYS present in JS (read only under POINTER_FX /\n // POINTER_RIPPLES); three uploads them only when the compiled program declares them, so their\n // presence never affects a non-interactive wave (byte-identity precedent: uDetailAmount).\n // Helix (vertex, under HELIX) + wireframe rungs (line fragment, under RUNGS). Always present\n // JS-side; three uploads them only when the compiled program declares them, so a wave with no\n // helix / no rungs is untouched (byte-identity precedent: uDetailAmount).\n uHelixTurns: { value: 0 },\n uHelixRadius: { value: 0 },\n uHelixRoll: { value: 0 },\n uHelixPhase: { value: 0 },\n // The path LUT (read only under PATH). The texture itself is per wave and swapped in by\n // syncPathTexture; null until a wave actually has a path.\n uPathTex: { value: null as THREE.Texture | null },\n // Radial fan (vertex, under RADIAL). Always present JS-side; three uploads them only when the\n // compiled program declares them, so a non-radial wave is untouched (precedent: uDetailAmount).\n uRadialAmount: { value: 0 },\n uRadialArc: { value: 160 },\n uRadialSpread: { value: 1 },\n uRadialRadius: { value: 40 },\n uRadialCenter: { value: 0 },\n uRadialCone: { value: 0 },\n uRadialSwirl: { value: 0 },\n uRungAmount: { value: 0 },\n uRungThickness: { value: 1 },\n // Dissolve (both fragment shaders, under DISSOLVE). Always present JS-side; three uploads\n // them only when the compiled program declares them (precedent: uDetailAmount).\n uDissolveAmount: { value: 0 },\n uDissolveBand: { value: 0.35 },\n uDissolveScale: { value: 90 },\n uDissolveBlocky: { value: 0.6 },\n uDissolveAxis: { value: 0 },\n uDissolveReverse: { value: 0 },\n // Read only by the particle emitter (mirrored there); the wave shaders never declare it, so\n // three never uploads it to the wave program.\n uDissolveDust: { value: 1 },\n uPointer: { value: new THREE.Vector2(0, 0) }, // smoothed pointer NDC\n uPointerActive: { value: 0 }, // presence ramp × per-wave influence\n uPointerRadius: { value: 0.6 }, // falloff radius in NDC-y (config radius × 2)\n uPointerAspect: { value: 1 }, // drawing-buffer dw/dh\n uPointerAgitate: { value: 0 },\n uPointerPush: { value: 0 }, // signed membrane dome at the cursor (+ repel / − attract)\n uPointerWake: { value: 0 }, // drag-wake trough amplitude\n uPointerVel: { value: new THREE.Vector2(0, 0) }, // smoothed pointer velocity, NDC/s (wake dir)\n uShapeFlow: { value: 0 }, // ribbon-flow footprint stretch (0 = plain circle)\n uPointerThin: { value: 0 },\n uPointerHue: { value: 0 },\n uPointerLighten: { value: 0 },\n uPointerRipple: { value: 0 }, // this wave's ripple amplitude (scales the shared envelope)\n uRippleOrigin: { value: rippleOrigin },\n uRippleAge: { value: rippleAge },\n uRippleAmp: { value: rippleAmp },\n };\n }\n\n /** Reconcile a wave's baked path LUT with its config. The texture is rewritten in place when the\n * points change — which is what makes dragging a control point cheap, since the alternative is\n * rebuilding 80k vertices per frame — and disposed when a wave loses its path. */\n private syncPathTexture(wave: Wave, sc: WaveConfig): void {\n const pts = sc.path;\n if (!pts || pts.length < 2) {\n if (wave.pathTexture) {\n wave.pathTexture.dispose();\n wave.pathTexture = undefined;\n wave.pathSig = undefined;\n }\n // Left as-is rather than nulled: on the TSL backend the node must always have a texture bound,\n // and with no PATH in the variant nothing samples it either way.\n return;\n }\n const sig = JSON.stringify(pts);\n if (!wave.pathTexture) {\n wave.pathTexture = bakePathTexture(pts);\n wave.pathSig = sig;\n } else if (sig !== wave.pathSig) {\n writePathTexture(wave.pathTexture.image.data as Float32Array, pts);\n wave.pathTexture.needsUpdate = true;\n wave.pathSig = sig;\n }\n wave.material.uniforms.uPathTex.value = wave.pathTexture;\n }\n\n /** Vertex-shader #defines for a wave: TWIST_MOTION (per-wave animated twist wobble) and\n * LOOP_MOTION (scene-level seamless loop). Both select #ifdef-gated code paths; an empty\n * object compiles the default (linear-time) program. */\n private waveDefines(sc: WaveConfig | undefined): Record<string, string> {\n const defines: Record<string, string> = {};\n if (sc?.twistMotion) defines.TWIST_MOTION = \"\";\n if ((this.config.loopSeconds ?? 0) > 0) defines.LOOP_MOTION = \"\";\n // A detailAmount binding on THIS wave also needs the octave compiled (else it no-ops on a wave\n // authored at 0). Only this wave's bindings matter now (bindings are per wave).\n const bindsDetail =\n sc?.interaction?.bindings?.some((b) => b.target === \"detailAmount\") ?? false;\n if ((sc?.detailAmount ?? 0) !== 0 || bindsDetail) defines.DETAIL_OCTAVE = \"\";\n if ((sc?.depthTint ?? 0) > 0) defines.DEPTH_TINT = \"\";\n if ((sc?.edgeFeather ?? 0.1) !== 0.1) defines.EDGE_FEATHER = \"\";\n // Helix: turns/phase alone move nothing — the block only displaces via radius or roll, so those\n // two are what decide whether it's compiled at all. A helix binding counts too: driving\n // helixRadius up from an authored 0 has to have somewhere to land (as with detailAmount above).\n const bindsHelix =\n sc?.interaction?.bindings?.some((b) => b.target.startsWith(\"helix\")) ?? false;\n if ((sc?.helixRadius ?? 0) !== 0 || (sc?.helixRoll ?? 0) !== 0 || bindsHelix) {\n defines.HELIX = \"\";\n }\n // Path: a wave with no centreline of its own compiles the program it always did.\n if (sc?.path && sc.path.length >= 2) defines.PATH = \"\";\n // Glass reads an interpolated vertex normal; the other themes keep their per-triangle one.\n if (sc?.theme === \"glass\") defines.VERTEX_NORMAL = \"\";\n // Radial fan: amount 0 is the identity mix, so it alone decides whether the block is compiled.\n // Not binding-driveable in v1 (not in WAVE_TARGET_NAMES), so no bindsRadial term is needed.\n if ((sc?.radialAmount ?? 0) !== 0) defines.RADIAL = \"\";\n // Rungs live in the wireframe fragment shader only; setting the define on a solid wave would\n // key a second, identical program for nothing.\n if (sc?.theme === \"wireframe\" && (sc.rungAmount ?? 0) > 0) defines.RUNGS = \"\";\n // Stripe hardening, wireframe only and only when asked for: 0 is the soft ramp the theme has\n // always drawn, and leaving the block uncompiled keeps that byte-identical.\n if (sc?.theme === \"wireframe\" && (sc.lineSharpness ?? 0) > 0) defines.LINE_SHARP = \"\";\n // Clear gaps, wireframe only: 1 is the opaque card the theme has always drawn, and leaving the\n // block uncompiled keeps that byte-identical (it also keeps the discard out of the program).\n if (sc?.theme === \"wireframe\" && gapAlpha(sc.lineGapColor) < 1) defines.LINE_CLEAR_GAPS = \"\";\n // Lighting, wireframe only and only when asked for: 0 is the flat theme, and leaving the block\n // uncompiled keeps that byte-identical (it also keeps the light uniforms out of the program).\n if (sc?.theme === \"wireframe\" && (sc.lineLight ?? 0) > 0) defines.LINE_LIGHT = \"\";\n // Dissolve: a `dissolveAmount` binding counts too — driving the front up from an authored 0\n // has to have somewhere to land (as with detailAmount / helix above).\n const bindsDissolve =\n sc?.interaction?.bindings?.some((b) => b.target === \"dissolveAmount\") ?? false;\n if (sc?.dissolve && ((sc.dissolve.amount ?? 0) > 0 || bindsDissolve)) defines.DISSOLVE = \"\";\n // Pointer field (per wave, config-only, so input never triggers a recompile). Ripples nest inside.\n if (sc && wavePointerFxActive(this.config, sc)) {\n defines.POINTER_FX = \"\";\n if (waveRipplesActive(this.config, sc)) defines.POINTER_RIPPLES = \"\";\n }\n return defines;\n }\n\n private addWave(): void {\n const geo = new WaveGeometry(this.segments);\n // Initialise defines/fragment/blend from the wave this material will represent, so the\n // first refresh() doesn't force a needless program recompile. Falls back to the first wave.\n const sc = this.config.waves[this.waves.length] ?? this.config.waves[0];\n const material = this.createWaveMaterial(sc);\n // Blending (incl. the squaring blend) is set from the wave's blendMode — see applyBlendMode —\n // so it survives refresh() instead of being a dead constructor flag.\n this.applyBlendMode(material, sc?.blendMode ?? \"squared\");\n const mesh = new THREE.Mesh(geo.geometry, material);\n mesh.frustumCulled = false;\n this.group.add(mesh);\n const palette = new WavePalette(\n (url) => this.createLoopingVideo(url),\n () => {\n this.updatePaletteTextures();\n this.syncVideoPlayback();\n if (!this.running) this.renderOnce();\n },\n );\n this.waves.push({ mesh, material, geometry: geo, palette });\n }\n\n /**\n * Build the material for one wave. The GLSL backend compiles a ShaderMaterial with `#define`\n * variants; the WebGPU subclass builds a TSL node graph instead. Both expose the same `uniforms`\n * surface, so everything downstream in `refresh()` is shared.\n */\n protected createWaveMaterial(sc: WaveConfig | undefined): WaveMaterial {\n return new THREE.ShaderMaterial({\n uniforms: this.makeUniforms(),\n // TWIST_MOTION / LOOP_MOTION select variant vertex-shader paths. Toggled live in refresh().\n defines: this.waveDefines(sc),\n vertexShader,\n // solid theme = surfaceColor shader; wireframe theme = thin-line shader.\n // Swapped live in refresh() when the wave's theme changes.\n fragmentShader: fragmentFor(sc),\n // Glass draws OPAQUE with depth write on: the see-through is sampled from the backdrop, not\n // blended. That is what keeps overlapping sheets robust — no sorting, no order dependence.\n transparent: sc?.theme !== \"glass\",\n depthTest: true,\n depthWrite: true,\n side: THREE.DoubleSide,\n }) as unknown as WaveMaterial;\n }\n\n /**\n * The glass LAYER-COUNT companion for one wave: the same vertex program on the same uniforms\n * object (shared by reference, so every per-frame write reaches it), with a fragment that only\n * marks coverage. Drawn additively with depth off, so every fold over a pixel adds up.\n *\n * A stock override material could not stand in for this: its vertex stage knows nothing of the\n * deformation and it culls back faces, so the count came out for the rest-pose plane — measured\n * at a tenth of the real silhouette on the Liquid Glass preset. The WebGPU subclass builds the\n * node twin from the same flags.\n */\n protected createLayerMaterial(sc: WaveConfig, material: WaveMaterial): THREE.Material {\n const main = material as unknown as THREE.ShaderMaterial;\n // Coverage needs no normal: skip the two extra shape evaluations per vertex.\n const defines = { ...(main.defines ?? this.waveDefines(sc)) };\n delete defines.VERTEX_NORMAL;\n return new THREE.ShaderMaterial({\n uniforms: main.uniforms,\n defines,\n vertexShader,\n fragmentShader: glassLayerFragmentShader,\n transparent: true,\n blending: THREE.AdditiveBlending,\n depthTest: false,\n depthWrite: false,\n side: THREE.DoubleSide,\n });\n }\n\n /** What the layer companion was built against; when this changes, it is rebuilt. */\n protected layerVariantKey(material: WaveMaterial): string {\n const defines = (material as unknown as THREE.ShaderMaterial).defines ?? {};\n return Object.keys(defines).sort().join(\",\");\n }\n\n private dropLayerMaterial(wave: Wave): void {\n if (!wave.layerMaterial) return;\n wave.layerMaterial.dispose();\n wave.layerMaterial = undefined;\n wave.layerKey = undefined;\n }\n\n /**\n * Re-select this wave's shader variant when its config changes shape (theme, twist motion, helix,\n * …). Returns true if the material needs recompiling. The GLSL backend swaps `defines` and the\n * fragment source; the WebGPU subclass rebuilds the node graph, since a TSL variant is a\n * different graph rather than a different define set.\n */\n protected applyWaveVariant(wave: Wave, sc: WaveConfig): boolean {\n const material = wave.material as unknown as THREE.ShaderMaterial;\n let changed = false;\n // Recompile the program when its #define set changes: TWIST_MOTION / DETAIL_OCTAVE / DEPTH_TINT\n // (per wave) and LOOP_MOTION (scene-level). Compare the whole set so any combination is handled.\n const wantDefines = this.waveDefines(sc);\n const curDefines = material.defines ?? {};\n if (Object.keys(wantDefines).sort().join(\",\") !== Object.keys(curDefines).sort().join(\",\")) {\n material.defines = wantDefines;\n changed = true;\n }\n // Swap the fragment shader when this wave's theme changes: solid surfaceColor <->\n // wireframe thin-line. Three recompiles the program on needsUpdate.\n const wantFrag = fragmentFor(sc);\n if (material.fragmentShader !== wantFrag) {\n material.fragmentShader = wantFrag;\n changed = true;\n }\n // Glass draws opaque; the other themes blend. Kept in step here so a wave switched to glass\n // LIVE leaves the transparent list too — the constructor alone only covered a fresh renderer.\n // Not a program define, so it needs no recompile.\n material.transparent = sc.theme !== \"glass\";\n return changed;\n }\n\n /**\n * Apply config.blendMode to a material. \"squared\" (the default) is the hero blend:\n * CustomBlending with AddEquation, src = SrcColorFactor, dst = ZeroFactor, so the\n * framebuffer result is fragColor² — the squaring deepens the colours into the vivid\n * hero look (without it the wave reads pastel). \"additive\"/\"normal\"/\"multiply\" are\n * authoring overrides. Multiply uses Three's premultiplied-alpha path; the custom\n * fragment shaders premultiply their output when Three injects PREMULTIPLIED_ALPHA.\n * Returns true if material state changed (caller flags needsUpdate).\n */\n protected applyBlendMode(material: WaveMaterial, mode: BlendMode): boolean {\n // \"squared\" is the deep hero look. It used to be a framebuffer-squaring CustomBlending\n // (src·src, dst×0) — which REPLACES the destination rather than compositing over it, so any\n // semi-transparent pixel (soft ribbon edges, and the large near-edge-on regions at oblique\n // camera angles) wiped the framebuffer's colour AND alpha, punching dark / see-through holes\n // through the background and through other waves. On a transparent page you never saw it; on\n // an opaque background or with overlapping waves it's the \"chunks vanish\" artifact.\n //\n // Fix: do the colour-squaring in the shader (uSquared) and composite it with ordinary\n // premultiplied alpha (NormalBlending). Over an opaque body the result is identical (col²);\n // soft edges now blend into what's behind them instead of erasing it.\n const squared = mode === \"squared\";\n const blending =\n mode === \"additive\"\n ? THREE.AdditiveBlending\n : mode === \"multiply\"\n ? THREE.MultiplyBlending\n : THREE.NormalBlending; // \"squared\" (deep, via uSquared) and \"normal\" both composite\n // Premultiplied so the squared/multiply colour composites correctly (the shaders premultiply\n // their output under the PREMULTIPLIED_ALPHA define Three injects for this).\n const premultipliedAlpha = mode === \"multiply\" || squared;\n material.uniforms.uSquared.value = squared ? 1 : 0;\n if (material.blending === blending && material.premultipliedAlpha === premultipliedAlpha) {\n return false;\n }\n material.blending = blending;\n material.premultipliedAlpha = premultipliedAlpha;\n return true;\n }\n\n private disposeWaves(): void {\n for (const s of this.waves) {\n this.group.remove(s.mesh);\n s.material.dispose();\n s.layerMaterial?.dispose();\n s.geometry.dispose();\n s.palette.dispose();\n if (s.particleField) {\n this.scene.remove(s.particleField.object);\n s.particleField.dispose();\n }\n }\n this.waves = [];\n }\n\n /**\n * Reconcile the wave pool to `waveCount` WITHOUT tearing everything down:\n * keep existing waves (so the compiled shader program is never deleted and\n * re-compiled — that churn can crash some GPU drivers), add/remove only the\n * delta, and resize each geometry to the current quality.\n */\n private buildWaves(): void {\n const target = Math.max(1, this.config.waves.length);\n while (this.waves.length > target) {\n const s = this.waves.pop();\n if (!s) break;\n this.group.remove(s.mesh);\n s.material.dispose();\n s.layerMaterial?.dispose();\n s.geometry.dispose();\n s.palette.dispose();\n // Also drop this wave's particle field, or its THREE.Points lingers in the scene and sheds stray\n // dust onto later frames — notably the shared thumbnail renderer (preset previews after a\n // particle-heavy preset showed leftover white specks).\n if (s.particleField) {\n this.scene.remove(s.particleField.object);\n s.particleField.dispose();\n }\n }\n while (this.waves.length < target) this.addWave();\n\n const segments = this.segments;\n this.waves.forEach((s, i) => {\n s.geometry.resize(segments);\n s.mesh.renderOrder = i;\n });\n this.refresh();\n }\n\n /** Re-read per-frame-independent values from the (mutated) config. */\n refresh(): void {\n this.applyBackground();\n this.applyPost();\n this.applyParticles();\n this.syncInteraction(); // create/dispose the interaction controller as config toggles it\n // Once an external driver (orbit / edit gizmo) owns the camera, don't fight it here; the shell\n // (no orbit) applies the saved camera position/target so it matches the authored view.\n if (!this.isCameraExternallyDriven()) {\n const p = this.config.cameraPosition;\n const tg = this.config.cameraTarget;\n this.camera.position.set(p.x, p.y, p.z);\n this.camera.lookAt(tg.x, tg.y, tg.z);\n }\n\n this.waves.forEach((wave, i) => {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n const u = wave.material.uniforms;\n if (this.applyBlendMode(wave.material, sc.blendMode)) wave.material.needsUpdate = true;\n if (this.applyWaveVariant(wave, sc)) wave.material.needsUpdate = true;\n\n const stops = [...sc.palette].sort((a, b) => a.pos - b.pos);\n const colorCount = Math.max(1, Math.min(stops.length, MAX_COLORS));\n const colors = u.uColors.value as THREE.Vector3[];\n const colorPos = u.uColorPos.value as number[];\n for (let c = 0; c < MAX_COLORS; c++) {\n const stop = stops[Math.min(c, colorCount - 1)] ?? { color: \"#ffffff\", pos: 0 };\n hexToLinearVec3(stop.color, colors[c]);\n colorPos[c] = stop.pos;\n }\n u.uColorCount.value = colorCount;\n u.uGradType.value =\n sc.gradientType === \"radial\"\n ? 1\n : sc.gradientType === \"conic\"\n ? 2\n : sc.gradientType === \"mesh\"\n ? 3\n : 0;\n u.uGradAngle.value = ((sc.gradientAngle ?? 0) * Math.PI) / 180;\n u.uGradShift.value = sc.gradientShift ?? 0;\n const meshPoints = sc.meshGradientPoints.slice(0, MAX_MESH_POINTS);\n const meshPointPos = u.uMeshPointPos.value as THREE.Vector2[];\n const meshPointColor = u.uMeshPointColor.value as THREE.Vector3[];\n const meshPointInfluence = u.uMeshPointInfluence.value as number[];\n for (let pointIndex = 0; pointIndex < MAX_MESH_POINTS; pointIndex++) {\n const point = meshPoints[pointIndex] ?? meshPoints[meshPoints.length - 1];\n if (!point) continue;\n meshPointPos[pointIndex].set(point.x, point.y);\n hexToLinearVec3(point.color, meshPointColor[pointIndex]);\n meshPointInfluence[pointIndex] = point.influence;\n }\n u.uMeshPointCount.value = meshPoints.length;\n u.uMeshSoftness.value = sc.meshGradientSoftness;\n (u.uPaletteScale.value as THREE.Vector2).set(\n sc.paletteTextureScale?.x ?? 1,\n sc.paletteTextureScale?.y ?? 1,\n );\n (u.uPaletteOffset.value as THREE.Vector2).set(\n sc.paletteTextureOffset?.x ?? 0,\n sc.paletteTextureOffset?.y ?? 0,\n );\n u.uPaletteRotation.value = ((sc.paletteTextureRotation ?? 0) * Math.PI) / 180;\n u.uHueShift.value = sc.hueShift;\n u.uContrast.value = sc.colorContrast;\n u.uSaturation.value = sc.colorSaturation;\n // Wireframe thin-line theme params (used only by lineFragmentShader). uClearColor is the\n // between-line colour = the page background (scene), fed in linear space like the palette.\n u.uLineAmount.value = sc.lineAmount ?? 425;\n u.uLineThickness.value = sc.lineThickness ?? 1;\n u.uLineDerivativePower.value = sc.lineDerivativePower ?? 0.95;\n u.uLineDepthFade.value = sc.lineDepthFade ?? 1;\n u.uLineSharpness.value = sc.lineSharpness ?? 0;\n // One authored field decides both what colour sits between the strands and how much of it\n // shows: absent = the page background, \"transparent\" or an 8-digit hex = partly or fully clear.\n u.uLineGapOpacity.value = gapAlpha(sc.lineGapColor);\n u.uLineLight.value = sc.lineLight ?? 0;\n // Clear-gap strands must not write DEPTH. They are thin transparent slivers layered many deep,\n // and any two sheets that pass close to coplanar then decide who occludes whom by depth\n // precision — which is arbitrary, differs between backends, and shows up as strands winking in\n // and out along a rim. With the write off they simply composite in draw order, which is the\n // wave order and therefore stable. Opaque gaps keep writing depth exactly as before.\n const clearGaps = sc.theme === \"wireframe\" && gapAlpha(sc.lineGapColor) < 1;\n const wantDepthWrite = !clearGaps;\n if (wave.material.depthWrite !== wantDepthWrite) wave.material.depthWrite = wantDepthWrite;\n u.uRungAmount.value = sc.rungAmount ?? 0;\n u.uRungThickness.value = sc.rungThickness ?? 1;\n // The between-strand colour. Defaults to the page background — a wireframe wave is then a\n // window onto the page — but an explicit `lineGapColor` makes the ribbon its own BODY: dark\n // gaps with bright strands combed over them, which is what an opaque striped surface looks\n // like, rather than bright paper showing between dark lines.\n hexToLinearVec3(\n gapColorHex(sc.lineGapColor) ?? this.config.background,\n u.uClearColor.value as THREE.Vector3,\n );\n u.uFiberCount.value = sc.fiberCount;\n u.uFiberStrength.value = sc.fiberStrength;\n u.uTexture.value = sc.texture;\n u.uCreaseLight.value = sc.creaseLight;\n u.uCreaseSharpness.value = sc.creaseSharpness;\n u.uCreaseSoftness.value = sc.creaseSoftness;\n u.uSheen.value = sc.sheen ?? 1;\n u.uRoundness.value = sc.roundness ?? 0.35;\n u.uIridescence.value = sc.iridescence ?? 0;\n u.uDepthTint.value = sc.depthTint ?? 0;\n hexToLinearVec3(sc.depthTintColor ?? \"#0a2540\", u.uDepthTintColor.value as THREE.Vector3);\n u.uEdgeFade.value = sc.edgeFade;\n u.uEdgeFeather.value = sc.edgeFeather ?? 0.1;\n if (sc.theme === \"glass\") {\n // The page behind a transparent scene — what a glass sample composites over.\n hexToLinearVec3(this.config.background, u.uClearColor.value as THREE.Vector3);\n u.uGlassStrength.value = sc.glassStrength ?? 90;\n u.uGlassChroma.value = sc.glassChroma ?? 0.7;\n u.uGlassFrost.value = sc.glassFrost ?? 0.08;\n u.uGlassSpec.value = sc.glassSpec ?? 1.2;\n u.uGlassVibrancy.value = sc.glassVibrancy ?? 0.05;\n u.uGlassTint.value = sc.glassTint ?? 0.12;\n u.uGlassRimPower.value = sc.glassRimPower ?? 1.2;\n u.uGlassRipple.value = sc.glassRipple ?? 0;\n u.uGlassRippleScale.value = sc.glassRippleScale ?? 0.012;\n // Snap the ripple to a whole number of cycles over loopSeconds, the same convention every\n // other animated frequency here follows — otherwise a seamless loop visibly jumps where the\n // water does not come back to where it started.\n {\n const flow = sc.glassFlow ?? 0.9;\n const loop = this.config.loopSeconds ?? 0;\n u.uGlassFlow.value =\n loop > 0 && flow !== 0\n ? (Math.max(1, Math.round((Math.abs(flow) * loop) / (Math.PI * 2))) * (Math.PI * 2)) /\n loop /\n (flow < 0 ? -1 : 1)\n : flow;\n }\n u.uGlassPath.value = sc.glassPath ?? 0.45;\n u.uGlassDensity.value = sc.glassDensity ?? 1.2;\n u.uGlassRim.value = sc.glassRim ?? 0.5;\n u.uGlassIrid.value = sc.glassIrid ?? 0;\n u.uGlassFilmNm.value = sc.glassFilmNm ?? 380;\n u.uGlassIor.value = sc.glassIor ?? 1.45;\n u.uGlassLayerGain.value = sc.glassLayerGain ?? 0.6;\n u.uGlassFusion.value = sc.glassFusion ?? 0;\n u.uGlassCaustic.value = sc.glassCaustic ?? 0.4;\n }\n // Lights + ambient are scene-level (shared by every wave).\n const lights = this.config.lights ?? [];\n u.uAmbient.value = this.config.ambient ?? 0.45;\n u.uNumLights.value = Math.min(lights.length, MAX_LIGHTS);\n const lPos = u.uLightPos.value as THREE.Vector3[];\n const lCol = u.uLightColor.value as THREE.Vector3[];\n const lInt = u.uLightIntensity.value as number[];\n for (let li = 0; li < MAX_LIGHTS; li++) {\n const light = lights[li];\n if (light) {\n lPos[li].set(light.position.x, light.position.y, light.position.z);\n hexToLinearVec3(light.color, lCol[li]);\n lInt[li] = light.intensity;\n } else {\n lInt[li] = 0;\n }\n }\n // Noise bands (per-region fiber overrides) — per wave\n const bands = sc.noiseBands ?? [];\n u.uNumNoiseBands.value = Math.min(bands.length, MAX_NOISE_BANDS);\n const bBounds = u.uNoiseBandBounds.value as THREE.Vector4[];\n const bParams = u.uNoiseBandParams.value as THREE.Vector4[];\n const bPara = u.uNoiseBandParaPow.value as number[];\n for (let bi = 0; bi < MAX_NOISE_BANDS; bi++) {\n const band = bands[bi];\n if (band) {\n bBounds[bi].set(band.startX, band.endX, band.startY, band.endY);\n bParams[bi].set(band.feather, band.strength, band.frequency, band.colorAttenuation);\n bPara[bi] = band.parabolaPower;\n }\n }\n // Deformation (absolute per wave)\n u.uSpeed.value = sc.speed;\n u.uSeed.value = sc.seed;\n u.uLoopSeconds.value = this.config.loopSeconds ?? 0; // scene-level; shared by every wave\n u.uDispFreqX.value = sc.displaceFrequency.x;\n u.uDispFreqZ.value = sc.displaceFrequency.y;\n u.uDispAmount.value = sc.displaceAmount;\n u.uDetailFreq.value = sc.detailFrequency ?? 0.04;\n u.uDetailAmount.value = sc.detailAmount ?? 0;\n u.uTwFreqX.value = sc.twistFrequency.x;\n u.uTwFreqY.value = sc.twistFrequency.y;\n u.uTwFreqZ.value = sc.twistFrequency.z;\n u.uTwPowX.value = sc.twistPower.x;\n u.uTwPowY.value = sc.twistPower.y;\n u.uTwPowZ.value = sc.twistPower.z;\n u.uHelixTurns.value = sc.helixTurns ?? 0;\n u.uHelixRadius.value = sc.helixRadius ?? 0;\n u.uHelixRoll.value = sc.helixRoll ?? 0;\n this.syncPathTexture(wave, sc);\n u.uHelixPhase.value = sc.helixPhase ?? 0;\n u.uRadialAmount.value = sc.radialAmount ?? 0;\n u.uRadialArc.value = sc.radialArc ?? 160;\n u.uRadialSpread.value = sc.radialSpread ?? 1;\n u.uRadialRadius.value = sc.radialRadius ?? 40;\n u.uRadialCenter.value = sc.radialCenter ?? 0;\n u.uRadialCone.value = sc.radialCone ?? 0;\n u.uRadialSwirl.value = sc.radialSwirl ?? 0;\n const dis = sc.dissolve;\n u.uDissolveAmount.value = dis?.amount ?? 0;\n u.uDissolveBand.value = dis?.band ?? 0.35;\n u.uDissolveScale.value = dis?.scale ?? 90;\n u.uDissolveBlocky.value = dis?.blocky ?? 0.6;\n u.uDissolveAxis.value = DISSOLVE_AXIS_INDEX[dis?.axis ?? \"length\"] ?? 0;\n u.uDissolveReverse.value = dis?.reverse ? 1 : 0;\n u.uDissolveDust.value = dis?.dust ?? 1;\n // Mesh transform — each wave's ABSOLUTE scale / rotation / position, applied via\n // modelMatrix using THREE's Euler XYZ order so the on-screen orientation matches the\n // authored view.\n wave.mesh.scale.set(sc.scale.x, sc.scale.y, sc.scale.z);\n wave.mesh.rotation.set(\n THREE.MathUtils.degToRad(sc.rotation.x),\n THREE.MathUtils.degToRad(sc.rotation.y),\n THREE.MathUtils.degToRad(sc.rotation.z),\n );\n wave.mesh.position.set(sc.position.x, sc.position.y, sc.position.z);\n u.uOpacity.value = sc.opacity;\n });\n\n // Static pointer-field params. The falloff radius and ribbon-flow footprint stretch are SHARED\n // (scene-level); the hover amplitudes and ripple amplitude are PER WAVE. The dynamic pointer state\n // (position/velocity/presence/ripple envelopes) is pushed each frame in applyPointerField().\n const sharedRadius = (this.config.interaction?.radius ?? 0.3) * 2; // radius = fraction of viewport H\n const sharedFlow = this.config.interaction?.ribbonFlow ?? 0.8; // on by default; 0 = plain circle\n this.waves.forEach((wave, i) => {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n if (!wavePointerFxActive(this.config, sc)) return;\n const u = wave.material.uniforms;\n const h = sc.interaction?.hover;\n u.uPointerRadius.value = sharedRadius;\n u.uPointerAgitate.value = h?.agitate ?? 0;\n u.uPointerPush.value = h?.push ?? 0;\n u.uPointerWake.value = h?.wake ?? 0;\n u.uPointerThin.value = h?.thin ?? 0;\n u.uPointerHue.value = h?.hueShift ?? 0;\n u.uPointerLighten.value = h?.lighten ?? 0;\n u.uPointerRipple.value = sc.interaction?.press?.ripple ?? 0;\n u.uShapeFlow.value = sharedFlow;\n });\n\n this.updatePaletteTextures();\n this.syncVideoPlayback();\n\n // Whole-wave mirror (world-space flip ≈ screen flip for the near-frontal camera).\n this.group.scale.set(this.config.mirrorH ? -1 : 1, this.config.mirrorV ? -1 : 1, 1);\n\n this.onAfterRefresh();\n if (!this.running) this.renderOnce();\n }\n\n /** Point every wave's palette sampler at its own WavePalette texture (each rebuilt only\n * when that wave's palette actually changes — see WavePalette.apply). */\n private updatePaletteTextures(): void {\n this.waves.forEach((wave, i) => {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n wave.palette.apply(sc, wave.material.uniforms);\n });\n }\n\n /** Dev: 0 = normal, 1 = visualise the crease value, 2 = visualise derivative normal. */\n setDebug(v: number): void {\n for (const s of this.waves) s.material.uniforms.uDebug.value = v;\n this.renderOnce();\n }\n\n /** Rebuild geometry + waves (call when waveCount or quality changes). */\n rebuild(): void {\n this.buildWaves();\n }\n\n private applyBackground(): void {\n if (this.config.transparentBackground) {\n this.scene.background = null;\n this.renderer.setClearColor(0x000000, 0);\n this.onBackgroundChanged();\n return;\n }\n\n const matte = new THREE.Color(this.config.background);\n this.renderer.setClearColor(matte, 1);\n if (this.config.backgroundMode === \"color\") {\n this.applyColorBackground(matte);\n this.onBackgroundChanged();\n return;\n }\n // Live video is redrawn every frame, so cap its staging canvas near 1080p. Still images\n // and procedural maps retain the full 4K-capable background texture path.\n const { width, height } = this.backgroundCanvasSize(\n this.config.backgroundVideoUrl ? 2048 : 4096,\n );\n if (this.config.backgroundMode === \"gradient\") this.applyGradientBackground(width, height);\n else this.applyImageBackground(matte, width, height);\n this.onBackgroundChanged();\n }\n\n /**\n * Build a wave's particle field. The GLSL backend returns a `THREE.Points` field; the TSL backend\n * returns an instanced-sprite one wired to this wave's own uniform registry.\n */\n protected createParticleField(\n _wave: Wave,\n _sc: WaveConfig,\n onReady: () => void,\n ): WaveParticleField {\n return new ParticleField(onReady);\n }\n\n /**\n * Largest texture edge the backend will accept, used to cap the background canvas. WebGL reports\n * it on `capabilities`; WebGPU has no such object, so the subclass reads the device limit.\n */\n protected maxTextureSize(): number {\n return this.renderer.capabilities.maxTextureSize;\n }\n\n /**\n * Called whenever the scene background changes. A no-op on WebGL, where `EffectComposer`'s\n * RenderPass reads the scene afresh each frame; the WebGPU subclass rebuilds its post chain,\n * because a `pass()` node captures the background state at the point its render context is built.\n */\n protected onBackgroundChanged(): void {}\n\n private applyColorBackground(matte: THREE.Color): void {\n this.clearBackgroundVideo();\n this.backgroundTexture?.dispose();\n this.backgroundTexture = undefined;\n this.backgroundSig = \"\";\n this.scene.background = matte;\n }\n\n private applyGradientBackground(width: number, height: number): void {\n this.clearBackgroundVideo();\n const gradType = this.config.backgroundGradientType;\n if (gradType === \"mesh\") {\n const pts = this.config.backgroundMeshPoints ?? [];\n const softness = this.config.backgroundMeshSoftness ?? 0.62;\n const ptSig = pts\n .map((p) => `${p.color}@${p.x.toFixed(3)},${p.y.toFixed(3)},${p.influence.toFixed(3)}`)\n .join(\",\");\n const sig = [\"mesh\", softness.toFixed(3), ptSig, width, height].join(\"|\");\n if (sig !== this.backgroundSig || !this.backgroundTexture) {\n const canvas = buildBackgroundMeshCanvas(pts, softness, width, height);\n this.backgroundTexture?.dispose();\n this.backgroundTexture = canvasToTexture(canvas);\n this.backgroundSig = sig;\n }\n this.scene.background = this.backgroundTexture;\n return;\n }\n const source = this.config.backgroundGradientSource ?? \"stops\";\n const def = PALETTE_MAPS[source];\n const stops =\n source !== \"stops\" && def?.kind === \"gradient\" && def.stops\n ? def.stops\n : this.config.backgroundPalette;\n const stopSig = stops.map((stop) => `${stop.color}@${stop.pos.toFixed(3)}`).join(\",\");\n const sig = [\n \"gradient\",\n source,\n gradType,\n this.config.backgroundGradientAngle,\n stopSig,\n width,\n height,\n ].join(\"|\");\n if (sig !== this.backgroundSig || !this.backgroundTexture) {\n const canvas = buildBackgroundGradientCanvas({\n stops,\n type: gradType,\n angle: this.config.backgroundGradientAngle,\n width,\n height,\n });\n this.backgroundTexture?.dispose();\n this.backgroundTexture = canvasToTexture(canvas);\n this.backgroundSig = sig;\n }\n this.scene.background = this.backgroundTexture;\n }\n\n /** Image mode: a live video, a user-loaded image, or a built-in map. `matte` shows while an\n * async source is still loading. */\n private applyImageBackground(matte: THREE.Color, width: number, height: number): void {\n const fit = this.config.backgroundImageFit ?? \"cover\";\n const zoom = this.config.backgroundImageZoom ?? 1;\n const position = this.config.backgroundImagePosition ?? { x: 0, y: 0 };\n const videoUrl = this.config.backgroundVideoUrl;\n const customUrl = this.config.backgroundImageUrl;\n let source: CanvasImageSource;\n let sourceWidth: number;\n let sourceHeight: number;\n let sourceSig: string;\n if (videoUrl) {\n this.ensureBackgroundVideo(videoUrl);\n if (!this.backgroundVideo || this.backgroundVideo.readyState < 2) {\n this.scene.background = matte;\n return;\n }\n source = this.backgroundVideo;\n sourceWidth = this.backgroundVideo.videoWidth;\n sourceHeight = this.backgroundVideo.videoHeight;\n sourceSig = `video|${videoUrl}`;\n } else if (customUrl) {\n this.clearBackgroundVideo();\n if (\n !this.backgroundImage ||\n this.backgroundImageUrl !== customUrl ||\n !this.backgroundImage.complete\n ) {\n this.loadBackgroundImage(customUrl);\n this.scene.background = matte;\n return;\n }\n source = this.backgroundImage;\n sourceWidth = this.backgroundImage.naturalWidth;\n sourceHeight = this.backgroundImage.naturalHeight;\n sourceSig = `custom|${customUrl}`;\n } else {\n this.clearBackgroundVideo();\n const imageSource = this.config.backgroundImageSource ?? \"vaporwave\";\n const canvas =\n imageSource === \"hero\"\n ? buildHeroPaletteCanvas()\n : PALETTE_MAPS[imageSource]?.kind === \"image\"\n ? paletteMapCanvas(PALETTE_MAPS[imageSource], Math.max(width, height))\n : null;\n if (!canvas) {\n this.scene.background = matte;\n return;\n }\n source = canvas;\n sourceWidth = canvas.width;\n sourceHeight = canvas.height;\n sourceSig = `map|${imageSource}`;\n }\n\n const sig = [\n \"image\",\n sourceSig,\n fit,\n zoom,\n position.x,\n position.y,\n this.config.background,\n width,\n height,\n ].join(\"|\");\n if (sig !== this.backgroundSig || !this.backgroundTexture) {\n const canvas = buildBackgroundImageCanvas(\n source,\n sourceWidth,\n sourceHeight,\n width,\n height,\n fit,\n this.config.background,\n zoom,\n position.x,\n position.y,\n );\n this.backgroundTexture?.dispose();\n this.backgroundTexture = canvasToTexture(canvas);\n this.backgroundSig = sig;\n this.backgroundVideoCanvas = videoUrl ? canvas : undefined;\n }\n this.scene.background = this.backgroundTexture;\n }\n\n private backgroundCanvasSize(maxRequestedEdge = 4096): { width: number; height: number } {\n const rawWidth = this.outputSize?.width ?? Math.max(1, this.container.clientWidth);\n const rawHeight = this.outputSize?.height ?? Math.max(1, this.container.clientHeight);\n const maxEdge = Math.min(maxRequestedEdge, this.maxTextureSize());\n const scale = Math.min(1, maxEdge / Math.max(rawWidth, rawHeight));\n return {\n width: Math.max(1, Math.round(rawWidth * scale)),\n height: Math.max(1, Math.round(rawHeight * scale)),\n };\n }\n\n private loadBackgroundImage(url: string): void {\n if (this.backgroundImageUrl === url || this.failedBackgroundImageUrl === url) return;\n this.backgroundImageUrl = url;\n this.backgroundImage = undefined;\n const image = new Image();\n image.decoding = \"async\";\n if (!url.startsWith(\"data:\") && !url.startsWith(\"blob:\")) image.crossOrigin = \"anonymous\";\n image.addEventListener(\n \"load\",\n () => {\n if (this.config.backgroundImageUrl !== url) return;\n this.backgroundImage = image;\n this.failedBackgroundImageUrl = \"\";\n this.backgroundSig = \"\";\n this.applyBackground();\n if (!this.running) this.renderOnce();\n },\n { once: true },\n );\n image.addEventListener(\n \"error\",\n () => {\n if (this.config.backgroundImageUrl !== url) return;\n this.failedBackgroundImageUrl = url;\n this.backgroundSig = \"\";\n this.applyBackground();\n if (!this.running) this.renderOnce();\n },\n { once: true },\n );\n image.src = url;\n }\n\n private ensureBackgroundVideo(url: string): void {\n if (this.backgroundVideoUrl === url || this.failedBackgroundVideoUrl === url) return;\n this.clearBackgroundVideo();\n this.backgroundVideoUrl = url;\n const video = this.createLoopingVideo(url);\n video.addEventListener(\n \"loadeddata\",\n () => {\n if (this.config.backgroundVideoUrl !== url) return;\n this.backgroundVideo = video;\n this.failedBackgroundVideoUrl = \"\";\n this.backgroundSig = \"\";\n this.applyBackground();\n this.syncVideoPlayback();\n if (!this.running) this.renderOnce();\n },\n { once: true },\n );\n video.addEventListener(\n \"error\",\n () => {\n if (this.config.backgroundVideoUrl !== url) return;\n this.failedBackgroundVideoUrl = url;\n this.backgroundSig = \"\";\n this.applyBackground();\n if (!this.running) this.renderOnce();\n },\n { once: true },\n );\n this.backgroundVideo = video;\n video.load();\n }\n\n private clearBackgroundVideo(): void {\n if (this.backgroundVideo) {\n this.backgroundVideo.pause();\n this.backgroundVideo.removeAttribute(\"src\");\n this.backgroundVideo.load();\n }\n this.backgroundVideo = undefined;\n this.backgroundVideoUrl = \"\";\n this.failedBackgroundVideoUrl = \"\";\n this.backgroundVideoCanvas = undefined;\n }\n\n private createLoopingVideo(url: string): HTMLVideoElement {\n const video = document.createElement(\"video\");\n video.muted = true;\n video.defaultMuted = true;\n video.loop = true;\n video.playsInline = true;\n video.preload = \"auto\";\n if (!url.startsWith(\"data:\") && !url.startsWith(\"blob:\")) video.crossOrigin = \"anonymous\";\n video.src = url;\n return video;\n }\n\n private syncVideoPlayback(): void {\n const backgroundActive =\n this.running &&\n !this.config.transparentBackground &&\n this.config.backgroundMode === \"image\" &&\n !!this.config.backgroundVideoUrl;\n if (this.backgroundVideo) {\n if (backgroundActive) void this.backgroundVideo.play().catch(() => {});\n else this.backgroundVideo.pause();\n }\n // Palette videos are per wave.\n this.waves.forEach((wave, i) => {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n wave.palette.syncPlayback(sc, this.running);\n });\n }\n\n private updateBackgroundVideoFrame(): void {\n const video = this.backgroundVideo;\n const canvas = this.backgroundVideoCanvas;\n if (!video || !canvas || video.readyState < 2 || !this.backgroundTexture) return;\n const position = this.config.backgroundImagePosition ?? { x: 0, y: 0 };\n drawBackgroundMediaFrame(\n canvas,\n video,\n video.videoWidth,\n video.videoHeight,\n this.config.backgroundImageFit ?? \"cover\",\n this.config.background,\n this.config.backgroundImageZoom ?? 1,\n position.x,\n position.y,\n );\n this.backgroundTexture.needsUpdate = true;\n }\n\n private applyPost(): void {\n const u = this.postPass.uniforms;\n u.uBlurAmount.value = this.config.blur;\n u.uGrainAmount.value = this.config.grain;\n u.uBlurSamples.value = Math.round(this.config.blurSamples ?? 6);\n this.applyBloom();\n this.applyInnerLight();\n this.applyHalftone();\n this.applyHeatmap();\n this.applyHalftoneCmyk();\n this.applyPaperTexture();\n this.applyDither();\n }\n\n /** Insert / tune / remove the bloom pass. strength 0 removes it from the composer entirely, so\n * cost and pixels are identical to bloom-off; the pass (and its mip-chain render targets) is\n * created lazily the first time bloom is enabled and disposed when turned back off. It sits\n * right after the scene RenderPass so it blooms the wave before the grain/blur pass. */\n private applyBloom(): void {\n const strength = this.config.bloomStrength ?? 0;\n if (strength > 0) {\n if (!this.bloomPass) {\n const size = this.renderer.getDrawingBufferSize(new THREE.Vector2());\n this.bloomPass = new UnrealBloomPass(\n size,\n strength,\n this.config.bloomRadius ?? 0.4,\n this.config.bloomThreshold ?? 0.85,\n );\n this.composer.insertPass(this.bloomPass, 1); // after RenderPass, before postPass/OutputPass\n }\n this.bloomPass.strength = strength;\n this.bloomPass.radius = this.config.bloomRadius ?? 0.4;\n this.bloomPass.threshold = this.config.bloomThreshold ?? 0.85;\n } else if (this.bloomPass) {\n this.composer.removePass(this.bloomPass);\n this.bloomPass.dispose();\n this.bloomPass = undefined;\n }\n }\n\n /** Insert / tune / remove the dithering pass — a self-contained \"layered\" post shader (an ordered\n * Bayer dither, in the spirit of paper-design/shaders). Like bloom, dither 0 removes the pass\n * entirely so cost and pixels match dither-off, and it's created lazily on first enable. It is\n * appended AFTER OutputPass so it runs last and quantizes display-space colour (tone-mapped +\n * sRGB) — dithering the linear composer buffer would crush the steps in the shadows. */\n private applyDither(): void {\n const strength = this.config.dither ?? 0;\n if (strength > 0) {\n if (!this.ditherPass) {\n this.ditherPass = new ShaderPass({\n uniforms: {\n tDiffuse: { value: null },\n uResolution: { value: this.renderer.getDrawingBufferSize(new THREE.Vector2()) },\n uDitherStrength: { value: strength },\n uDitherScale: { value: this.config.ditherScale ?? 2 },\n uDitherSteps: { value: this.config.ditherSteps ?? 4 },\n },\n vertexShader: postVertexShader,\n fragmentShader: ditherFragmentShader,\n });\n this.composer.addPass(this.ditherPass); // last pass → renders the dithered image to screen\n }\n const u = this.ditherPass.uniforms;\n u.uDitherStrength.value = strength;\n u.uDitherScale.value = Math.max(1, this.config.ditherScale ?? 2);\n u.uDitherSteps.value = Math.max(2, Math.round(this.config.ditherSteps ?? 4));\n } else if (this.ditherPass) {\n this.composer.removePass(this.ditherPass);\n this.ditherPass.dispose();\n this.ditherPass = undefined;\n }\n }\n\n /** Insert / tune / remove the innerLight pass — volumetric light streaks scattered from the bright\n * wave toward a light point (innerLightX/Y in UV). Scene zone (index 1) so it scatters the raw wave\n * like bloom. innerLight 0 removes the pass entirely; created lazily on first enable. */\n private applyInnerLight(): void {\n const strength = this.config.innerLight ?? 0;\n if (strength > 0) {\n const cx = this.config.innerLightX ?? 0.5;\n const cy = this.config.innerLightY ?? 0.15;\n if (!this.innerLightPass) {\n this.innerLightPass = new ShaderPass({\n uniforms: {\n tDiffuse: { value: null },\n uInnerLight: { value: strength },\n uInnerLightDensity: { value: this.config.innerLightDensity ?? 0.5 },\n uInnerLightDecay: { value: this.config.innerLightDecay ?? 0.95 },\n uInnerLightCenter: { value: new THREE.Vector2(cx, cy) },\n },\n vertexShader: postVertexShader,\n fragmentShader: innerLightFragmentShader,\n });\n this.composer.insertPass(this.innerLightPass, 1); // scene zone: scatter the raw wave\n }\n const u = this.innerLightPass.uniforms;\n u.uInnerLight.value = strength;\n u.uInnerLightDensity.value = this.config.innerLightDensity ?? 0.5;\n u.uInnerLightDecay.value = this.config.innerLightDecay ?? 0.95;\n (u.uInnerLightCenter.value as THREE.Vector2).set(cx, cy);\n } else if (this.innerLightPass) {\n this.composer.removePass(this.innerLightPass);\n this.innerLightPass.dispose();\n this.innerLightPass = undefined;\n }\n }\n\n /** Insert / tune / remove the halftone pass — a rotated dot screen (dot size scales with local\n * brightness) over the finished image. halftone 0 removes the pass; created lazily on enable. */\n private applyHalftone(): void {\n const strength = this.config.halftone ?? 0;\n if (strength > 0) {\n if (!this.halftonePass) {\n this.halftonePass = new ShaderPass({\n uniforms: {\n tDiffuse: { value: null },\n uResolution: { value: this.renderer.getDrawingBufferSize(new THREE.Vector2()) },\n uHalftone: { value: strength },\n uHalftoneCell: { value: this.config.halftoneCell ?? 6 },\n uHalftoneAngle: { value: this.config.halftoneAngle ?? 0.4 },\n },\n vertexShader: postVertexShader,\n fragmentShader: halftoneFragmentShader,\n });\n this.composer.addPass(this.halftonePass); // finish zone: display-space stylization\n }\n const u = this.halftonePass.uniforms;\n u.uHalftone.value = strength;\n u.uHalftoneCell.value = Math.max(2, this.config.halftoneCell ?? 6);\n u.uHalftoneAngle.value = this.config.halftoneAngle ?? 0.4;\n } else if (this.halftonePass) {\n this.composer.removePass(this.halftonePass);\n this.halftonePass.dispose();\n this.halftonePass = undefined;\n }\n }\n\n /** Heatmap: recolour the final image by luminance → thermal palette. Finish zone. */\n private applyHeatmap(): void {\n const strength = this.config.heatmap ?? 0;\n if (strength > 0) {\n if (!this.heatmapPass) {\n this.heatmapPass = new ShaderPass({\n uniforms: { tDiffuse: { value: null }, uHeatmap: { value: strength } },\n vertexShader: postVertexShader,\n fragmentShader: heatmapFragmentShader,\n });\n this.composer.addPass(this.heatmapPass);\n }\n this.heatmapPass.uniforms.uHeatmap.value = strength;\n } else if (this.heatmapPass) {\n this.composer.removePass(this.heatmapPass);\n this.heatmapPass.dispose();\n this.heatmapPass = undefined;\n }\n }\n\n /** Paper texture: fibrous substrate shading multiplied over the image. Finish zone. */\n private applyPaperTexture(): void {\n const strength = this.config.paperTexture ?? 0;\n if (strength > 0) {\n if (!this.paperTexturePass) {\n this.paperTexturePass = new ShaderPass({\n uniforms: {\n tDiffuse: { value: null },\n uPaper: { value: strength },\n uPaperScale: { value: this.config.paperTextureScale ?? 2 },\n },\n vertexShader: postVertexShader,\n fragmentShader: paperTextureFragmentShader,\n });\n this.composer.addPass(this.paperTexturePass);\n }\n const u = this.paperTexturePass.uniforms;\n u.uPaper.value = strength;\n u.uPaperScale.value = Math.max(0.5, this.config.paperTextureScale ?? 2);\n } else if (this.paperTexturePass) {\n this.composer.removePass(this.paperTexturePass);\n this.paperTexturePass.dispose();\n this.paperTexturePass = undefined;\n }\n }\n\n /** CMYK halftone: four rotated dot screens (cyan/magenta/yellow/black). Finish zone. */\n private applyHalftoneCmyk(): void {\n const strength = this.config.halftoneCmyk ?? 0;\n if (strength > 0) {\n if (!this.halftoneCmykPass) {\n this.halftoneCmykPass = new ShaderPass({\n uniforms: {\n tDiffuse: { value: null },\n uHalftoneCmyk: { value: strength },\n uHalftoneCmykCell: { value: this.config.halftoneCmykCell ?? 6 },\n },\n vertexShader: postVertexShader,\n fragmentShader: halftoneCmykFragmentShader,\n });\n this.composer.addPass(this.halftoneCmykPass);\n }\n const u = this.halftoneCmykPass.uniforms;\n u.uHalftoneCmyk.value = strength;\n u.uHalftoneCmykCell.value = Math.max(2, this.config.halftoneCmykCell ?? 6);\n } else if (this.halftoneCmykPass) {\n this.composer.removePass(this.halftoneCmykPass);\n this.halftoneCmykPass.dispose();\n this.halftoneCmykPass = undefined;\n }\n }\n\n /** Coalesce observer-driven resizes to one per frame, and drop any that don't move a device pixel.\n *\n * resize() is expensive — composer.setSize reallocates every pass's render target, and\n * applyBackground() rebuilds a container-sized canvas + texture for gradient/image backgrounds.\n * The old 1:1 `observe → resize()` paid that for observations that changed nothing: the observer\n * reports fractional content-box sizes, so sub-pixel layout shifts (and anything that rounds to\n * the same backing buffer) triggered a full reallocation, as did every observation while an\n * export frame is pinned and the container is not what drives the buffer at all.\n *\n * Genuine per-frame changes — a mobile URL bar collapsing animates the container height — still\n * resize every frame. That work is necessary; the canvas would otherwise stretch. What is\n * removed is the redundant work, not the real work.\n *\n * Only this path is throttled. `resize()` itself stays synchronous and unconditional — context\n * restore and setOutputSize must re-apply immediately, and on a fresh GPU context the metrics\n * are unchanged but the resources are not. */\n private onResize = (): void => {\n if (this.resizeRaf) return;\n this.resizeRaf = requestAnimationFrame(() => {\n this.resizeRaf = 0;\n const next = this.viewportMetrics();\n const last = this.lastResize;\n if (last && next.w === last.w && next.h === last.h && next.dpr === last.dpr) return;\n this.resize();\n });\n };\n\n /** Re-arm the DPR watch and re-render at the new device-pixel ratio.\n *\n * ResizeObserver watches the CSS box only, so browser zoom or dragging the window to a monitor\n * with a different DPR changes devicePixelRatio without changing the box — the backing buffer\n * stayed at the old resolution and the wave went soft until something else forced a resize. */\n private onDprChange = (): void => {\n this.watchDpr();\n this.resize();\n };\n\n /** A `(resolution: Xdppx)` query only fires when we LEAVE the current ratio, so it is re-armed at\n * the new one on every change. */\n private watchDpr(): void {\n this.dprQuery?.removeEventListener(\"change\", this.onDprChange);\n this.dprQuery = window.matchMedia(`(resolution: ${window.devicePixelRatio || 1}dppx)`);\n this.dprQuery.addEventListener(\"change\", this.onDprChange);\n }\n\n /** The backing-buffer metrics resize() will apply: the export frame when one is pinned, else the\n * container box at the (clamped) device-pixel ratio. */\n private viewportMetrics(): { w: number; h: number; dpr: number } {\n return {\n w: this.outputSize?.width ?? Math.max(1, this.container.clientWidth),\n h: this.outputSize?.height ?? Math.max(1, this.container.clientHeight),\n dpr: this.outputSize ? 1 : Math.min(window.devicePixelRatio || 1, this.config.dprMax),\n };\n }\n\n private onContextLost = (e: Event): void => {\n e.preventDefault(); // tell the browser we'll recover → no \"Aw, Snap\" crash\n cancelAnimationFrame(this.rafId);\n this.running = false;\n };\n\n private onContextRestored = (): void => {\n // old GPU resources are invalid on a fresh context — disposeWaves drops each wave's palette AND\n // its particle Points, so refresh() (via buildWaves → applyParticles) rebuilds them fresh.\n this.disposeWaves();\n this.backgroundTexture?.dispose();\n this.backgroundTexture = undefined;\n this.backgroundSig = \"\";\n this.buildWaves();\n this.resize();\n this.updateRunning();\n };\n\n resize(): void {\n const { w, h, dpr } = this.viewportMetrics();\n this.lastResize = { w, h, dpr }; // what onResize compares against to skip no-op observations\n this.renderer.setPixelRatio(dpr);\n this.renderer.setSize(w, h, !this.outputSize);\n if (this.outputSize) {\n // setSize(..., false) preserves the exact backing buffer without writing fixed CSS\n // pixel dimensions. Keep the canvas stretched to the visible aspect-ratio frame.\n this.renderer.domElement.style.width = \"100%\";\n this.renderer.domElement.style.height = \"100%\";\n }\n this.resizePost(w, h, dpr);\n const dw = w * dpr;\n const dh = h * dpr;\n (this.postPass.uniforms.uResolution.value as THREE.Vector2).set(dw, dh);\n if (this.ditherPass) (this.ditherPass.uniforms.uResolution.value as THREE.Vector2).set(dw, dh);\n if (this.halftonePass)\n (this.halftonePass.uniforms.uResolution.value as THREE.Vector2).set(dw, dh);\n for (const s of this.waves) {\n (s.material.uniforms.uResolution.value as THREE.Vector2).set(dw, dh);\n }\n // The responsive ortho framing: the frustum = the canvas in DEVICE pixels (1 world unit =\n // 1px at zoom 1). Combined with the ×10 mesh scale, the wave overflows the frame.\n this.camera.left = -dw / 2;\n this.camera.right = dw / 2;\n this.camera.top = dh / 2;\n this.camera.bottom = -dh / 2;\n this.applyZoom(); // responsive ortho zoom (maps FRAME_W world units onto the canvas)\n this.applyBackground();\n this.onAfterResize();\n if (!this.running) this.renderOnce();\n }\n\n /** Set an exact output buffer while CSS scales the canvas into the on-screen export frame. */\n setOutputSize(width: number, height: number): void {\n this.outputSize = {\n width: THREE.MathUtils.clamp(Math.round(width), 64, 8192),\n height: THREE.MathUtils.clamp(Math.round(height), 64, 8192),\n };\n this.resize();\n }\n\n start(): void {\n this.started = true;\n this.updateRunning();\n }\n\n stop(): void {\n this.started = false;\n this.updateRunning();\n }\n\n private onMotionChange = (e: MediaQueryListEvent): void => {\n this.reducedMotion = this.respectReducedMotion && e.matches;\n this.updateRunning();\n };\n\n private onVisibilityChange = (): void => {\n this.pageVisible = document.visibilityState === \"visible\";\n this.updateRunning();\n };\n\n private updateRunning(): void {\n const shouldAnimate =\n this.started &&\n this.visible &&\n this.pageVisible &&\n !this.config.paused &&\n !this.reducedMotion;\n\n if (shouldAnimate && !this.running) {\n this.running = true;\n // Reset the delta baseline (as Clock.start() + a discarded getDelta() used to) so the first\n // frame advances by ~one frame, not by the whole idle/pause gap that elapsed while stopped.\n this.timer.update();\n this.rafId = requestAnimationFrame(this.loop);\n } else if (!shouldAnimate && this.running) {\n this.running = false;\n cancelAnimationFrame(this.rafId);\n }\n // When not animating (paused / reduced-motion / static export) show the FULL frame, not a\n // frozen mid-ease, by forcing introTimeRamp = 1.\n if (!this.running) {\n this.introTimeRamp = 1;\n this.interaction?.settle(); // collapse pointer/input to rest before the one settled frame\n this.renderOnce();\n }\n this.syncVideoPlayback();\n }\n\n private loop = (): void => {\n if (!this.running) return;\n this.timer.update();\n const dt = Math.min(this.timer.getDelta(), MAX_FRAME_SECONDS);\n this.time += dt;\n this.interaction?.update(dt); // advance smoothed input by the SAME delta (no time-model change)\n if (this.introTimeRamp < 1) this.introTimeRamp = Math.min(1, this.introTimeRamp + 0.016); // ~1s to full at 60fps\n this.renderOnce();\n this.rafId = requestAnimationFrame(this.loop);\n };\n\n /** Advance the per-frame time uniforms (geometry itself is static). Time model:\n * time = elapsed·introTimeRamp + timeOffset — the ramp eases the animation in on load. */\n private updateTime(): void {\n // Skip the ease-in when asked: a fresh renderer (first load, or the studio's HMR hot-swap)\n // starts introTimeRamp at 0, and replaying the ramp on every save reads as a \"speed up\" when\n // you tab back. The studio passes skipIntroRamp in dev; prod builds + the embed keep the ease-in.\n const ramp = this.config.introRamp === false || this.skipIntroRamp ? 1 : this.introTimeRamp;\n const t = this.time * ramp + (this.config.timeOffset ?? 0) + this.interactionTimeOffset;\n // Indexed loop (no per-frame closure) — this runs every frame.\n for (let i = 0; i < this.waves.length; i++) {\n const u = this.waves[i].material.uniforms;\n u.uTime.value = t;\n // Palette drift: flow the colour along the ribbon independently of the geometry by drifting\n // uPaletteOffset over time. Only touch it when nonzero, so refresh()'s static base offset —\n // and every drift-off preset — is left byte-for-byte unchanged.\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n const dx = sc.paletteDriftX ?? 0;\n const dy = sc.paletteDriftY ?? 0;\n if (dx !== 0 || dy !== 0) {\n const base = sc.paletteTextureOffset;\n (u.uPaletteOffset.value as THREE.Vector2).set(base.x + dx * t, base.y + dy * t);\n }\n }\n this.postPass.uniforms.uTime.value = t;\n // same t as the waves → scrub / loop / pause stay in sync\n for (const w of this.waves) w.particleField?.setTime(t);\n }\n\n /** Draw the composed frame. WebGL runs the EffectComposer; WebGPU runs a node post chain. */\n protected renderComposed(): void {\n this.renderGlassPasses();\n this.composer.render();\n }\n\n /** Capture what sits BEHIND the glass waves, so they have something to bend.\n *\n * Glass is drawn opaque and fakes its see-through by sampling this texture, which is why\n * overlapping sheets need no sorting. The pass is skipped entirely when no wave asks for it, so\n * a scene without glass renders exactly as before.\n *\n * The texture MUST be unbound while we render into it: binding a target as a texture while it is\n * the render target is a framebuffer feedback loop, and the frame is undefined. */\n /** The world axis from a surface toward the camera. Constant under an orthographic projection,\n * which is exactly why it is a uniform rather than something the shader derives per fragment. */\n private pushViewAxis(): void {\n const fwd = this.camera.getWorldDirection(this.viewAxisTmp).multiplyScalar(-1);\n for (const w of this.waves) {\n (w.material.uniforms.uViewAxis.value as THREE.Vector3).copy(fwd);\n }\n }\n\n private viewAxisTmp = new THREE.Vector3();\n private empty?: THREE.DataTexture;\n private backdropClear = new THREE.Color();\n private prevClear = new THREE.Color();\n private depthTmp = new THREE.Vector3();\n private sizeTmp = new THREE.Vector2();\n\n /** Pin a capture's sampling so both backends read it the same way. Left to their defaults the\n * two disagree about filtering and edge behaviour, and a glass sheet samples FAR from the\n * fragment — one offset per channel — so the disagreement shows up as a red/blue bias with green\n * untouched, which looks like a dispersion bug and is not one. */\n private pinSampling(t: THREE.WebGLRenderTarget): THREE.WebGLRenderTarget {\n t.texture.minFilter = THREE.LinearFilter;\n t.texture.magFilter = THREE.LinearFilter;\n t.texture.wrapS = THREE.ClampToEdgeWrapping;\n t.texture.wrapT = THREE.ClampToEdgeWrapping;\n t.texture.generateMipmaps = false;\n return t;\n }\n\n /** A 1x1 transparent texture, used wherever a sampler must be BOUND but must contribute nothing. */\n private emptyTexture(): THREE.DataTexture {\n if (!this.empty) {\n this.empty = new THREE.DataTexture(new Uint8Array([0, 0, 0, 0]), 1, 1);\n this.empty.needsUpdate = true;\n }\n return this.empty;\n }\n\n protected renderGlassPasses(): void {\n this.pushViewAxis();\n const glassIdx: number[] = [];\n for (let i = 0; i < this.waves.length; i++) {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n if (sc?.theme === \"glass\") glassIdx.push(i);\n }\n if (glassIdx.length === 0) {\n if (this.backdropTarget)\n for (const w of this.waves) w.material.uniforms.uBackdrop.value = null;\n for (const w of this.waves) this.dropLayerMaterial(w);\n return;\n }\n\n // What stays OUT of the backdrop: the glass itself, and any wave in front of a glass sheet — a\n // sheet must not refract what sits on top of it. \"In front\" is depth toward the camera, with\n // array order breaking ties: the studio stacks equal-depth waves by array order, and a wave\n // moved behind the sheet on z has to stay visible THROUGH it rather than vanish (the opaque\n // sheet depth-occludes it, so the backdrop is the only place it can show).\n this.scene.updateMatrixWorld();\n const axis = this.viewAxisTmp; // toward the camera, from pushViewAxis()\n const depthOf = (i: number) => this.waves[i].mesh.getWorldPosition(this.depthTmp).dot(axis);\n const glassDepth = glassIdx.map(depthOf);\n const excluded: THREE.Mesh[] = [];\n for (let i = 0; i < this.waves.length; i++) {\n const d = depthOf(i);\n const inFront = glassIdx.some((g, k) => {\n const dd = d - glassDepth[k];\n return dd > 1e-3 || (Math.abs(dd) <= 1e-3 && i > g);\n });\n if (glassIdx.includes(i) || inFront) excluded.push(this.waves[i].mesh);\n }\n\n const size = this.renderer.getDrawingBufferSize(this.sizeTmp);\n if (!this.backdropTarget) {\n this.backdropTarget = this.pinSampling(\n new THREE.WebGLRenderTarget(size.x, size.y, { depthBuffer: true, stencilBuffer: false }),\n );\n } else if (this.backdropTarget.width !== size.x || this.backdropTarget.height !== size.y) {\n this.backdropTarget.setSize(size.x, size.y);\n }\n // The passes below change the renderer's clear colour and the scene background; both are\n // global state the main frame relies on, so they are put back exactly as found.\n const prevTarget = this.renderer.getRenderTarget();\n const prevClearAlpha = this.renderer.getClearAlpha();\n this.renderer.getClearColor(this.prevClear);\n const prevBg = this.scene.background;\n const restore = () => {\n this.renderer.setRenderTarget(prevTarget);\n this.renderer.setClearColor(this.prevClear, prevClearAlpha);\n this.scene.background = prevBg;\n };\n\n // Unbind with a 1x1 rather than null: a TSL texture node cannot hold null, and binding a target\n // as a texture while rendering into it is a framebuffer feedback loop either way.\n for (const w of this.waves) w.material.uniforms.uBackdrop.value = this.emptyTexture();\n for (const m of excluded) m.visible = false;\n this.renderer.setRenderTarget(this.backdropTarget);\n // Clear to the PAGE colour, opaque, rather than to transparent black. A transparent capture\n // forces every sample to composite over a fallback by its own alpha, and the two backends do\n // not agree on what alpha a render target hands back — the parity case showed the same window\n // washed on one and vivid on the other. An opaque capture takes alpha out of the question, and\n // it is also what is actually behind the glass: a transparent scene is showing the page.\n // setClearColor is called with the target BOUND, because three encodes the colour for whatever\n // is bound at the time.\n this.backdropClear.set(this.config.background || \"#ffffff\");\n this.renderer.setClearColor(this.backdropClear, 1);\n this.renderer.clear(true, true, false);\n this.renderer.render(this.scene, this.camera);\n for (const m of excluded) m.visible = true;\n for (const w of this.waves) w.material.uniforms.uBackdrop.value = this.backdropTarget.texture;\n\n // The two captures below want ONLY the glass sheets: no page, no other waves, no dust — a\n // particle sprite in the normal buffer decodes as a surface normal and bends the caustic\n // around it.\n const hidden: THREE.Object3D[] = [];\n const hide = (o: THREE.Object3D | undefined) => {\n if (o && o.visible) {\n o.visible = false;\n hidden.push(o);\n }\n };\n for (let i = 0; i < this.waves.length; i++) {\n if (!glassIdx.includes(i)) hide(this.waves[i].mesh);\n hide(this.waves[i].particleField?.object);\n }\n this.scene.background = null;\n\n // Layer count: each glass wave drawn by its COMPANION program — the same vertex stage, so the\n // count follows every twist and fold, and both faces — additively with depth off, 1/8 per\n // layer, so the channel saturates at eight folds, well past anything a ribbon does to itself.\n if (!this.layerTarget) {\n this.layerTarget = this.pinSampling(\n new THREE.WebGLRenderTarget(size.x, size.y, { depthBuffer: false, stencilBuffer: false }),\n );\n } else if (this.layerTarget.width !== size.x || this.layerTarget.height !== size.y) {\n this.layerTarget.setSize(size.x, size.y);\n }\n for (const w of this.waves) w.material.uniforms.uLayers.value = this.emptyTexture();\n const swapped: Wave[] = [];\n for (let i = 0; i < this.waves.length; i++) {\n const w = this.waves[i];\n if (!glassIdx.includes(i)) {\n this.dropLayerMaterial(w); // left the theme: no stale companion\n continue;\n }\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n const key = this.layerVariantKey(w.material);\n if (w.layerMaterial && w.layerKey !== key) this.dropLayerMaterial(w);\n if (!w.layerMaterial) {\n w.layerMaterial = this.createLayerMaterial(sc, w.material);\n w.layerKey = key;\n }\n w.mesh.material = w.layerMaterial;\n swapped.push(w);\n }\n this.renderer.setRenderTarget(this.layerTarget);\n this.renderer.setClearColor(0x000000, 0);\n this.renderer.clear(true, false, false);\n this.renderer.render(this.scene, this.camera);\n for (const w of swapped) w.mesh.material = w.material;\n for (const w of this.waves) w.material.uniforms.uLayers.value = this.layerTarget.texture;\n\n for (const o of hidden) o.visible = true;\n restore();\n }\n\n /** Resize the post chain's render targets. */\n protected resizePost(w: number, h: number, dpr: number): void {\n this.composer.setPixelRatio(dpr);\n this.composer.setSize(w, h);\n }\n\n /** Release the post chain's GPU resources. */\n protected disposePost(): void {\n this.composer.dispose();\n this.backdropTarget?.dispose();\n this.backdropTarget = undefined;\n this.layerTarget?.dispose();\n this.layerTarget = undefined;\n this.empty?.dispose();\n this.empty = undefined;\n }\n\n /** Render exactly one frame at the current time. */\n renderOnce(): void {\n // A backend with asynchronous startup (WebGPU) is not drawable until init() resolves; the base\n // WebGL renderer sets `ready` at construction, so this never trips there.\n if (!this.ready) return;\n this.updateBackgroundVideoFrame();\n this.updateTime();\n this.applyInteraction(); // write pointer + binding uniforms (no-op when off / capturing)\n this.updateSceneFx(); // feed the particle field (before clip fitting)\n this.updateClipPlanes(); // keep near/far bracketing the scene so no camera angle clips the wave\n this.renderComposed();\n // Editor overlays (gizmo/helpers + camera-rig minimap) draw on top of the composed frame —\n // the studio subclass plugs them in here; the base renders nothing extra.\n this.onAfterRenderFrame();\n }\n\n /** Insert / sync / remove EACH wave's particle field — mirrors applyBloom's lazy lifecycle, per wave.\n * An absent block or count 0 means no THREE.Points for that wave (byte-identical). The seeded buffers\n * rebuild only when the (count, seed, edgeBias, bias) signature changes; the rest are live uniforms.\n * configure() (the wave-shape binding) runs later in updateSceneFx, once transforms are current. */\n private applyParticles(): void {\n const loop = this.config.loopSeconds ?? 0;\n this.waves.forEach((wave, i) => {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n const cfg = sc?.particles;\n if (cfg && cfg.count > 0) {\n if (!wave.particleField) {\n // The callback fires when a sprite image finishes rasterizing: a paused / settled\n // renderer has no next frame to pick it up, so it has to be asked to draw one.\n wave.particleField = this.createParticleField(wave, sc, () => {\n if (!this.running) this.renderOnce();\n });\n this.scene.add(wave.particleField.object);\n }\n wave.particleField.sync(cfg, loop);\n } else if (wave.particleField) {\n this.scene.remove(wave.particleField.object);\n wave.particleField.dispose();\n wave.particleField = undefined;\n }\n });\n }\n\n /** Per-frame placement for every wave's particle field: derive the camera's screen basis + each\n * wave's world centre (drift radiates from there) and bind that wave's live shape (#defines +\n * uniforms + matrixWorld) so the dust rides the SAME deform as the ribbon. Runs after refresh()'s\n * per-wave loop so the wave transforms/uniforms are current. No allocation; a no-op when off. */\n private updateSceneFx(): void {\n let anyField = false;\n for (const w of this.waves) {\n if (w.particleField) {\n anyField = true;\n break;\n }\n }\n if (!anyField) return;\n this.camera.updateMatrixWorld();\n this.fxRight.setFromMatrixColumn(this.camera.matrixWorld, 0).normalize();\n this.fxUp.setFromMatrixColumn(this.camera.matrixWorld, 1).normalize();\n const pr = this.renderer.getPixelRatio();\n this.waves.forEach((wave, i) => {\n const field = wave.particleField;\n if (!field) return;\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n wave.mesh.updateWorldMatrix(true, false);\n wave.mesh.getWorldPosition(this.fxCenter); // drift radiates from this wave's world centre\n const frame: ParticleFrame = { center: this.fxCenter, right: this.fxRight, up: this.fxUp };\n field.frame(frame, pr);\n field.configure({\n defines: this.shapeDefines(sc),\n uniforms: wave.material.uniforms,\n matrixWorld: wave.mesh.matrixWorld,\n speed: sc.speed,\n seed: sc.seed,\n });\n });\n }\n\n /** The subset of waveDefines() the particle program shares: the shape gates (what waveShape reads,\n * so the dust matches its wave's deform) plus the pointer gates (what pointerField reads, so the\n * dust reacts to the same cursor). Every one of these is derived from CONFIG alone — live input\n * must never reach here, or each frame would flip the define set and recompile the point program. */\n private shapeDefines(sc: WaveConfig): Record<string, string> {\n const all = this.waveDefines(sc);\n const out: Record<string, string> = {};\n for (const k of [\n \"LOOP_MOTION\",\n \"DETAIL_OCTAVE\",\n \"HELIX\",\n \"TWIST_MOTION\",\n \"RADIAL\",\n \"PATH\",\n \"POINTER_FX\",\n \"POINTER_RIPPLES\",\n \"DISSOLVE\",\n ]) {\n if (k in all) out[k] = \"\";\n }\n return out;\n }\n\n /** Create/dispose the interaction controller as config toggles interaction on/off. Called from\n * refresh(); the compiled define set (POINTER_FX etc.) is handled separately by waveDefines(). */\n private syncInteraction(): void {\n const active = interactionActive(this.config);\n if (active && !this.interaction) {\n void this.loadInteraction();\n } else if (!active && this.interaction) {\n this.interaction.dispose();\n this.interaction = undefined;\n this.interactionModule = undefined;\n this.interactionTimeOffset = 0;\n // Clear any live scroll→cameraZoom multiplier left in camera.zoom: with the controller gone\n // applyBindings won't run to reset it, so recompute the zoom here (no-op when already 1).\n if (this.interactionZoom !== 1) {\n this.interactionZoom = 1;\n this.applyZoom();\n }\n }\n }\n\n /**\n * Fetch the interactivity runtime and attach it. Deliberately a DYNAMIC import: the controller,\n * its listeners, the applier tables and the tilt sensor are ~3.8 KB gzipped that a scene with no\n * `interaction` block never runs, and a static import would put all of it in every bundle. The\n * cost is that interaction goes live a chunk-fetch after the first frame instead of on it —\n * invisible in practice, since there is nothing to react to until a reader moves.\n *\n * A failed fetch is not fatal: the wave renders exactly as an inert one does, which is the same\n * thing that happens on a config with no interaction at all.\n */\n private async loadInteraction(): Promise<void> {\n if (this.interactionLoading) return;\n this.interactionLoading = true;\n try {\n const mod = await import(\"./interaction\");\n // The renderer may have been disposed, or the config edited back to inert, in flight.\n if (this.disposed || this.interaction || !interactionActive(this.config)) return;\n this.interactionModule = mod;\n this.interaction = new mod.InteractionController(this.container, () => this.config);\n if (this.stagedInputs) {\n for (const [name, value] of this.stagedInputs) this.interaction.setInput(name, value);\n this.stagedInputs = undefined;\n }\n this.onInteractionReady();\n } catch {\n // Chunk unavailable (offline, a stale deploy): stay inert rather than throwing into the loop.\n } finally {\n this.interactionLoading = false;\n }\n }\n\n /** Hook for subclasses that hold state the controller must be told about once it exists (the\n * studio's scroll preview). No-op in the base renderer. */\n protected onInteractionReady(): void {}\n\n /** Per-frame interaction write: dynamic pointer-field uniforms + bindings. No-op without a\n * controller. While capturing it writes the REST state instead (pointer field zeroed, every bound\n * param at its authored base) — merely skipping the write would freeze whatever live hover/scroll\n * state the previous frame left in the uniforms, so exports wouldn't be deterministic. */\n private applyInteraction(): void {\n const ic = this.interaction;\n const mod = this.interactionModule;\n if (!ic || !mod) return; // set together, so this is \"before the chunk landed\"\n if (this.capturing) {\n this.applyInteractionRest(mod);\n return;\n }\n if (anyPointerFxActive(this.config)) this.applyPointerField(ic);\n this.applyBindings(ic, mod);\n }\n\n /** Write the capture-frame interaction state: exactly what this config renders with no input —\n * pointer presence + ripple envelopes zeroed (vPointerFall gates every hover effect to 0) and each\n * bound param at its authored base. Live controller state is left untouched, so the frame after\n * the capture resumes mid-gesture; the trailing renderOnce() in captureImage restores the preview.\n * interactionZoom is deliberately NOT reset — captureImage strips it from camera.zoom itself, and\n * the post-capture restore depends on it being unchanged. */\n private applyInteractionRest(mod: InteractionModule): void {\n for (let i = 0; i < this.waves.length; i++) {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n if (!wavePointerFxActive(this.config, sc)) continue;\n const u = this.waves[i].material.uniforms;\n u.uPointerActive.value = 0;\n const rAmp = u.uRippleAmp.value as number[];\n for (let r = 0; r < RIPPLE_SLOTS; r++) rAmp[r] = 0;\n }\n // Scene bindings → authored base: blur/grain write straight to the post uniforms; the time\n // offset delta is simply 0 at base. (Live applyBindings recomputes all of these next frame.)\n const sceneBindings = this.config.interaction?.bindings;\n if (sceneBindings) {\n this.interactionSceneOut.timeOffset = this.config.timeOffset ?? 0;\n this.interactionSceneOut.zoom = this.config.cameraZoom ?? 1;\n const sceneArgs = { post: this.postPass.uniforms, out: this.interactionSceneOut };\n for (const b of sceneBindings) {\n const applier = mod.SCENE_APPLIERS[b.target];\n applier.apply(applier.base(this.config), sceneArgs);\n }\n }\n if (this.interactionTimeOffset !== 0) {\n // renderOnce ran updateTime() BEFORE this, so uTime already carries the live offset for the\n // frame about to draw — zero it and re-run updateTime so the capture uses the authored time.\n this.interactionTimeOffset = 0;\n this.updateTime();\n }\n // Per-wave bindings → that wave's authored base.\n for (let i = 0; i < this.waves.length; i++) {\n const sc = this.config.waves[i];\n const bindings = sc?.interaction?.bindings;\n if (!sc || !bindings || bindings.length === 0) continue;\n const wave = this.waves[i];\n for (const b of bindings) {\n const applier = mod.WAVE_APPLIERS[b.target];\n applier.apply(applier.base(sc), { u: wave.material.uniforms, mesh: wave.mesh });\n }\n }\n }\n\n /** Write the dynamic pointer-field uniforms to every wave that HAS a pointer field. Position /\n * presence are PER WAVE (each trails the cursor at its own hover smoothing); ripple origins/ages\n * are shared. Per-wave amplitudes were already pushed statically in refresh(). */\n private applyPointerField(ic: InteractionController): void {\n const dw = this.camera.right - this.camera.left;\n const dh = this.camera.top - this.camera.bottom;\n const aspect = dh !== 0 ? dw / dh : 1;\n const ripples = ic.sample().ripples;\n // Velocity-driven agitation: the configured `agitate` is the full-gesture strength; when the\n // cursor is still the drive drops to a low idle floor, so the churn tracks how you move instead\n // of buzzing at a constant rate the whole time the cursor is merely present.\n const AGITATE_IDLE = 0.2; // fraction of `agitate` still shown when the cursor holds still\n const AGITATE_GAIN = 0.85; // how much cursor speed adds back on top (capped at full strength)\n const agitateDrive = Math.min(1, AGITATE_IDLE + AGITATE_GAIN * ic.pointerFlux());\n const vel = ic.pointerVelocity();\n for (let i = 0; i < this.waves.length; i++) {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n if (!wavePointerFxActive(this.config, sc)) continue;\n const u = this.waves[i].material.uniforms;\n u.uPointerAgitate.value = (sc.interaction?.hover?.agitate ?? 0) * agitateDrive;\n (u.uPointerVel.value as THREE.Vector2).copy(vel);\n const f = ic.fieldFor(i);\n if (f) {\n (u.uPointer.value as THREE.Vector2).copy(f.ndc);\n u.uPointerActive.value = f.presence;\n } else {\n u.uPointerActive.value = 0; // wave not advanced by update() yet → rest (no hover)\n }\n u.uPointerAspect.value = aspect;\n const rOrigin = u.uRippleOrigin.value as THREE.Vector2[];\n const rAge = u.uRippleAge.value as number[];\n const rAmp = u.uRippleAmp.value as number[];\n for (let r = 0; r < RIPPLE_SLOTS; r++) {\n rOrigin[r].copy(ripples[r].origin);\n rAge[r] = ripples[r].age;\n rAmp[r] = ripples[r].amp;\n }\n }\n }\n\n /** Evaluate bindings via the applier tables: value = mix(from ?? base, to, smoothedSource). Scene\n * bindings drive scene params; each wave's bindings drive that wave's uniforms. */\n private applyBindings(ic: InteractionController, mod: InteractionModule): void {\n // Scene bindings → scene params. Seed out-params at base; appliers overwrite only what they drive,\n // so with no scene binding these stay at base → interactionTimeOffset 0 / interactionZoom 1.\n this.interactionSceneOut.timeOffset = this.config.timeOffset ?? 0;\n this.interactionSceneOut.zoom = this.config.cameraZoom ?? 1;\n const sceneArgs = { post: this.postPass.uniforms, out: this.interactionSceneOut };\n for (const b of this.config.interaction?.bindings ?? []) {\n const applier = mod.SCENE_APPLIERS[b.target];\n const value = THREE.MathUtils.lerp(\n b.from ?? applier.base(this.config),\n b.to,\n ic.bindingValue(b),\n );\n applier.apply(value, sceneArgs);\n }\n // Per-wave bindings → that wave's uniforms / mesh.\n for (let i = 0; i < this.waves.length; i++) {\n const sc = this.config.waves[i];\n const bindings = sc?.interaction?.bindings;\n if (!sc || !bindings || bindings.length === 0) continue;\n const wave = this.waves[i];\n for (const b of bindings) {\n const applier = mod.WAVE_APPLIERS[b.target];\n const value = THREE.MathUtils.lerp(b.from ?? applier.base(sc), b.to, ic.bindingValue(b));\n applier.apply(value, { u: wave.material.uniforms, mesh: wave.mesh });\n }\n }\n // interactionTimeOffset is the DELTA over config.timeOffset (updateTime adds both together).\n this.interactionTimeOffset =\n this.interactionSceneOut.timeOffset - (this.config.timeOffset ?? 0);\n // interactionZoom is a MULTIPLIER over config.cameraZoom (applyZoom multiplies both). This runs\n // even in the studio (where orbit owns the camera) so a scroll→cameraZoom reaction previews:\n // applyZoom only re-fires when the multiplier actually CHANGES (i.e. you scrub the scroll\n // preview), so it never fights an idle orbit; writeCameraToConfig divides it back out so it can't\n // bake into the saved/exported config, and captureImage strips it so posters use the authored zoom.\n const nextZoom = this.interactionSceneOut.zoom / (this.config.cameraZoom || 1);\n if (nextZoom !== this.interactionZoom) {\n this.interactionZoom = nextZoom;\n this.applyZoom();\n }\n }\n\n /** Feed a `custom:<name>` interaction input (developer API). No-op when interaction is off.\n * Values fed before the interaction chunk lands are STAGED (last one per name wins) and replayed\n * when it does — otherwise a one-shot input sent right after `onReady` would vanish into the\n * fetch window. */\n setInteractionInput(name: string, value: number): void {\n if (this.interaction) {\n this.interaction.setInput(name, value);\n return;\n }\n if (typeof name !== \"string\" || !Number.isFinite(value)) return;\n if (!interactionActive(this.config)) return; // nothing will ever consume it\n (this.stagedInputs ??= new Map()).set(name, value);\n }\n\n /**\n * Explicitly ask for the device-orientation sensor. OPTIONAL, and on iOS it opens a modal\n * permission dialog — so this belongs to a page where tilt is the point, not to a decorative\n * background, which should simply go without tilt there. Nothing calls this for you.\n *\n * CALL IT FROM A USER GESTURE: iOS 13+ only grants the sensor from inside a tap handler, and\n * awaiting anything before it (a fetch, a timeout) spends the gesture. Resolves true once\n * readings can flow — false when\n * the platform has no sensor, the scene declares no `interaction.tilt`, or the reader refused.\n * Where no permission is required the sensor is already live and this resolves true.\n */\n enableTilt(): Promise<boolean> {\n // NOT awaited before the permission call: iOS only grants the sensor to a synchronous chain\n // from the gesture, so this hands straight to the controller. In the window before the\n // interaction chunk lands there is nothing to hand to — start the fetch and report false, and\n // the reader's next tap works. (That window is the first frames after mount: any tilt binding\n // makes the layer active, so the fetch begins on the first refresh, long before a tap.)\n if (!this.interaction) {\n if (interactionActive(this.config)) void this.loadInteraction();\n return Promise.resolve(false);\n }\n return this.interaction.enableTilt();\n }\n\n /** Where the tilt sensor stands. `\"prompt\"` is exactly when a tap-to-enable affordance helps. */\n tiltStatus(): TiltStatus {\n if (this.interaction) return this.interaction.tiltStatus();\n // Before the chunk lands there is no sensor to ask, but the platform check needs no chunk.\n const supported =\n typeof window !== \"undefined\" && typeof window.DeviceOrientationEvent !== \"undefined\";\n return supported ? \"prompt\" : \"unsupported\";\n }\n\n /** Take the next orientation reading as the neutral pose — for when the reader has changed grip. */\n recenterTilt(): void {\n this.interaction?.recenterTilt();\n }\n\n /** Re-evaluate play/pause after `config.paused` changes. */\n refreshPlayback(): void {\n this.updateRunning();\n }\n\n /** Jump the camera to the config's authored framing (cameraPosition / cameraTarget /\n * cameraZoom). Called on whole-config swaps (preset / reset / randomize / import). The base\n * applies the pose directly; the studio subclass overrides it to also drive the orbit target\n * and sync the panel. Hook ⑤. */\n protected applyCameraFromConfig(): void {\n const p = this.config.cameraPosition;\n const tg = this.config.cameraTarget;\n this.camera.position.set(p.x, p.y, p.z);\n this.camera.up.set(0, 1, 0);\n this.camera.lookAt(tg.x, tg.y, tg.z);\n this.applyZoom();\n if (!this.running) this.renderOnce();\n }\n\n // ---- Editor hook points (no-ops in the base; the studio subclass overrides them) ----\n\n /** Hook ①: true while orbit or an edit gizmo owns the camera, so refresh() won't reset it. */\n protected isCameraExternallyDriven(): boolean {\n return false;\n }\n\n /** Hook ②: called at the end of refresh(), before the trailing renderOnce(). */\n protected onAfterRefresh(): void {}\n\n /** Hook ③: called at the end of renderOnce(), after the composed frame is drawn. */\n protected onAfterRenderFrame(): void {}\n\n /** Hook ④: called at the end of resize(), before the trailing renderOnce(). */\n protected onAfterResize(): void {}\n\n /** Responsive ortho zoom: map the FRAME_W × FRAME_H reference frame onto the canvas (per\n * config.cameraFit / cameraMinVisibleWidth — see {@link frameZoom}) so the wave frames the same\n * at any size/aspect/dpr, times the user's cameraZoom. */\n protected applyZoom(): void {\n const dw = this.camera.right - this.camera.left; // device px (set in resize)\n const dh = this.camera.top - this.camera.bottom;\n // A cameraZoom binding multiplies in here. It stays 1 unless a binding drives it; when one does,\n // writeCameraToConfig divides it back out (so the studio's orbit-persisted config isn't polluted)\n // and captureImage strips it from camera.zoom (so exports use the authored framing).\n this.camera.zoom =\n this.baseFrameZoom(dw, dh) * (this.config.cameraZoom ?? 1) * this.interactionZoom;\n this.camera.updateProjectionMatrix();\n }\n\n /** The responsive base zoom for the current config's framing policy, before the cameraZoom\n * multiplier. Subclasses invert this to recover cameraZoom from a live camera. */\n protected baseFrameZoom(dw: number, dh: number): number {\n return frameZoom(\n dw,\n dh,\n this.config.cameraFit ?? \"cover\",\n this.config.cameraMinVisibleWidth ?? 0,\n );\n }\n\n /** Fit the orthographic near/far planes to the scene before every render, so no part of a wave\n * is ever clipped as the camera orbits / dollies / pans (or when waves are added or scaled).\n *\n * The camera is *constructed* with fixed 1..10000 planes that only suit the authored hero\n * framing; once the view moves, the wave's depth extent along the view axis easily crosses\n * them and the GPU hard-slices the geometry along a flat plane — chunks of the wave vanish.\n *\n * The in-shader twist rotates each vertex about its LOCAL origin, so it can never move a vertex\n * further from that origin than the base geometry already sits. A sphere at each mesh's origin\n * (radius = base extent × the mesh's largest world scale, plus the Y displacement, ×1.2 slack)\n * therefore safely contains the fully-deformed wave. We union those, then bracket the union\n * along the view axis with a margin. Bracketing to the scene (rather than a fixed huge slab)\n * keeps clip-space depth scene-normalised — so the wireframe theme's depth fade, which reads\n * gl_Position.z, looks the same at any camera distance instead of washing out. Runs each frame;\n * it's a handful of vector ops over 1–8 meshes with no allocation. */\n private updateClipPlanes(): void {\n this.clipBox.makeEmpty();\n for (let i = 0; i < this.waves.length; i++) {\n const wave = this.waves[i];\n const mesh = wave.mesh;\n mesh.updateWorldMatrix(true, false);\n const bs = mesh.geometry.boundingSphere;\n if (!bs) continue;\n // The twist pivot (the mesh's local origin) in world space = the safe sphere's centre.\n const center = this.clipTmpA.setFromMatrixPosition(mesh.matrixWorld);\n const disp = Math.abs(Number(wave.material.uniforms.uDispAmount.value) || 0);\n // Extra wave-local displacement THIS wave's pointer field can add (agitation + push/wake +\n // ripple — worst case they all stack at the cursor), so the deformed surface never crosses the\n // fitted near/far planes. 0 when off → byte-identical.\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n let pointerDisp = 0;\n if (wavePointerFxActive(this.config, sc)) {\n const h = sc.interaction?.hover;\n pointerDisp =\n (h?.agitate ?? 0) +\n Math.abs(h?.push ?? 0) +\n (h?.wake ?? 0) +\n (sc.interaction?.press?.ripple ?? 0);\n }\n const localRadius = bs.center.length() + bs.radius + disp + pointerDisp;\n // This wave's dust (if any) spawns on its surface and drifts OUTWARD by up to `drift` world\n // units as it ages — added post-scale so the particles never cross the fitted near/far. No field\n // / drift 0 → byte-identical.\n const drift = wave.particleField ? Math.abs(sc.particles?.drift ?? 0) : 0;\n const radius = localRadius * mesh.matrixWorld.getMaxScaleOnAxis() * 1.2 + drift;\n // Enclose this wave's sphere by adding its axis-aligned bounding cube corners.\n this.clipBox.expandByPoint(this.clipTmpB.copy(center).addScalar(radius));\n this.clipBox.expandByPoint(this.clipTmpB.copy(center).addScalar(-radius));\n }\n if (this.clipBox.isEmpty()) return;\n this.clipBox.getBoundingSphere(this.clipSphere);\n const viewDir = this.camera.getWorldDirection(this.clipTmpA); // normalised view axis\n const centerDepth = this.clipTmpB\n .copy(this.clipSphere.center)\n .sub(this.camera.position)\n .dot(viewDir);\n const radius = this.clipSphere.radius;\n const margin = radius * 0.25 + 10;\n // Orthographic: a negative near is legal — the slab may extend behind the camera origin.\n this.camera.near = centerDepth - radius - margin;\n this.camera.far = centerDepth + radius + margin;\n this.camera.updateProjectionMatrix();\n }\n\n /** A world-space position delta that drops a duplicated wave into open frame space beside the\n * one it was copied from — screen-left and a touch down, sized to the visible frame — instead\n * of hidden exactly on top of it or (for the hero, which fills the right of the frame) pushed\n * off-frame. Camera-relative: it's \"left on screen\" no matter how the view is rotated/zoomed,\n * because it's built from the camera's right/up axes and the frame's visible world size. */\n\n async captureImage(\n mime: string,\n transparent = true,\n quality?: number,\n time?: number,\n ): Promise<Blob> {\n const prevBg = this.config.transparentBackground;\n if (transparent !== prevBg) {\n this.config.transparentBackground = transparent;\n this.applyBackground();\n }\n // Deterministic frame: render at a fixed animation-time (with the ease-in ramp forced full) so\n // a captured poster is reproducible instead of whatever frame happened to be on screen — pass\n // time = 0 for the frame the wave opens on. Restored in `finally`. Undefined = the live frame.\n const fixTime = time !== undefined;\n const prevTime = this.time;\n const prevRamp = this.introTimeRamp;\n if (fixTime) {\n this.time = time;\n this.introTimeRamp = 1;\n }\n let blob: Blob | null = null;\n // Exports use the AUTHORED framing — strip any live scroll→cameraZoom multiplier from camera.zoom\n // for the capture render (config itself is never polluted, so code/embed exports are already\n // clean; this covers the pixel capture). The trailing renderOnce() restores the live preview.\n const prevZoom = this.camera.zoom;\n const strippingZoom = this.interactionZoom !== 1;\n try {\n this.capturing = true;\n if (strippingZoom) {\n this.camera.zoom = prevZoom / this.interactionZoom;\n this.camera.updateProjectionMatrix();\n }\n this.renderOnce();\n blob = await new Promise<Blob | null>((resolve) =>\n this.canvas.toBlob(resolve, mime, quality),\n );\n } finally {\n this.capturing = false;\n if (strippingZoom) {\n this.camera.zoom = prevZoom;\n this.camera.updateProjectionMatrix();\n }\n if (transparent !== prevBg) {\n this.config.transparentBackground = prevBg;\n this.applyBackground();\n }\n if (fixTime) {\n this.time = prevTime;\n this.introTimeRamp = prevRamp;\n }\n this.renderOnce();\n }\n if (!blob || blob.type !== mime) throw new Error(`Failed to capture ${mime}`);\n return blob;\n }\n\n captureStream(fps = 60): MediaStream {\n return this.canvas.captureStream(fps);\n }\n\n get canvas(): HTMLCanvasElement {\n return this.renderer.domElement;\n }\n\n getConfig(): StudioConfig {\n return this.config;\n }\n\n setConfig(config: StudioConfig): void {\n const next = ensureStudioConfig(config);\n const structural =\n next.waves.length !== this.waves.length || next.quality !== this.config.quality;\n this.config = next;\n if (structural) this.rebuild();\n else this.refresh();\n // A whole new config (preset/reset/randomize/import) carries its own authored framing —\n // apply it even in the studio, where refresh() leaves the camera to orbit. Without this,\n // selecting a preset updated the wave but kept the previous camera (wrong framing).\n this.applyCameraFromConfig();\n }\n\n dispose(): void {\n this.disposed = true; // an interaction chunk still in flight must not attach to a dead renderer\n cancelAnimationFrame(this.rafId);\n cancelAnimationFrame(this.resizeRaf); // a coalesced resize may still be queued\n this.running = false;\n this.interaction?.dispose();\n this.interaction = undefined;\n this.interactionModule = undefined;\n this.resizeObserver.disconnect();\n this.intersectionObserver.disconnect();\n this.motionQuery.removeEventListener(\"change\", this.onMotionChange);\n this.dprQuery?.removeEventListener(\"change\", this.onDprChange);\n document.removeEventListener(\"visibilitychange\", this.onVisibilityChange);\n this.renderer.domElement.removeEventListener(\"webglcontextlost\", this.onContextLost);\n this.renderer.domElement.removeEventListener(\"webglcontextrestored\", this.onContextRestored);\n this.clearBackgroundVideo();\n this.backgroundTexture?.dispose();\n for (const s of this.waves) {\n s.material.dispose();\n s.layerMaterial?.dispose();\n s.geometry.dispose();\n s.palette.dispose();\n s.particleField?.dispose();\n s.pathTexture?.dispose();\n }\n this.bloomPass?.dispose();\n this.ditherPass?.dispose();\n this.innerLightPass?.dispose();\n this.halftonePass?.dispose();\n this.heatmapPass?.dispose();\n this.paperTexturePass?.dispose();\n this.halftoneCmykPass?.dispose();\n this.disposePost();\n this.renderer.dispose();\n this.renderer.domElement.remove();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgEA,SAAS,YAAY,IAAoC;CACvD,IAAI,IAAI,UAAU,aAAa,OAAO;CACtC,IAAI,IAAI,UAAU,SAAS,OAAO;CAClC,OAAO;AACT;;;AAIA,SAAS,SAAS,OAAmC;CACnD,IAAI,CAAC,OAAO,OAAO;CACnB,IAAI,UAAU,eAAe,OAAO;CACpC,IAAI,kBAAkB,KAAK,KAAK,GAAG,OAAO,SAAS,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,IAAI;CAC5E,OAAO;AACT;;;AAIA,SAAS,YAAY,OAA+C;CAClE,IAAI,CAAC,SAAS,UAAU,eAAe,OAAO,KAAA;CAC9C,OAAO,kBAAkB,KAAK,KAAK,IAAI,MAAM,MAAM,GAAG,CAAC,IAAI;AAC7D;;AAGA,MAAM,sBAAoD;CACxD,QAAQ;CACR,OAAO;CACP,SAAS;CACT,SAAS;AACX;AAEA,MAAM,gBAAgB;;;;;;AAMtB,MAAM,oBAAoB;;;;;;;AAQ1B,MAAa,UAAU;AACvB,MAAa,UAAU;;;;;;;;;;;;;;AAevB,SAAgB,UAAU,IAAY,IAAY,KAAgB,kBAAkB,GAAW;CAC7F,MAAM,UAAU,KAAK;CACrB,MAAM,WAAW,KAAA;CACjB,IAAI;CACJ,QAAQ,KAAR;EACE,KAAK;GACH,OAAO,KAAK,IAAI,SAAS,QAAQ;GACjC;EACF,KAAK;GACH,OAAO;GACP;EACF,KAAK;GACH,OAAO;GACP;EACF,SACE,OAAO,KAAK,IAAI,SAAS,QAAQ;CACrC;CACA,IAAI,kBAAkB,GAEpB,OAAO,KAAK,IAAI,MAAM,MAAM,UAAU,gBAAgB;CAExD,OAAO;AACT;;;;;;AAoBA,IAAM,cAAN,MAAkB;CAQG;CACA;CARnB;CACA,MAAc;CACd;CACA,WAAmB;CACnB,YAAoB;CAEpB,YACE,WACA,cACA;EAFiB,KAAA,YAAA;EACA,KAAA,eAAA;CAChB;;;CAIH,MAAM,KAAiB,UAAgD;EACrE,MAAM,WAAW,IAAI;EACrB,MAAM,MAAM,IAAI;EAChB,MAAM,SAAS,IAAI,iBAAiB;EACpC,IAAI;EACJ,IAAI;EACJ,IAAI,UAAU;GACZ,KAAK,YAAY,QAAQ;GACzB,IAAI,KAAK,OAAO,cAAc,KAAK,MAAM,cAAc,GAAG;IACxD,MAAM,WAAW;IACjB,cAAc,wBAAwB,IAAI,MAAM,aAAa,KAAK,KAAM,CAAC;GAC3E,OAAO;IACL,MAAM,mBAAmB;IACzB,cAAc,wBAAwB;GACxC;EACF,OAAO,IAAI,KAAK;GACd,KAAK,WAAW;GAChB,MAAM,SAAS;GACf,cAAc,iBAAiB,GAAG;EACpC,OAAO,IAAI,WAAW,SAAS;GAC7B,KAAK,WAAW;GAChB,MAAM,OAAO;IACX,OAAO,IAAI;IACX,WAAW,IAAI,oBAAoB;IACnC,YAAY,IAAI,qBAAqB;GACvC;GACA,MAAM,WAAW,iBAAiB,IAAI;GACtC,cAAc,oBAAoB,IAAI;EACxC,OAAO,IAAI,aAAa,SAAS;GAC/B,KAAK,WAAW;GAChB,MAAM,SAAS;GACf,cAAc,gBAAgB,iBAAiB,aAAa,OAAO,CAAC;EACtE,OAAO;GACL,KAAK,WAAW;GAChB,MAAM;GACN,cAAc,wBAAwB;EACxC;EACA,IAAI,QAAQ,KAAK,OAAO,CAAC,KAAK,SAAS;GACrC,KAAK,SAAS,QAAQ;GACtB,KAAK,UAAU,MAAM;GACrB,KAAK,MAAM;EACb;EACA,SAAS,SAAS,QAAQ,KAAK;EAC/B,SAAS,YAAY,QAAQ,IAAI,sBAAsB,QAAQ,IAAI;EACnE,SAAS,YAAY,QAAQ;CAC/B;;CAGA,aAAa,KAAiB,SAAwB;EACpD,MAAM,IAAI,KAAK;EACf,IAAI,CAAC,GAAG;EAMR,IAJE,WACA,IAAI,iBAAiB,UACrB,IAAI,sBAAsB,SAC1B,CAAC,CAAC,IAAI,iBACI,EAAO,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC;OACnC,EAAE,MAAM;CACf;CAEA,YAAoB,KAAmB;EACrC,IAAI,KAAK,aAAa,OAAO,KAAK,cAAc,KAAK;EACrD,KAAK,WAAW;EAChB,KAAK,WAAW;EAChB,MAAM,QAAQ,KAAK,UAAU,GAAG;EAChC,MAAM,iBACJ,oBACM;GACJ,KAAK,QAAQ;GACb,KAAK,YAAY;GACjB,KAAK,MAAM;GACX,KAAK,aAAa;EACpB,GACA,EAAE,MAAM,KAAK,CACf;EACA,MAAM,iBACJ,eACM;GACJ,KAAK,YAAY;GACjB,KAAK,MAAM;GACX,KAAK,aAAa;EACpB,GACA,EAAE,MAAM,KAAK,CACf;EACA,KAAK,QAAQ;EACb,MAAM,KAAK;CACb;CAEA,aAA2B;EACzB,IAAI,KAAK,OAAO;GACd,KAAK,MAAM,MAAM;GACjB,KAAK,MAAM,gBAAgB,KAAK;GAChC,KAAK,MAAM,KAAK;EAClB;EACA,KAAK,QAAQ,KAAA;EACb,KAAK,WAAW;CAClB;CAEA,UAAgB;EACd,KAAK,SAAS,QAAQ;EACtB,KAAK,UAAU,KAAA;EACf,KAAK,WAAW;EAChB,KAAK,YAAY;CACnB;AACF;AAuDA,MAAM,cAAc,IAAI,MAAM,MAAM;;;AAIpC,SAAgB,gBAAgB,KAAa,QAAsC;CAKjF,MAAM,IAAI,YAAY,IAAI,GAAG;CAC7B,OAAO,OAAO,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AACjC;;;;;;AAOA,IAAa,eAAb,MAA0B;CACxB;;;;;;CAMA,QAAkB;CAClB,QAA2B,IAAI,MAAM,MAAM;CAC3C;CACA,QAA2B,IAAI,MAAM,MAAM;CAC3C;CACA;;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,WAA4B,IAAI,MAAM,QAAQ;CAC9C,UAA2B,IAAI,MAAM,QAAQ;CAC7C,OAAwB,IAAI,MAAM,QAAQ;CAC1C;CACA;CACA;CAEA;CACA,QAA0B,CAAC;CAG3B;;CAEA;;;;CAIA;CACA,gBAAwB;CACxB;CACA,qBAA6B;CAC7B,2BAAmC;CACnC;CACA,qBAA6B;CAC7B,2BAAmC;CACnC;;CAEA,aAAgC,IAAI,MAAM,QAAQ;CAClD,gBAAmC,IAAI,MAAM,QAAQ;CAIrD,UAA2B,IAAI,MAAM,KAAK;CAC1C,aAA8B,IAAI,MAAM,OAAO;CAC/C,WAA4B,IAAI,MAAM,QAAQ;CAC9C,WAA4B,IAAI,MAAM,QAAQ;;;CAK9C;;CAEA;;CAEA,qBAA6B;;CAE7B;;CAEA,WAAmB;;;CAGnB,kBAA4B;;CAE5B,wBAAgC;;CAEhC,sBAAuC;EAAE,YAAY;EAAG,MAAM;CAAE;CAEhE,QAAyB,IAAI,MAAM,MAAM;CACzC,OAAe;CACf,QAAgB;CAChB,UAAoB;CACpB,UAAkB;CAElB,UAAkB;;;;;;CAMlB,cAAsB,SAAS,oBAAoB;CACnD,gBAAwB;;CAExB,gBAAwB;CAExB;CACA;CACA;;CAEA;;CAEA,YAAoB;CACpB;CAEA,YAAsB;;;CAGtB;CAEA,YAAY,WAAwB,QAAsB,UAA+B,CAAC,GAAG;EAC3F,KAAK,YAAY;EACjB,KAAK,SAAS,mBAAmB,MAAM;EACvC,KAAK,uBAAuB,QAAQ,wBAAwB;EAC5D,KAAK,gBAAgB,QAAQ,iBAAiB;EAE9C,KAAK,WAAW,KAAK,eAAe;EACpC,KAAK,SAAS,mBAAmB,MAAM;EACvC,KAAK,SAAS,cAAc,GAAU,CAAC;EACvC,UAAU,YAAY,KAAK,SAAS,UAAU;EAI9C,KAAK,SAAS,WAAW,iBAAiB,oBAAoB,KAAK,eAAe,KAAK;EACvF,KAAK,SAAS,WAAW,iBACvB,wBACA,KAAK,mBACL,KACF;EAOA,KAAK,SAAS,IAAI,MAAM,mBAAmB,IAAI,GAAG,GAAG,IAAI,GAAG,GAAK;EACjE,KAAK,OAAO,SAAS,IACnB,OAAO,eAAe,GACtB,OAAO,eAAe,GACtB,OAAO,eAAe,CACxB;EACA,KAAK,OAAO,OAAO,OAAO,cAAc;EACxC,KAAK,OAAO,OAAO,OAAO,aAAa,GAAG,OAAO,aAAa,GAAG,OAAO,aAAa,CAAC;EACtF,KAAK,OAAO,uBAAuB;EAGnC,KAAK,WAAW,KAAK,KAAK,OAAO,QAAQ;EACzC,KAAK,cAAc,IAAI,OAAO,aAAa,GAAG,OAAO,aAAa,GAAG,OAAO,aAAa,CAAC;EAC1F,KAAK,MAAM,IAAI,KAAK,KAAK;EAEzB,KAAK,WAAW,IAAI,eAAe,KAAK,QAAQ;EAChD,KAAK,SAAS,QAAQ,IAAI,WAAW,KAAK,OAAO,KAAK,MAAM,CAAC;EAC7D,KAAK,WAAW,IAAI,WAAW;GAC7B,UAAU;IACR,UAAU,EAAE,OAAO,KAAK;IACxB,aAAa,EAAE,OAAO,IAAI,MAAM,QAAQ,GAAG,CAAC,EAAE;IAC9C,aAAa,EAAE,OAAO,OAAO,KAAK;IAClC,cAAc,EAAE,OAAO,EAAE;IACzB,cAAc,EAAE,OAAO,OAAO,MAAM;IACpC,OAAO,EAAE,OAAO,EAAE;GACpB;GACA,cAAc;GACd,gBAAgB;EAClB,CAAC;EACD,KAAK,SAAS,QAAQ,KAAK,QAAQ;EACnC,KAAK,SAAS,QAAQ,IAAI,WAAW,CAAC;EAEtC,KAAK,cAAc,OAAO,WAAW,kCAAkC;EACvE,KAAK,gBAAgB,KAAK,wBAAwB,KAAK,YAAY;EACnE,KAAK,YAAY,iBAAiB,UAAU,KAAK,cAAc;EAC/D,SAAS,iBAAiB,oBAAoB,KAAK,kBAAkB;EAErE,KAAK,uBAAuB,IAAI,sBAC7B,YAAY;GACX,KAAK,UAAU,QAAQ,EAAE,EAAE,kBAAkB;GAC7C,KAAK,cAAc;EACrB,GACA,EAAE,YAAY,QAAQ,CACxB;EACA,KAAK,qBAAqB,QAAQ,SAAS;EAE3C,KAAK,iBAAiB,IAAI,eAAe,KAAK,QAAQ;EACtD,KAAK,eAAe,QAAQ,SAAS;EACrC,KAAK,SAAS;EAEd,KAAK,gBAAgB;EACrB,KAAK,WAAW;EAChB,KAAK,OAAO;CACd;;;;;;CAOA,iBAAgD;EAC9C,OAAO,IAAI,MAAM,cAAc;GAG7B,WAAW;GACX,OAAO;GACP,uBAAuB;GACvB,iBAAiB;EACnB,CAAC;CACH;;;;;CAMA,MAAM,OAAsB,CAAC;CAE7B,IAAY,WAAmB;EAE7B,MAAM,IAAI,KAAK,OAAO,UAAU,KAAK,KAAK,KAAK,IAAI,GAAG,KAAK,OAAO,MAAM,MAAM,CAAC;EAC/E,OAAO,MAAM,UAAU,MAAM,KAAK,MAAM,gBAAgB,CAAC,GAAG,IAAI,GAAG;CACrE;CAEA,eAAuD;EACrD,MAAM,SAA0B,CAAC;EACjC,MAAM,WAAqB,CAAC;EAC5B,KAAK,IAAI,IAAI,GAAG,IAAA,GAAgB,KAAK;GACnC,OAAO,KAAK,IAAI,MAAM,QAAQ,GAAG,GAAG,CAAC,CAAC;GACtC,SAAS,KAAsB,IAAA,CAAwB;EACzD;EACA,MAAM,eAAgC,CAAC;EACvC,MAAM,iBAAkC,CAAC;EACzC,MAAM,qBAA+B,CAAC;EACtC,KAAK,IAAI,IAAI,GAAG,IAAA,GAAqB,KAAK;GACxC,aAAa,KAAK,IAAI,MAAM,QAAQ,IAAK,EAAG,CAAC;GAC7C,eAAe,KAAK,IAAI,MAAM,QAAQ,GAAG,GAAG,CAAC,CAAC;GAC9C,mBAAmB,KAAK,GAAI;EAC9B;EACA,MAAM,WAA4B,CAAC;EACnC,MAAM,aAA8B,CAAC;EACrC,MAAM,iBAA2B,CAAC;EAClC,KAAK,IAAI,IAAI,GAAG,IAAA,GAAgB,KAAK;GACnC,SAAS,KAAK,IAAI,MAAM,QAAQ,CAAC;GACjC,WAAW,KAAK,IAAI,MAAM,QAAQ,GAAG,GAAG,CAAC,CAAC;GAC1C,eAAe,KAAK,CAAC;EACvB;EACA,MAAM,aAA8B,CAAC;EACrC,MAAM,aAA8B,CAAC;EACrC,MAAM,cAAwB,CAAC;EAC/B,KAAK,IAAI,IAAI,GAAG,IAAA,GAAqB,KAAK;GACxC,WAAW,KAAK,IAAI,MAAM,QAAQ,CAAC;GACnC,WAAW,KAAK,IAAI,MAAM,QAAQ,CAAC;GACnC,YAAY,KAAK,CAAC;EACpB;EAEA,MAAM,eAAgC,CAAC;EACvC,MAAM,YAAsB,CAAC;EAC7B,MAAM,YAAsB,CAAC;EAC7B,KAAK,IAAI,IAAI,GAAG,IAAA,GAAkB,KAAK;GACrC,aAAa,KAAK,IAAI,MAAM,QAAQ,CAAC;GACrC,UAAU,KAAK,CAAC;GAChB,UAAU,KAAK,CAAC;EAClB;EACA,OAAO;GAEL,OAAO,EAAE,OAAO,EAAE;GAClB,QAAQ,EAAE,OAAO,IAAK;GACtB,OAAO,EAAE,OAAO,EAAE;GAClB,YAAY,EAAE,OAAO,QAAS;GAC9B,YAAY,EAAE,OAAO,OAAQ;GAC7B,aAAa,EAAE,OAAO,MAAM;GAC5B,aAAa,EAAE,OAAO,IAAK;GAC3B,eAAe,EAAE,OAAO,EAAE;GAC1B,UAAU,EAAE,OAAO,MAAO;GAC1B,UAAU,EAAE,OAAO,KAAM;GACzB,UAAU,EAAE,OAAO,MAAO;GAC1B,SAAS,EAAE,OAAO,KAAK;GACvB,SAAS,EAAE,OAAO,KAAK;GACvB,SAAS,EAAE,OAAO,KAAK;GACvB,cAAc,EAAE,OAAO,EAAE;GAEzB,SAAS,EAAE,OAAO,OAAO;GACzB,WAAW,EAAE,OAAO,SAAS;GAC7B,aAAa,EAAE,OAAO,EAAE;GACxB,WAAW,EAAE,OAAO,EAAE;GACtB,YAAY,EAAE,OAAO,EAAE;GACvB,YAAY,EAAE,OAAO,IAAK;GAC1B,eAAe,EAAE,OAAO,aAAa;GACrC,iBAAiB,EAAE,OAAO,eAAe;GACzC,qBAAqB,EAAE,OAAO,mBAAmB;GACjD,iBAAiB,EAAE,OAAO,EAAE;GAC5B,eAAe,EAAE,OAAO,IAAK;GAC7B,UAAU,EAAE,OAAO,KAAK;GACxB,aAAa,EAAE,OAAO,EAAE;GACxB,aAAa,EAAE,OAAO,EAAE;GACxB,eAAe,EAAE,OAAO,IAAI,MAAM,QAAQ,GAAG,CAAC,EAAE;GAChD,gBAAgB,EAAE,OAAO,IAAI,MAAM,QAAQ,GAAG,CAAC,EAAE;GACjD,kBAAkB,EAAE,OAAO,EAAE;GAC7B,QAAQ,EAAE,OAAO,EAAE;GACnB,QAAQ,EAAE,OAAO,EAAE;GACnB,YAAY,EAAE,OAAO,IAAK;GAC1B,cAAc,EAAE,OAAO,EAAE;GACzB,YAAY,EAAE,OAAO,EAAE;GACvB,iBAAiB,EAAE,OAAO,IAAI,MAAM,QAAQ,EAAE;GAC9C,WAAW,EAAE,OAAO,EAAE;GACtB,WAAW,EAAE,OAAO,EAAE;GACtB,aAAa,EAAE,OAAO,EAAE;GACxB,aAAa,EAAE,OAAO,GAAG;GACzB,gBAAgB,EAAE,OAAO,IAAK;GAC9B,UAAU,EAAE,OAAO,EAAE;GACrB,cAAc,EAAE,OAAO,IAAK;GAC5B,kBAAkB,EAAE,OAAO,EAAI;GAC/B,iBAAiB,EAAE,OAAO,EAAI;GAC9B,WAAW,EAAE,OAAO,IAAK;GACzB,cAAc,EAAE,OAAO,GAAI;GAI3B,WAAW,EAAE,OAAO,KAA6B;GACjD,gBAAgB,EAAE,OAAO,GAAG;GAC5B,cAAc,EAAE,OAAO,GAAI;GAC3B,aAAa,EAAE,OAAO,IAAK;GAC3B,YAAY,EAAE,OAAO,IAAI;GACzB,gBAAgB,EAAE,OAAO,IAAK;GAC9B,YAAY,EAAE,OAAO,IAAK;GAC1B,gBAAgB,EAAE,OAAO,IAAI;GAC7B,cAAc,EAAE,OAAO,EAAE;GACzB,mBAAmB,EAAE,OAAO,KAAM;GAClC,YAAY,EAAE,OAAO,GAAI;GACzB,YAAY,EAAE,OAAO,IAAK;GAC1B,eAAe,EAAE,OAAO,IAAI;GAC5B,WAAW,EAAE,OAAO,GAAI;GACxB,YAAY,EAAE,OAAO,EAAE;GACvB,cAAc,EAAE,OAAO,IAAI;GAC3B,WAAW,EAAE,OAAO,KAAK;GACzB,SAAS,EAAE,OAAO,KAA6B;GAC/C,iBAAiB,EAAE,OAAO,GAAI;GAC9B,cAAc,EAAE,OAAO,EAAE;GACzB,eAAe,EAAE,OAAO,GAAI;GAC5B,WAAW,EAAE,OAAO,IAAI,MAAM,QAAQ,GAAG,GAAG,CAAC,EAAE;GAC/C,UAAU,EAAE,OAAO,EAAE;GACrB,UAAU,EAAE,OAAO,EAAE;GAKrB,aAAa,EAAE,OAAO,KAAK,SAAS,qBAAqB,IAAI,MAAM,QAAQ,CAAC,EAAE;GAC9E,UAAU,EAAE,OAAO,IAAK;GACxB,YAAY,EAAE,OAAO,EAAE;GACvB,WAAW,EAAE,OAAO,SAAS;GAC7B,aAAa,EAAE,OAAO,WAAW;GACjC,iBAAiB,EAAE,OAAO,eAAe;GACzC,gBAAgB,EAAE,OAAO,EAAE;GAC3B,kBAAkB,EAAE,OAAO,WAAW;GACtC,kBAAkB,EAAE,OAAO,WAAW;GACtC,mBAAmB,EAAE,OAAO,YAAY;GAExC,aAAa,EAAE,OAAO,IAAI;GAC1B,gBAAgB,EAAE,OAAO,EAAE;GAC3B,sBAAsB,EAAE,OAAO,IAAK;GACpC,gBAAgB,EAAE,OAAO,EAAE;GAC3B,gBAAgB,EAAE,OAAO,EAAE;GAC3B,iBAAiB,EAAE,OAAO,EAAE;GAC5B,YAAY,EAAE,OAAO,EAAE;GACvB,aAAa,EAAE,OAAO,IAAI,MAAM,QAAQ,GAAG,GAAG,CAAC,EAAE;GAOjD,aAAa,EAAE,OAAO,EAAE;GACxB,cAAc,EAAE,OAAO,EAAE;GACzB,YAAY,EAAE,OAAO,EAAE;GACvB,aAAa,EAAE,OAAO,EAAE;GAGxB,UAAU,EAAE,OAAO,KAA6B;GAGhD,eAAe,EAAE,OAAO,EAAE;GAC1B,YAAY,EAAE,OAAO,IAAI;GACzB,eAAe,EAAE,OAAO,EAAE;GAC1B,eAAe,EAAE,OAAO,GAAG;GAC3B,eAAe,EAAE,OAAO,EAAE;GAC1B,aAAa,EAAE,OAAO,EAAE;GACxB,cAAc,EAAE,OAAO,EAAE;GACzB,aAAa,EAAE,OAAO,EAAE;GACxB,gBAAgB,EAAE,OAAO,EAAE;GAG3B,iBAAiB,EAAE,OAAO,EAAE;GAC5B,eAAe,EAAE,OAAO,IAAK;GAC7B,gBAAgB,EAAE,OAAO,GAAG;GAC5B,iBAAiB,EAAE,OAAO,GAAI;GAC9B,eAAe,EAAE,OAAO,EAAE;GAC1B,kBAAkB,EAAE,OAAO,EAAE;GAG7B,eAAe,EAAE,OAAO,EAAE;GAC1B,UAAU,EAAE,OAAO,IAAI,MAAM,QAAQ,GAAG,CAAC,EAAE;GAC3C,gBAAgB,EAAE,OAAO,EAAE;GAC3B,gBAAgB,EAAE,OAAO,GAAI;GAC7B,gBAAgB,EAAE,OAAO,EAAE;GAC3B,iBAAiB,EAAE,OAAO,EAAE;GAC5B,cAAc,EAAE,OAAO,EAAE;GACzB,cAAc,EAAE,OAAO,EAAE;GACzB,aAAa,EAAE,OAAO,IAAI,MAAM,QAAQ,GAAG,CAAC,EAAE;GAC9C,YAAY,EAAE,OAAO,EAAE;GACvB,cAAc,EAAE,OAAO,EAAE;GACzB,aAAa,EAAE,OAAO,EAAE;GACxB,iBAAiB,EAAE,OAAO,EAAE;GAC5B,gBAAgB,EAAE,OAAO,EAAE;GAC3B,eAAe,EAAE,OAAO,aAAa;GACrC,YAAY,EAAE,OAAO,UAAU;GAC/B,YAAY,EAAE,OAAO,UAAU;EACjC;CACF;;;;CAKA,gBAAwB,MAAY,IAAsB;EACxD,MAAM,MAAM,GAAG;EACf,IAAI,CAAC,OAAO,IAAI,SAAS,GAAG;GAC1B,IAAI,KAAK,aAAa;IACpB,KAAK,YAAY,QAAQ;IACzB,KAAK,cAAc,KAAA;IACnB,KAAK,UAAU,KAAA;GACjB;GAGA;EACF;EACA,MAAM,MAAM,KAAK,UAAU,GAAG;EAC9B,IAAI,CAAC,KAAK,aAAa;GACrB,KAAK,cAAc,gBAAgB,GAAG;GACtC,KAAK,UAAU;EACjB,OAAO,IAAI,QAAQ,KAAK,SAAS;GAC/B,iBAAiB,KAAK,YAAY,MAAM,MAAsB,GAAG;GACjE,KAAK,YAAY,cAAc;GAC/B,KAAK,UAAU;EACjB;EACA,KAAK,SAAS,SAAS,SAAS,QAAQ,KAAK;CAC/C;;;;CAKA,YAAoB,IAAoD;EACtE,MAAM,UAAkC,CAAC;EACzC,IAAI,IAAI,aAAa,QAAQ,eAAe;EAC5C,KAAK,KAAK,OAAO,eAAe,KAAK,GAAG,QAAQ,cAAc;EAG9D,MAAM,cACJ,IAAI,aAAa,UAAU,MAAM,MAAM,EAAE,WAAW,cAAc,KAAK;EACzE,KAAK,IAAI,gBAAgB,OAAO,KAAK,aAAa,QAAQ,gBAAgB;EAC1E,KAAK,IAAI,aAAa,KAAK,GAAG,QAAQ,aAAa;EACnD,KAAK,IAAI,eAAe,QAAS,IAAK,QAAQ,eAAe;EAI7D,MAAM,aACJ,IAAI,aAAa,UAAU,MAAM,MAAM,EAAE,OAAO,WAAW,OAAO,CAAC,KAAK;EAC1E,KAAK,IAAI,eAAe,OAAO,MAAM,IAAI,aAAa,OAAO,KAAK,YAChE,QAAQ,QAAQ;EAGlB,IAAI,IAAI,QAAQ,GAAG,KAAK,UAAU,GAAG,QAAQ,OAAO;EAEpD,IAAI,IAAI,UAAU,SAAS,QAAQ,gBAAgB;EAGnD,KAAK,IAAI,gBAAgB,OAAO,GAAG,QAAQ,SAAS;EAGpD,IAAI,IAAI,UAAU,gBAAgB,GAAG,cAAc,KAAK,GAAG,QAAQ,QAAQ;EAG3E,IAAI,IAAI,UAAU,gBAAgB,GAAG,iBAAiB,KAAK,GAAG,QAAQ,aAAa;EAGnF,IAAI,IAAI,UAAU,eAAe,SAAS,GAAG,YAAY,IAAI,GAAG,QAAQ,kBAAkB;EAG1F,IAAI,IAAI,UAAU,gBAAgB,GAAG,aAAa,KAAK,GAAG,QAAQ,aAAa;EAG/E,MAAM,gBACJ,IAAI,aAAa,UAAU,MAAM,MAAM,EAAE,WAAW,gBAAgB,KAAK;EAC3E,IAAI,IAAI,cAAc,GAAG,SAAS,UAAU,KAAK,KAAK,gBAAgB,QAAQ,WAAW;EAEzF,IAAI,MAAM,oBAAoB,KAAK,QAAQ,EAAE,GAAG;GAC9C,QAAQ,aAAa;GACrB,IAAI,kBAAkB,KAAK,QAAQ,EAAE,GAAG,QAAQ,kBAAkB;EACpE;EACA,OAAO;CACT;CAEA,UAAwB;EACtB,MAAM,MAAM,IAAI,aAAa,KAAK,QAAQ;EAG1C,MAAM,KAAK,KAAK,OAAO,MAAM,KAAK,MAAM,WAAW,KAAK,OAAO,MAAM;EACrE,MAAM,WAAW,KAAK,mBAAmB,EAAE;EAG3C,KAAK,eAAe,UAAU,IAAI,aAAa,SAAS;EACxD,MAAM,OAAO,IAAI,MAAM,KAAK,IAAI,UAAU,QAAQ;EAClD,KAAK,gBAAgB;EACrB,KAAK,MAAM,IAAI,IAAI;EACnB,MAAM,UAAU,IAAI,aACjB,QAAQ,KAAK,mBAAmB,GAAG,SAC9B;GACJ,KAAK,sBAAsB;GAC3B,KAAK,kBAAkB;GACvB,IAAI,CAAC,KAAK,SAAS,KAAK,WAAW;EACrC,CACF;EACA,KAAK,MAAM,KAAK;GAAE;GAAM;GAAU,UAAU;GAAK;EAAQ,CAAC;CAC5D;;;;;;CAOA,mBAA6B,IAA0C;EACrE,OAAO,IAAI,MAAM,eAAe;GAC9B,UAAU,KAAK,aAAa;GAE5B,SAAS,KAAK,YAAY,EAAE;GAC5B;GAGA,gBAAgB,YAAY,EAAE;GAG9B,aAAa,IAAI,UAAU;GAC3B,WAAW;GACX,YAAY;GACZ,MAAM,MAAM;EACd,CAAC;CACH;;;;;;;;;;;CAYA,oBAA8B,IAAgB,UAAwC;EACpF,MAAM,OAAO;EAEb,MAAM,UAAU,EAAE,GAAI,KAAK,WAAW,KAAK,YAAY,EAAE,EAAG;EAC5D,OAAO,QAAQ;EACf,OAAO,IAAI,MAAM,eAAe;GAC9B,UAAU,KAAK;GACf;GACA;GACA,gBAAgB;GAChB,aAAa;GACb,UAAU,MAAM;GAChB,WAAW;GACX,YAAY;GACZ,MAAM,MAAM;EACd,CAAC;CACH;;CAGA,gBAA0B,UAAgC;EACxD,MAAM,UAAW,SAA6C,WAAW,CAAC;EAC1E,OAAO,OAAO,KAAK,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG;CAC7C;CAEA,kBAA0B,MAAkB;EAC1C,IAAI,CAAC,KAAK,eAAe;EACzB,KAAK,cAAc,QAAQ;EAC3B,KAAK,gBAAgB,KAAA;EACrB,KAAK,WAAW,KAAA;CAClB;;;;;;;CAQA,iBAA2B,MAAY,IAAyB;EAC9D,MAAM,WAAW,KAAK;EACtB,IAAI,UAAU;EAGd,MAAM,cAAc,KAAK,YAAY,EAAE;EACvC,MAAM,aAAa,SAAS,WAAW,CAAC;EACxC,IAAI,OAAO,KAAK,WAAW,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,MAAM,OAAO,KAAK,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,GAAG;GAC1F,SAAS,UAAU;GACnB,UAAU;EACZ;EAGA,MAAM,WAAW,YAAY,EAAE;EAC/B,IAAI,SAAS,mBAAmB,UAAU;GACxC,SAAS,iBAAiB;GAC1B,UAAU;EACZ;EAIA,SAAS,cAAc,GAAG,UAAU;EACpC,OAAO;CACT;;;;;;;;;;CAWA,eAAyB,UAAwB,MAA0B;EAWzE,MAAM,UAAU,SAAS;EACzB,MAAM,WACJ,SAAS,aACL,MAAM,mBACN,SAAS,aACP,MAAM,mBACN,MAAM;EAGd,MAAM,qBAAqB,SAAS,cAAc;EAClD,SAAS,SAAS,SAAS,QAAQ,UAAU,IAAI;EACjD,IAAI,SAAS,aAAa,YAAY,SAAS,uBAAuB,oBACpE,OAAO;EAET,SAAS,WAAW;EACpB,SAAS,qBAAqB;EAC9B,OAAO;CACT;CAEA,eAA6B;EAC3B,KAAK,MAAM,KAAK,KAAK,OAAO;GAC1B,KAAK,MAAM,OAAO,EAAE,IAAI;GACxB,EAAE,SAAS,QAAQ;GACnB,EAAE,eAAe,QAAQ;GACzB,EAAE,SAAS,QAAQ;GACnB,EAAE,QAAQ,QAAQ;GAClB,IAAI,EAAE,eAAe;IACnB,KAAK,MAAM,OAAO,EAAE,cAAc,MAAM;IACxC,EAAE,cAAc,QAAQ;GAC1B;EACF;EACA,KAAK,QAAQ,CAAC;CAChB;;;;;;;CAQA,aAA2B;EACzB,MAAM,SAAS,KAAK,IAAI,GAAG,KAAK,OAAO,MAAM,MAAM;EACnD,OAAO,KAAK,MAAM,SAAS,QAAQ;GACjC,MAAM,IAAI,KAAK,MAAM,IAAI;GACzB,IAAI,CAAC,GAAG;GACR,KAAK,MAAM,OAAO,EAAE,IAAI;GACxB,EAAE,SAAS,QAAQ;GACnB,EAAE,eAAe,QAAQ;GACzB,EAAE,SAAS,QAAQ;GACnB,EAAE,QAAQ,QAAQ;GAIlB,IAAI,EAAE,eAAe;IACnB,KAAK,MAAM,OAAO,EAAE,cAAc,MAAM;IACxC,EAAE,cAAc,QAAQ;GAC1B;EACF;EACA,OAAO,KAAK,MAAM,SAAS,QAAQ,KAAK,QAAQ;EAEhD,MAAM,WAAW,KAAK;EACtB,KAAK,MAAM,SAAS,GAAG,MAAM;GAC3B,EAAE,SAAS,OAAO,QAAQ;GAC1B,EAAE,KAAK,cAAc;EACvB,CAAC;EACD,KAAK,QAAQ;CACf;;CAGA,UAAgB;EACd,KAAK,gBAAgB;EACrB,KAAK,UAAU;EACf,KAAK,eAAe;EACpB,KAAK,gBAAgB;EAGrB,IAAI,CAAC,KAAK,yBAAyB,GAAG;GACpC,MAAM,IAAI,KAAK,OAAO;GACtB,MAAM,KAAK,KAAK,OAAO;GACvB,KAAK,OAAO,SAAS,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;GACtC,KAAK,OAAO,OAAO,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;EACrC;EAEA,KAAK,MAAM,SAAS,MAAM,MAAM;GAC9B,MAAM,KAAK,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,MAAM,KAAK,OAAO,MAAM,SAAS;GAChF,MAAM,IAAI,KAAK,SAAS;GACxB,IAAI,KAAK,eAAe,KAAK,UAAU,GAAG,SAAS,GAAG,KAAK,SAAS,cAAc;GAClF,IAAI,KAAK,iBAAiB,MAAM,EAAE,GAAG,KAAK,SAAS,cAAc;GAEjE,MAAM,QAAQ,CAAC,GAAG,GAAG,OAAO,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG;GAC1D,MAAM,aAAa,KAAK,IAAI,GAAG,KAAK,IAAI,MAAM,QAAA,CAAkB,CAAC;GACjE,MAAM,SAAS,EAAE,QAAQ;GACzB,MAAM,WAAW,EAAE,UAAU;GAC7B,KAAK,IAAI,IAAI,GAAG,IAAA,GAAgB,KAAK;IACnC,MAAM,OAAO,MAAM,KAAK,IAAI,GAAG,aAAa,CAAC,MAAM;KAAE,OAAO;KAAW,KAAK;IAAE;IAC9E,gBAAgB,KAAK,OAAO,OAAO,EAAE;IACrC,SAAS,KAAK,KAAK;GACrB;GACA,EAAE,YAAY,QAAQ;GACtB,EAAE,UAAU,QACV,GAAG,iBAAiB,WAChB,IACA,GAAG,iBAAiB,UAClB,IACA,GAAG,iBAAiB,SAClB,IACA;GACV,EAAE,WAAW,SAAU,GAAG,iBAAiB,KAAK,KAAK,KAAM;GAC3D,EAAE,WAAW,QAAQ,GAAG,iBAAiB;GACzC,MAAM,aAAa,GAAG,mBAAmB,MAAM,GAAA,CAAkB;GACjE,MAAM,eAAe,EAAE,cAAc;GACrC,MAAM,iBAAiB,EAAE,gBAAgB;GACzC,MAAM,qBAAqB,EAAE,oBAAoB;GACjD,KAAK,IAAI,aAAa,GAAG,aAAA,GAA8B,cAAc;IACnE,MAAM,QAAQ,WAAW,eAAe,WAAW,WAAW,SAAS;IACvE,IAAI,CAAC,OAAO;IACZ,aAAa,WAAW,CAAC,IAAI,MAAM,GAAG,MAAM,CAAC;IAC7C,gBAAgB,MAAM,OAAO,eAAe,WAAW;IACvD,mBAAmB,cAAc,MAAM;GACzC;GACA,EAAE,gBAAgB,QAAQ,WAAW;GACrC,EAAE,cAAc,QAAQ,GAAG;GAC3B,EAAG,cAAc,MAAwB,IACvC,GAAG,qBAAqB,KAAK,GAC7B,GAAG,qBAAqB,KAAK,CAC/B;GACA,EAAG,eAAe,MAAwB,IACxC,GAAG,sBAAsB,KAAK,GAC9B,GAAG,sBAAsB,KAAK,CAChC;GACA,EAAE,iBAAiB,SAAU,GAAG,0BAA0B,KAAK,KAAK,KAAM;GAC1E,EAAE,UAAU,QAAQ,GAAG;GACvB,EAAE,UAAU,QAAQ,GAAG;GACvB,EAAE,YAAY,QAAQ,GAAG;GAGzB,EAAE,YAAY,QAAQ,GAAG,cAAc;GACvC,EAAE,eAAe,QAAQ,GAAG,iBAAiB;GAC7C,EAAE,qBAAqB,QAAQ,GAAG,uBAAuB;GACzD,EAAE,eAAe,QAAQ,GAAG,iBAAiB;GAC7C,EAAE,eAAe,QAAQ,GAAG,iBAAiB;GAG7C,EAAE,gBAAgB,QAAQ,SAAS,GAAG,YAAY;GAClD,EAAE,WAAW,QAAQ,GAAG,aAAa;GAOrC,MAAM,iBAAiB,EADL,GAAG,UAAU,eAAe,SAAS,GAAG,YAAY,IAAI;GAE1E,IAAI,KAAK,SAAS,eAAe,gBAAgB,KAAK,SAAS,aAAa;GAC5E,EAAE,YAAY,QAAQ,GAAG,cAAc;GACvC,EAAE,eAAe,QAAQ,GAAG,iBAAiB;GAK7C,gBACE,YAAY,GAAG,YAAY,KAAK,KAAK,OAAO,YAC5C,EAAE,YAAY,KAChB;GACA,EAAE,YAAY,QAAQ,GAAG;GACzB,EAAE,eAAe,QAAQ,GAAG;GAC5B,EAAE,SAAS,QAAQ,GAAG;GACtB,EAAE,aAAa,QAAQ,GAAG;GAC1B,EAAE,iBAAiB,QAAQ,GAAG;GAC9B,EAAE,gBAAgB,QAAQ,GAAG;GAC7B,EAAE,OAAO,QAAQ,GAAG,SAAS;GAC7B,EAAE,WAAW,QAAQ,GAAG,aAAa;GACrC,EAAE,aAAa,QAAQ,GAAG,eAAe;GACzC,EAAE,WAAW,QAAQ,GAAG,aAAa;GACrC,gBAAgB,GAAG,kBAAkB,WAAW,EAAE,gBAAgB,KAAsB;GACxF,EAAE,UAAU,QAAQ,GAAG;GACvB,EAAE,aAAa,QAAQ,GAAG,eAAe;GACzC,IAAI,GAAG,UAAU,SAAS;IAExB,gBAAgB,KAAK,OAAO,YAAY,EAAE,YAAY,KAAsB;IAC5E,EAAE,eAAe,QAAQ,GAAG,iBAAiB;IAC7C,EAAE,aAAa,QAAQ,GAAG,eAAe;IACzC,EAAE,YAAY,QAAQ,GAAG,cAAc;IACvC,EAAE,WAAW,QAAQ,GAAG,aAAa;IACrC,EAAE,eAAe,QAAQ,GAAG,iBAAiB;IAC7C,EAAE,WAAW,QAAQ,GAAG,aAAa;IACrC,EAAE,eAAe,QAAQ,GAAG,iBAAiB;IAC7C,EAAE,aAAa,QAAQ,GAAG,eAAe;IACzC,EAAE,kBAAkB,QAAQ,GAAG,oBAAoB;IAInD;KACE,MAAM,OAAO,GAAG,aAAa;KAC7B,MAAM,OAAO,KAAK,OAAO,eAAe;KACxC,EAAE,WAAW,QACX,OAAO,KAAK,SAAS,IAChB,KAAK,IAAI,GAAG,KAAK,MAAO,KAAK,IAAI,IAAI,IAAI,QAAS,KAAK,KAAK,EAAE,CAAC,KAAK,KAAK,KAAK,KAC/E,QACC,OAAO,IAAI,KAAK,KACjB;IACR;IACA,EAAE,WAAW,QAAQ,GAAG,aAAa;IACrC,EAAE,cAAc,QAAQ,GAAG,gBAAgB;IAC3C,EAAE,UAAU,QAAQ,GAAG,YAAY;IACnC,EAAE,WAAW,QAAQ,GAAG,aAAa;IACrC,EAAE,aAAa,QAAQ,GAAG,eAAe;IACzC,EAAE,UAAU,QAAQ,GAAG,YAAY;IACnC,EAAE,gBAAgB,QAAQ,GAAG,kBAAkB;IAC/C,EAAE,aAAa,QAAQ,GAAG,eAAe;IACzC,EAAE,cAAc,QAAQ,GAAG,gBAAgB;GAC7C;GAEA,MAAM,SAAS,KAAK,OAAO,UAAU,CAAC;GACtC,EAAE,SAAS,QAAQ,KAAK,OAAO,WAAW;GAC1C,EAAE,WAAW,QAAQ,KAAK,IAAI,OAAO,QAAA,CAAkB;GACvD,MAAM,OAAO,EAAE,UAAU;GACzB,MAAM,OAAO,EAAE,YAAY;GAC3B,MAAM,OAAO,EAAE,gBAAgB;GAC/B,KAAK,IAAI,KAAK,GAAG,KAAA,GAAiB,MAAM;IACtC,MAAM,QAAQ,OAAO;IACrB,IAAI,OAAO;KACT,KAAK,GAAG,CAAC,IAAI,MAAM,SAAS,GAAG,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC;KACjE,gBAAgB,MAAM,OAAO,KAAK,GAAG;KACrC,KAAK,MAAM,MAAM;IACnB,OACE,KAAK,MAAM;GAEf;GAEA,MAAM,QAAQ,GAAG,cAAc,CAAC;GAChC,EAAE,eAAe,QAAQ,KAAK,IAAI,MAAM,QAAA,CAAuB;GAC/D,MAAM,UAAU,EAAE,iBAAiB;GACnC,MAAM,UAAU,EAAE,iBAAiB;GACnC,MAAM,QAAQ,EAAE,kBAAkB;GAClC,KAAK,IAAI,KAAK,GAAG,KAAA,GAAsB,MAAM;IAC3C,MAAM,OAAO,MAAM;IACnB,IAAI,MAAM;KACR,QAAQ,GAAG,CAAC,IAAI,KAAK,QAAQ,KAAK,MAAM,KAAK,QAAQ,KAAK,IAAI;KAC9D,QAAQ,GAAG,CAAC,IAAI,KAAK,SAAS,KAAK,UAAU,KAAK,WAAW,KAAK,gBAAgB;KAClF,MAAM,MAAM,KAAK;IACnB;GACF;GAEA,EAAE,OAAO,QAAQ,GAAG;GACpB,EAAE,MAAM,QAAQ,GAAG;GACnB,EAAE,aAAa,QAAQ,KAAK,OAAO,eAAe;GAClD,EAAE,WAAW,QAAQ,GAAG,kBAAkB;GAC1C,EAAE,WAAW,QAAQ,GAAG,kBAAkB;GAC1C,EAAE,YAAY,QAAQ,GAAG;GACzB,EAAE,YAAY,QAAQ,GAAG,mBAAmB;GAC5C,EAAE,cAAc,QAAQ,GAAG,gBAAgB;GAC3C,EAAE,SAAS,QAAQ,GAAG,eAAe;GACrC,EAAE,SAAS,QAAQ,GAAG,eAAe;GACrC,EAAE,SAAS,QAAQ,GAAG,eAAe;GACrC,EAAE,QAAQ,QAAQ,GAAG,WAAW;GAChC,EAAE,QAAQ,QAAQ,GAAG,WAAW;GAChC,EAAE,QAAQ,QAAQ,GAAG,WAAW;GAChC,EAAE,YAAY,QAAQ,GAAG,cAAc;GACvC,EAAE,aAAa,QAAQ,GAAG,eAAe;GACzC,EAAE,WAAW,QAAQ,GAAG,aAAa;GACrC,KAAK,gBAAgB,MAAM,EAAE;GAC7B,EAAE,YAAY,QAAQ,GAAG,cAAc;GACvC,EAAE,cAAc,QAAQ,GAAG,gBAAgB;GAC3C,EAAE,WAAW,QAAQ,GAAG,aAAa;GACrC,EAAE,cAAc,QAAQ,GAAG,gBAAgB;GAC3C,EAAE,cAAc,QAAQ,GAAG,gBAAgB;GAC3C,EAAE,cAAc,QAAQ,GAAG,gBAAgB;GAC3C,EAAE,YAAY,QAAQ,GAAG,cAAc;GACvC,EAAE,aAAa,QAAQ,GAAG,eAAe;GACzC,MAAM,MAAM,GAAG;GACf,EAAE,gBAAgB,QAAQ,KAAK,UAAU;GACzC,EAAE,cAAc,QAAQ,KAAK,QAAQ;GACrC,EAAE,eAAe,QAAQ,KAAK,SAAS;GACvC,EAAE,gBAAgB,QAAQ,KAAK,UAAU;GACzC,EAAE,cAAc,QAAQ,oBAAoB,KAAK,QAAQ,aAAa;GACtE,EAAE,iBAAiB,QAAQ,KAAK,UAAU,IAAI;GAC9C,EAAE,cAAc,QAAQ,KAAK,QAAQ;GAIrC,KAAK,KAAK,MAAM,IAAI,GAAG,MAAM,GAAG,GAAG,MAAM,GAAG,GAAG,MAAM,CAAC;GACtD,KAAK,KAAK,SAAS,IACjB,MAAM,UAAU,SAAS,GAAG,SAAS,CAAC,GACtC,MAAM,UAAU,SAAS,GAAG,SAAS,CAAC,GACtC,MAAM,UAAU,SAAS,GAAG,SAAS,CAAC,CACxC;GACA,KAAK,KAAK,SAAS,IAAI,GAAG,SAAS,GAAG,GAAG,SAAS,GAAG,GAAG,SAAS,CAAC;GAClE,EAAE,SAAS,QAAQ,GAAG;EACxB,CAAC;EAKD,MAAM,gBAAgB,KAAK,OAAO,aAAa,UAAU,MAAO;EAChE,MAAM,aAAa,KAAK,OAAO,aAAa,cAAc;EAC1D,KAAK,MAAM,SAAS,MAAM,MAAM;GAC9B,MAAM,KAAK,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,MAAM,KAAK,OAAO,MAAM,SAAS;GAChF,IAAI,CAAC,oBAAoB,KAAK,QAAQ,EAAE,GAAG;GAC3C,MAAM,IAAI,KAAK,SAAS;GACxB,MAAM,IAAI,GAAG,aAAa;GAC1B,EAAE,eAAe,QAAQ;GACzB,EAAE,gBAAgB,QAAQ,GAAG,WAAW;GACxC,EAAE,aAAa,QAAQ,GAAG,QAAQ;GAClC,EAAE,aAAa,QAAQ,GAAG,QAAQ;GAClC,EAAE,aAAa,QAAQ,GAAG,QAAQ;GAClC,EAAE,YAAY,QAAQ,GAAG,YAAY;GACrC,EAAE,gBAAgB,QAAQ,GAAG,WAAW;GACxC,EAAE,eAAe,QAAQ,GAAG,aAAa,OAAO,UAAU;GAC1D,EAAE,WAAW,QAAQ;EACvB,CAAC;EAED,KAAK,sBAAsB;EAC3B,KAAK,kBAAkB;EAGvB,KAAK,MAAM,MAAM,IAAI,KAAK,OAAO,UAAU,KAAK,GAAG,KAAK,OAAO,UAAU,KAAK,GAAG,CAAC;EAElF,KAAK,eAAe;EACpB,IAAI,CAAC,KAAK,SAAS,KAAK,WAAW;CACrC;;;CAIA,wBAAsC;EACpC,KAAK,MAAM,SAAS,MAAM,MAAM;GAC9B,MAAM,KAAK,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,MAAM,KAAK,OAAO,MAAM,SAAS;GAChF,KAAK,QAAQ,MAAM,IAAI,KAAK,SAAS,QAAQ;EAC/C,CAAC;CACH;;CAGA,SAAS,GAAiB;EACxB,KAAK,MAAM,KAAK,KAAK,OAAO,EAAE,SAAS,SAAS,OAAO,QAAQ;EAC/D,KAAK,WAAW;CAClB;;CAGA,UAAgB;EACd,KAAK,WAAW;CAClB;CAEA,kBAAgC;EAC9B,IAAI,KAAK,OAAO,uBAAuB;GACrC,KAAK,MAAM,aAAa;GACxB,KAAK,SAAS,cAAc,GAAU,CAAC;GACvC,KAAK,oBAAoB;GACzB;EACF;EAEA,MAAM,QAAQ,IAAI,MAAM,MAAM,KAAK,OAAO,UAAU;EACpD,KAAK,SAAS,cAAc,OAAO,CAAC;EACpC,IAAI,KAAK,OAAO,mBAAmB,SAAS;GAC1C,KAAK,qBAAqB,KAAK;GAC/B,KAAK,oBAAoB;GACzB;EACF;EAGA,MAAM,EAAE,OAAO,WAAW,KAAK,qBAC7B,KAAK,OAAO,qBAAqB,OAAO,IAC1C;EACA,IAAI,KAAK,OAAO,mBAAmB,YAAY,KAAK,wBAAwB,OAAO,MAAM;OACpF,KAAK,qBAAqB,OAAO,OAAO,MAAM;EACnD,KAAK,oBAAoB;CAC3B;;;;;CAMA,oBACE,OACA,KACA,SACmB;EACnB,OAAO,IAAI,cAAc,OAAO;CAClC;;;;;CAMA,iBAAmC;EACjC,OAAO,KAAK,SAAS,aAAa;CACpC;;;;;;CAOA,sBAAsC,CAAC;CAEvC,qBAA6B,OAA0B;EACrD,KAAK,qBAAqB;EAC1B,KAAK,mBAAmB,QAAQ;EAChC,KAAK,oBAAoB,KAAA;EACzB,KAAK,gBAAgB;EACrB,KAAK,MAAM,aAAa;CAC1B;CAEA,wBAAgC,OAAe,QAAsB;EACnE,KAAK,qBAAqB;EAC1B,MAAM,WAAW,KAAK,OAAO;EAC7B,IAAI,aAAa,QAAQ;GACvB,MAAM,MAAM,KAAK,OAAO,wBAAwB,CAAC;GACjD,MAAM,WAAW,KAAK,OAAO,0BAA0B;GACvD,MAAM,QAAQ,IACX,KAAK,MAAM,GAAG,EAAE,MAAM,GAAG,EAAE,EAAE,QAAQ,CAAC,EAAE,GAAG,EAAE,EAAE,QAAQ,CAAC,EAAE,GAAG,EAAE,UAAU,QAAQ,CAAC,GAAG,CAAC,CACtF,KAAK,GAAG;GACX,MAAM,MAAM;IAAC;IAAQ,SAAS,QAAQ,CAAC;IAAG;IAAO;IAAO;GAAM,CAAC,CAAC,KAAK,GAAG;GACxE,IAAI,QAAQ,KAAK,iBAAiB,CAAC,KAAK,mBAAmB;IACzD,MAAM,SAAS,0BAA0B,KAAK,UAAU,OAAO,MAAM;IACrE,KAAK,mBAAmB,QAAQ;IAChC,KAAK,oBAAoB,gBAAgB,MAAM;IAC/C,KAAK,gBAAgB;GACvB;GACA,KAAK,MAAM,aAAa,KAAK;GAC7B;EACF;EACA,MAAM,SAAS,KAAK,OAAO,4BAA4B;EACvD,MAAM,MAAM,aAAa;EACzB,MAAM,QACJ,WAAW,WAAW,KAAK,SAAS,cAAc,IAAI,QAClD,IAAI,QACJ,KAAK,OAAO;EAClB,MAAM,UAAU,MAAM,KAAK,SAAS,GAAG,KAAK,MAAM,GAAG,KAAK,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG;EACpF,MAAM,MAAM;GACV;GACA;GACA;GACA,KAAK,OAAO;GACZ;GACA;GACA;EACF,CAAC,CAAC,KAAK,GAAG;EACV,IAAI,QAAQ,KAAK,iBAAiB,CAAC,KAAK,mBAAmB;GACzD,MAAM,SAAS,8BAA8B;IAC3C;IACA,MAAM;IACN,OAAO,KAAK,OAAO;IACnB;IACA;GACF,CAAC;GACD,KAAK,mBAAmB,QAAQ;GAChC,KAAK,oBAAoB,gBAAgB,MAAM;GAC/C,KAAK,gBAAgB;EACvB;EACA,KAAK,MAAM,aAAa,KAAK;CAC/B;;;CAIA,qBAA6B,OAAoB,OAAe,QAAsB;EACpF,MAAM,MAAM,KAAK,OAAO,sBAAsB;EAC9C,MAAM,OAAO,KAAK,OAAO,uBAAuB;EAChD,MAAM,WAAW,KAAK,OAAO,2BAA2B;GAAE,GAAG;GAAG,GAAG;EAAE;EACrE,MAAM,WAAW,KAAK,OAAO;EAC7B,MAAM,YAAY,KAAK,OAAO;EAC9B,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI,UAAU;GACZ,KAAK,sBAAsB,QAAQ;GACnC,IAAI,CAAC,KAAK,mBAAmB,KAAK,gBAAgB,aAAa,GAAG;IAChE,KAAK,MAAM,aAAa;IACxB;GACF;GACA,SAAS,KAAK;GACd,cAAc,KAAK,gBAAgB;GACnC,eAAe,KAAK,gBAAgB;GACpC,YAAY,SAAS;EACvB,OAAO,IAAI,WAAW;GACpB,KAAK,qBAAqB;GAC1B,IACE,CAAC,KAAK,mBACN,KAAK,uBAAuB,aAC5B,CAAC,KAAK,gBAAgB,UACtB;IACA,KAAK,oBAAoB,SAAS;IAClC,KAAK,MAAM,aAAa;IACxB;GACF;GACA,SAAS,KAAK;GACd,cAAc,KAAK,gBAAgB;GACnC,eAAe,KAAK,gBAAgB;GACpC,YAAY,UAAU;EACxB,OAAO;GACL,KAAK,qBAAqB;GAC1B,MAAM,cAAc,KAAK,OAAO,yBAAyB;GACzD,MAAM,SACJ,gBAAgB,SACZ,uBAAuB,IACvB,aAAa,YAAY,EAAE,SAAS,UAClC,iBAAiB,aAAa,cAAc,KAAK,IAAI,OAAO,MAAM,CAAC,IACnE;GACR,IAAI,CAAC,QAAQ;IACX,KAAK,MAAM,aAAa;IACxB;GACF;GACA,SAAS;GACT,cAAc,OAAO;GACrB,eAAe,OAAO;GACtB,YAAY,OAAO;EACrB;EAEA,MAAM,MAAM;GACV;GACA;GACA;GACA;GACA,SAAS;GACT,SAAS;GACT,KAAK,OAAO;GACZ;GACA;EACF,CAAC,CAAC,KAAK,GAAG;EACV,IAAI,QAAQ,KAAK,iBAAiB,CAAC,KAAK,mBAAmB;GACzD,MAAM,SAAS,2BACb,QACA,aACA,cACA,OACA,QACA,KACA,KAAK,OAAO,YACZ,MACA,SAAS,GACT,SAAS,CACX;GACA,KAAK,mBAAmB,QAAQ;GAChC,KAAK,oBAAoB,gBAAgB,MAAM;GAC/C,KAAK,gBAAgB;GACrB,KAAK,wBAAwB,WAAW,SAAS,KAAA;EACnD;EACA,KAAK,MAAM,aAAa,KAAK;CAC/B;CAEA,qBAA6B,mBAAmB,MAAyC;EACvF,MAAM,WAAW,KAAK,YAAY,SAAS,KAAK,IAAI,GAAG,KAAK,UAAU,WAAW;EACjF,MAAM,YAAY,KAAK,YAAY,UAAU,KAAK,IAAI,GAAG,KAAK,UAAU,YAAY;EACpF,MAAM,UAAU,KAAK,IAAI,kBAAkB,KAAK,eAAe,CAAC;EAChE,MAAM,QAAQ,KAAK,IAAI,GAAG,UAAU,KAAK,IAAI,UAAU,SAAS,CAAC;EACjE,OAAO;GACL,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,WAAW,KAAK,CAAC;GAC/C,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,KAAK,CAAC;EACnD;CACF;CAEA,oBAA4B,KAAmB;EAC7C,IAAI,KAAK,uBAAuB,OAAO,KAAK,6BAA6B,KAAK;EAC9E,KAAK,qBAAqB;EAC1B,KAAK,kBAAkB,KAAA;EACvB,MAAM,QAAQ,IAAI,MAAM;EACxB,MAAM,WAAW;EACjB,IAAI,CAAC,IAAI,WAAW,OAAO,KAAK,CAAC,IAAI,WAAW,OAAO,GAAG,MAAM,cAAc;EAC9E,MAAM,iBACJ,cACM;GACJ,IAAI,KAAK,OAAO,uBAAuB,KAAK;GAC5C,KAAK,kBAAkB;GACvB,KAAK,2BAA2B;GAChC,KAAK,gBAAgB;GACrB,KAAK,gBAAgB;GACrB,IAAI,CAAC,KAAK,SAAS,KAAK,WAAW;EACrC,GACA,EAAE,MAAM,KAAK,CACf;EACA,MAAM,iBACJ,eACM;GACJ,IAAI,KAAK,OAAO,uBAAuB,KAAK;GAC5C,KAAK,2BAA2B;GAChC,KAAK,gBAAgB;GACrB,KAAK,gBAAgB;GACrB,IAAI,CAAC,KAAK,SAAS,KAAK,WAAW;EACrC,GACA,EAAE,MAAM,KAAK,CACf;EACA,MAAM,MAAM;CACd;CAEA,sBAA8B,KAAmB;EAC/C,IAAI,KAAK,uBAAuB,OAAO,KAAK,6BAA6B,KAAK;EAC9E,KAAK,qBAAqB;EAC1B,KAAK,qBAAqB;EAC1B,MAAM,QAAQ,KAAK,mBAAmB,GAAG;EACzC,MAAM,iBACJ,oBACM;GACJ,IAAI,KAAK,OAAO,uBAAuB,KAAK;GAC5C,KAAK,kBAAkB;GACvB,KAAK,2BAA2B;GAChC,KAAK,gBAAgB;GACrB,KAAK,gBAAgB;GACrB,KAAK,kBAAkB;GACvB,IAAI,CAAC,KAAK,SAAS,KAAK,WAAW;EACrC,GACA,EAAE,MAAM,KAAK,CACf;EACA,MAAM,iBACJ,eACM;GACJ,IAAI,KAAK,OAAO,uBAAuB,KAAK;GAC5C,KAAK,2BAA2B;GAChC,KAAK,gBAAgB;GACrB,KAAK,gBAAgB;GACrB,IAAI,CAAC,KAAK,SAAS,KAAK,WAAW;EACrC,GACA,EAAE,MAAM,KAAK,CACf;EACA,KAAK,kBAAkB;EACvB,MAAM,KAAK;CACb;CAEA,uBAAqC;EACnC,IAAI,KAAK,iBAAiB;GACxB,KAAK,gBAAgB,MAAM;GAC3B,KAAK,gBAAgB,gBAAgB,KAAK;GAC1C,KAAK,gBAAgB,KAAK;EAC5B;EACA,KAAK,kBAAkB,KAAA;EACvB,KAAK,qBAAqB;EAC1B,KAAK,2BAA2B;EAChC,KAAK,wBAAwB,KAAA;CAC/B;CAEA,mBAA2B,KAA+B;EACxD,MAAM,QAAQ,SAAS,cAAc,OAAO;EAC5C,MAAM,QAAQ;EACd,MAAM,eAAe;EACrB,MAAM,OAAO;EACb,MAAM,cAAc;EACpB,MAAM,UAAU;EAChB,IAAI,CAAC,IAAI,WAAW,OAAO,KAAK,CAAC,IAAI,WAAW,OAAO,GAAG,MAAM,cAAc;EAC9E,MAAM,MAAM;EACZ,OAAO;CACT;CAEA,oBAAkC;EAChC,MAAM,mBACJ,KAAK,WACL,CAAC,KAAK,OAAO,yBACb,KAAK,OAAO,mBAAmB,WAC/B,CAAC,CAAC,KAAK,OAAO;EAChB,IAAI,KAAK,iBACP,IAAI,kBAAkB,KAAU,gBAAgB,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC;OAChE,KAAK,gBAAgB,MAAM;EAGlC,KAAK,MAAM,SAAS,MAAM,MAAM;GAC9B,MAAM,KAAK,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,MAAM,KAAK,OAAO,MAAM,SAAS;GAChF,KAAK,QAAQ,aAAa,IAAI,KAAK,OAAO;EAC5C,CAAC;CACH;CAEA,6BAA2C;EACzC,MAAM,QAAQ,KAAK;EACnB,MAAM,SAAS,KAAK;EACpB,IAAI,CAAC,SAAS,CAAC,UAAU,MAAM,aAAa,KAAK,CAAC,KAAK,mBAAmB;EAC1E,MAAM,WAAW,KAAK,OAAO,2BAA2B;GAAE,GAAG;GAAG,GAAG;EAAE;EACrE,yBACE,QACA,OACA,MAAM,YACN,MAAM,aACN,KAAK,OAAO,sBAAsB,SAClC,KAAK,OAAO,YACZ,KAAK,OAAO,uBAAuB,GACnC,SAAS,GACT,SAAS,CACX;EACA,KAAK,kBAAkB,cAAc;CACvC;CAEA,YAA0B;EACxB,MAAM,IAAI,KAAK,SAAS;EACxB,EAAE,YAAY,QAAQ,KAAK,OAAO;EAClC,EAAE,aAAa,QAAQ,KAAK,OAAO;EACnC,EAAE,aAAa,QAAQ,KAAK,MAAM,KAAK,OAAO,eAAe,CAAC;EAC9D,KAAK,WAAW;EAChB,KAAK,gBAAgB;EACrB,KAAK,cAAc;EACnB,KAAK,aAAa;EAClB,KAAK,kBAAkB;EACvB,KAAK,kBAAkB;EACvB,KAAK,YAAY;CACnB;;;;;CAMA,aAA2B;EACzB,MAAM,WAAW,KAAK,OAAO,iBAAiB;EAC9C,IAAI,WAAW,GAAG;GAChB,IAAI,CAAC,KAAK,WAAW;IACnB,MAAM,OAAO,KAAK,SAAS,qBAAqB,IAAI,MAAM,QAAQ,CAAC;IACnE,KAAK,YAAY,IAAI,gBACnB,MACA,UACA,KAAK,OAAO,eAAe,IAC3B,KAAK,OAAO,kBAAkB,GAChC;IACA,KAAK,SAAS,WAAW,KAAK,WAAW,CAAC;GAC5C;GACA,KAAK,UAAU,WAAW;GAC1B,KAAK,UAAU,SAAS,KAAK,OAAO,eAAe;GACnD,KAAK,UAAU,YAAY,KAAK,OAAO,kBAAkB;EAC3D,OAAO,IAAI,KAAK,WAAW;GACzB,KAAK,SAAS,WAAW,KAAK,SAAS;GACvC,KAAK,UAAU,QAAQ;GACvB,KAAK,YAAY,KAAA;EACnB;CACF;;;;;;CAOA,cAA4B;EAC1B,MAAM,WAAW,KAAK,OAAO,UAAU;EACvC,IAAI,WAAW,GAAG;GAChB,IAAI,CAAC,KAAK,YAAY;IACpB,KAAK,aAAa,IAAI,WAAW;KAC/B,UAAU;MACR,UAAU,EAAE,OAAO,KAAK;MACxB,aAAa,EAAE,OAAO,KAAK,SAAS,qBAAqB,IAAI,MAAM,QAAQ,CAAC,EAAE;MAC9E,iBAAiB,EAAE,OAAO,SAAS;MACnC,cAAc,EAAE,OAAO,KAAK,OAAO,eAAe,EAAE;MACpD,cAAc,EAAE,OAAO,KAAK,OAAO,eAAe,EAAE;KACtD;KACA,cAAc;KACd,gBAAgB;IAClB,CAAC;IACD,KAAK,SAAS,QAAQ,KAAK,UAAU;GACvC;GACA,MAAM,IAAI,KAAK,WAAW;GAC1B,EAAE,gBAAgB,QAAQ;GAC1B,EAAE,aAAa,QAAQ,KAAK,IAAI,GAAG,KAAK,OAAO,eAAe,CAAC;GAC/D,EAAE,aAAa,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,OAAO,eAAe,CAAC,CAAC;EAC7E,OAAO,IAAI,KAAK,YAAY;GAC1B,KAAK,SAAS,WAAW,KAAK,UAAU;GACxC,KAAK,WAAW,QAAQ;GACxB,KAAK,aAAa,KAAA;EACpB;CACF;;;;CAKA,kBAAgC;EAC9B,MAAM,WAAW,KAAK,OAAO,cAAc;EAC3C,IAAI,WAAW,GAAG;GAChB,MAAM,KAAK,KAAK,OAAO,eAAe;GACtC,MAAM,KAAK,KAAK,OAAO,eAAe;GACtC,IAAI,CAAC,KAAK,gBAAgB;IACxB,KAAK,iBAAiB,IAAI,WAAW;KACnC,UAAU;MACR,UAAU,EAAE,OAAO,KAAK;MACxB,aAAa,EAAE,OAAO,SAAS;MAC/B,oBAAoB,EAAE,OAAO,KAAK,OAAO,qBAAqB,GAAI;MAClE,kBAAkB,EAAE,OAAO,KAAK,OAAO,mBAAmB,IAAK;MAC/D,mBAAmB,EAAE,OAAO,IAAI,MAAM,QAAQ,IAAI,EAAE,EAAE;KACxD;KACA,cAAc;KACd,gBAAgB;IAClB,CAAC;IACD,KAAK,SAAS,WAAW,KAAK,gBAAgB,CAAC;GACjD;GACA,MAAM,IAAI,KAAK,eAAe;GAC9B,EAAE,YAAY,QAAQ;GACtB,EAAE,mBAAmB,QAAQ,KAAK,OAAO,qBAAqB;GAC9D,EAAE,iBAAiB,QAAQ,KAAK,OAAO,mBAAmB;GAC1D,EAAG,kBAAkB,MAAwB,IAAI,IAAI,EAAE;EACzD,OAAO,IAAI,KAAK,gBAAgB;GAC9B,KAAK,SAAS,WAAW,KAAK,cAAc;GAC5C,KAAK,eAAe,QAAQ;GAC5B,KAAK,iBAAiB,KAAA;EACxB;CACF;;;CAIA,gBAA8B;EAC5B,MAAM,WAAW,KAAK,OAAO,YAAY;EACzC,IAAI,WAAW,GAAG;GAChB,IAAI,CAAC,KAAK,cAAc;IACtB,KAAK,eAAe,IAAI,WAAW;KACjC,UAAU;MACR,UAAU,EAAE,OAAO,KAAK;MACxB,aAAa,EAAE,OAAO,KAAK,SAAS,qBAAqB,IAAI,MAAM,QAAQ,CAAC,EAAE;MAC9E,WAAW,EAAE,OAAO,SAAS;MAC7B,eAAe,EAAE,OAAO,KAAK,OAAO,gBAAgB,EAAE;MACtD,gBAAgB,EAAE,OAAO,KAAK,OAAO,iBAAiB,GAAI;KAC5D;KACA,cAAc;KACd,gBAAgB;IAClB,CAAC;IACD,KAAK,SAAS,QAAQ,KAAK,YAAY;GACzC;GACA,MAAM,IAAI,KAAK,aAAa;GAC5B,EAAE,UAAU,QAAQ;GACpB,EAAE,cAAc,QAAQ,KAAK,IAAI,GAAG,KAAK,OAAO,gBAAgB,CAAC;GACjE,EAAE,eAAe,QAAQ,KAAK,OAAO,iBAAiB;EACxD,OAAO,IAAI,KAAK,cAAc;GAC5B,KAAK,SAAS,WAAW,KAAK,YAAY;GAC1C,KAAK,aAAa,QAAQ;GAC1B,KAAK,eAAe,KAAA;EACtB;CACF;;CAGA,eAA6B;EAC3B,MAAM,WAAW,KAAK,OAAO,WAAW;EACxC,IAAI,WAAW,GAAG;GAChB,IAAI,CAAC,KAAK,aAAa;IACrB,KAAK,cAAc,IAAI,WAAW;KAChC,UAAU;MAAE,UAAU,EAAE,OAAO,KAAK;MAAG,UAAU,EAAE,OAAO,SAAS;KAAE;KACrE,cAAc;KACd,gBAAgB;IAClB,CAAC;IACD,KAAK,SAAS,QAAQ,KAAK,WAAW;GACxC;GACA,KAAK,YAAY,SAAS,SAAS,QAAQ;EAC7C,OAAO,IAAI,KAAK,aAAa;GAC3B,KAAK,SAAS,WAAW,KAAK,WAAW;GACzC,KAAK,YAAY,QAAQ;GACzB,KAAK,cAAc,KAAA;EACrB;CACF;;CAGA,oBAAkC;EAChC,MAAM,WAAW,KAAK,OAAO,gBAAgB;EAC7C,IAAI,WAAW,GAAG;GAChB,IAAI,CAAC,KAAK,kBAAkB;IAC1B,KAAK,mBAAmB,IAAI,WAAW;KACrC,UAAU;MACR,UAAU,EAAE,OAAO,KAAK;MACxB,QAAQ,EAAE,OAAO,SAAS;MAC1B,aAAa,EAAE,OAAO,KAAK,OAAO,qBAAqB,EAAE;KAC3D;KACA,cAAc;KACd,gBAAgB;IAClB,CAAC;IACD,KAAK,SAAS,QAAQ,KAAK,gBAAgB;GAC7C;GACA,MAAM,IAAI,KAAK,iBAAiB;GAChC,EAAE,OAAO,QAAQ;GACjB,EAAE,YAAY,QAAQ,KAAK,IAAI,IAAK,KAAK,OAAO,qBAAqB,CAAC;EACxE,OAAO,IAAI,KAAK,kBAAkB;GAChC,KAAK,SAAS,WAAW,KAAK,gBAAgB;GAC9C,KAAK,iBAAiB,QAAQ;GAC9B,KAAK,mBAAmB,KAAA;EAC1B;CACF;;CAGA,oBAAkC;EAChC,MAAM,WAAW,KAAK,OAAO,gBAAgB;EAC7C,IAAI,WAAW,GAAG;GAChB,IAAI,CAAC,KAAK,kBAAkB;IAC1B,KAAK,mBAAmB,IAAI,WAAW;KACrC,UAAU;MACR,UAAU,EAAE,OAAO,KAAK;MACxB,eAAe,EAAE,OAAO,SAAS;MACjC,mBAAmB,EAAE,OAAO,KAAK,OAAO,oBAAoB,EAAE;KAChE;KACA,cAAc;KACd,gBAAgB;IAClB,CAAC;IACD,KAAK,SAAS,QAAQ,KAAK,gBAAgB;GAC7C;GACA,MAAM,IAAI,KAAK,iBAAiB;GAChC,EAAE,cAAc,QAAQ;GACxB,EAAE,kBAAkB,QAAQ,KAAK,IAAI,GAAG,KAAK,OAAO,oBAAoB,CAAC;EAC3E,OAAO,IAAI,KAAK,kBAAkB;GAChC,KAAK,SAAS,WAAW,KAAK,gBAAgB;GAC9C,KAAK,iBAAiB,QAAQ;GAC9B,KAAK,mBAAmB,KAAA;EAC1B;CACF;;;;;;;;;;;;;;;;;CAkBA,iBAA+B;EAC7B,IAAI,KAAK,WAAW;EACpB,KAAK,YAAY,4BAA4B;GAC3C,KAAK,YAAY;GACjB,MAAM,OAAO,KAAK,gBAAgB;GAClC,MAAM,OAAO,KAAK;GAClB,IAAI,QAAQ,KAAK,MAAM,KAAK,KAAK,KAAK,MAAM,KAAK,KAAK,KAAK,QAAQ,KAAK,KAAK;GAC7E,KAAK,OAAO;EACd,CAAC;CACH;;;;;;CAOA,oBAAkC;EAChC,KAAK,SAAS;EACd,KAAK,OAAO;CACd;;;CAIA,WAAyB;EACvB,KAAK,UAAU,oBAAoB,UAAU,KAAK,WAAW;EAC7D,KAAK,WAAW,OAAO,WAAW,gBAAgB,OAAO,oBAAoB,EAAE,MAAM;EACrF,KAAK,SAAS,iBAAiB,UAAU,KAAK,WAAW;CAC3D;;;CAIA,kBAAiE;EAC/D,OAAO;GACL,GAAG,KAAK,YAAY,SAAS,KAAK,IAAI,GAAG,KAAK,UAAU,WAAW;GACnE,GAAG,KAAK,YAAY,UAAU,KAAK,IAAI,GAAG,KAAK,UAAU,YAAY;GACrE,KAAK,KAAK,aAAa,IAAI,KAAK,IAAI,OAAO,oBAAoB,GAAG,KAAK,OAAO,MAAM;EACtF;CACF;CAEA,iBAAyB,MAAmB;EAC1C,EAAE,eAAe;EACjB,qBAAqB,KAAK,KAAK;EAC/B,KAAK,UAAU;CACjB;CAEA,0BAAwC;EAGtC,KAAK,aAAa;EAClB,KAAK,mBAAmB,QAAQ;EAChC,KAAK,oBAAoB,KAAA;EACzB,KAAK,gBAAgB;EACrB,KAAK,WAAW;EAChB,KAAK,OAAO;EACZ,KAAK,cAAc;CACrB;CAEA,SAAe;EACb,MAAM,EAAE,GAAG,GAAG,QAAQ,KAAK,gBAAgB;EAC3C,KAAK,aAAa;GAAE;GAAG;GAAG;EAAI;EAC9B,KAAK,SAAS,cAAc,GAAG;EAC/B,KAAK,SAAS,QAAQ,GAAG,GAAG,CAAC,KAAK,UAAU;EAC5C,IAAI,KAAK,YAAY;GAGnB,KAAK,SAAS,WAAW,MAAM,QAAQ;GACvC,KAAK,SAAS,WAAW,MAAM,SAAS;EAC1C;EACA,KAAK,WAAW,GAAG,GAAG,GAAG;EACzB,MAAM,KAAK,IAAI;EACf,MAAM,KAAK,IAAI;EACf,KAAM,SAAS,SAAS,YAAY,MAAwB,IAAI,IAAI,EAAE;EACtE,IAAI,KAAK,YAAY,KAAM,WAAW,SAAS,YAAY,MAAwB,IAAI,IAAI,EAAE;EAC7F,IAAI,KAAK,cACP,KAAM,aAAa,SAAS,YAAY,MAAwB,IAAI,IAAI,EAAE;EAC5E,KAAK,MAAM,KAAK,KAAK,OACnB,EAAG,SAAS,SAAS,YAAY,MAAwB,IAAI,IAAI,EAAE;EAIrE,KAAK,OAAO,OAAO,CAAC,KAAK;EACzB,KAAK,OAAO,QAAQ,KAAK;EACzB,KAAK,OAAO,MAAM,KAAK;EACvB,KAAK,OAAO,SAAS,CAAC,KAAK;EAC3B,KAAK,UAAU;EACf,KAAK,gBAAgB;EACrB,KAAK,cAAc;EACnB,IAAI,CAAC,KAAK,SAAS,KAAK,WAAW;CACrC;;CAGA,cAAc,OAAe,QAAsB;EACjD,KAAK,aAAa;GAChB,OAAO,MAAM,UAAU,MAAM,KAAK,MAAM,KAAK,GAAG,IAAI,IAAI;GACxD,QAAQ,MAAM,UAAU,MAAM,KAAK,MAAM,MAAM,GAAG,IAAI,IAAI;EAC5D;EACA,KAAK,OAAO;CACd;CAEA,QAAc;EACZ,KAAK,UAAU;EACf,KAAK,cAAc;CACrB;CAEA,OAAa;EACX,KAAK,UAAU;EACf,KAAK,cAAc;CACrB;CAEA,kBAA0B,MAAiC;EACzD,KAAK,gBAAgB,KAAK,wBAAwB,EAAE;EACpD,KAAK,cAAc;CACrB;CAEA,2BAAyC;EACvC,KAAK,cAAc,SAAS,oBAAoB;EAChD,KAAK,cAAc;CACrB;CAEA,gBAA8B;EAC5B,MAAM,gBACJ,KAAK,WACL,KAAK,WACL,KAAK,eACL,CAAC,KAAK,OAAO,UACb,CAAC,KAAK;EAER,IAAI,iBAAiB,CAAC,KAAK,SAAS;GAClC,KAAK,UAAU;GAGf,KAAK,MAAM,OAAO;GAClB,KAAK,QAAQ,sBAAsB,KAAK,IAAI;EAC9C,OAAO,IAAI,CAAC,iBAAiB,KAAK,SAAS;GACzC,KAAK,UAAU;GACf,qBAAqB,KAAK,KAAK;EACjC;EAGA,IAAI,CAAC,KAAK,SAAS;GACjB,KAAK,gBAAgB;GACrB,KAAK,aAAa,OAAO;GACzB,KAAK,WAAW;EAClB;EACA,KAAK,kBAAkB;CACzB;CAEA,aAA2B;EACzB,IAAI,CAAC,KAAK,SAAS;EACnB,KAAK,MAAM,OAAO;EAClB,MAAM,KAAK,KAAK,IAAI,KAAK,MAAM,SAAS,GAAG,iBAAiB;EAC5D,KAAK,QAAQ;EACb,KAAK,aAAa,OAAO,EAAE;EAC3B,IAAI,KAAK,gBAAgB,GAAG,KAAK,gBAAgB,KAAK,IAAI,GAAG,KAAK,gBAAgB,IAAK;EACvF,KAAK,WAAW;EAChB,KAAK,QAAQ,sBAAsB,KAAK,IAAI;CAC9C;;;CAIA,aAA2B;EAIzB,MAAM,OAAO,KAAK,OAAO,cAAc,SAAS,KAAK,gBAAgB,IAAI,KAAK;EAC9E,MAAM,IAAI,KAAK,OAAO,QAAQ,KAAK,OAAO,cAAc,KAAK,KAAK;EAElE,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,MAAM,QAAQ,KAAK;GAC1C,MAAM,IAAI,KAAK,MAAM,EAAE,CAAC,SAAS;GACjC,EAAE,MAAM,QAAQ;GAIhB,MAAM,KAAK,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,MAAM,KAAK,OAAO,MAAM,SAAS;GAChF,MAAM,KAAK,GAAG,iBAAiB;GAC/B,MAAM,KAAK,GAAG,iBAAiB;GAC/B,IAAI,OAAO,KAAK,OAAO,GAAG;IACxB,MAAM,OAAO,GAAG;IAChB,EAAG,eAAe,MAAwB,IAAI,KAAK,IAAI,KAAK,GAAG,KAAK,IAAI,KAAK,CAAC;GAChF;EACF;EACA,KAAK,SAAS,SAAS,MAAM,QAAQ;EAErC,KAAK,MAAM,KAAK,KAAK,OAAO,EAAE,eAAe,QAAQ,CAAC;CACxD;;CAGA,iBAAiC;EAC/B,KAAK,kBAAkB;EACvB,KAAK,SAAS,OAAO;CACvB;;;;;;;;;;;CAYA,eAA6B;EAC3B,MAAM,MAAM,KAAK,OAAO,kBAAkB,KAAK,WAAW,CAAC,CAAC,eAAe,EAAE;EAC7E,KAAK,MAAM,KAAK,KAAK,OACnB,EAAG,SAAS,SAAS,UAAU,MAAwB,KAAK,GAAG;CAEnE;CAEA,cAAsB,IAAI,MAAM,QAAQ;CACxC;CACA,gBAAwB,IAAI,MAAM,MAAM;CACxC,YAAoB,IAAI,MAAM,MAAM;CACpC,WAAmB,IAAI,MAAM,QAAQ;CACrC,UAAkB,IAAI,MAAM,QAAQ;;;;;CAMpC,YAAoB,GAAqD;EACvE,EAAE,QAAQ,YAAY,MAAM;EAC5B,EAAE,QAAQ,YAAY,MAAM;EAC5B,EAAE,QAAQ,QAAQ,MAAM;EACxB,EAAE,QAAQ,QAAQ,MAAM;EACxB,EAAE,QAAQ,kBAAkB;EAC5B,OAAO;CACT;;CAGA,eAA0C;EACxC,IAAI,CAAC,KAAK,OAAO;GACf,KAAK,QAAQ,IAAI,MAAM,YAAY,IAAI,WAAW;IAAC;IAAG;IAAG;IAAG;GAAC,CAAC,GAAG,GAAG,CAAC;GACrE,KAAK,MAAM,cAAc;EAC3B;EACA,OAAO,KAAK;CACd;CAEA,oBAAoC;EAClC,KAAK,aAAa;EAClB,MAAM,WAAqB,CAAC;EAC5B,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,MAAM,QAAQ,KAErC,KADW,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,MAAM,KAAK,OAAO,MAAM,SAAS,GAAA,EACxE,UAAU,SAAS,SAAS,KAAK,CAAC;EAE5C,IAAI,SAAS,WAAW,GAAG;GACzB,IAAI,KAAK,gBACP,KAAK,MAAM,KAAK,KAAK,OAAO,EAAE,SAAS,SAAS,UAAU,QAAQ;GACpE,KAAK,MAAM,KAAK,KAAK,OAAO,KAAK,kBAAkB,CAAC;GACpD;EACF;EAOA,KAAK,MAAM,kBAAkB;EAC7B,MAAM,OAAO,KAAK;EAClB,MAAM,WAAW,MAAc,KAAK,MAAM,EAAE,CAAC,KAAK,iBAAiB,KAAK,QAAQ,CAAC,CAAC,IAAI,IAAI;EAC1F,MAAM,aAAa,SAAS,IAAI,OAAO;EACvC,MAAM,WAAyB,CAAC;EAChC,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,MAAM,QAAQ,KAAK;GAC1C,MAAM,IAAI,QAAQ,CAAC;GACnB,MAAM,UAAU,SAAS,MAAM,GAAG,MAAM;IACtC,MAAM,KAAK,IAAI,WAAW;IAC1B,OAAO,KAAK,QAAS,KAAK,IAAI,EAAE,KAAK,QAAQ,IAAI;GACnD,CAAC;GACD,IAAI,SAAS,SAAS,CAAC,KAAK,SAAS,SAAS,KAAK,KAAK,MAAM,EAAE,CAAC,IAAI;EACvE;EAEA,MAAM,OAAO,KAAK,SAAS,qBAAqB,KAAK,OAAO;EAC5D,IAAI,CAAC,KAAK,gBACR,KAAK,iBAAiB,KAAK,YACzB,IAAI,MAAM,kBAAkB,KAAK,GAAG,KAAK,GAAG;GAAE,aAAa;GAAM,eAAe;EAAM,CAAC,CACzF;OACK,IAAI,KAAK,eAAe,UAAU,KAAK,KAAK,KAAK,eAAe,WAAW,KAAK,GACrF,KAAK,eAAe,QAAQ,KAAK,GAAG,KAAK,CAAC;EAI5C,MAAM,aAAa,KAAK,SAAS,gBAAgB;EACjD,MAAM,iBAAiB,KAAK,SAAS,cAAc;EACnD,KAAK,SAAS,cAAc,KAAK,SAAS;EAC1C,MAAM,SAAS,KAAK,MAAM;EAC1B,MAAM,gBAAgB;GACpB,KAAK,SAAS,gBAAgB,UAAU;GACxC,KAAK,SAAS,cAAc,KAAK,WAAW,cAAc;GAC1D,KAAK,MAAM,aAAa;EAC1B;EAIA,KAAK,MAAM,KAAK,KAAK,OAAO,EAAE,SAAS,SAAS,UAAU,QAAQ,KAAK,aAAa;EACpF,KAAK,MAAM,KAAK,UAAU,EAAE,UAAU;EACtC,KAAK,SAAS,gBAAgB,KAAK,cAAc;EAQjD,KAAK,cAAc,IAAI,KAAK,OAAO,cAAc,SAAS;EAC1D,KAAK,SAAS,cAAc,KAAK,eAAe,CAAC;EACjD,KAAK,SAAS,MAAM,MAAM,MAAM,KAAK;EACrC,KAAK,SAAS,OAAO,KAAK,OAAO,KAAK,MAAM;EAC5C,KAAK,MAAM,KAAK,UAAU,EAAE,UAAU;EACtC,KAAK,MAAM,KAAK,KAAK,OAAO,EAAE,SAAS,SAAS,UAAU,QAAQ,KAAK,eAAe;EAKtF,MAAM,SAA2B,CAAC;EAClC,MAAM,QAAQ,MAAkC;GAC9C,IAAI,KAAK,EAAE,SAAS;IAClB,EAAE,UAAU;IACZ,OAAO,KAAK,CAAC;GACf;EACF;EACA,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,MAAM,QAAQ,KAAK;GAC1C,IAAI,CAAC,SAAS,SAAS,CAAC,GAAG,KAAK,KAAK,MAAM,EAAE,CAAC,IAAI;GAClD,KAAK,KAAK,MAAM,EAAE,CAAC,eAAe,MAAM;EAC1C;EACA,KAAK,MAAM,aAAa;EAKxB,IAAI,CAAC,KAAK,aACR,KAAK,cAAc,KAAK,YACtB,IAAI,MAAM,kBAAkB,KAAK,GAAG,KAAK,GAAG;GAAE,aAAa;GAAO,eAAe;EAAM,CAAC,CAC1F;OACK,IAAI,KAAK,YAAY,UAAU,KAAK,KAAK,KAAK,YAAY,WAAW,KAAK,GAC/E,KAAK,YAAY,QAAQ,KAAK,GAAG,KAAK,CAAC;EAEzC,KAAK,MAAM,KAAK,KAAK,OAAO,EAAE,SAAS,SAAS,QAAQ,QAAQ,KAAK,aAAa;EAClF,MAAM,UAAkB,CAAC;EACzB,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,MAAM,QAAQ,KAAK;GAC1C,MAAM,IAAI,KAAK,MAAM;GACrB,IAAI,CAAC,SAAS,SAAS,CAAC,GAAG;IACzB,KAAK,kBAAkB,CAAC;IACxB;GACF;GACA,MAAM,KAAK,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,MAAM,KAAK,OAAO,MAAM,SAAS;GAChF,MAAM,MAAM,KAAK,gBAAgB,EAAE,QAAQ;GAC3C,IAAI,EAAE,iBAAiB,EAAE,aAAa,KAAK,KAAK,kBAAkB,CAAC;GACnE,IAAI,CAAC,EAAE,eAAe;IACpB,EAAE,gBAAgB,KAAK,oBAAoB,IAAI,EAAE,QAAQ;IACzD,EAAE,WAAW;GACf;GACA,EAAE,KAAK,WAAW,EAAE;GACpB,QAAQ,KAAK,CAAC;EAChB;EACA,KAAK,SAAS,gBAAgB,KAAK,WAAW;EAC9C,KAAK,SAAS,cAAc,GAAU,CAAC;EACvC,KAAK,SAAS,MAAM,MAAM,OAAO,KAAK;EACtC,KAAK,SAAS,OAAO,KAAK,OAAO,KAAK,MAAM;EAC5C,KAAK,MAAM,KAAK,SAAS,EAAE,KAAK,WAAW,EAAE;EAC7C,KAAK,MAAM,KAAK,KAAK,OAAO,EAAE,SAAS,SAAS,QAAQ,QAAQ,KAAK,YAAY;EAEjF,KAAK,MAAM,KAAK,QAAQ,EAAE,UAAU;EACpC,QAAQ;CACV;;CAGA,WAAqB,GAAW,GAAW,KAAmB;EAC5D,KAAK,SAAS,cAAc,GAAG;EAC/B,KAAK,SAAS,QAAQ,GAAG,CAAC;CAC5B;;CAGA,cAA8B;EAC5B,KAAK,SAAS,QAAQ;EACtB,KAAK,gBAAgB,QAAQ;EAC7B,KAAK,iBAAiB,KAAA;EACtB,KAAK,aAAa,QAAQ;EAC1B,KAAK,cAAc,KAAA;EACnB,KAAK,OAAO,QAAQ;EACpB,KAAK,QAAQ,KAAA;CACf;;CAGA,aAAmB;EAGjB,IAAI,CAAC,KAAK,OAAO;EACjB,KAAK,2BAA2B;EAChC,KAAK,WAAW;EAChB,KAAK,iBAAiB;EACtB,KAAK,cAAc;EACnB,KAAK,iBAAiB;EACtB,KAAK,eAAe;EAGpB,KAAK,mBAAmB;CAC1B;;;;;CAMA,iBAA+B;EAC7B,MAAM,OAAO,KAAK,OAAO,eAAe;EACxC,KAAK,MAAM,SAAS,MAAM,MAAM;GAC9B,MAAM,KAAK,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,MAAM,KAAK,OAAO,MAAM,SAAS;GAChF,MAAM,MAAM,IAAI;GAChB,IAAI,OAAO,IAAI,QAAQ,GAAG;IACxB,IAAI,CAAC,KAAK,eAAe;KAGvB,KAAK,gBAAgB,KAAK,oBAAoB,MAAM,UAAU;MAC5D,IAAI,CAAC,KAAK,SAAS,KAAK,WAAW;KACrC,CAAC;KACD,KAAK,MAAM,IAAI,KAAK,cAAc,MAAM;IAC1C;IACA,KAAK,cAAc,KAAK,KAAK,IAAI;GACnC,OAAO,IAAI,KAAK,eAAe;IAC7B,KAAK,MAAM,OAAO,KAAK,cAAc,MAAM;IAC3C,KAAK,cAAc,QAAQ;IAC3B,KAAK,gBAAgB,KAAA;GACvB;EACF,CAAC;CACH;;;;;CAMA,gBAA8B;EAC5B,IAAI,WAAW;EACf,KAAK,MAAM,KAAK,KAAK,OACnB,IAAI,EAAE,eAAe;GACnB,WAAW;GACX;EACF;EAEF,IAAI,CAAC,UAAU;EACf,KAAK,OAAO,kBAAkB;EAC9B,KAAK,QAAQ,oBAAoB,KAAK,OAAO,aAAa,CAAC,CAAC,CAAC,UAAU;EACvE,KAAK,KAAK,oBAAoB,KAAK,OAAO,aAAa,CAAC,CAAC,CAAC,UAAU;EACpE,MAAM,KAAK,KAAK,SAAS,cAAc;EACvC,KAAK,MAAM,SAAS,MAAM,MAAM;GAC9B,MAAM,QAAQ,KAAK;GACnB,IAAI,CAAC,OAAO;GACZ,MAAM,KAAK,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,MAAM,KAAK,OAAO,MAAM,SAAS;GAChF,KAAK,KAAK,kBAAkB,MAAM,KAAK;GACvC,KAAK,KAAK,iBAAiB,KAAK,QAAQ;GACxC,MAAM,QAAuB;IAAE,QAAQ,KAAK;IAAU,OAAO,KAAK;IAAS,IAAI,KAAK;GAAK;GACzF,MAAM,MAAM,OAAO,EAAE;GACrB,MAAM,UAAU;IACd,SAAS,KAAK,aAAa,EAAE;IAC7B,UAAU,KAAK,SAAS;IACxB,aAAa,KAAK,KAAK;IACvB,OAAO,GAAG;IACV,MAAM,GAAG;GACX,CAAC;EACH,CAAC;CACH;;;;;CAMA,aAAqB,IAAwC;EAC3D,MAAM,MAAM,KAAK,YAAY,EAAE;EAC/B,MAAM,MAA8B,CAAC;EACrC,KAAK,MAAM,KAAK;GACd;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,GACE,IAAI,KAAK,KAAK,IAAI,KAAK;EAEzB,OAAO;CACT;;;CAIA,kBAAgC;EAC9B,MAAM,SAAS,kBAAkB,KAAK,MAAM;EAC5C,IAAI,UAAU,CAAC,KAAK,aAClB,KAAU,gBAAgB;OACrB,IAAI,CAAC,UAAU,KAAK,aAAa;GACtC,KAAK,YAAY,QAAQ;GACzB,KAAK,cAAc,KAAA;GACnB,KAAK,oBAAoB,KAAA;GACzB,KAAK,wBAAwB;GAG7B,IAAI,KAAK,oBAAoB,GAAG;IAC9B,KAAK,kBAAkB;IACvB,KAAK,UAAU;GACjB;EACF;CACF;;;;;;;;;;;CAYA,MAAc,kBAAiC;EAC7C,IAAI,KAAK,oBAAoB;EAC7B,KAAK,qBAAqB;EAC1B,IAAI;GACF,MAAM,MAAM,MAAM,OAAO;GAEzB,IAAI,KAAK,YAAY,KAAK,eAAe,CAAC,kBAAkB,KAAK,MAAM,GAAG;GAC1E,KAAK,oBAAoB;GACzB,KAAK,cAAc,IAAI,IAAI,sBAAsB,KAAK,iBAAiB,KAAK,MAAM;GAClF,IAAI,KAAK,cAAc;IACrB,KAAK,MAAM,CAAC,MAAM,UAAU,KAAK,cAAc,KAAK,YAAY,SAAS,MAAM,KAAK;IACpF,KAAK,eAAe,KAAA;GACtB;GACA,KAAK,mBAAmB;EAC1B,QAAQ,CAER,UAAU;GACR,KAAK,qBAAqB;EAC5B;CACF;;;CAIA,qBAAqC,CAAC;;;;;CAMtC,mBAAiC;EAC/B,MAAM,KAAK,KAAK;EAChB,MAAM,MAAM,KAAK;EACjB,IAAI,CAAC,MAAM,CAAC,KAAK;EACjB,IAAI,KAAK,WAAW;GAClB,KAAK,qBAAqB,GAAG;GAC7B;EACF;EACA,IAAI,mBAAmB,KAAK,MAAM,GAAG,KAAK,kBAAkB,EAAE;EAC9D,KAAK,cAAc,IAAI,GAAG;CAC5B;;;;;;;CAQA,qBAA6B,KAA8B;EACzD,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,MAAM,QAAQ,KAAK;GAC1C,MAAM,KAAK,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,MAAM,KAAK,OAAO,MAAM,SAAS;GAChF,IAAI,CAAC,oBAAoB,KAAK,QAAQ,EAAE,GAAG;GAC3C,MAAM,IAAI,KAAK,MAAM,EAAE,CAAC,SAAS;GACjC,EAAE,eAAe,QAAQ;GACzB,MAAM,OAAO,EAAE,WAAW;GAC1B,KAAK,IAAI,IAAI,GAAG,IAAA,GAAkB,KAAK,KAAK,KAAK;EACnD;EAGA,MAAM,gBAAgB,KAAK,OAAO,aAAa;EAC/C,IAAI,eAAe;GACjB,KAAK,oBAAoB,aAAa,KAAK,OAAO,cAAc;GAChE,KAAK,oBAAoB,OAAO,KAAK,OAAO,cAAc;GAC1D,MAAM,YAAY;IAAE,MAAM,KAAK,SAAS;IAAU,KAAK,KAAK;GAAoB;GAChF,KAAK,MAAM,KAAK,eAAe;IAC7B,MAAM,UAAU,IAAI,eAAe,EAAE;IACrC,QAAQ,MAAM,QAAQ,KAAK,KAAK,MAAM,GAAG,SAAS;GACpD;EACF;EACA,IAAI,KAAK,0BAA0B,GAAG;GAGpC,KAAK,wBAAwB;GAC7B,KAAK,WAAW;EAClB;EAEA,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,MAAM,QAAQ,KAAK;GAC1C,MAAM,KAAK,KAAK,OAAO,MAAM;GAC7B,MAAM,WAAW,IAAI,aAAa;GAClC,IAAI,CAAC,MAAM,CAAC,YAAY,SAAS,WAAW,GAAG;GAC/C,MAAM,OAAO,KAAK,MAAM;GACxB,KAAK,MAAM,KAAK,UAAU;IACxB,MAAM,UAAU,IAAI,cAAc,EAAE;IACpC,QAAQ,MAAM,QAAQ,KAAK,EAAE,GAAG;KAAE,GAAG,KAAK,SAAS;KAAU,MAAM,KAAK;IAAK,CAAC;GAChF;EACF;CACF;;;;CAKA,kBAA0B,IAAiC;EACzD,MAAM,KAAK,KAAK,OAAO,QAAQ,KAAK,OAAO;EAC3C,MAAM,KAAK,KAAK,OAAO,MAAM,KAAK,OAAO;EACzC,MAAM,SAAS,OAAO,IAAI,KAAK,KAAK;EACpC,MAAM,UAAU,GAAG,OAAO,CAAC,CAAC;EAM5B,MAAM,eAAe,KAAK,IAAI,GAAG,KAAe,MAAe,GAAG,YAAY,CAAC;EAC/E,MAAM,MAAM,GAAG,gBAAgB;EAC/B,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,MAAM,QAAQ,KAAK;GAC1C,MAAM,KAAK,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,MAAM,KAAK,OAAO,MAAM,SAAS;GAChF,IAAI,CAAC,oBAAoB,KAAK,QAAQ,EAAE,GAAG;GAC3C,MAAM,IAAI,KAAK,MAAM,EAAE,CAAC,SAAS;GACjC,EAAE,gBAAgB,SAAS,GAAG,aAAa,OAAO,WAAW,KAAK;GAClE,EAAG,YAAY,MAAwB,KAAK,GAAG;GAC/C,MAAM,IAAI,GAAG,SAAS,CAAC;GACvB,IAAI,GAAG;IACL,EAAG,SAAS,MAAwB,KAAK,EAAE,GAAG;IAC9C,EAAE,eAAe,QAAQ,EAAE;GAC7B,OACE,EAAE,eAAe,QAAQ;GAE3B,EAAE,eAAe,QAAQ;GACzB,MAAM,UAAU,EAAE,cAAc;GAChC,MAAM,OAAO,EAAE,WAAW;GAC1B,MAAM,OAAO,EAAE,WAAW;GAC1B,KAAK,IAAI,IAAI,GAAG,IAAA,GAAkB,KAAK;IACrC,QAAQ,EAAE,CAAC,KAAK,QAAQ,EAAE,CAAC,MAAM;IACjC,KAAK,KAAK,QAAQ,EAAE,CAAC;IACrB,KAAK,KAAK,QAAQ,EAAE,CAAC;GACvB;EACF;CACF;;;CAIA,cAAsB,IAA2B,KAA8B;EAG7E,KAAK,oBAAoB,aAAa,KAAK,OAAO,cAAc;EAChE,KAAK,oBAAoB,OAAO,KAAK,OAAO,cAAc;EAC1D,MAAM,YAAY;GAAE,MAAM,KAAK,SAAS;GAAU,KAAK,KAAK;EAAoB;EAChF,KAAK,MAAM,KAAK,KAAK,OAAO,aAAa,YAAY,CAAC,GAAG;GACvD,MAAM,UAAU,IAAI,eAAe,EAAE;GACrC,MAAM,QAAQ,MAAM,UAAU,KAC5B,EAAE,QAAQ,QAAQ,KAAK,KAAK,MAAM,GAClC,EAAE,IACF,GAAG,aAAa,CAAC,CACnB;GACA,QAAQ,MAAM,OAAO,SAAS;EAChC;EAEA,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,MAAM,QAAQ,KAAK;GAC1C,MAAM,KAAK,KAAK,OAAO,MAAM;GAC7B,MAAM,WAAW,IAAI,aAAa;GAClC,IAAI,CAAC,MAAM,CAAC,YAAY,SAAS,WAAW,GAAG;GAC/C,MAAM,OAAO,KAAK,MAAM;GACxB,KAAK,MAAM,KAAK,UAAU;IACxB,MAAM,UAAU,IAAI,cAAc,EAAE;IACpC,MAAM,QAAQ,MAAM,UAAU,KAAK,EAAE,QAAQ,QAAQ,KAAK,EAAE,GAAG,EAAE,IAAI,GAAG,aAAa,CAAC,CAAC;IACvF,QAAQ,MAAM,OAAO;KAAE,GAAG,KAAK,SAAS;KAAU,MAAM,KAAK;IAAK,CAAC;GACrE;EACF;EAEA,KAAK,wBACH,KAAK,oBAAoB,cAAc,KAAK,OAAO,cAAc;EAMnE,MAAM,WAAW,KAAK,oBAAoB,QAAQ,KAAK,OAAO,cAAc;EAC5E,IAAI,aAAa,KAAK,iBAAiB;GACrC,KAAK,kBAAkB;GACvB,KAAK,UAAU;EACjB;CACF;;;;;CAMA,oBAAoB,MAAc,OAAqB;EACrD,IAAI,KAAK,aAAa;GACpB,KAAK,YAAY,SAAS,MAAM,KAAK;GACrC;EACF;EACA,IAAI,OAAO,SAAS,YAAY,CAAC,OAAO,SAAS,KAAK,GAAG;EACzD,IAAI,CAAC,kBAAkB,KAAK,MAAM,GAAG;EACrC,CAAC,KAAK,iCAAiB,IAAI,IAAI,EAAA,CAAG,IAAI,MAAM,KAAK;CACnD;;;;;;;;;;;;CAaA,aAA+B;EAM7B,IAAI,CAAC,KAAK,aAAa;GACrB,IAAI,kBAAkB,KAAK,MAAM,GAAG,KAAU,gBAAgB;GAC9D,OAAO,QAAQ,QAAQ,KAAK;EAC9B;EACA,OAAO,KAAK,YAAY,WAAW;CACrC;;CAGA,aAAyB;EACvB,IAAI,KAAK,aAAa,OAAO,KAAK,YAAY,WAAW;EAIzD,OADE,OAAO,WAAW,eAAe,OAAO,OAAO,2BAA2B,cACzD,WAAW;CAChC;;CAGA,eAAqB;EACnB,KAAK,aAAa,aAAa;CACjC;;CAGA,kBAAwB;EACtB,KAAK,cAAc;CACrB;;;;;CAMA,wBAAwC;EACtC,MAAM,IAAI,KAAK,OAAO;EACtB,MAAM,KAAK,KAAK,OAAO;EACvB,KAAK,OAAO,SAAS,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;EACtC,KAAK,OAAO,GAAG,IAAI,GAAG,GAAG,CAAC;EAC1B,KAAK,OAAO,OAAO,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;EACnC,KAAK,UAAU;EACf,IAAI,CAAC,KAAK,SAAS,KAAK,WAAW;CACrC;;CAKA,2BAA8C;EAC5C,OAAO;CACT;;CAGA,iBAAiC,CAAC;;CAGlC,qBAAqC,CAAC;;CAGtC,gBAAgC,CAAC;;;;CAKjC,YAA4B;EAC1B,MAAM,KAAK,KAAK,OAAO,QAAQ,KAAK,OAAO;EAC3C,MAAM,KAAK,KAAK,OAAO,MAAM,KAAK,OAAO;EAIzC,KAAK,OAAO,OACV,KAAK,cAAc,IAAI,EAAE,KAAK,KAAK,OAAO,cAAc,KAAK,KAAK;EACpE,KAAK,OAAO,uBAAuB;CACrC;;;CAIA,cAAwB,IAAY,IAAoB;EACtD,OAAO,UACL,IACA,IACA,KAAK,OAAO,aAAa,SACzB,KAAK,OAAO,yBAAyB,CACvC;CACF;;;;;;;;;;;;;;;;CAiBA,mBAAiC;EAC/B,KAAK,QAAQ,UAAU;EACvB,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,MAAM,QAAQ,KAAK;GAC1C,MAAM,OAAO,KAAK,MAAM;GACxB,MAAM,OAAO,KAAK;GAClB,KAAK,kBAAkB,MAAM,KAAK;GAClC,MAAM,KAAK,KAAK,SAAS;GACzB,IAAI,CAAC,IAAI;GAET,MAAM,SAAS,KAAK,SAAS,sBAAsB,KAAK,WAAW;GACnE,MAAM,OAAO,KAAK,IAAI,OAAO,KAAK,SAAS,SAAS,YAAY,KAAK,KAAK,CAAC;GAI3E,MAAM,KAAK,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,MAAM,KAAK,OAAO,MAAM,SAAS;GAChF,IAAI,cAAc;GAClB,IAAI,oBAAoB,KAAK,QAAQ,EAAE,GAAG;IACxC,MAAM,IAAI,GAAG,aAAa;IAC1B,eACG,GAAG,WAAW,KACf,KAAK,IAAI,GAAG,QAAQ,CAAC,KACpB,GAAG,QAAQ,MACX,GAAG,aAAa,OAAO,UAAU;GACtC;GACA,MAAM,cAAc,GAAG,OAAO,OAAO,IAAI,GAAG,SAAS,OAAO;GAI5D,MAAM,QAAQ,KAAK,gBAAgB,KAAK,IAAI,GAAG,WAAW,SAAS,CAAC,IAAI;GACxE,MAAM,SAAS,cAAc,KAAK,YAAY,kBAAkB,IAAI,MAAM;GAE1E,KAAK,QAAQ,cAAc,KAAK,SAAS,KAAK,MAAM,CAAC,CAAC,UAAU,MAAM,CAAC;GACvE,KAAK,QAAQ,cAAc,KAAK,SAAS,KAAK,MAAM,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC;EAC1E;EACA,IAAI,KAAK,QAAQ,QAAQ,GAAG;EAC5B,KAAK,QAAQ,kBAAkB,KAAK,UAAU;EAC9C,MAAM,UAAU,KAAK,OAAO,kBAAkB,KAAK,QAAQ;EAC3D,MAAM,cAAc,KAAK,SACtB,KAAK,KAAK,WAAW,MAAM,CAAC,CAC5B,IAAI,KAAK,OAAO,QAAQ,CAAC,CACzB,IAAI,OAAO;EACd,MAAM,SAAS,KAAK,WAAW;EAC/B,MAAM,SAAS,SAAS,MAAO;EAE/B,KAAK,OAAO,OAAO,cAAc,SAAS;EAC1C,KAAK,OAAO,MAAM,cAAc,SAAS;EACzC,KAAK,OAAO,uBAAuB;CACrC;;;;;;CAQA,MAAM,aACJ,MACA,cAAc,MACd,SACA,MACe;EACf,MAAM,SAAS,KAAK,OAAO;EAC3B,IAAI,gBAAgB,QAAQ;GAC1B,KAAK,OAAO,wBAAwB;GACpC,KAAK,gBAAgB;EACvB;EAIA,MAAM,UAAU,SAAS,KAAA;EACzB,MAAM,WAAW,KAAK;EACtB,MAAM,WAAW,KAAK;EACtB,IAAI,SAAS;GACX,KAAK,OAAO;GACZ,KAAK,gBAAgB;EACvB;EACA,IAAI,OAAoB;EAIxB,MAAM,WAAW,KAAK,OAAO;EAC7B,MAAM,gBAAgB,KAAK,oBAAoB;EAC/C,IAAI;GACF,KAAK,YAAY;GACjB,IAAI,eAAe;IACjB,KAAK,OAAO,OAAO,WAAW,KAAK;IACnC,KAAK,OAAO,uBAAuB;GACrC;GACA,KAAK,WAAW;GAChB,OAAO,MAAM,IAAI,SAAsB,YACrC,KAAK,OAAO,OAAO,SAAS,MAAM,OAAO,CAC3C;EACF,UAAU;GACR,KAAK,YAAY;GACjB,IAAI,eAAe;IACjB,KAAK,OAAO,OAAO;IACnB,KAAK,OAAO,uBAAuB;GACrC;GACA,IAAI,gBAAgB,QAAQ;IAC1B,KAAK,OAAO,wBAAwB;IACpC,KAAK,gBAAgB;GACvB;GACA,IAAI,SAAS;IACX,KAAK,OAAO;IACZ,KAAK,gBAAgB;GACvB;GACA,KAAK,WAAW;EAClB;EACA,IAAI,CAAC,QAAQ,KAAK,SAAS,MAAM,MAAM,IAAI,MAAM,qBAAqB,MAAM;EAC5E,OAAO;CACT;CAEA,cAAc,MAAM,IAAiB;EACnC,OAAO,KAAK,OAAO,cAAc,GAAG;CACtC;CAEA,IAAI,SAA4B;EAC9B,OAAO,KAAK,SAAS;CACvB;CAEA,YAA0B;EACxB,OAAO,KAAK;CACd;CAEA,UAAU,QAA4B;EACpC,MAAM,OAAO,mBAAmB,MAAM;EACtC,MAAM,aACJ,KAAK,MAAM,WAAW,KAAK,MAAM,UAAU,KAAK,YAAY,KAAK,OAAO;EAC1E,KAAK,SAAS;EACd,IAAI,YAAY,KAAK,QAAQ;OACxB,KAAK,QAAQ;EAIlB,KAAK,sBAAsB;CAC7B;CAEA,UAAgB;EACd,KAAK,WAAW;EAChB,qBAAqB,KAAK,KAAK;EAC/B,qBAAqB,KAAK,SAAS;EACnC,KAAK,UAAU;EACf,KAAK,aAAa,QAAQ;EAC1B,KAAK,cAAc,KAAA;EACnB,KAAK,oBAAoB,KAAA;EACzB,KAAK,eAAe,WAAW;EAC/B,KAAK,qBAAqB,WAAW;EACrC,KAAK,YAAY,oBAAoB,UAAU,KAAK,cAAc;EAClE,KAAK,UAAU,oBAAoB,UAAU,KAAK,WAAW;EAC7D,SAAS,oBAAoB,oBAAoB,KAAK,kBAAkB;EACxE,KAAK,SAAS,WAAW,oBAAoB,oBAAoB,KAAK,aAAa;EACnF,KAAK,SAAS,WAAW,oBAAoB,wBAAwB,KAAK,iBAAiB;EAC3F,KAAK,qBAAqB;EAC1B,KAAK,mBAAmB,QAAQ;EAChC,KAAK,MAAM,KAAK,KAAK,OAAO;GAC1B,EAAE,SAAS,QAAQ;GACnB,EAAE,eAAe,QAAQ;GACzB,EAAE,SAAS,QAAQ;GACnB,EAAE,QAAQ,QAAQ;GAClB,EAAE,eAAe,QAAQ;GACzB,EAAE,aAAa,QAAQ;EACzB;EACA,KAAK,WAAW,QAAQ;EACxB,KAAK,YAAY,QAAQ;EACzB,KAAK,gBAAgB,QAAQ;EAC7B,KAAK,cAAc,QAAQ;EAC3B,KAAK,aAAa,QAAQ;EAC1B,KAAK,kBAAkB,QAAQ;EAC/B,KAAK,kBAAkB,QAAQ;EAC/B,KAAK,YAAY;EACjB,KAAK,SAAS,QAAQ;EACtB,KAAK,SAAS,WAAW,OAAO;CAClC;AACF"}