asciify-engine 4.20.0 → 4.22.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 (40) hide show
  1. package/README.md +14 -0
  2. package/dist/{animation-presets-ZujFF3i8.d.ts → animation-presets-CEdD0FMM.d.cts} +42 -28
  3. package/dist/{animation-presets-DhswE0D_.d.cts → animation-presets-KnEWoJ_q.d.ts} +42 -28
  4. package/dist/core.cjs +1 -1
  5. package/dist/core.d.cts +4 -4
  6. package/dist/core.d.ts +4 -4
  7. package/dist/core.js +1 -1
  8. package/dist/field.cjs +1 -1
  9. package/dist/field.d.cts +3 -1
  10. package/dist/field.d.ts +3 -1
  11. package/dist/field.js +1 -1
  12. package/dist/hover-guide-CAfsLjFZ.d.cts +190 -0
  13. package/dist/hover-guide-CAfsLjFZ.d.ts +190 -0
  14. package/dist/hover.cjs +1 -1
  15. package/dist/hover.d.cts +14 -7
  16. package/dist/hover.d.ts +14 -7
  17. package/dist/hover.js +1 -1
  18. package/dist/index.cjs +1 -1
  19. package/dist/index.d.cts +3 -3
  20. package/dist/index.d.ts +3 -3
  21. package/dist/index.js +1 -1
  22. package/dist/{model-9SbRVqqI.d.cts → model-BHIQYYk-.d.ts} +11 -2
  23. package/dist/{model-DXHhQhxP.d.ts → model-BzgEEqzP.d.cts} +11 -2
  24. package/dist/studio.cjs +1 -1
  25. package/dist/studio.d.cts +4 -5
  26. package/dist/studio.d.ts +4 -5
  27. package/dist/studio.js +1 -1
  28. package/dist/{types-CM4452nD.d.ts → types-C9lka3yC.d.ts} +2 -2
  29. package/dist/{types-FkqhgDYx.d.cts → types-DOl5f2x3.d.cts} +2 -2
  30. package/package.json +1 -1
  31. package/skills/asciify-engine/SKILL.md +3 -3
  32. package/skills/asciify-engine/references/design-matching.md +2 -1
  33. package/skills/asciify-engine/references/glyph-field.md +1 -1
  34. package/skills/asciify-engine/references/hero-finish.md +3 -0
  35. package/skills/asciify-engine/references/looks.md +55 -55
  36. package/skills/asciify-engine/references/studio-effects.md +73 -24
  37. package/skills/asciify-engine/references/studio-workspace.md +6 -7
  38. package/skills/asciify-engine/references/upgrading.md +6 -0
  39. package/dist/hover-catalog-D85jh6dR.d.cts +0 -173
  40. package/dist/hover-catalog-D85jh6dR.d.ts +0 -173
@@ -0,0 +1,190 @@
1
+ type AmbientMotion = 'none' | 'relight' | 'sheen' | 'glow' | 'shimmer' | 'parallax' | 'push' | 'focus' | 'mist' | 'current' | 'develop';
2
+ /** Retired after 4.21: each still loads, as its closest photo-aware replacement. */
3
+ type LegacyAmbientMotion = 'caustics' | 'grain' | 'trace' | 'tidal' | 'weave' | 'breeze' | 'reform' | 'print' | 'unfold';
4
+ declare const DEPRECATED_MOTIONS: Readonly<Record<LegacyAmbientMotion, AmbientMotion>>;
5
+ declare const MOTION_STYLES: readonly [{
6
+ readonly value: "none";
7
+ readonly mode: "none";
8
+ readonly group: "Still";
9
+ readonly label: "Off";
10
+ readonly description: "Keep the image still. Hover stays available.";
11
+ }, {
12
+ readonly value: "relight";
13
+ readonly mode: "relight";
14
+ readonly group: "Light";
15
+ readonly label: "Relight";
16
+ readonly description: "A key light circles the subject and models its own relief, like moving a lamp around a sculpture.";
17
+ }, {
18
+ readonly value: "sheen";
19
+ readonly mode: "sheen";
20
+ readonly group: "Light";
21
+ readonly label: "Specular sweep";
22
+ readonly description: "One slow pass of light glances across the highlights and follows their form, then rests.";
23
+ }, {
24
+ readonly value: "glow";
25
+ readonly mode: "glow";
26
+ readonly group: "Light";
27
+ readonly label: "Highlight bloom";
28
+ readonly description: "The brightest areas breathe a soft halo into their surroundings. Shadows stay deep.";
29
+ }, {
30
+ readonly value: "shimmer";
31
+ readonly mode: "shimmer";
32
+ readonly group: "Light";
33
+ readonly label: "Glints";
34
+ readonly description: "Sparse glints on bright fine detail, like light on water, metal or an eye. Everything else holds still.";
35
+ }, {
36
+ readonly value: "parallax";
37
+ readonly mode: "parallax";
38
+ readonly group: "Depth";
39
+ readonly label: "Parallax";
40
+ readonly description: "A slow camera drift: the subject moves against its background, read from the photo’s own depth cues.";
41
+ }, {
42
+ readonly value: "push";
43
+ readonly mode: "push";
44
+ readonly group: "Depth";
45
+ readonly label: "Slow push";
46
+ readonly description: "A gentle push toward the subject and back, with nearer areas moving slightly more.";
47
+ }, {
48
+ readonly value: "focus";
49
+ readonly mode: "focus";
50
+ readonly group: "Depth";
51
+ readonly label: "Focus pull";
52
+ readonly description: "Focus racks from the subject to the background and back; the characters soften out of focus.";
53
+ }, {
54
+ readonly value: "mist";
55
+ readonly mode: "mist";
56
+ readonly group: "Atmosphere";
57
+ readonly label: "Mist";
58
+ readonly description: "Soft mist drifts through the distance and plain areas, leaving the subject clear.";
59
+ }, {
60
+ readonly value: "current";
61
+ readonly mode: "current";
62
+ readonly group: "Atmosphere";
63
+ readonly label: "Drift";
64
+ readonly description: "Sky, water and smooth grounds flow slowly, like a cinemagraph; detailed areas hold still.";
65
+ }, {
66
+ readonly value: "develop";
67
+ readonly mode: "develop";
68
+ readonly group: "Reveal";
69
+ readonly label: "Develop";
70
+ readonly description: "The image holds, fades, then develops back in from its highlights to its shadows, like a print in the tray.";
71
+ }];
72
+
73
+ /** One public catalog for the hero, studio, documentation and integrations. */
74
+ declare const SURFACE_HOVERS: readonly [{
75
+ readonly value: "none";
76
+ readonly label: "Off";
77
+ readonly group: "Still";
78
+ readonly description: "Keep the artwork still under the pointer.";
79
+ }, {
80
+ readonly value: "trail";
81
+ readonly label: "Trail";
82
+ readonly group: "Change characters";
83
+ readonly description: "A fluid wake changes character density and settles behind your cursor.";
84
+ }, {
85
+ readonly value: "dissolve";
86
+ readonly label: "Dissolve";
87
+ readonly group: "Change characters";
88
+ readonly description: "Erase a soft path through the characters; the image gradually reforms.";
89
+ }, {
90
+ readonly value: "contour";
91
+ readonly label: "Contour";
92
+ readonly group: "Change characters";
93
+ readonly description: "Fine tonal rings trace your path without moving the grid.";
94
+ }, {
95
+ readonly value: "etch";
96
+ readonly label: "Emboss";
97
+ readonly group: "Change characters";
98
+ readonly description: "Press a light-and-shadow impression into the characters; the grid stays fixed.";
99
+ }, {
100
+ readonly value: "ripple";
101
+ readonly label: "Ripple rings";
102
+ readonly group: "Change characters";
103
+ readonly description: "Expanding light-and-shadow rings follow your stroke. Faster movement excites stronger rings; the grid stays still.";
104
+ }, {
105
+ readonly value: "bleed";
106
+ readonly label: "Ink bleed";
107
+ readonly group: "Change characters";
108
+ readonly description: "Ink wells up where the cursor rests, spreads along the paper grain, then dries back.";
109
+ }, {
110
+ readonly value: "water";
111
+ readonly label: "Water";
112
+ readonly group: "Move the image";
113
+ readonly description: "Soft ripples refract the image and settle after your stroke.";
114
+ }, {
115
+ readonly value: "silk";
116
+ readonly label: "Silk";
117
+ readonly group: "Move the image";
118
+ readonly description: "Directional folds follow the movement of your hand.";
119
+ }, {
120
+ readonly value: "vortex";
121
+ readonly label: "Vortex";
122
+ readonly group: "Move the image";
123
+ readonly description: "A swirling wake curls through the image.";
124
+ }, {
125
+ readonly value: "magnetic";
126
+ readonly label: "Magnetic Pull";
127
+ readonly group: "Move the image";
128
+ readonly description: "The image gathers toward your stroke, then relaxes.";
129
+ }, {
130
+ readonly value: "scatter";
131
+ readonly label: "Scatter";
132
+ readonly group: "Move the image";
133
+ readonly description: "Characters scatter in soft drifts from your stroke, then spring back into place.";
134
+ }, {
135
+ readonly value: "elastic";
136
+ readonly label: "Elastic";
137
+ readonly group: "Move the image";
138
+ readonly description: "Push the image with your stroke; it recoils past its rest once, then settles.";
139
+ }, {
140
+ readonly value: "lens";
141
+ readonly label: "Lens wake";
142
+ readonly group: "Move the image";
143
+ readonly description: "A soft magnifying wake brings details closer, then relaxes back into focus.";
144
+ }, {
145
+ readonly value: "smudge";
146
+ readonly label: "Smudge";
147
+ readonly group: "Move the image";
148
+ readonly description: "Drag the image in the direction of your stroke; a smooth, lingering pull settles without a bounce.";
149
+ }, {
150
+ readonly value: "loupe";
151
+ readonly label: "Loupe";
152
+ readonly group: "Follow the cursor";
153
+ readonly description: "A glass loupe glides after the cursor and magnifies what it covers, easing through a soft rim.";
154
+ }, {
155
+ readonly value: "reveal";
156
+ readonly label: "Reveal";
157
+ readonly group: "Follow the cursor";
158
+ readonly description: "A soft spotlight shows the original photograph through the characters.";
159
+ }, {
160
+ readonly value: "repel";
161
+ readonly label: "Repel";
162
+ readonly group: "Follow the cursor";
163
+ readonly description: "Characters part around the cursor and spring back with one soft rebound when it leaves.";
164
+ }, {
165
+ readonly value: "tilt";
166
+ readonly label: "Tilt";
167
+ readonly group: "Follow the cursor";
168
+ readonly description: "The image leans toward the cursor; bright areas sit nearer and move more, like a parallax card.";
169
+ }, {
170
+ readonly value: "haze";
171
+ readonly label: "Heat haze";
172
+ readonly group: "Follow the cursor";
173
+ readonly description: "A rising shimmer bends the characters above the cursor, livelier while you move.";
174
+ }];
175
+ /** Retired hover IDs and the effect each one now plays. Saved settings and old integrations keep working. */
176
+ declare const DEPRECATED_SURFACE_HOVERS: {
177
+ readonly rake: "silk";
178
+ };
179
+ type DeprecatedSurfaceHover = keyof typeof DEPRECATED_SURFACE_HOVERS;
180
+ type SurfaceHover = typeof SURFACE_HOVERS[number]['value'];
181
+
182
+ /** Where a hover may act. `all` is the whole artwork; `subject` keeps it on the subject; `outside` keeps it off. */
183
+ type HoverScope = 'all' | 'subject' | 'outside';
184
+ declare const HOVER_SCOPES: readonly {
185
+ value: HoverScope;
186
+ label: string;
187
+ description: string;
188
+ }[];
189
+
190
+ export { type AmbientMotion as A, type DeprecatedSurfaceHover as D, type HoverScope as H, type LegacyAmbientMotion as L, MOTION_STYLES as M, type SurfaceHover as S, DEPRECATED_SURFACE_HOVERS as a, DEPRECATED_MOTIONS as b, HOVER_SCOPES as c, SURFACE_HOVERS as d };
package/dist/hover.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";function t(t,e,i=()=>{}){const s=Array.isArray(e)?e:[e],a=document.createElement("canvas"),n=a.getContext("2d");let o,r=!0,h=!1,l=0;const c=()=>{h||(r=!0,i())},d=new ResizeObserver(c);d.observe(t),s.forEach(t=>d.observe(t));const m=new MutationObserver(c);return s.forEach(t=>m.observe(t,{subtree:!0,childList:!0,characterData:!0,attributes:!0})),document.fonts?.addEventListener("loadingdone",c),document.fonts?.ready.then(c),window.addEventListener("resize",c),{invalidate:c,read(){if(h)return;if(!r)return o;r=!1;const e=t.getBoundingClientRect(),i=Math.min(devicePixelRatio||1,1.5,Math.sqrt(4e6/Math.max(1,e.width*e.height)));a.width=Math.max(1,Math.round(e.width*i)),a.height=Math.max(1,Math.round(e.height*i)),n.setTransform(i,0,0,i,0,0),n.fillStyle="#fff",n.textBaseline="alphabetic";for(const t of s){const i=document.createTreeWalker(t,NodeFilter.SHOW_TEXT);for(let t=i.nextNode();t;t=i.nextNode()){const i=t.parentElement;if(!i)continue;const s=getComputedStyle(i);if("hidden"===s.visibility||"none"===s.display)continue;n.font=`${s.fontStyle} ${s.fontWeight} ${s.fontSize} ${s.fontFamily}`;const a=n.measureText("Hg"),o=a.fontBoundingBoxAscent??.8*parseFloat(s.fontSize),r=a.fontBoundingBoxDescent??.2*parseFloat(s.fontSize),h=t.textContent??"",l=document.createRange();let c=0;for(const i of h){if(l.setStart(t,c),c+=i.length,l.setEnd(t,c),/\s/.test(i))continue;const s=l.getBoundingClientRect();s.width&&s.height&&n.fillText(i,s.left-e.left,s.top-e.top+(s.height-o-r)/2+o)}}}return o={canvas:a,revision:++l},o},destroy(){h=!0,d.disconnect(),m.disconnect(),document.fonts?.removeEventListener("loadingdone",c),window.removeEventListener("resize",c),a.width=a.height=1,o=void 0}}}var e=[{value:"none",mode:"none",group:"Still",label:"Off",description:"Keep the image still. Hover stays available."},{value:"caustics",mode:"caustics",group:"Light and texture",label:"Caustics",description:"Soft ribbons of light travel across a stationary character grid."},{value:"current",mode:"current",group:"Flow and depth",label:"Slow Current",description:"A gentle flow through the image, with steady edges."},{value:"reform",mode:"reform",group:"Rebuild",label:"Reveal & Reform",description:"Disperse, gather, then hold the complete image."},{value:"sheen",mode:"sheen",group:"Light and texture",label:"Sheen",description:"A broad diagonal light sweep; characters stay anchored."},{value:"tidal",mode:"tidal",group:"Flow and depth",label:"Tidal Rings",description:"Concentric currents travel outward, with protected edges."},{value:"grain",mode:"grain",group:"Light and texture",label:"Living Grain",description:"Slow, individually phased tonal changes on a stationary grid."},{value:"parallax",mode:"parallax",group:"Flow and depth",label:"Parallax",description:"A shallow, continuous drift through the image, with the edges held in place."},{value:"weave",mode:"weave",group:"Flow and depth",label:"Woven Flow",description:"Interlaced bands move in opposing directions, like a flexible printed fabric."},{value:"print",mode:"print",group:"Rebuild",label:"Print Shift",description:"Staggered rows lift, soften and register back into the image."},{value:"trace",mode:"trace",group:"Light and texture",label:"Contour Light",description:"Light follows the source image’s tonal contours; every character stays anchored."},{value:"relight",mode:"relight",group:"Light and texture",label:"Relight",description:"A slow orbit of light models the image’s midtones, while the character grid stays fixed."},{value:"shimmer",mode:"shimmer",group:"Light and texture",label:"Shimmer",description:"Small highlights glint at different moments. Shadows stay quiet and the image stays in place."},{value:"breeze",mode:"breeze",group:"Flow and depth",label:"Breeze",description:"A traveling bend gently sways the image, like a print suspended in moving air."},{value:"unfold",mode:"unfold",group:"Rebuild",label:"Unfold",description:"Narrow panels turn, soften and return to a complete image, with a readable hold between cycles."}];function i(t){return e.find(e=>e.value===t||e.mode===t)?.mode??"none"}var s=t=>Math.max(0,Math.min(1,t)),a=(t,e,i)=>{const a=s((i-t)/(e-t));return a*a*(3-2*a)},n=(t,e)=>{const i=(.06711056*t+.00583715*e)%1*52.9829189;return i-Math.floor(i)},o=class{constructor(){this.last=null,this.mode="none",this.phase=0}update(t,e,i=1){return Number.isFinite(t)?((e!==this.mode||null!==this.last&&t<this.last)&&(this.mode=e,this.phase=0,this.last=t),null!==this.last&&(this.phase+=Math.max(0,t-this.last)*Math.max(.1,Math.min(3,Number.isFinite(i)?i:1))),this.last=t,this.phase):this.phase}};function r(t,e,i,o,r,h=.5){if(r[0]=r[1]=r[2]=0,r[3]=1,"none"===t)return r;const l=o*Math.PI/6,c=a(0,.12,Math.min(e,1-e,i,1-i));if("caustics"===t){const t=Math.sin(9*e+5*i+l)+Math.sin(8*i-3*e-l),s=Math.pow(Math.max(0,1-.72*Math.abs(t)),3);r[2]=(.22*s-.045)*c}else if("current"===t)r[0]=Math.sin(9*i+l)*Math.cos(7*e-l)*10*c,r[1]=Math.cos(8*e+l)*Math.sin(6*i-l)*7*c;else if("sheen"===t)r[2]=.2*Math.pow(.5+.5*Math.cos(5*e+3*i-l),8)-.025;else if("tidal"===t){const t=e-.5,s=i-.5,a=Math.hypot(t,s),n=Math.sin(22*a-l)*c;r[0]=t*n*9,r[1]=s*n*9,r[2]=.035*n}else if("grain"===t){const t=n(Math.floor(160*e),Math.floor(100*i))*Math.PI*2;r[2]=.07*Math.sin(l+t)}else if("parallax"===t){const t=.45+.55*Math.max(0,1-1.4*Math.hypot(e-.5,i-.5));r[0]=12*Math.sin(l)*t*c,r[1]=6*(Math.cos(l)-1)*t*c}else if("weave"===t)r[0]=Math.sin(i*Math.PI*6+l)*Math.sin(l)*8*c,r[1]=Math.sin(e*Math.PI*6-l)*Math.sin(l)*6*c;else if("print"===t){const t=Math.floor(24*i),e=((o+3*n(t,3))%12+12)%12,s=a(1,2.5,e)*(1-a(3.5,5,e));r[0]=Math.sin(2.4*t)*s*9*c,r[1]=5*-s*c,r[2]=.22*-s,r[3]=1-.45*s}else if("trace"===t){const t=.5+.5*Math.cos(l);r[2]=.22*Math.pow(Math.max(0,1-Math.abs(s(h)-t)/.16),3)-.015}else if("relight"===t){const t=i-.5,a=1.3*((e-.5)*Math.cos(l)+t*Math.sin(l));r[2]=.3*a*(.25+3*s(h)*(1-s(h)))*c}else if("shimmer"===t){const t=n(Math.floor(160*e),Math.floor(100*i))*Math.PI*2,s=Math.pow(.5+.5*Math.cos(l+t),10);r[2]=.28*(s-.1762)*a(.35,.9,h)}else if("breeze"===t){const t=Math.sin(l-4*i),s=.35+.65*(1-i);r[0]=10*t*s*c,r[1]=2.5*Math.sin(2*l-4*i+3*e)*s*c,r[2]=.035*Math.cos(l-4*i)*c}else if("unfold"===t){const t=Math.floor(12*e),i=12*e-t-.5,s=((o-.11*t)%12+12)%12,n=a(1,3,s)*(1-a(5,7,s));r[0]=-i*n*24*c,r[1]=Math.sin(1.7*t)*n*3*c,r[2]=.12*-n,r[3]=1-.65*n}else if("reform"===t){const t=(o%12+12)%12,s=n(Math.floor(160*e),Math.floor(100*i)),h=.65*e+.2*i+.15*s+.08*Math.sin(8*e-5*i),d=a(1.8+1.2*h,3.8+1.2*h,t)*(1-a(5+1.7*h,7.2+1.7*h,t));r[0]=Math.sin(9*i+l)*d*(1-d)*24*c,r[1]=-d*(1-d)*(8+12*s)*c,r[2]=.45*-d,r[3]=1-.9*d}return r}function h(t,e,i){const s=Math.min(t*Math.max(1,i),(1-t)*Math.max(1,i),e*Math.max(1,1/i),(1-e)*Math.max(1,1/i)),a=Math.max(0,Math.min(1,(s-.035)/.105));return a*a*a*(a*(6*a-15)+10)}var l=class{constructor(t,e){this.edgeSafe=!1,this.rings=new Float64Array(40),this.slot=0,this.clock=0,this.lastRing=-1,this.previous=null,this.alive=!1,this.width=t,this.height=e,this.pixels=new Uint8Array(t*e*4),this.values=new Float32Array(t*e),this.clear()}get active(){return this.alive}clear(){this.rings.fill(0),this.values.fill(0),this.pixels.fill(0);for(let t=0;t<this.pixels.length;t+=4)this.pixels[t]=128;this.previous=null,this.alive=!1,this.slot=0,this.lastRing=-1,this.clock=0}leave(){this.previous=null}move(t,e,i,s){if(!Number.isFinite(t+e+i))return;t=Math.max(0,Math.min(1,t)),e=Math.max(0,Math.min(1,e));const a=Number.isFinite(s)?s/1e3:this.clock,n=this.previous;if(this.previous={x:t,y:e,time:a},!n)return;const o=this.width/this.height,r=Math.hypot((t-n.x)*Math.max(1,o),(e-n.y)*Math.max(1,1/o));if(r<1e-4||this.clock-this.lastRing<1/24)return;const h=r/Math.max(1/240,Math.min(.1,a-n.time||1/60)),l=5*this.slot;this.slot=(this.slot+1)%8,this.rings[l]=t,this.rings[l+1]=e,this.rings[l+2]=0,this.rings[l+3]=Math.min(.82,.14+.16*h),this.rings[l+4]=.035+.13*Math.max(.1,Math.min(1,i)),this.alive=!0,this.lastRing=this.clock}step(t){if(!Number.isFinite(t)||t<=0)return;const e=Math.min(.05,t);if(this.clock+=e,!this.alive)return;this.values.fill(0),this.alive=!1;const i=this.width/this.height,s=Math.max(1,i),a=Math.max(1,1/i);for(let t=0;t<this.rings.length;t+=5){if(!this.rings[t+3])continue;const i=this.rings[t+2]+=e;if(i>2.8){this.rings[t+3]=0;continue}this.alive=!0;const n=this.rings[t+4],o=.17*i,r=this.rings[t+3]*(1-Math.exp(20*-i))*Math.exp(2.5*-i),h=o+1.8*n,l=this.rings[t],c=this.rings[t+1],d=Math.max(0,Math.floor((l-h/s)*(this.width-1))),m=Math.min(this.width-1,Math.ceil((l+h/s)*(this.width-1))),u=Math.max(0,Math.floor((c-h/a)*(this.height-1))),f=Math.min(this.height-1,Math.ceil((c+h/a)*(this.height-1)));for(let t=u;t<=f;t++)for(let e=d;e<=m;e++){const i=(Math.hypot((e/(this.width-1)-l)*s,(t/(this.height-1)-c)*a)-o)/n;Math.abs(i)>1.8||(this.values[t*this.width+e]+=r*Math.cos(i*Math.PI*2)*Math.exp(-i*i*2.5))}}for(let t=0;t<this.height;t++)for(let e=0;e<this.width;e++){const s=t*this.width+e,a=this.values[s]=Math.max(-1,Math.min(1,this.values[s]))*(this.edgeSafe?h(e/(this.width-1),t/(this.height-1),i):1),n=Math.round(32767*a+32768);this.pixels[4*s]=n>>>8,this.pixels[4*s+1]=255&n}}sample(t,e,i){const s=Math.max(0,Math.min(this.width-1.001,t*(this.width-1))),a=Math.max(0,Math.min(this.height-1.001,e*(this.height-1))),n=Math.floor(s),o=Math.floor(a),r=s-n,h=a-o,l=o*this.width+n,c=l+this.width;i[0]=i[1]=0,i[2]=3*((this.values[l]*(1-r)+this.values[l+1]*r)*(1-h)+(this.values[c]*(1-r)+this.values[c+1]*r)*h)}},c=t=>["dissolve","silk","vortex","magnetic","scatter","etch","elastic","rake","lens","smudge"].includes(t),d=class{constructor(t,e){this.edgeSafe=!1,this.previous=null,this.peak=0,this.scatterDirections=null,this.springX=null,this.springY=null,this.mode="dissolve",this.width=t,this.height=e;const i=t*e;this.pixels=new Uint8Array(4*i),this.ink=new Float32Array(i),this.targetX=new Float32Array(i),this.targetY=new Float32Array(i),this.offsetX=new Float32Array(i),this.offsetY=new Float32Array(i),this.edge=new Float32Array(i);for(let i=0;i<e;i++)for(let s=0;s<t;s++)this.edge[i*t+s]=h(s/(t-1),i/(e-1),t/e);this.clear()}get active(){return this.peak>2e-4}setMode(t){if("elastic"!==t||this.springX||(this.springX=new Float32Array(this.ink.length),this.springY=new Float32Array(this.ink.length)),"scatter"===t&&!this.scatterDirections){const t=this.scatterDirections=new Float64Array(this.width*this.height*2);for(let e=0;e<this.height;e++)for(let i=0;i<this.width;i++){const s=43758.5453*Math.sin(127.1*i+311.7*e),a=(s-Math.floor(s))*Math.PI*2,n=2*(e*this.width+i);t[n]=.7*Math.cos(a),t[n+1]=.7*Math.sin(a)}}t!==this.mode&&(this.mode=t,this.clear())}clear(){for(const t of[this.ink,this.targetX,this.targetY,this.offsetX,this.offsetY,this.pixels])t.fill(0);if(this.previous=null,this.peak=0,this.springX?.fill(0),this.springY?.fill(0),"dissolve"!==this.mode)for(let t=0;t<this.pixels.length;t+=4)this.pixels[t]=this.pixels[t+2]=128}leave(){this.previous=null}move(t,e,i){if(!Number.isFinite(t+e+i))return;t=Math.max(0,Math.min(1,t)),e=Math.max(0,Math.min(1,e));const s=this.previous;this.previous={x:t,y:e};const a=this.width,n=this.height,o=Math.max(1,a/n),r=Math.max(1,n/a),h=s?(t-s.x)*o:0,l=s?(e-s.y)*r:0,c=Math.hypot(h,l);if(!s&&"dissolve"!==this.mode&&"etch"!==this.mode&&"lens"!==this.mode)return;if(s&&c<1e-4)return;const d=.035+.11*Math.max(.1,Math.min(1,i)),m=Math.min(128,Math.max(1,Math.ceil(c/(.45*d)))),u=c?h/c:1,f=c?l/c:0,g=s||"lens"!==this.mode?Math.min(1,c/(d*m)*2.2):.32;for(let i=0;i<m;i++){const h=(i+.5)/m,l=s?s.x+(t-s.x)*h:t,c=s?s.y+(e-s.y)*h:e,p=Math.max(0,Math.floor((l-2.5*d/o)*(a-1))),v=Math.min(a-1,Math.ceil((l+2.5*d/o)*(a-1))),x=Math.max(0,Math.floor((c-2.5*d/r)*(n-1))),M=Math.min(n-1,Math.ceil((c+2.5*d/r)*(n-1)));for(let t=x;t<=M;t++)for(let e=p;e<=v;e++){const i=(e/(a-1)-l)*o/d,s=(t/(n-1)-c)*r/d,h=Math.exp(1.5*-(i*i+s*s)),m=t*a+e;if("dissolve"===this.mode||"etch"===this.mode)this.ink[m]=Math.max(this.ink[m],h);else if("elastic"===this.mode)this.springX[m]=Math.max(-24,Math.min(24,this.springX[m]+u*h*g*8)),this.springY[m]=Math.max(-24,Math.min(24,this.springY[m]+f*h*g*8));else if("lens"===this.mode)this.targetX[m]=Math.max(-.65,Math.min(.65,this.targetX[m]+i*h*g*1.6)),this.targetY[m]=Math.max(-.65,Math.min(.65,this.targetY[m]+s*h*g*1.6));else if("smudge"===this.mode)this.targetX[m]=Math.max(-1,Math.min(1,this.targetX[m]+u*h*g*1.3)),this.targetY[m]=Math.max(-1,Math.min(1,this.targetY[m]+f*h*g*1.3));else{const a=(-i*f+s*u)*h*2.8,n="rake"===this.mode?u*Math.sin(t*Math.PI/3)*h*2:"magnetic"===this.mode?-i*h*2.5:"scatter"===this.mode?(i+this.scatterDirections[2*m])*h*2:"silk"===this.mode?u*a:-s*h*2.5,o="rake"===this.mode?f*Math.sin(e*Math.PI/3)*h*2:"magnetic"===this.mode?-s*h*2.5:"scatter"===this.mode?(s+this.scatterDirections[2*m+1])*h*2:"silk"===this.mode?f*a:i*h*2.5;this.targetX[m]=Math.max(-1,Math.min(1,this.targetX[m]+n*g)),this.targetY[m]=Math.max(-1,Math.min(1,this.targetY[m]+o*g))}}}this.peak=1}step(t){if(!this.active||!Number.isFinite(t))return;const e=Math.max(0,Math.min(.05,t)),i="dissolve"===this.mode?2.15:"smudge"===this.mode?2.4:3.2,s="lens"===this.mode?16:22,a=Math.exp(-i*e),n=1-Math.exp(-s*e);let o=0;const r=Math.sqrt(336),h=Math.exp(-8*e),l=Math.cos(r*e),c=Math.sin(r*e)/r;for(let t=0;t<this.ink.length;t++)if("etch"===this.mode)this.ink[t]*=a,o=Math.max(o,this.ink[t]);else if("dissolve"===this.mode){this.ink[t]*=a,o=Math.max(o,this.ink[t]);const e=Math.round(65535*this.ink[t]);this.pixels[4*t]=e>>>8,this.pixels[4*t+1]=255&e}else{if("elastic"===this.mode){const e=this.offsetX[t],i=this.offsetY[t],s=this.springX[t],a=this.springY[t];this.offsetX[t]=Math.max(-1,Math.min(1,h*(e*l+(s+8*e)*c))),this.offsetY[t]=Math.max(-1,Math.min(1,h*(i*l+(a+8*i)*c))),this.springX[t]=h*(s*l-(8*s+400*e)*c),this.springY[t]=h*(a*l-(8*a+400*i)*c),o=Math.max(o,.05*Math.abs(this.springX[t]),.05*Math.abs(this.springY[t]))}else if("lens"===this.mode||"smudge"===this.mode){const e=1-n,o=s/(s-i)*(a-e);this.offsetX[t]=this.offsetX[t]*e+this.targetX[t]*o,this.offsetY[t]=this.offsetY[t]*e+this.targetY[t]*o,this.targetX[t]*=a,this.targetY[t]*=a}else this.targetX[t]*=a,this.targetY[t]*=a,this.offsetX[t]+=(this.targetX[t]-this.offsetX[t])*n,this.offsetY[t]+=(this.targetY[t]-this.offsetY[t])*n;o=Math.max(o,Math.abs(this.offsetX[t]),Math.abs(this.offsetY[t]),Math.abs(this.targetX[t]),Math.abs(this.targetY[t]));const e=Math.round(this.offsetX[t]*(this.edgeSafe?this.edge[t]:1)*32767+32768),r=Math.round(this.offsetY[t]*(this.edgeSafe?this.edge[t]:1)*32767+32768);this.pixels[4*t]=e>>>8,this.pixels[4*t+1]=255&e,this.pixels[4*t+2]=r>>>8,this.pixels[4*t+3]=255&r}if("etch"===this.mode)for(let t=0;t<this.height;t++)for(let e=0;e<this.width;e++){const i=t*this.width+e,s=3*(this.ink[t*this.width+Math.max(0,e-1)]-this.ink[t*this.width+Math.min(this.width-1,e+1)]+this.ink[Math.max(0,t-1)*this.width+e]-this.ink[Math.min(this.height-1,t+1)*this.width+e]);this.offsetX[i]=Math.max(-1,Math.min(1,s))*(this.edgeSafe?this.edge[i]:1);const a=Math.round(32767*this.offsetX[i]+32768);this.pixels[4*i]=a>>>8,this.pixels[4*i+1]=255&a}this.peak=o,this.active||this.clear()}sample(t,e,i){const s=Math.max(0,Math.min(this.width-1.001,t*(this.width-1))),a=Math.max(0,Math.min(this.height-1.001,e*(this.height-1))),n=Math.floor(s),o=Math.floor(a),r=s-n,l=a-o,c=o*this.width+n,d=c+this.width,m=t=>(t[c]*(1-r)+t[c+1]*r)*(1-l)+(t[d]*(1-r)+t[d+1]*r)*l;if("etch"===this.mode)return i[0]=i[1]=0,void(i[2]=3*m(this.offsetX));i[0]=m(this.offsetX)*(this.edgeSafe?h(t,e,this.width/this.height):1)*.08,i[1]=m(this.offsetY)*(this.edgeSafe?h(t,e,this.width/this.height):1)*.08,i[2]="dissolve"===this.mode?6*-m(this.ink):0}},m=class{constructor(t,e){this.rings=[],this.previous=null,this.elapsed=1,this.echoPeak=0,this.width=t,this.height=e,this.pixels=new Uint8Array(t*e*4),this.echo=new Float32Array(t*e)}get active(){return this.rings.length>0||this.echoPeak>2e-4}clear(){this.rings=[],this.previous=null,this.elapsed=1,this.pixels.fill(0),this.echo.fill(0),this.echoPeak=0}leave(){this.previous=null}move(t,e,i){const s=Math.max(1,this.width/this.height),a=Math.max(1,this.height/this.width);this.previous&&(Math.hypot((t-this.previous.x)*s,(e-this.previous.y)*a)<.065||this.elapsed<.3)||4!==this.rings.length&&(this.previous={x:t,y:e},this.elapsed=0,this.rings.push({x:t,y:e,age:0,radius:i}))}ringSample(t,e){const i=Math.max(1,this.width/this.height),s=Math.max(1,this.height/this.width);let a=0;for(const n of this.rings){const o=.015+n.age*(.15+.22*n.radius),r=.009+.014*n.radius,h=(Math.hypot((t-n.x)*i,(e-n.y)*s)-o)/r,l=Math.sin(Math.min(1,n.age/.07)*Math.PI/2)*Math.max(0,1-n.age/1.35)**2;a=Math.max(a,Math.exp(-h*h)*l)}return a}sample(t,e){const i=Math.max(0,Math.min(this.width-1,Math.round(t*(this.width-1)))),s=Math.max(0,Math.min(this.height-1,Math.round(e*(this.height-1))));return Math.max(this.ringSample(t,e),this.echo[s*this.width+i])}step(t){if(!Number.isFinite(t))return;const e=Math.max(0,Math.min(.05,t));this.elapsed+=e;for(const t of this.rings)t.age+=e;if(this.rings=this.rings.filter(t=>t.age<1.35),!this.active)return void this.pixels.fill(0);const i=Math.exp(-5.5*e);let s=0;for(let t=0;t<this.height;t++)for(let e=0;e<this.width;e++){const a=t*this.width+e;this.echo[a]=Math.max(this.ringSample(e/(this.width-1),t/(this.height-1)),this.echo[a]*i),s=Math.max(s,this.echo[a]);const n=Math.round(65535*this.echo[a]),o=4*a;this.pixels[o]=n>>>8,this.pixels[o+1]=255&n}this.echoPeak=s,this.active||(this.echo.fill(0),this.pixels.fill(0))}},u=class{constructor(t,e){this.previous=null,this.peak=0,this.width=t,this.height=e;const i=t*e;this.pixels=new Uint8Array(4*i),this.signal=new Float32Array(i),this.offsetX=new Float32Array(i),this.offsetY=new Float32Array(i),this.driftX=new Float32Array(i),this.driftY=new Float32Array(i),this.u=new Float32Array(i),this.v=new Float32Array(i),this.ink=new Float32Array(i),this.a=new Float32Array(i),this.b=new Float32Array(i),this.c=new Float32Array(i),this.pressure=new Float32Array(i),this.pressureNext=new Float32Array(i),this.divergence=new Float32Array(i),this.curl=new Float32Array(i)}get active(){return this.peak>2e-4}clear(){for(const t of[this.u,this.v,this.ink,this.a,this.b,this.c,this.pressure,this.pressureNext,this.divergence,this.curl,this.signal,this.pixels,this.offsetX,this.offsetY,this.driftX,this.driftY])t.fill(0);this.previous=null,this.peak=0}leave(){this.previous=null}move(t,e,i,s=performance.now()){if(!Number.isFinite(t+e+i+s))return;t=Math.max(0,Math.min(1,t)),e=Math.max(0,Math.min(1,e)),i=Math.max(.1,Math.min(1,i));const a=this.previous;if(this.previous={x:t,y:e,time:s},!a)return;const n=(t-a.x)*(this.width-1),o=(e-a.y)*(this.height-1),r=Math.hypot(n,o);if(r<.001)return;const h=Math.min(this.width,this.height),l=s>a.time?Math.max(1/240,(s-a.time)/1e3):1/60,c=Math.min(12,r/h/l),d=1-Math.exp(-c/2.2),m=h*(.022+.075*i)*(.65+.95*d),u=Math.min(128,Math.max(1,Math.ceil(r/(.4*m)))),f=r/m*(.32+.22*d)/u,g=n/r,p=o/r,v=-p,x=g,M=r*(2+18*d)/u;for(let i=0;i<u;i++){const s=(i+.5)/u,n=(a.x+(t-a.x)*s)*(this.width-1),o=(a.y+(e-a.y)*s)*(this.height-1),r=Math.max(1,Math.floor(n-3*m)),h=Math.min(this.width-2,Math.ceil(n+3*m)),l=Math.max(1,Math.floor(o-3*m)),c=Math.min(this.height-2,Math.ceil(o+3*m));for(let t=l;t<=c;t++)for(let e=r;e<=h;e++){const i=((e-n)*g+(t-o)*p)/m,s=((e-n)*v+(t-o)*x)/m,a=Math.exp(-i*i*.65-s*s*1.7),r=t*this.width+e;this.ink[r]=1-(1-this.ink[r])*Math.exp(-a*f);const h=1-s*s*.55,l=i*s*.7;this.u[r]=Math.max(-80,Math.min(80,this.u[r]+(g*h+v*l)*a*M)),this.v[r]=Math.max(-80,Math.min(80,this.v[r]+(p*h+x*l)*a*M))}}this.peak=1}read(t,e,i){const s=0|(e=Math.max(.5,Math.min(this.width-1.5,e))),a=0|(i=Math.max(.5,Math.min(this.height-1.5,i))),n=e-s,o=i-a,r=a*this.width+s;return(t[r]*(1-n)+t[r+1]*n)*(1-o)+(t[r+this.width]*(1-n)+t[r+this.width+1]*n)*o}sample(t,e){return this.read(this.signal,t*(this.width-1),e*(this.height-1))}displacement(t,e,i){i[0]=this.read(this.offsetX,t*(this.width-1),e*(this.height-1)),i[1]=this.read(this.offsetY,t*(this.width-1),e*(this.height-1))}step(t){if(!this.active||!Number.isFinite(t))return;let e=Math.max(0,Math.min(.05,t));const i=this.width,s=this.height;for(;e>1e-7;){const t=Math.min(1/60,e);e-=t;const a=Math.exp(-1.9*t),n=Math.exp(-3.2*t);for(let e=1;e<s-1;e++)for(let o=1;o<i-1;o++){const r=e*i+o,h=Math.max(.5,Math.min(i-1.5,o-this.u[r]*t)),l=Math.max(.5,Math.min(s-1.5,e-this.v[r]*t)),c=0|h,d=0|l,m=h-c,u=l-d,f=d*i+c,g=(1-m)*(1-u),p=m*(1-u),v=(1-m)*u,x=m*u;this.a[r]=(this.u[f]*g+this.u[f+1]*p+this.u[f+i]*v+this.u[f+i+1]*x)*a,this.b[r]=(this.v[f]*g+this.v[f+1]*p+this.v[f+i]*v+this.v[f+i+1]*x)*a,this.c[r]=(this.ink[f]*g+this.ink[f+1]*p+this.ink[f+i]*v+this.ink[f+i+1]*x)*n}[this.u,this.a]=[this.a,this.u],[this.v,this.b]=[this.b,this.v],[this.ink,this.c]=[this.c,this.ink];for(let t=1;t<s-1;t++)for(let e=1;e<i-1;e++){const s=t*i+e;this.curl[s]=.5*(this.v[s+1]-this.v[s-1]-this.u[s+i]+this.u[s-i])}for(let e=1;e<s-1;e++)for(let s=1;s<i-1;s++){const a=e*i+s,n=Math.abs(this.curl[a+1])-Math.abs(this.curl[a-1]),o=Math.abs(this.curl[a+i])-Math.abs(this.curl[a-i]),r=Math.hypot(n,o)+1e-4,h=this.curl[a]*t*10,l=Math.hypot(this.u[a],this.v[a]);this.u[a]+=o/r*h,this.v[a]-=n/r*h;const c=Math.min(1,l/(Math.hypot(this.u[a],this.v[a])+1e-6));this.u[a]*=c,this.v[a]*=c,this.divergence[a]=-.5*(this.u[a+1]-this.u[a-1]+this.v[a+i]-this.v[a-i])}this.pressure.fill(0);for(let t=0;t<4;t++){for(let t=1;t<s-1;t++)for(let e=1;e<i-1;e++){const s=t*i+e;this.pressureNext[s]=.25*(this.divergence[s]+this.pressure[s-1]+this.pressure[s+1]+this.pressure[s-i]+this.pressure[s+i])}[this.pressure,this.pressureNext]=[this.pressureNext,this.pressure]}let o=0;for(let e=1;e<s-1;e++)for(let s=1;s<i-1;s++){const a=e*i+s;this.u[a]=Math.max(-80,Math.min(80,this.u[a]-.5*(this.pressure[a+1]-this.pressure[a-1]))),this.v[a]=Math.max(-80,Math.min(80,this.v[a]-.5*(this.pressure[a+i]-this.pressure[a-i]))),this.driftX[a]+=(3.6*this.u[a]-58*this.offsetX[a]-12*this.driftX[a])*t,this.driftY[a]+=(3.6*this.v[a]-58*this.offsetY[a]-12*this.driftY[a])*t,this.offsetX[a]=Math.max(-2,Math.min(2,this.offsetX[a]+this.driftX[a]*t)),this.offsetY[a]=Math.max(-2,Math.min(2,this.offsetY[a]+this.driftY[a]*t)),this.signal[a]=1-Math.exp(.06*-Math.hypot(this.u[a],this.v[a])-.16*this.ink[a]),o=Math.max(o,this.signal[a])}this.peak=o}if(this.active)for(let t=0;t<this.ink.length;t++){const e=Math.round(65535*Math.min(1,this.signal[t]));this.pixels[4*t]=e>>>8,this.pixels[4*t+1]=255&e,this.pixels[4*t+2]=Math.round(128+63.5*this.offsetX[t]),this.pixels[4*t+3]=Math.round(128+63.5*this.offsetY[t])}else this.clear()}},f=t=>["trail","contour","dissolve","etch","ripple"].includes(t),g=(t,e,i,s)=>("etch"===i||"ripple"===i?(256*t[e]+t[e+1]-32768)/32767:(256*t[e]+t[e+1])/65535)*s;function p(t,e,i){const s=Math.max(0,e-1),a=1-Math.exp(3.5*-Math.max(0,i));return Math.max(0,s-Math.abs(s-(t+a*s*2.1)))}function v(t,e,i,s,a,n,o,r,h,l,c,d=!1){const m=Math.min(a,n,i-1-a,s-1-n),u=Math.max(0,Math.min(1,(m-1)/4)),f=d?u*u*(3-2*u):1;o*=f;const g=1-Math.exp(3.5*-Math.max(0,o)),v=Math.max(0,Math.min(i-1,a-r*g*.026*i*f)),x=Math.max(0,Math.min(s-1,n-h*g*.026*s*f)),M=Math.floor(v),y=Math.floor(x),w=v-M,S=x-y;c.fill(0);for(let a=0;a<2;a++)for(let n=0;n<2;n++){const o=4*(Math.min(s-1,y+a)*i+Math.min(i-1,M+n)),r=(n?w:1-w)*(a?S:1-S);c[0]+=(t[o]+256*t[o+1])*r,c[1]+=t[o+2]/255*r;for(let t=0;t<4;t++)c[t+2]+=e[o+t]*r}const b=43758.5453*Math.sin(127.1*a+311.7*n),E=b-Math.floor(b);c[0]=Math.max(0,p(c[0],l,o)-g*(1-g)*E*3)}var x=(t,e)=>"water"===t?30*e/.55:"silk"===t?28*e:"vortex"===t?38*e:["magnetic","scatter","elastic","rake","lens","smudge"].includes(t)?32*e:0,M=class{constructor(t=1){this.ripple=null,this.mode="water",this.amount=.55,this.radius=.2,this.lensX=.5,this.lensY=.5,this.lensStrength=0,this.previous=null,this.energy=0,this.pending=!1,this.remainder=0,t=Math.max(.25,Math.min(4,t)),this.columns=Math.round(t>=1?128:128*t),this.rows=Math.round(t>=1?128/t:128),this.xScale=Math.max(1,t),this.yScale=Math.max(1,1/t),this.heights=new Float32Array(this.columns*this.rows),this.velocity=new Float32Array(this.heights.length),this.next=new Float32Array(this.heights.length),this.normals=new Float32Array(2*this.heights.length),this.edgeMask=new Float32Array(this.heights.length);for(let e=0;e<this.rows;e++)for(let i=0;i<this.columns;i++)this.edgeMask[e*this.columns+i]=h(i/(this.columns-1),e/(this.rows-1),t);this.refraction={width:this.columns,height:this.rows,pixels:new Uint8Array(4*this.heights.length),strength:30,mode:"water",focus:[.5,.5,0,.2]},this.waterPixels=this.refraction.pixels,this.trail=new u(this.columns,this.rows),this.contour=new m(this.columns,this.rows),this.afterimage=new d(this.columns,this.rows),this.clear()}get invertsDensity(){return"trail"===this.mode}get densityTrail(){return["trail","contour","dissolve","etch","ripple"].includes(this.mode)}get hasRefraction(){return this.active||this.lensStrength>15e-5}get active(){return"ripple"===this.mode?this.ripple?.active??!1:c(this.mode)?this.afterimage.active:"contour"===this.mode?this.contour.active:"trail"===this.mode?this.trail.active:this.pending||this.energy>15e-5}setMode(t){t!==this.mode&&(this.clear(),this.mode=t,this.refraction.mode=t,c(t)&&this.afterimage.setMode(t),"ripple"!==t||this.ripple||(this.ripple=new l(this.columns,this.rows)),this.refraction.pixels="ripple"===t?this.ripple.pixels:"trail"===t?this.trail.pixels:"contour"===t?this.contour.pixels:c(t)?this.afterimage.pixels:this.waterPixels,this.refraction.strength=x(t,this.amount))}configure(t,e=.55,i=.2,s=!1){this.setMode(t),this.refraction.edgeSafe=s,this.afterimage.edgeSafe=s,this.ripple&&(this.ripple.edgeSafe=s),this.amount=Number.isFinite(e)?Math.max(0,Math.min(1,e)):.55,this.radius=Number.isFinite(i)?Math.max(.1,Math.min(1,i)):.2,this.refraction.strength=x(t,this.amount),this.refraction.focus[2]=this.densityTrail?this.amount:this.lensStrength*this.amount,this.refraction.focus[3]=this.radius}move(t,e,i){if("none"===this.mode||0===this.amount)return;if(!Number.isFinite(t+e))return;if(t=Math.max(0,Math.min(1,t)),e=Math.max(0,Math.min(1,e)),"ripple"===this.mode)return void this.ripple.move(t,e,this.radius,i);if(c(this.mode))return void this.afterimage.move(t,e,this.radius);if("contour"===this.mode)return void this.contour.move(t,e,this.radius);if("trail"===this.mode)return void this.trail.move(t,e,this.radius,i);if("water"!==this.mode)return!this.previous&&this.lensStrength<.001&&(this.lensX=t,this.lensY=e),this.previous={x:t,y:e},void(this.pending=!0);if(!this.previous)return void(this.previous={x:t,y:e});const s=this.previous;this.previous={x:t,y:e};const a=(t-s.x)*this.xScale,n=(e-s.y)*this.yScale,o=Math.hypot(a,n);if(o<1e-5)return;const r=.035+.1*this.radius,h=3*r,l=Math.min(192,Math.max(1,Math.ceil(o/(.5*r)))),d=95*Math.min(o,1)/l;for(let i=0;i<l;i++){const a=(i+.5)/l,n=s.x+(t-s.x)*a,o=s.y+(e-s.y)*a,c=Math.max(1,Math.floor((n-h/this.xScale)*(this.columns-1))),m=Math.min(this.columns-2,Math.ceil((n+h/this.xScale)*(this.columns-1))),u=Math.max(1,Math.floor((o-h/this.yScale)*(this.rows-1))),f=Math.min(this.rows-2,Math.ceil((o+h/this.yScale)*(this.rows-1)));for(let t=u;t<=f;t++)for(let e=c;e<=m;e++){const i=(e/(this.columns-1)-n)*this.xScale,s=(t/(this.rows-1)-o)*this.yScale,a=Math.exp(-(i*i+s*s)/(r*r))*d,h=t*this.columns+e;this.velocity[h]=Math.max(-12,this.velocity[h]-a)}}this.pending=!0}leave(){this.ripple?.leave(),this.afterimage.leave(),this.contour.leave(),this.trail.leave(),this.previous=null,"water"!==this.mode&&this.lensStrength&&(this.pending=!0)}clear(){this.trail.clear(),this.contour.clear(),this.afterimage.clear(),this.ripple?.clear(),this.heights.fill(0),this.velocity.fill(0),this.next.fill(0),this.normals.fill(0),this.refraction.focus[2]=0,this.previous=null,this.lensStrength=0,this.energy=0,this.pending=!1,this.remainder=0;for(let t=0;t<this.waterPixels.length;t+=4)this.waterPixels[t]=this.waterPixels[t+2]=128,this.waterPixels[t+1]=this.waterPixels[t+3]=0}step(t){if(!this.active||!Number.isFinite(t))return;if("ripple"===this.mode)return void this.ripple.step(t);if(c(this.mode))return void this.afterimage.step(t);if("contour"===this.mode)return void this.contour.step(t);if("trail"===this.mode)return void this.trail.step(t);if("water"!==this.mode)return void this.stepLight(Math.max(0,Math.min(.05,t)));this.remainder+=Math.max(0,Math.min(.05,t));const e=1/120,i=Math.exp(-3.8*e),s=.3*Math.min(this.columns,this.rows);for(;this.remainder+1e-8>=e;){this.remainder-=e;let t=0;for(let a=1;a<this.rows-1;a++)for(let n=1;n<this.columns-1;n++){const o=a*this.columns+n,r=this.heights[o],h=this.heights[o-1]+this.heights[o+1]+this.heights[o-this.columns]+this.heights[o+this.columns]-4*r,l=Math.min(n,a,this.columns-1-n,this.rows-1-a),c=l<5?.88+.024*l:1,d=(this.velocity[o]+(h*s*s-8*r)*e)*i*c;this.velocity[o]=d,this.next[o]=(r+d*e)*c,t=Math.max(t,Math.abs(this.next[o]),.1*Math.abs(d))}this.heights.set(this.next),this.energy=t,this.pending=!1}if(!this.active)return void this.clear();const a=.18*Math.min(this.columns,this.rows);for(let t=0;t<this.rows;t++)for(let e=0;e<this.columns;e++){const i=t*this.columns+e,s=(this.heights[t*this.columns+Math.max(0,e-1)]-this.heights[t*this.columns+Math.min(this.columns-1,e+1)])*a,n=(this.heights[Math.max(0,t-1)*this.columns+e]-this.heights[Math.min(this.rows-1,t+1)*this.columns+e])*a,o=Math.tanh(s)*(this.refraction.edgeSafe?this.edgeMask[i]:1),r=Math.tanh(n)*(this.refraction.edgeSafe?this.edgeMask[i]:1);this.normals[2*i]=o,this.normals[2*i+1]=r;const h=Math.round(32767*o+32768),l=Math.round(32767*r+32768);this.refraction.pixels[4*i]=h>>>8,this.refraction.pixels[4*i+1]=255&h,this.refraction.pixels[4*i+2]=l>>>8,this.refraction.pixels[4*i+3]=255&l}}stepLight(t){const e=this.previous,i=1-Math.exp(-30*t),s=1-Math.exp(-12*t);e&&(this.lensX+=(e.x-this.lensX)*i,this.lensY+=(e.y-this.lensY)*i),this.lensStrength+=((e?1:0)-this.lensStrength)*s,this.energy=Math.max(e?Math.abs(e.x-this.lensX)+Math.abs(e.y-this.lensY):0,Math.abs((e?1:0)-this.lensStrength)),this.pending=!1,e||this.active?this.refraction.focus=[this.lensX,this.lensY,this.lensStrength*this.amount,this.radius]:this.clear()}sample(t,e,i){if("ripple"===this.mode)return this.ripple.sample(t,e,i),void(i[2]*=this.amount);if(c(this.mode))return this.afterimage.sample(t,e,i),i[0]*=this.amount,i[1]*=this.amount,void(i[2]*=this.amount);if("contour"===this.mode)return i[0]=i[1]=0,void(i[2]=this.contour.sample(t,e)*this.amount*3);if("trail"===this.mode)return i[0]=i[1]=0,void(i[2]=this.trail.sample(t,e)*this.amount*3);const s=Math.max(0,Math.min(this.columns-1.001,t*(this.columns-1))),a=Math.max(0,Math.min(this.rows-1.001,e*(this.rows-1))),n=Math.floor(s),o=Math.floor(a),r=s-n,h=a-o,l=2*(o*this.columns+n),d=l+2*this.columns;for(let t=0;t<2;t++)i[t]=.08*((this.normals[l+t]*(1-r)+this.normals[l+2+t]*r)*(1-h)+(this.normals[d+t]*(1-r)+this.normals[d+2+t]*r)*h);i[0]*=this.amount/.55,i[1]*=this.amount/.55,i[2]="light"===this.mode||"scan"===this.mode?function(t,e,i,s){const[a,n,o,r]=i.focus,h=(t-a)*Math.max(1,s)/r,l=(e-n)*Math.max(1,1/s)/r;return"light"===i.mode?Math.exp(-3*(h*h+l*l))*o:"scan"===i.mode?(1.3*Math.exp(-h*h*110)-.3*Math.exp(-h*h*12))*Math.exp(-l*l*1.8)*o:0}(t,e,this.refraction,this.xScale/this.yScale):0}};var y=[{value:"clean",label:"Clean",description:"Crisp characters with no optical texture."},{value:"signal",label:"Signal",description:"Broken scanlines at the edges, with a clear center."},{value:"prism",label:"Prism",description:"Soft red/cyan separation at the edges, with a crisp center."},{value:"etch",label:"Etch",description:"A fine engraved texture, keeping the ink monochrome."}];function w(t){const e=Intl.Segmenter;return e?Array.from(new e(void 0,{granularity:"grapheme"}).segment(t),t=>t.segment):Array.from(t)}function S(){const t=document.createElement("canvas"),e=document.createElement("canvas"),i=t.getContext("2d"),s=e.getContext("2d",{willReadFrequently:!0}),a=function(){const t=new WeakMap;return(e,i,s="")=>{if(!e)return i;let a=t.get(e);if(!a){const i=new Set;for(const t of e)for(const e of t)e.char&&i.add(e.char);a=[...i].sort(),t.set(e,a)}const n=i+s;return i+a.filter(t=>!n.includes(t)).join("")}}();let n,o=null;return{update(r,h,l,c,d,m,u,f){const g=u?.[0]?.length??Math.max(1,Math.round(h/c)),p=u?.length??Math.max(1,Math.round(l/d)),v=f??{charset:" .:-=+*#%@",customText:"",colorMode:"fullcolor"},x=a(u,v.charset+(v.charsetFrames?.join("")??""),v.customText),M=function(t,e,i,s,a,n,o,r=4096,h=0,l=1){if(![i,s,a,n,o,r].every(t=>Number.isFinite(t)&&t>0))throw new Error("Glyph atlas dimensions must be positive and finite.");const c=JSON.stringify([e.charset,e.customText,i,s,a,n,o,r,h,l]);if(t?.key===c)return t;const d=[...new Set([" ",...w(e.charset),...w(e.customText??"")])];if(d.length>65536)throw new Error("This character set exceeds the glyph texture capacity.");const m=i/n,u=s/o,f=.9*Math.min(m/.55,u)*l,g=Math.ceil(m*a)+6,p=Math.ceil(u*a)+6,v=Math.min(d.length,Math.floor(r/g));if(v<1||Math.ceil(d.length/v)*p>r)throw new Error("The glyph atlas exceeds the available GPU texture size.");return{key:c,glyphs:d,indices:new Map(d.map((t,e)=>[t,e])),cellWidth:m,cellHeight:u,fontSize:f,padding:3,tileWidth:g,tileHeight:p,atlasColumns:v,atlasWidth:v*g,atlasHeight:Math.ceil(d.length/v)*p}}(o,{...v,charset:x},h,l,m,g,p);if(M!==o&&(t.width=M.atlasWidth,t.height=M.atlasHeight,i.font=M.fontSize*m+'px "JetBrains Mono", monospace',i.textAlign="center",i.textBaseline="middle",i.fillStyle="#fff",M.glyphs.forEach((t,e)=>i.fillText(t,e%M.atlasColumns*M.tileWidth+M.padding+M.cellWidth*m*.5,Math.floor(e/M.atlasColumns)*M.tileHeight+M.padding+M.cellHeight*m*.5))),o=M,u)n=function(t,e,i,s,a=!1){const n=t.length,o=t[0]?.length??0,r=s?.cols===o&&s.rows===n?s:{cols:o,rows:n,indices:new Uint8Array(o*n*4),colors:new Uint8ClampedArray(o*n*4)};r.indices.fill(0),r.colors.fill(0);const h="accent"===i.colorMode?[1,3,5].map(t=>parseInt(i.accentColor.slice(t,t+2),16)):null;for(let s=0;s<n;s++){if(t[s].length!==o)throw new Error("Glyph frames must have a rectangular cell grid.");for(let n=0;n<o;n++){const l=t[s][n];if(!l.char||!a&&" "===l.char||l.a<10)continue;const c=e.get(l.char);if(void 0===c)throw new Error(`Glyph is absent from the configured character set: ${l.char}`);const d=4*(s*o+n);let m=l.r,u=l.g,f=l.b;if(h)[m,u,f]=h;else if("fullcolor"!==i.colorMode){const t=.299*m+.587*u+.114*f|0;m="matrix"===i.colorMode?0:t,u=t,f=m}(a||0!==m||0!==u||0!==f)&&(r.indices[d]=255&c,r.indices[d+1]=c>>>8,r.indices[d+2]=Math.round(.299*l.r+.587*l.g+.114*l.b),r.colors[d]=m,r.colors[d+1]=u,r.colors[d+2]=f,r.colors[d+3]=l.a)}}return r}(u,o.indices,v,n,!0);else{e.width=g,e.height=p,s.drawImage(r,0,0,g,p);const t=s.getImageData(0,0,g,p).data;n={cols:g,rows:p,indices:new Uint8Array(g*p*4),colors:new Uint8ClampedArray(t)};for(let e=0;e<t.length;e+=4){const i=Math.min(1,(.299*t[e]+.587*t[e+1]+.114*t[e+2])/255*(t[e+3]/255)*3);n.indices[e+2]=Math.round(255*i),n.indices[e]=Math.min(o.glyphs.length-1,Math.floor(i*o.glyphs.length))}}return{plan:o,packed:n,atlas:t,dots:"dots"===v.renderMode,dotSize:v.dotSizeRatio??.72}},reset(){o=null},destroy(){t.width=t.height=e.width=e.height=1,n=void 0,o=null}}}function b(t){return!!t&&"ascii"===t.renderMode&&!/\p{Extended_Pictographic}/u.test(t.customText||t.charset)&&!t.charsetFrames?.length&&["fullcolor","grayscale","matrix","accent"].includes(t.colorMode)&&("accent"!==t.colorMode||/^#[\da-f]{6}$/i.test(t.accentColor))}function E(t){for(let e=t;e;e=e.parentElement){const t=getComputedStyle(e).backgroundColor.match(/[\d.]+/g)?.map(Number);if(t&&t.length>=3&&(t[3]??1)>.5)return(.299*t[0]+.587*t[1]+.114*t[2])/255<.4?[10/255,10/255,10/255,1]:[250/255,249/255,247/255,1]}const e=document.documentElement.dataset.theme;if("dark"!==e&&"light"!==e&&!t.isConnected&&document.body)return E(document.body);return"dark"===e||"light"!==e&&(document.documentElement.classList.contains("dark")||matchMedia("(prefers-color-scheme: dark)").matches)?[10/255,10/255,10/255,1]:[250/255,249/255,247/255,1]}function k(t,s,a){const h=s.style.visibility;let l=document.createElement("canvas"),c=null,d=null,m=null,u=null;const x=[],M=[];let w=!1,k=!1,T=4096,A=null,z=0,R=0,D=0,C=0,F=!1;const X=function(){const t=document.createElement("canvas"),e=t.getContext("2d");return{paint(i,s,a,n){s&&(t.width===i.canvas.width&&t.height===i.canvas.height||(t.width=i.canvas.width,t.height=i.canvas.height),e.globalCompositeOperation="copy",e.drawImage(i.canvas,0,0),e.globalCompositeOperation="destination-in",e.drawImage(s.canvas,0,0,t.width,t.height),e.globalCompositeOperation="source-in",e.fillStyle="#090909",e.fillRect(0,0,t.width,t.height),e.globalCompositeOperation="source-over",i.drawImage(t,0,0,a,n))},destroy(){t.width=t.height=1}}}();let I;const P=document.createElement("canvas"),_=document.createElement("canvas"),L=P.getContext("2d"),U=_.getContext("2d");let Y=!1;const N=new o,G=S();let B=null,O={};const W=()=>{l.className="ascii-hover-surface",l.style.cssText="position:absolute;inset:0;width:100%;height:100%;pointer-events:none",l.setAttribute("role","img"),l.setAttribute("aria-label","ASCII artwork preview"),l.dataset.renderer=c?"surface-gpu":"surface-canvas",t.prepend(l),s.style.visibility="hidden"},H=()=>{c&&(x.forEach(t=>c.deleteTexture(t)),M.forEach(t=>c.deleteShader(t)),m&&c.deleteProgram(m),u&&c.deleteBuffer(u))},K=()=>{w||d||(l.removeEventListener("webglcontextlost",K),H(),l.remove(),c=null,l=document.createElement("canvas"),d=l.getContext("2d"),W(),a())};try{if(c=l.getContext("webgl",{alpha:!0,antialias:!1,depth:!1,stencil:!1,premultipliedAlpha:!0,preserveDrawingBuffer:!0}),!c)throw new Error("WebGL unavailable");T=c.getParameter(c.MAX_TEXTURE_SIZE);const t=(t,e)=>{const i=c.createShader(t);if(M.push(i),c.shaderSource(i,e),c.compileShader(i),!c.getShaderParameter(i,c.COMPILE_STATUS))throw new Error(c.getShaderInfoLog(i)||"Shader compilation failed");return i};if(m=c.createProgram(),c.attachShader(m,t(c.VERTEX_SHADER,"attribute vec2 position; void main(){gl_Position=vec4(position,0.,1.);}")),c.attachShader(m,t(c.FRAGMENT_SHADER,"\n precision highp float;\n uniform sampler2D artwork; uniform sampler2D surface;\n uniform vec2 resolution; uniform vec2 sceneSize; uniform vec2 cellSize;\n uniform vec2 surfaceSize; uniform float strength;\n \nuniform float hoverMode;\nuniform vec4 hoverFocus;\nfloat surfaceLight(vec2 uv, vec2 size) {\n vec2 delta = (uv - hoverFocus.xy) * size / min(size.x, size.y) / hoverFocus.w;\n float light = exp(-3.0 * dot(delta, delta));\n if (hoverMode > 1.5) light = (exp(-delta.x * delta.x * 110.0) * 1.3 - exp(-delta.x * delta.x * 12.0) * .3) * exp(-delta.y * delta.y * 1.8);\n return hoverMode > .5 ? light * hoverFocus.z : 0.0;\n}\nvec3 illuminate(vec3 ink, float light) {\n return mix(ink, vec3(1.0), max(0.0, light) * .82) * (1.0 + min(0.0, light));\n}\n\n uniform sampler2D codes; uniform sampler2D cellColors; uniform sampler2D glyphAtlas;\n uniform vec2 gridSize; uniform vec2 atlasSize; uniform vec2 tileSize;\n uniform float atlasColumns; uniform float padding; uniform float glyphCount;\n uniform float dots; uniform float dotSize; uniform float directGlyphs; uniform vec4 backdrop;\n uniform float edgeEffect; uniform sampler2D textMask; uniform float hasTextMask;\n \nuniform float finishMode;\nuniform float finishPixelRatio;\nuniform float finishSoftness;\nvec3 edgeCoverage(sampler2D sheet, vec2 uv, vec2 atlas, vec2 pixel, vec2 viewport, float amount) {\n float a=texture2D(sheet,uv).a;\n if(amount<=0.0 || finishMode<.5) return vec3(a);\n vec2 radial=(pixel/viewport-.5)*2.0;\n float edge=smoothstep(.3,1.05,length(radial));\n float strength=clamp(amount,0.0,1.0)*edge;\n if(strength<=0.0) return vec3(a);\n vec2 cssPixel=pixel/max(1.0,finishPixelRatio);\n // Etch is a single texture sample: linework, not an opacity wash.\n if(finishMode>1.5 && finishMode<2.5) {\n float hatch=step(.68,fract((cssPixel.y+cssPixel.x*.22)*.5));\n return vec3(a*(1.0-hatch*strength*.64));\n }\n // Samples remain inside the current atlas tile's three-pixel padding.\n if(finishMode>2.5) {\n float band=floor(cssPixel.y/3.0);\n float phase=fract(sin(band*12.9898)*43758.5453);\n float tear=(phase-.5)*3.6*strength;\n float displaced=texture2D(sheet,uv+vec2(tear,0.0)/atlas).a;\n float cut=step(.72,fract(cssPixel.y*.5));\n return vec3(mix(a,displaced,strength)*(1.0-cut*.55*strength));\n }\n // A five-tap lens: opposed red/cyan edges with a short soft shoulder.\n // The center remains untouched; all offsets stay within three atlas texels.\n vec2 direction=normalize(vec2(radial.x-radial.y*.3,radial.y+radial.x*.3)+vec2(.00001));\n vec2 shift=direction*(2.35+.45*finishSoftness)*strength/atlas;\n float left=texture2D(sheet,uv-shift).a,right=texture2D(sheet,uv+shift).a;\n float softLeft=texture2D(sheet,uv-shift*.45).a,softRight=texture2D(sheet,uv+shift*.45).a;\n float red=left*.7+softLeft*.22+a*.08;\n float cyan=right*.7+softRight*.22+a*.08;\n // Reuse the same taps for a short optical blur and peripheral fade.\n // No whole-canvas blur, extra pass or softened HTML controls.\n float soft=a*.4+(left+right+softLeft+softRight)*.15;\n vec3 fringe=mix(vec3(red,cyan,cyan),vec3(soft),finishSoftness*.45);\n return mix(vec3(a),fringe,strength)*(1.-finishSoftness*strength*.32);\n\n}\n\n \nuniform float edgeSafe;\nuniform float trailAmount;\nuniform float trailKind;\nvec4 trailField(vec2 address) {\n return texture2D(surface,(address*(surfaceSize-1.0)+.5)/surfaceSize);\n}\nfloat trailFieldDensity(vec4 field) {\n float packed=field.r*65280.0+field.g*255.0;\n return (trailKind>2.5?(packed-32768.)/32767.:packed/65535.)*trailAmount;\n}\nfloat trailDensity(vec2 address) { return trailFieldDensity(trailField(address)); }\nfloat trailPin(vec2 cell, vec2 grid) {\n vec2 edge=min(cell,grid-1.0-cell);\n return mix(1.,smoothstep(1.0,5.0,min(edge.x,edge.y)),edgeSafe);\n}\nvec2 trailSource(vec2 cell, vec2 grid, vec4 field) {\n vec2 home=(cell+.5)/grid;\n if(trailKind>.5 || trailAmount<=0.) return home;\n float pin=trailPin(cell,grid);\n float response=1.-exp(-trailFieldDensity(field)*pin*3.5);\n vec2 drift=(field.ba*255.-128.)/127.;\n return clamp(home-drift*response*.026*pin,.5/grid,1.-.5/grid);\n}\nvec2 decodedGlyph(vec4 code) {\n return vec2(floor(code.r*255.+.5)+floor(code.g*255.+.5)*256.,code.b);\n}\n// Bilinear interpolation happens before glyph selection, never between bitmap\n// letters. Four small metadata reads replace the old four full-glyph gathers.\nvec2 flowGlyph(sampler2D codes,vec2 uv,vec2 grid) {\n vec2 p=uv*grid-.5,b=floor(p),f=fract(p);\n return mix(mix(decodedGlyph(texture2D(codes,(b+.5)/grid)),decodedGlyph(texture2D(codes,(b+vec2(1.5,.5))/grid)),f.x),\n mix(decodedGlyph(texture2D(codes,(b+vec2(.5,1.5))/grid)),decodedGlyph(texture2D(codes,(b+1.5)/grid)),f.x),f.y);\n}\nvec3 trailInk(vec3 ink, float density) {\n if (trailKind < .5) return ink;\n float peak=max(ink.r,max(ink.g,ink.b));\n float raised=trailKind>1.5?peak:max(peak,min(.44,density*.75));\n return peak>.001 ? ink*(raised/peak) : vec3(raised);\n}\nfloat flowIndex(float index,float count,float density) {\n float last=max(0.,count-1.);\n float response=1.-exp(-max(0.,density)*3.5);\n return max(0.,last-abs(last-(index+response*last*2.1)));\n}\nfloat trailTone(float tone, float density) {\n if (trailKind > 2.5) return clamp(tone+density*.35,0.,1.);\n if (trailKind < .5) return flowIndex(tone,2.,density);\n return clamp(tone + (trailKind > 1.5 ? -density : density) * .65, 0.0, 1.0);\n}\nfloat trailIndex(float index, float count, float density, vec2 cell) {\n float grain=fract((cell.x*cell.x*17.0+cell.y*cell.y*23.0+cell.x*cell.y*19.0)*.0137);\n if(trailKind>2.5) return clamp(floor(index+density*(count-1.)*.35+.5),0.,count-1.);\n if(trailKind>1.5) return max(0.0,index-floor(density*count*(1.35+grain*.9)));\n if(trailKind<.5) {\n float seed=fract(sin(dot(cell,vec2(127.1,311.7)))*43758.5453);\n float response=1.-exp(-max(0.,density)*3.5);\n return floor(max(0.,flowIndex(index,count,density)-response*(1.-response)*seed*3.)+.5);\n }\n return min(count-1.0,index+floor(density*count*.85));\n}\n\n \n uniform float motionMode; uniform float motionTime; uniform float fineDither; uniform float ditherAmount;\n float printGrain(vec2 p) { return fract(52.9829189*fract(dot(p,vec2(.06711056,.00583715)))); }\n vec4 ambientAt(vec2 uv, float tone) {\n float p=motionTime*.5235987756;\n float edge=smoothstep(0.,.12,min(min(uv.x,1.-uv.x),min(uv.y,1.-uv.y)));\n if(motionMode<.5) return vec4(0.,0.,0.,1.);\n if(motionMode<1.5) {\n float fold=sin(uv.x*9.+uv.y*5.+p)+sin(uv.y*8.-uv.x*3.-p);\n float light=pow(max(0.,1.-abs(fold)*.72),3.);\n return vec4(0.,0.,(light*.22-.045)*edge,1.);\n }\n if(motionMode<2.5) return vec4(sin(uv.y*9.+p)*cos(uv.x*7.-p)*10.*edge,cos(uv.x*8.+p)*sin(uv.y*6.-p)*7.*edge,0.,1.);\n if(motionMode>13.5) {\n float column=floor(uv.x*12.),local=uv.x*12.-column-.5;\n float cycle=mod(motionTime-column*.11,12.);\n float turn=smoothstep(1.,3.,cycle)*(1.-smoothstep(5.,7.,cycle));\n return vec4(-local*turn*24.*edge,sin(column*1.7)*turn*3.*edge,-turn*.12,1.-turn*.65);\n }\n if(motionMode>12.5) {\n float depth=.35+.65*(1.-uv.y);\n return vec4(sin(p-uv.y*4.)*10.*depth*edge,sin(p*2.-uv.y*4.+uv.x*3.)*2.5*depth*edge,cos(p-uv.y*4.)*.035*edge,1.);\n }\n if(motionMode>11.5) {\n float phase=printGrain(floor(uv*vec2(160.,100.)))*6.28318530718;\n float glint=pow(.5+.5*cos(p+phase),10.);\n return vec4(0.,0.,(glint-.1762)*.28*smoothstep(.35,.9,tone),1.);\n }\n if(motionMode>10.5) {\n float light=dot(uv-.5,vec2(cos(p),sin(p)))*1.3,level=clamp(tone,0.,1.);\n return vec4(0.,0.,light*.3*(.25+3.*level*(1.-level))*edge,1.);\n }\n if(motionMode>9.5) {\n float level=.5+.5*cos(p);\n return vec4(0.,0.,pow(max(0.,1.-abs(clamp(tone,0.,1.)-level)/.16),3.)*.22-.015,1.);\n }\n if(motionMode>8.5) {\n float row=floor(uv.y*24.),cycle=mod(motionTime+printGrain(vec2(row,3.))*3.,12.);\n float lift=smoothstep(1.,2.5,cycle)*(1.-smoothstep(3.5,5.,cycle));\n return vec4(sin(row*2.4)*lift*9.*edge,-lift*5.*edge,-lift*.22,1.-lift*.45);\n }\n if(motionMode>7.5) return vec4(sin(uv.y*18.8495559215+p)*sin(p)*8.*edge,sin(uv.x*18.8495559215-p)*sin(p)*6.*edge,0.,1.);\n if(motionMode>6.5) {\n float depth=.45+.55*max(0.,1.-length(uv-.5)*1.4);\n return vec4(sin(p)*12.*depth*edge,(cos(p)-1.)*6.*depth*edge,0.,1.);\n }\n if(motionMode>5.5) return vec4(0.,0.,sin(p+printGrain(floor(uv*vec2(160.,100.)))*6.28318530718)*.07,1.);\n if(motionMode>4.5) {\n vec2 delta=uv-.5; float wave=sin(length(delta)*22.-p)*edge;\n return vec4(delta*wave*9.,wave*.035,1.);\n }\n if(motionMode>3.5) return vec4(0.,0.,pow(.5+.5*cos(uv.x*5.+uv.y*3.-p),8.)*.2-.025,1.);\n float cycle=mod(motionTime,12.);\n float grain=printGrain(floor(uv*vec2(160.,100.)));\n float order=uv.x*.65+uv.y*.2+grain*.15+sin(uv.x*8.-uv.y*5.)*.08;\n float gone=smoothstep(1.8+order*1.2,3.8+order*1.2,cycle)*(1.-smoothstep(5.+order*1.7,7.2+order*1.7,cycle));\n return vec4(sin(uv.y*9.+p)*gone*(1.-gone)*24.*edge,-gone*(1.-gone)*(8.+grain*12.)*edge,-gone*.45,1.-gone*.9);\n }\n\n float toneAt(vec2 uv) {\n vec2 p=uv*gridSize-.5, b=floor(p), f=fract(p);\n return mix(mix(texture2D(codes,(b+.5)/gridSize).b,texture2D(codes,(b+vec2(1.5,.5))/gridSize).b,f.x),mix(texture2D(codes,(b+vec2(.5,1.5))/gridSize).b,texture2D(codes,(b+1.5)/gridSize).b,f.x),f.y);\n }\n vec4 flowingCharacter(vec2 pixel, vec2 cell, vec4 ambient) {\n if(cell.x<0. || cell.y<0. || cell.x>=gridSize.x || cell.y>=gridSize.y) return vec4(0.);\n vec2 address=(cell+.5)/gridSize;\n vec4 field=trailField(address);\n vec2 local=pixel-cell*cellSize;\n if(local.x<0. || local.y<0. || local.x>=cellSize.x || local.y>=cellSize.y) return vec4(0.);\n vec2 source=trailSource(cell,gridSize,field);\n vec2 encoded=flowGlyph(codes,source,gridSize);\n vec4 color=texture2D(cellColors,source);\n float original=encoded.x;\n float density=trailFieldDensity(field)*trailPin(cell,gridSize),index=trailIndex(original,glyphCount,density,cell);\n if(original>.5) index=clamp(floor(index+ambient.z*(glyphCount-1.)+printGrain(cell)),0.,glyphCount-1.);\n if(index<.5 || color.a<.01) return vec4(0.);\n vec2 slot=vec2(mod(index,atlasColumns),floor(index/atlasColumns));\n float alpha=texture2D(glyphAtlas,(slot*tileSize+padding+local*resolution/sceneSize)/atlasSize).a;\n if(dots>.5) {\n vec2 d=(local-cellSize*.5)/min(cellSize.x,cellSize.y);\n float r=min(.48,(index/(glyphCount-1.))*.5*dotSize);\n alpha=1.-smoothstep(r-.06,r+.02,length(d));\n }\n vec3 coverage=dots>.5?vec3(alpha):edgeCoverage(glyphAtlas,(slot*tileSize+padding+local*resolution/sceneSize)/atlasSize,atlasSize,pixel,sceneSize,edgeEffect);\n coverage*=color.a;alpha=max(coverage.r,max(coverage.g,coverage.b));\n return vec4(trailInk(color.rgb,density)*coverage,alpha);\n }\n void main(){\n vec2 uv = vec2(gl_FragCoord.x, resolution.y - gl_FragCoord.y) / resolution;\n vec2 pixel = uv * sceneSize;\n if (strength > 0.) {\n vec4 normal = texture2D(surface, (uv * (surfaceSize - 1.) + .5) / surfaceSize) * 255.;\n vec2 slope = (vec2(normal.r * 256. + normal.g, normal.b * 256. + normal.a) - 32768.) / 32767.;\n vec2 edge = min(pixel, sceneSize - pixel) / cellSize;\n uv -= slope * strength * mix(1.,smoothstep(2.,8.,min(edge.x,edge.y)),edgeSafe) / sceneSize;\n }\n vec4 ambient=ambientAt(uv,motionMode>9.5?toneAt(uv):.5);\n uv -= ambient.xy/sceneSize;\n pixel=uv*sceneSize;\n vec4 ink = vec4(0.);\n if (directGlyphs > .5) {\n vec2 cell=floor(pixel/cellSize), address=(cell+.5)/gridSize;\n vec4 encoded=texture2D(codes,address), color=texture2D(cellColors,address);\n float index=floor(encoded.r*255.+.5)+floor(encoded.g*255.+.5)*256.;\n if(index>.5 && color.a>.01) {\n vec2 local=(pixel-cell*cellSize)*resolution/sceneSize;\n vec2 slot=vec2(mod(index,atlasColumns),floor(index/atlasColumns));\n float alpha=texture2D(glyphAtlas,(slot*tileSize+padding+local)/atlasSize).a*color.a;\n vec3 coverage=edgeCoverage(glyphAtlas,(slot*tileSize+padding+local)/atlasSize,atlasSize,pixel,sceneSize,edgeEffect)*color.a;\n ink=vec4(color.rgb*coverage,max(coverage.r,max(coverage.g,coverage.b)));\n }\n } else ink = texture2D(artwork, uv);\n if (fineDither > .5) {\n vec2 cell=floor(pixel/cellSize),address=(cell+.5)/gridSize;\n vec4 color=texture2D(cellColors,address);\n float density=trailAmount>0.?trailDensity(address):0.;\n vec2 source=trailAmount>0. && trailKind<.5?trailSource(cell,gridSize,trailField(address)):uv;\n float tone=trailTone(clamp(toneAt(source)+ambient.z,0.,1.),density*(trailKind<.5?trailPin(cell,gridSize):1.));\n float pitch=max(1.,min(cellSize.x,cellSize.y)*.32);\n float grain=.5+(printGrain(floor(pixel/pitch))-.5)*ditherAmount;\n float alpha=(tone<.001?0.:tone>.999?1.:smoothstep(grain-.035,grain+.035,tone))*color.a*ambient.w;\n // Preserve hue; tone is carried by mark coverage rather than muddy ink.\n vec3 hue=color.rgb/max(.001,max(color.r,max(color.g,color.b)));\n if(hasTextMask>.5) hue=mix(hue,vec3(.035),texture2D(textMask,vec2(gl_FragCoord.x,resolution.y-gl_FragCoord.y)/resolution).a);\n gl_FragColor=vec4(hue*alpha,alpha); return;\n }\n if ((trailAmount > 0. && trailKind > .5) || motionMode > .5) {\n vec2 cell=floor(pixel/cellSize), address=(cell+.5)/gridSize;\n float density=trailAmount>0.?trailDensity(address):0.;\n vec4 encoded=texture2D(codes,address);\n float original=floor(encoded.r*255.+.5)+floor(encoded.g*255.+.5)*256.;\n float index=trailIndex(original,glyphCount,density,cell);\n if(original>.5) index=clamp(floor(index+ambient.z*(glyphCount-1.)+printGrain(cell)),0.,glyphCount-1.);\n vec4 color=texture2D(cellColors,address);\n if((index!=original || motionMode>.5) && color.a>.01) {\n vec2 local=(pixel-cell*cellSize)*resolution/sceneSize;\n vec2 slot=vec2(mod(index,atlasColumns),floor(index/atlasColumns));\n float alpha=texture2D(glyphAtlas,(slot*tileSize+padding+local)/atlasSize).a;\n if(dots>.5) {\n vec2 d=(pixel-(cell+.5)*cellSize)/min(cellSize.x,cellSize.y);\n float r=min(.48,(index/(glyphCount-1.))*.5*dotSize);\n alpha=1.-smoothstep(r-.06,r+.02,length(d));\n }\n // Replace the cell, rather than blending two glyphs into a ghost.\n vec3 rgb=trailInk(color.rgb,density);\n ink=vec4(rgb*alpha*color.a,alpha*color.a);\n }\n }\n if(trailAmount>0. && trailKind<.5 && trailDensity((floor(pixel/cellSize)+.5)/gridSize)>.001) {\n ink=flowingCharacter(pixel,floor(pixel/cellSize),ambient);\n }\n vec3 straightInk = ink.a > .0 ? ink.rgb / ink.a : vec3(.0);\n float value = max(straightInk.r, max(straightInk.g, straightInk.b));\n // Lighting belongs to character ink; black/transparent gaps stay empty.\n float light = surfaceLight((floor(pixel / cellSize) + .5) * cellSize / sceneSize, sceneSize);\n vec3 lit = illuminate(straightInk, light * smoothstep(0., .22, value)) * ink.a;\n if(hasTextMask>.5) lit=mix(lit,vec3(.035)*ink.a,texture2D(textMask,vec2(gl_FragCoord.x,resolution.y-gl_FragCoord.y)/resolution).a);\n vec4 result = vec4(lit, ink.a)*ambient.w;\n gl_FragColor = directGlyphs > .5 ? result + backdrop*(1.-result.a) : result;\n }")),c.linkProgram(m),!c.getProgramParameter(m,c.LINK_STATUS))throw new Error("Surface shader link failed");c.useProgram(m),u=c.createBuffer(),c.bindBuffer(c.ARRAY_BUFFER,u),c.bufferData(c.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,1,1]),c.STATIC_DRAW);const e=c.getAttribLocation(m,"position");c.enableVertexAttribArray(e),c.vertexAttribPointer(e,2,c.FLOAT,!1,0,0),O=Object.fromEntries(["finishSoftness","finishMode","finishPixelRatio","edgeEffect","textMask","hasTextMask","artwork","surface","resolution","sceneSize","cellSize","surfaceSize","strength","hoverMode","hoverFocus","codes","cellColors","glyphAtlas","gridSize","atlasSize","tileSize","atlasColumns","padding","glyphCount","dots","dotSize","directGlyphs","backdrop","trailKind","edgeSafe","trailAmount","motionMode","motionTime","fineDither","ditherAmount"].map(t=>[t,c.getUniformLocation(m,t)]));for(let t=0;t<6;t++){const e=c.createTexture();x.push(e),c.activeTexture(c.TEXTURE0+t),c.bindTexture(c.TEXTURE_2D,e),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_MIN_FILTER,c.LINEAR),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_MAG_FILTER,c.LINEAR),c.texImage2D(c.TEXTURE_2D,0,c.RGBA,1,1,0,c.RGBA,c.UNSIGNED_BYTE,new Uint8Array(4)),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_WRAP_S,c.CLAMP_TO_EDGE),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_WRAP_T,c.CLAMP_TO_EDGE)}c.uniform1i(O.textMask,5),c.uniform1i(O.codes,2),c.uniform1i(O.cellColors,3),c.uniform1i(O.glyphAtlas,4),c.uniform1i(O.artwork,0),c.uniform1i(O.surface,1),c.pixelStorei(c.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1),c.disable(c.BLEND),c.disable(c.DEPTH_TEST),l.addEventListener("webglcontextlost",K),W()}catch{K()}const $=()=>{w||(G.reset(),B=null,k=!1,a())};return document.fonts?.addEventListener("loadingdone",$),document.fonts?.ready.then($),{get canvas(){return l},canRenderGlyphs:t=>!!c&&!w&&b(t),render(t,a,o,h,m,u,M,S,W=0,H={}){const $=i(S?.animationStyle),q=N.update(W,$,S?.animationSpeed),V=(t=>!0===t?.fineDither&&!t.customText&&!t.charsetFrames?.length&&!/[A-Za-z]/.test(t.charset??""))(S),j=!!c&&!!M&&b(S);if(j!==F&&(k=!1,F=j),w||!o||!h||!s.width||!s.height)return;const J=Math.min(s.width/o,1.5,Math.sqrt(4e6/(o*h))),Z=Math.max(1,Math.round(o*J)),Q=Math.max(1,Math.round(h*J));if(l.width===Z&&l.height===Q||(l.width=Z,l.height=Q,k=!1),c&&(s.width>T||s.height>T)&&K(),(j||f(a.mode)||"none"!==$||V)&&(t||!k||!B)&&(B=G.update(s,o,h,m,u,J,M,S),c)){const{plan:t,packed:e,atlas:i}=B;for(const[t,i]of[[2,e.indices],[3,e.colors]])c.activeTexture(c.TEXTURE0+t),c.bindTexture(c.TEXTURE_2D,x[t]),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_MIN_FILTER,3===t?c.LINEAR:c.NEAREST),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_MAG_FILTER,3===t?c.LINEAR:c.NEAREST),z!==e.cols||R!==e.rows?c.texImage2D(c.TEXTURE_2D,0,c.RGBA,e.cols,e.rows,0,c.RGBA,c.UNSIGNED_BYTE,i):c.texSubImage2D(c.TEXTURE_2D,0,0,0,e.cols,e.rows,c.RGBA,c.UNSIGNED_BYTE,i);z=e.cols,R=e.rows,A!==t&&(c.activeTexture(c.TEXTURE4),c.bindTexture(c.TEXTURE_2D,x[4]),c.texImage2D(c.TEXTURE_2D,0,c.RGBA,c.RGBA,c.UNSIGNED_BYTE,i),A=t),c.uniform2f(O.gridSize,e.cols,e.rows),c.uniform2f(O.atlasSize,t.atlasWidth,t.atlasHeight),c.uniform2f(O.tileSize,t.tileWidth,t.tileHeight),c.uniform1f(O.atlasColumns,t.atlasColumns),c.uniform1f(O.padding,t.padding),c.uniform1f(O.glyphCount,t.glyphs.length),c.uniform1f(O.dots,B.dots?1:0),c.uniform1f(O.dotSize,B.dotSize)}if(c){c.uniform1f(O.motionMode,(t=>e.findIndex(e=>e.mode===t))($)),c.uniform1f(O.motionTime,q),c.uniform1f(O.fineDither,V?1:0),c.uniform1f(O.ditherAmount,Math.max(0,Math.min(2,S?.ditherStrength??1))),c.uniform1f(O.trailKind,(t=>"etch"===t||"ripple"===t?3:"dissolve"===t?2:"contour"===t?1:0)(a.mode)),c.uniform1f(O.edgeSafe,a.edgeSafe?1:0),c.uniform1f(O.trailAmount,f(a.mode)?a.focus[2]:0),c.viewport(0,0,Z,Q),j||!t&&k||(c.activeTexture(c.TEXTURE0),c.bindTexture(c.TEXTURE_2D,x[0]),c.pixelStorei(c.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!0),c.texImage2D(c.TEXTURE_2D,0,c.RGBA,c.RGBA,c.UNSIGNED_BYTE,s),c.pixelStorei(c.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1)),c.uniform1f(O.directGlyphs,j?1:0),!j||!t&&k||c.uniform4fv(O.backdrop,H.textMask||function(t){const e=t.length,i=t[0]?.length??0;for(let s=0;s<e;s+=Math.max(1,e>>2))for(let e=0;e<i;e+=Math.max(1,i>>2))if(t[s][e].a<200)return!0;return!1}(M)?[0,0,0,0]:E(s)),k=!0,c.activeTexture(c.TEXTURE1),c.bindTexture(c.TEXTURE_2D,x[1]),D!==a.width||C!==a.height?(c.texImage2D(c.TEXTURE_2D,0,c.RGBA,a.width,a.height,0,c.RGBA,c.UNSIGNED_BYTE,a.pixels),D=a.width,C=a.height):c.texSubImage2D(c.TEXTURE_2D,0,0,0,a.width,a.height,c.RGBA,c.UNSIGNED_BYTE,a.pixels),c.uniform2f(O.resolution,Z,Q),c.uniform2f(O.sceneSize,o,h),c.uniform2f(O.cellSize,m,u),c.uniform2f(O.surfaceSize,a.width,a.height),c.uniform1f(O.strength,a.strength),c.uniform1f(O.hoverMode,"light"===a.mode?1:"scan"===a.mode?2:0),c.uniform4fv(O.hoverFocus,a.focus),H.textMask&&H.textMask!==I&&(c.activeTexture(c.TEXTURE5),c.bindTexture(c.TEXTURE_2D,x[5]),c.texImage2D(c.TEXTURE_2D,0,c.RGBA,c.RGBA,c.UNSIGNED_BYTE,H.textMask.canvas),I=H.textMask),c.uniform1f(O.hasTextMask,H.textMask?1:0);const i=function(t={}){const e=y.some(e=>e.value===t.filter)?t.filter:"prism",i=Number.isFinite(t.edgeEffect)?Math.max(0,Math.min(1,t.edgeEffect)):t.filter?1:0,s=Number.isFinite(t.edgeSoftness)?Math.max(0,Math.min(1,t.edgeSoftness)):0;return{mode:["clean","prism","etch","signal"].indexOf(e),amount:"clean"===e?0:i,softness:"prism"===e?s:0}}(H);c.uniform1f(O.edgeEffect,i.amount),c.uniform1f(O.finishMode,i.mode),c.uniform1f(O.finishSoftness,i.softness),c.uniform1f(O.finishPixelRatio,1),c.drawArrays(c.TRIANGLE_STRIP,0,4)}else if(d){if(d.setTransform(J,0,0,J,0,0),d.clearRect(0,0,o,h),d.drawImage(s,0,0,o,h),("none"!==$||V)&&B){d.clearRect(0,0,o,h);const{plan:t,packed:e,atlas:i}=B,s=[0,0,0,1],l=[0,0,0,0,0,0],c=Math.max(1,.32*Math.min(m,u));for(let o=0;o<e.rows;o++)for(let h=0;h<e.cols;h++){const x=(h+.5)/e.cols,M=(o+.5)/e.rows,y=4*(o*e.cols+h);if(e.colors[y+3]<10)continue;r($,x,M,q,s,e.indices[y+2]/255);const w=4*(Math.min(a.height-1,Math.round(M*(a.height-1)))*a.width+Math.min(a.width-1,Math.round(x*(a.width-1)))),b=f(a.mode)?g(a.pixels,w,a.mode,a.focus[2]):0,E=e.indices[y]+256*e.indices[y+1];"trail"===a.mode&&v(e.indices,e.colors,e.cols,e.rows,h,o,b,(a.pixels[w+2]-128)/127,(a.pixels[w+3]-128)/127,t.glyphs.length,l,a.edgeSafe);let k="trail"===a.mode?l[0]:"etch"===a.mode||"ripple"===a.mode?Math.round(E+b*(t.glyphs.length-1)*.35):"dissolve"===a.mode?E-Math.floor(b*t.glyphs.length*1.8):E+Math.floor(b*t.glyphs.length*.85);E>0&&(k+=s[2]*(t.glyphs.length-1)),k=Math.max(0,Math.min(t.glyphs.length-1,Math.floor(k+n(h,o))));let T=s[0],A=s[1];a.strength>0&&(T+=(256*a.pixels[w]+a.pixels[w+1]-32768)/32767*a.strength,A+=(256*a.pixels[w+2]+a.pixels[w+3]-32768)/32767*a.strength);const z=h*m+T,R=o*u+A,D=("trail"===a.mode?l[5]:e.colors[y+3])/255*s[3],C="trail"===a.mode?l[2]:e.colors[y],F="trail"===a.mode?l[3]:e.colors[y+1],X="trail"===a.mode?l[4]:e.colors[y+2],I=V?Math.max(1,C,F,X):255,P=`rgb(${C/I*255},${F/I*255},${X/I*255})`;if(d.fillStyle=P,V){const t="trail"===a.mode?p(Math.max(0,Math.min(1,l[1]+s[2])),2,b):Math.max(0,Math.min(1,e.indices[y+2]/255+s[2]+("etch"===a.mode||"ripple"===a.mode?.35*b:.65*("dissolve"===a.mode?-b:b))));d.globalAlpha=D;for(let e=0;e<u;e+=c)for(let i=0;i<m;i+=c)t>.5+(n(Math.floor((z+i)/c),Math.floor((R+e)/c))-.5)*Math.max(0,Math.min(2,S?.ditherStrength??1))&&d.fillRect(z+i,R+e,Math.min(c,m-i),Math.min(c,u-e))}else if(k>0)if(d.globalAlpha=D,B.dots){const e=Math.min(.48,k/(t.glyphs.length-1)*.5*B.dotSize)*Math.min(m,u);d.beginPath(),d.arc(z+.5*m,R+.5*u,e,0,2*Math.PI),d.fill()}else d.drawImage(i,k%t.atlasColumns*t.tileWidth+t.padding,Math.floor(k/t.atlasColumns)*t.tileHeight+t.padding,t.cellWidth*J,t.cellHeight*J,z,R,m,u),d.globalAlpha=1,d.globalCompositeOperation="source-atop",d.fillRect(z,R,m,u),d.globalCompositeOperation="source-over"}d.globalAlpha=1}else if(f(a.mode)&&B){const{plan:t,packed:e,atlas:i}=B,s=[0,0,0,0,0,0];for(let n=0;n<e.rows;n++)for(let o=0;o<e.cols;o++){const r=4*(Math.min(a.height-1,Math.round((n+.5)/e.rows*(a.height-1)))*a.width+Math.min(a.width-1,Math.round((o+.5)/e.cols*(a.width-1)))),h=g(a.pixels,r,a.mode,a.focus[2]),l=4*(n*e.cols+o),c=.0137*(o*o*17+n*n*23+o*n*19),f=c-Math.floor(c);"trail"===a.mode&&v(e.indices,e.colors,e.cols,e.rows,o,n,h,(a.pixels[r+2]-128)/127,(a.pixels[r+3]-128)/127,t.glyphs.length,s,a.edgeSafe);const p=e.indices[l]+256*e.indices[l+1],x="trail"===a.mode?Math.round(s[0]):"etch"===a.mode||"ripple"===a.mode?Math.max(0,Math.min(t.glyphs.length-1,Math.round(p+h*(t.glyphs.length-1)*.35))):"dissolve"===a.mode?Math.max(0,p-Math.floor(h*t.glyphs.length*(1.35+.9*f))):Math.min(t.glyphs.length-1,p+Math.floor(h*t.glyphs.length*.85));if(x===p&&("trail"!==a.mode||h<1e-4)||e.colors[l+3]<10)continue;const M=o*m,y=n*u;if(d.clearRect(M,y,m,u),d.globalAlpha=("trail"===a.mode?s[5]:e.colors[l+3])/255,B.dots){const e=Math.min(.48,x/(t.glyphs.length-1)*.5*B.dotSize)*Math.min(m,u);d.fillStyle="#fff",d.beginPath(),d.arc(M+.5*m,y+.5*u,e,0,2*Math.PI),d.fill()}else d.drawImage(i,x%t.atlasColumns*t.tileWidth+t.padding,Math.floor(x/t.atlasColumns)*t.tileHeight+t.padding,t.cellWidth*J,t.cellHeight*J,M,y,m,u);d.globalAlpha=1,d.globalCompositeOperation="source-atop";const w="trail"===a.mode?s[2]:e.colors[l],S="trail"===a.mode?s[3]:e.colors[l+1],b="trail"===a.mode?s[4]:e.colors[l+2],E=Math.max(w,S,b),k="dissolve"===a.mode||"trail"===a.mode||"etch"===a.mode||"ripple"===a.mode?E:Math.max(E,Math.min(112,191*h)),T=E?k/E:0;d.fillStyle=`rgb(${E?w*T:k},${E?S*T:k},${E?b*T:k})`,d.fillRect(M,y,m,u),d.globalCompositeOperation="source-over"}}else if("light"===a.mode||"scan"===a.mode){!t&&Y&&P.width===Z&&P.height===Q||(P.width=Z,P.height=Q,_.width=Z,_.height=Q,L.filter="brightness(3)",L.drawImage(s,0,0,Z,Q),L.filter="none",Y=!0);const[e,i,n,r]=a.focus;if(n>.001){U.setTransform(1,0,0,1,0,0),U.clearRect(0,0,Z,Q),U.drawImage(P,0,0),U.globalCompositeOperation="destination-in",U.save(),U.translate(e*Z,i*Q),U.scale("scan"===a.mode?.13:1,1);const t=r*Math.min(Z,Q),s=U.createRadialGradient(0,0,0,0,0,t);s.addColorStop(0,`rgba(255,255,255,${n})`),s.addColorStop(.5,`rgba(255,255,255,${.4*n})`),s.addColorStop(1,"rgba(255,255,255,0)"),U.fillStyle=s,U.fillRect(10*-Z,10*-Q,20*Z,20*Q),U.restore(),U.globalCompositeOperation="source-over",d.globalCompositeOperation="source-atop",d.drawImage(_,0,0,o,h),d.globalCompositeOperation="source-over"}}else if(a.strength>0){const t=32,e=18,i=o/t,n=h/e;for(let r=0;r<e;r++)for(let l=0;l<t;l++){const c=(l+.5)/t,f=(r+.5)/e,g=Math.min(a.width-1,Math.round(c*(a.width-1))),p=4*(Math.min(a.height-1,Math.round(f*(a.height-1)))*a.width+g),v=a.pixels,x=Math.min(l*i/m,(o-(l+1)*i)/m,r*n/u,(h-(r+1)*n)/u),M=Math.max(0,Math.min(1,(x-2)/6)),y=a.edgeSafe?M*M*(3-2*M):1,w=(256*v[p]+v[p+1]-32768)/32767*a.strength*y,S=(256*v[p+2]+v[p+3]-32768)/32767*a.strength*y;Math.abs(w)+Math.abs(S)<.02||(d.clearRect(l*i,r*n,i,n),d.drawImage(s,Math.max(0,Math.min(o-i,l*i-w))*s.width/o,Math.max(0,Math.min(h-n,r*n-S))*s.height/h,i*s.width/o,n*s.height/h,l*i,r*n,i,n))}}X.paint(d,H.textMask,o,h)}},destroy(){w=!0,X.destroy(),document.fonts?.removeEventListener("loadingdone",$),G.destroy(),l.removeEventListener("webglcontextlost",K),H(),l.remove(),P.width=P.height=_.width=_.height=1,s.style.visibility=h,c?.getExtension("WEBGL_lose_context")?.loseContext()}}}function T(e,s,a={}){let n,o,r={effect:"trail",strength:.55,radius:a.effect&&"trail"!==a.effect?.2:.45,animated:!1,fps:30,fontSize:7,charAspect:.58,charSpacing:1,paused:!1,charset:" .:-=+*#%@",customText:"",renderMode:"ascii",dotSizeRatio:.72,...a},h=new M(s.clientWidth/Math.max(1,s.clientHeight)),l=!1,c=!1,d=!0,m=0,u=0,f=-1/0,g=0,p=null,v=!1,x=[];const y=matchMedia("(prefers-reduced-motion: reduce)"),w=()=>{l||!c||document.hidden||m||(m=requestAnimationFrame(b))},S=k(e,s,()=>queueMicrotask(()=>{d=!0,w()}));function b(t){if(m=0,l||!c||document.hidden)return;const e=s.clientWidth,a=s.clientHeight;if(!e||!a)return;const M=y.matches||r.paused;h.configure(r.effect,r.strength,r.radius,r.edgeSafe);const b=h.active;if(!M){if(g+=u?Math.min(.1,(t-u)/1e3):0,v){const t=s.getBoundingClientRect();if(t.width&&t.height)for(const e of x)h.move((e.x-t.left)/t.width,(e.y-t.top)/t.height,e.time);x=[],p||h.leave(),v=!1}h.step(u?(t-u)/1e3:1/60)}const E=d||r.animated&&!M&&t-f>=1e3/Math.max(1,Math.min(60,r.fps))-.5;(E||b||h.active||!M&&"none"!==i(r.motion))&&(E&&(n=r.getFrame?.()),S.render(E,h.refraction,e,a,r.fontSize*r.charSpacing,r.fontSize/r.charAspect*r.charSpacing,n,{charset:r.charset,customText:r.customText,renderMode:r.renderMode,dotSizeRatio:r.dotSizeRatio,colorMode:r.colorMode??"fullcolor",accentColor:r.accentColor??"#888888",animationStyle:r.motion,animationSpeed:r.motionSpeed??1,artStyle:"classic",fineDither:r.fineDither,ditherStrength:r.ditherStrength??1},g,{filter:r.filter,edgeEffect:r.edgeEffect,edgeSoftness:r.edgeSoftness,textMask:o?.read()}),E&&(f=t),d=!1),u=t,M||!r.animated&&!h.active&&"none"===i(r.motion)||w()}r.textMask&&(o=t(e,r.textMask,()=>{d=!0,w()}));const E=()=>{h.clear(),p=null,x=[],v=!1,u=0,d=!0,cancelAnimationFrame(m),m=0,w()},T=t=>{if("touch"===t.pointerType||r.paused||y.matches)return;if(t.target instanceof Element&&t.target.closest("button,a,input,summary,select"))return void A();const e=s.getBoundingClientRect();t.clientX<e.left||t.clientX>e.right||t.clientY<e.top||t.clientY>e.bottom?A():(p={x:t.clientX,y:t.clientY,time:t.timeStamp},x.push(p),x.length>6&&x.shift(),v=!0,w())},A=()=>{p=null,v=!0,w()},z=new IntersectionObserver(([t])=>{c=t.isIntersecting,E()});z.observe(e);const R=new ResizeObserver(()=>{h=new M(s.clientWidth/Math.max(1,s.clientHeight)),E()});return R.observe(s),e.addEventListener("pointermove",T),e.addEventListener("pointerleave",A),e.addEventListener("pointercancel",A),document.addEventListener("visibilitychange",E),y.addEventListener("change",E),{get canvas(){return S.canvas},invalidate(){o?.invalidate(),d=!0,w()},update(i){"textMask"in i&&(o?.destroy(),o=i.textMask?t(e,i.textMask,()=>{d=!0,w()}):void 0),r={...r,...i},E()},destroy(){l||(l=!0,cancelAnimationFrame(m),z.disconnect(),R.disconnect(),S.destroy(),o?.destroy(),e.removeEventListener("pointermove",T),e.removeEventListener("pointerleave",A),e.removeEventListener("pointercancel",A),document.removeEventListener("visibilitychange",E),y.removeEventListener("change",E))}}}exports.STUDIO_ANIMATION_PRESETS=[{id:"quiet-light",label:"Quiet light",description:"Stationary detail with slowly turning light and a soft magnifying wake.",motion:{type:"relight",speed:.6,amount:.7},hover:{effect:"lens",radius:.45,strength:.5}},{id:"living-paper",label:"Living paper",description:"A gentle traveling bend and a directional pull under your cursor.",motion:{type:"breeze",speed:.8,amount:.6},hover:{effect:"smudge",radius:.4,strength:.5}},{id:"silver-grain",label:"Silver grain",description:"Slow highlight glints with a fluid character trail.",motion:{type:"shimmer",speed:.75,amount:.85},hover:{effect:"trail",radius:.35,strength:.55}},{id:"ripple-study",label:"Ripple study",description:"Soft moving light and expanding tonal rings on an anchored grid.",motion:{type:"caustics",speed:.6,amount:.55},hover:{effect:"ripple",radius:.45,strength:.75}},{id:"unfolding-print",label:"Unfolding print",description:"Panels turn and reassemble; your cursor dissolves a soft path.",motion:{type:"unfold",speed:.7,amount:.75},hover:{effect:"dissolve",radius:.35,strength:.55}},{id:"silk-flow",label:"Silk flow",description:"A slow current with directional folds that follow your hand.",motion:{type:"current",speed:.6,amount:.55},hover:{effect:"silk",radius:.4,strength:.5}}],exports.STUDIO_HOVERS=[{value:"none",label:"Off",group:"Still",description:"Keep the artwork still under the pointer."},{value:"trail",label:"Trail",group:"Change characters",description:"A fluid wake changes character density and settles behind your cursor."},{value:"dissolve",label:"Dissolve",group:"Change characters",description:"Erase a soft path through the characters; the image gradually reforms."},{value:"contour",label:"Contour",group:"Change characters",description:"Fine tonal rings trace your path without moving the grid."},{value:"etch",label:"Emboss",group:"Change characters",description:"Press a light-and-shadow impression into the characters; the grid stays fixed."},{value:"ripple",label:"Ripple rings",group:"Change characters",description:"Expanding light-and-shadow rings follow your stroke. Faster movement excites stronger rings; the grid stays still."},{value:"water",label:"Water",group:"Move the image",description:"Soft ripples refract the image and settle after your stroke."},{value:"silk",label:"Silk",group:"Move the image",description:"Directional folds follow the movement of your hand."},{value:"vortex",label:"Vortex",group:"Move the image",description:"A swirling wake curls through the image."},{value:"magnetic",label:"Magnetic Pull",group:"Move the image",description:"The image gathers toward your stroke, then relaxes."},{value:"scatter",label:"Scatter",group:"Move the image",description:"A textured outward scatter settles behind your cursor."},{value:"elastic",label:"Elastic",group:"Move the image",description:"Push the image with your stroke; it recoils and springs back into place."},{value:"rake",label:"Rake",group:"Move the image",description:"Comb the image into alternating ribbons that glide back into alignment."},{value:"lens",label:"Lens wake",group:"Move the image",description:"A soft magnifying wake brings details closer, then relaxes back into focus."},{value:"smudge",label:"Smudge",group:"Move the image",description:"Drag the image in the direction of your stroke; a smooth, lingering pull settles without a bounce."}],exports.STUDIO_MOTIONS=e,exports.createTextMask=t,exports.mountHover=T,exports.mountStudioHover=T;//# sourceMappingURL=hover.cjs.map
1
+ "use strict";function t(t,e,i=()=>{}){const s=Array.isArray(e)?e:[e],a=document.createElement("canvas"),o=a.getContext("2d");let n,h=!0,r=!1,l=0;const c=()=>{r||(h=!0,i())},d=new ResizeObserver(c);d.observe(t),s.forEach(t=>d.observe(t));const u=new MutationObserver(c);return s.forEach(t=>u.observe(t,{subtree:!0,childList:!0,characterData:!0,attributes:!0})),document.fonts?.addEventListener("loadingdone",c),document.fonts?.ready.then(c),window.addEventListener("resize",c),{invalidate:c,read(){if(r)return;if(!h)return n;h=!1;const e=t.getBoundingClientRect(),i=Math.min(devicePixelRatio||1,1.5,Math.sqrt(4e6/Math.max(1,e.width*e.height)));a.width=Math.max(1,Math.round(e.width*i)),a.height=Math.max(1,Math.round(e.height*i)),o.setTransform(i,0,0,i,0,0),o.fillStyle="#fff",o.textBaseline="alphabetic";for(const t of s){const i=document.createTreeWalker(t,NodeFilter.SHOW_TEXT);for(let t=i.nextNode();t;t=i.nextNode()){const i=t.parentElement;if(!i)continue;const s=getComputedStyle(i);if("hidden"===s.visibility||"none"===s.display)continue;o.font=`${s.fontStyle} ${s.fontWeight} ${s.fontSize} ${s.fontFamily}`;const a=o.measureText("Hg"),n=a.fontBoundingBoxAscent??.8*parseFloat(s.fontSize),h=a.fontBoundingBoxDescent??.2*parseFloat(s.fontSize),r=t.textContent??"",l=document.createRange();let c=0;for(const i of r){if(l.setStart(t,c),c+=i.length,l.setEnd(t,c),/\s/.test(i))continue;const s=l.getBoundingClientRect();s.width&&s.height&&o.fillText(i,s.left-e.left,s.top-e.top+(s.height-n-h)/2+n)}}}return n={canvas:a,revision:++l},n},destroy(){r=!0,d.disconnect(),u.disconnect(),document.fonts?.removeEventListener("loadingdone",c),window.removeEventListener("resize",c),a.width=a.height=1,n=void 0}}}var e={caustics:"sheen",grain:"shimmer",trace:"relight",tidal:"current",weave:"current",breeze:"current",reform:"develop",print:"develop",unfold:"develop"},i=[{value:"none",mode:"none",group:"Still",label:"Off",description:"Keep the image still. Hover stays available."},{value:"relight",mode:"relight",group:"Light",label:"Relight",description:"A key light circles the subject and models its own relief, like moving a lamp around a sculpture."},{value:"sheen",mode:"sheen",group:"Light",label:"Specular sweep",description:"One slow pass of light glances across the highlights and follows their form, then rests."},{value:"glow",mode:"glow",group:"Light",label:"Highlight bloom",description:"The brightest areas breathe a soft halo into their surroundings. Shadows stay deep."},{value:"shimmer",mode:"shimmer",group:"Light",label:"Glints",description:"Sparse glints on bright fine detail, like light on water, metal or an eye. Everything else holds still."},{value:"parallax",mode:"parallax",group:"Depth",label:"Parallax",description:"A slow camera drift: the subject moves against its background, read from the photo’s own depth cues."},{value:"push",mode:"push",group:"Depth",label:"Slow push",description:"A gentle push toward the subject and back, with nearer areas moving slightly more."},{value:"focus",mode:"focus",group:"Depth",label:"Focus pull",description:"Focus racks from the subject to the background and back; the characters soften out of focus."},{value:"mist",mode:"mist",group:"Atmosphere",label:"Mist",description:"Soft mist drifts through the distance and plain areas, leaving the subject clear."},{value:"current",mode:"current",group:"Atmosphere",label:"Drift",description:"Sky, water and smooth grounds flow slowly, like a cinemagraph; detailed areas hold still."},{value:"develop",mode:"develop",group:"Reveal",label:"Develop",description:"The image holds, fades, then develops back in from its highlights to its shadows, like a print in the tray."}];function s(t){const s=t&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:t;return i.find(t=>t.value===s||t.mode===s)?.mode??"none"}var a=t=>Math.max(0,Math.min(1,t)),o=(t,e,i)=>{const s=a((i-t)/(e-t));return s*s*(3-2*s)},n=(t,e)=>{const i=(.06711056*t+.00583715*e)%1*52.9829189;return i-Math.floor(i)},h=class{constructor(){this.last=null,this.mode="none",this.phase=0}update(t,e,i=1){const a=s(e);return Number.isFinite(t)?((a!==this.mode||null!==this.last&&t<this.last)&&(this.mode=a,this.phase=0,this.last=t),null!==this.last&&(this.phase+=Math.max(0,t-this.last)*Math.max(.1,Math.min(3,Number.isFinite(i)?i:1))),this.last=t,this.phase):this.phase}};function r(t,e,i,s,h,r=.5,l,c=0){if(h[0]=h[1]=h[2]=0,h[3]=1,"none"===t)return h;r=a(r);const d=l?l[c]:r,u=l?l[c+1]:.5+.5*a(1-2*Math.hypot(e-.5,i-.55)),m=l?l[c+2]:o(.55,1,r)**2,f=l?l[c+3]:.5,g=l?l[c+4]:0,p=l?l[c+5]:0,M=s*Math.PI/6,v=(s%12+12)%12,x=o(0,.12,Math.min(e,1-e,i,1-i));if("relight"===t){const t=M+.6,s=Math.cos(t),a=Math.sin(t),n=Math.max(-1,Math.min(1,g*s+p*a))+.35*(s*(e-.5)+a*(i-.5));h[2]=.2*n*(.3+.7*o(.04,.3,d)*(1-.6*o(.82,1,r)))}else if("sheen"===t){const t=1.9*o(0,8,v)-.35,s=(.75*e+.45*i)/1.2+.05*(.75*g+.45*p),a=Math.exp(-(((s-t)/.12)**2));h[2]=a*o(.3,.8,d)*(1-.5*o(.9,1,r))*.45}else if("glow"===t){const t=.5-.5*Math.cos(M),e=1-.8*t;h[2]=.2*o(e-.3,e+.02,m)*Math.sqrt(m)*(1-.65*r)+.05*t*o(.6,.95,r)}else if("shimmer"===t){const t=n(Math.floor(160*e),Math.floor(100*i))*Math.PI*2,s=Math.pow(.5+.5*Math.cos(2*M+t),28);h[2]=s*o(.5,.88,r)*(.35+.65*o(.1,.55,f))*.42}else if("parallax"===t){const t=u-.45;h[0]=16*t*Math.sin(M)*x,h[1]=7*t*Math.sin(2*M)*x}else if("push"===t){const t=.045*(.5-.5*Math.cos(M))*(.7+.6*u)*960,s=l?l[c+6]:.5,a=l?l[c+7]:.5;h[0]=(e-s)*t,h[1]=(i-a)*t}else if("focus"===t){const t=Math.sin(M),e=t*t*o(.15,.6,t>=0?1-u:u);h[2]=(d-r)*e*.9}else if("mist"===t){const t=Math.pow(1-u,1.5)*(1-.6*f),s=.5*Math.sin(4.1*e-M+.9*Math.sin(3.3*i+M))+.3*Math.sin(7.3*e+2.1*i-2*M+1.1*Math.sin(2.7*e-5.1*i+M))+.2*Math.sin(5.7*i-1.9*e+M);h[2]=o(-.3,.9,s)*t*.16*(1-.55*r)}else if("current"===t){const t=(1-.4*f)*(1-.8*u)*x,s=t=>14*i-t+1.5*Math.sin(3*e+t);h[0]=7*(Math.sin(s(M))-Math.sin(s(0))+.43*(Math.sin(5*e-3*i+M)-Math.sin(5*e-3*i)))*t,h[1]=2*(Math.sin(11*e+M)-Math.sin(11*e))*t,h[2]=.03*(Math.cos(s(M))-Math.cos(s(0)))*t}else if("develop"===t){const t=o(7,8.5,v),s=a((v-8.5)/3.5),l=v<8.5?1-t:o(0,1,(1.45*s-(1-r)+.06*(n(Math.floor(160*e),Math.floor(100*i))-.5))/.3);h[2]=.3*-(1-l),h[3]=1-.9*(1-l)}return h}function l(t,e,i){const s=Math.min(t*Math.max(1,i),(1-t)*Math.max(1,i),e*Math.max(1,1/i),(1-e)*Math.max(1,1/i)),a=Math.max(0,Math.min(1,(s-.035)/.105));return a*a*a*(a*(6*a-15)+10)}var c=class{constructor(t,e){this.edgeSafe=!1,this.rings=new Float64Array(40),this.slot=0,this.clock=0,this.lastRing=-1,this.previous=null,this.alive=!1,this.width=t,this.height=e,this.pixels=new Uint8Array(t*e*4),this.values=new Float32Array(t*e),this.clear()}get active(){return this.alive}clear(){this.rings.fill(0),this.values.fill(0),this.pixels.fill(0);for(let t=0;t<this.pixels.length;t+=4)this.pixels[t]=128;this.previous=null,this.alive=!1,this.slot=0,this.lastRing=-1,this.clock=0}leave(){this.previous=null}move(t,e,i,s){if(!Number.isFinite(t+e+i))return;t=Math.max(0,Math.min(1,t)),e=Math.max(0,Math.min(1,e));const a=Number.isFinite(s)?s/1e3:this.clock,o=this.previous;if(this.previous={x:t,y:e,time:a},!o)return;const n=this.width/this.height,h=Math.hypot((t-o.x)*Math.max(1,n),(e-o.y)*Math.max(1,1/n));if(h<1e-4||this.clock-this.lastRing<1/24)return;const r=h/Math.max(1/240,Math.min(.1,a-o.time||1/60)),l=5*this.slot;this.slot=(this.slot+1)%8,this.rings[l]=t,this.rings[l+1]=e,this.rings[l+2]=0,this.rings[l+3]=Math.min(.9,.26+.2*r),this.rings[l+4]=.035+.13*Math.max(.1,Math.min(1,i)),this.alive=!0,this.lastRing=this.clock}step(t){if(!Number.isFinite(t)||t<=0)return;const e=Math.min(.05,t);if(this.clock+=e,!this.alive)return;this.values.fill(0),this.alive=!1;const i=this.width/this.height,s=Math.max(1,i),a=Math.max(1,1/i);for(let t=0;t<this.rings.length;t+=5){if(!this.rings[t+3])continue;const i=this.rings[t+2]+=e;if(i>2.8){this.rings[t+3]=0;continue}this.alive=!0;const o=this.rings[t+4],n=.17*i,h=this.rings[t+3]*(1-Math.exp(20*-i))*Math.exp(2.5*-i),r=n+1.8*o,l=this.rings[t],c=this.rings[t+1],d=Math.max(0,Math.floor((l-r/s)*(this.width-1))),u=Math.min(this.width-1,Math.ceil((l+r/s)*(this.width-1))),m=Math.max(0,Math.floor((c-r/a)*(this.height-1))),f=Math.min(this.height-1,Math.ceil((c+r/a)*(this.height-1)));for(let t=m;t<=f;t++)for(let e=d;e<=u;e++){const i=(Math.hypot((e/(this.width-1)-l)*s,(t/(this.height-1)-c)*a)-n)/o;Math.abs(i)>1.8||(this.values[t*this.width+e]+=h*Math.cos(i*Math.PI*2)*Math.exp(-i*i*2.5))}}for(let t=0;t<this.height;t++)for(let e=0;e<this.width;e++){const s=t*this.width+e,a=this.values[s]=Math.max(-1,Math.min(1,this.values[s]))*(this.edgeSafe?l(e/(this.width-1),t/(this.height-1),i):1),o=Math.round(32767*a+32768);this.pixels[4*s]=o>>>8,this.pixels[4*s+1]=255&o}}sample(t,e,i){const s=Math.max(0,Math.min(this.width-1.001,t*(this.width-1))),a=Math.max(0,Math.min(this.height-1.001,e*(this.height-1))),o=Math.floor(s),n=Math.floor(a),h=s-o,r=a-n,l=n*this.width+o,c=l+this.width;i[0]=i[1]=0,i[2]=3*((this.values[l]*(1-h)+this.values[l+1]*h)*(1-r)+(this.values[c]*(1-h)+this.values[c+1]*h)*r)}},d=t=>["dissolve","silk","vortex","magnetic","scatter","etch","elastic","lens","smudge"].includes(t),u=t=>"elastic"===t||"scatter"===t,m={elastic:[150,4.2],scatter:[90,3.6]},f=class{constructor(t,e){this.edgeSafe=!1,this.previous=null,this.peak=0,this.scatterDirections=null,this.springX=null,this.springY=null,this.mode="dissolve",this.width=t,this.height=e;const i=t*e;this.pixels=new Uint8Array(4*i),this.ink=new Float32Array(i),this.targetX=new Float32Array(i),this.targetY=new Float32Array(i),this.offsetX=new Float32Array(i),this.offsetY=new Float32Array(i),this.edge=new Float32Array(i);for(let i=0;i<e;i++)for(let s=0;s<t;s++)this.edge[i*t+s]=l(s/(t-1),i/(e-1),t/e);this.clear()}get active(){return this.peak>2e-4}setMode(t){if(u(t)&&!this.springX&&(this.springX=new Float32Array(this.ink.length),this.springY=new Float32Array(this.ink.length)),"scatter"===t&&!this.scatterDirections){const t=this.scatterDirections=new Float64Array(this.width*this.height*2),e=(t,e)=>{const i=43758.5453*Math.sin(127.1*t+311.7*e);return i-Math.floor(i)};for(let i=0;i<this.height;i++)for(let s=0;s<this.width;s++){const a=.09*s,o=.09*i,n=Math.floor(a),h=Math.floor(o),r=a-n,l=o-h,c=r*r*(3-2*r),d=l*l*(3-2*l),u=((e(n,h)*(1-c)+e(n+1,h)*c)*(1-d)+(e(n,h+1)*(1-c)+e(n+1,h+1)*c)*d)*Math.PI*4,m=2*(i*this.width+s);t[m]=.7*Math.cos(u),t[m+1]=.7*Math.sin(u)}}t!==this.mode&&(this.mode=t,this.clear())}clear(){for(const t of[this.ink,this.targetX,this.targetY,this.offsetX,this.offsetY,this.pixels])t.fill(0);if(this.previous=null,this.peak=0,this.springX?.fill(0),this.springY?.fill(0),"dissolve"!==this.mode)for(let t=0;t<this.pixels.length;t+=4)this.pixels[t]=this.pixels[t+2]=128}leave(){this.previous=null}move(t,e,i){if(!Number.isFinite(t+e+i))return;t=Math.max(0,Math.min(1,t)),e=Math.max(0,Math.min(1,e));const s=this.previous;this.previous={x:t,y:e};const a=this.width,o=this.height,n=Math.max(1,a/o),h=Math.max(1,o/a),r=s?(t-s.x)*n:0,l=s?(e-s.y)*h:0,c=Math.hypot(r,l);if(!s&&"dissolve"!==this.mode&&"etch"!==this.mode&&"lens"!==this.mode)return;if(s&&c<1e-4)return;const d=.035+.11*Math.max(.1,Math.min(1,i)),u=Math.min(128,Math.max(1,Math.ceil(c/(.45*d)))),m=c?r/c:1,f=c?l/c:0,g=s||"lens"!==this.mode?Math.min(1,c/(d*u)*2.2):.32;for(let i=0;i<u;i++){const r=(i+.5)/u,l=s?s.x+(t-s.x)*r:t,c=s?s.y+(e-s.y)*r:e,p=Math.max(0,Math.floor((l-2.5*d/n)*(a-1))),M=Math.min(a-1,Math.ceil((l+2.5*d/n)*(a-1))),v=Math.max(0,Math.floor((c-2.5*d/h)*(o-1))),x=Math.min(o-1,Math.ceil((c+2.5*d/h)*(o-1)));for(let t=v;t<=x;t++)for(let e=p;e<=M;e++){const i=(e/(a-1)-l)*n/d,s=(t/(o-1)-c)*h/d,r=Math.exp(1.5*-(i*i+s*s)),u=t*a+e;if("dissolve"===this.mode||"etch"===this.mode)this.ink[u]=Math.max(this.ink[u],r);else if("elastic"===this.mode)this.springX[u]=Math.max(-24,Math.min(24,this.springX[u]+m*r*g*12)),this.springY[u]=Math.max(-24,Math.min(24,this.springY[u]+f*r*g*12));else if("scatter"===this.mode){const t=this.scatterDirections;this.springX[u]=Math.max(-24,Math.min(24,this.springX[u]+(.6*i+t[2*u])*r*g*7)),this.springY[u]=Math.max(-24,Math.min(24,this.springY[u]+(.6*s+t[2*u+1])*r*g*7))}else if("lens"===this.mode)this.targetX[u]=Math.max(-.65,Math.min(.65,this.targetX[u]+i*r*g*1.6)),this.targetY[u]=Math.max(-.65,Math.min(.65,this.targetY[u]+s*r*g*1.6));else if("smudge"===this.mode)this.targetX[u]=Math.max(-1,Math.min(1,this.targetX[u]+m*r*g*1.3)),this.targetY[u]=Math.max(-1,Math.min(1,this.targetY[u]+f*r*g*1.3));else{const t=(-i*f+s*m)*r*2.8,e="magnetic"===this.mode?-i*r*2.5:"silk"===this.mode?m*t:-s*r*2.5,a="magnetic"===this.mode?-s*r*2.5:"silk"===this.mode?f*t:i*r*2.5;this.targetX[u]=Math.max(-1,Math.min(1,this.targetX[u]+e*g)),this.targetY[u]=Math.max(-1,Math.min(1,this.targetY[u]+a*g))}}}this.peak=1}step(t){if(!this.active||!Number.isFinite(t))return;const e=Math.max(0,Math.min(.05,t)),i="dissolve"===this.mode?2.15:"smudge"===this.mode?2.4:3.2,s="lens"===this.mode?16:22,a=Math.exp(-i*e),o=1-Math.exp(-s*e);let n=0;const[h,r]="scatter"===this.mode?m.scatter:m.elastic,l=Math.sqrt(h-r*r),c=Math.exp(-r*e),d=Math.cos(l*e),f=Math.sin(l*e)/l;for(let t=0;t<this.ink.length;t++)if("etch"===this.mode)this.ink[t]*=a,n=Math.max(n,this.ink[t]);else if("dissolve"===this.mode){this.ink[t]*=a,n=Math.max(n,this.ink[t]);const e=Math.round(65535*this.ink[t]);this.pixels[4*t]=e>>>8,this.pixels[4*t+1]=255&e}else{if(u(this.mode)){const e=this.offsetX[t],i=this.offsetY[t],s=this.springX[t],a=this.springY[t];this.offsetX[t]=Math.max(-1,Math.min(1,c*(e*d+(s+r*e)*f))),this.offsetY[t]=Math.max(-1,Math.min(1,c*(i*d+(a+r*i)*f))),this.springX[t]=c*(s*d-(r*s+h*e)*f),this.springY[t]=c*(a*d-(r*a+h*i)*f),n=Math.max(n,.05*Math.abs(this.springX[t]),.05*Math.abs(this.springY[t]))}else if("lens"===this.mode||"smudge"===this.mode){const e=1-o,n=s/(s-i)*(a-e);this.offsetX[t]=this.offsetX[t]*e+this.targetX[t]*n,this.offsetY[t]=this.offsetY[t]*e+this.targetY[t]*n,this.targetX[t]*=a,this.targetY[t]*=a}else this.targetX[t]*=a,this.targetY[t]*=a,this.offsetX[t]+=(this.targetX[t]-this.offsetX[t])*o,this.offsetY[t]+=(this.targetY[t]-this.offsetY[t])*o;n=Math.max(n,Math.abs(this.offsetX[t]),Math.abs(this.offsetY[t]),Math.abs(this.targetX[t]),Math.abs(this.targetY[t]));const e=Math.round(this.offsetX[t]*(this.edgeSafe?this.edge[t]:1)*32767+32768),l=Math.round(this.offsetY[t]*(this.edgeSafe?this.edge[t]:1)*32767+32768);this.pixels[4*t]=e>>>8,this.pixels[4*t+1]=255&e,this.pixels[4*t+2]=l>>>8,this.pixels[4*t+3]=255&l}if("etch"===this.mode)for(let t=0;t<this.height;t++)for(let e=0;e<this.width;e++){const i=t*this.width+e,s=3*(this.ink[t*this.width+Math.max(0,e-1)]-this.ink[t*this.width+Math.min(this.width-1,e+1)]+this.ink[Math.max(0,t-1)*this.width+e]-this.ink[Math.min(this.height-1,t+1)*this.width+e]);this.offsetX[i]=Math.max(-1,Math.min(1,s))*(this.edgeSafe?this.edge[i]:1);const a=Math.round(32767*this.offsetX[i]+32768);this.pixels[4*i]=a>>>8,this.pixels[4*i+1]=255&a}this.peak=n,this.active||this.clear()}sample(t,e,i){const s=Math.max(0,Math.min(this.width-1.001,t*(this.width-1))),a=Math.max(0,Math.min(this.height-1.001,e*(this.height-1))),o=Math.floor(s),n=Math.floor(a),h=s-o,r=a-n,c=n*this.width+o,d=c+this.width,u=t=>(t[c]*(1-h)+t[c+1]*h)*(1-r)+(t[d]*(1-h)+t[d+1]*h)*r;if("etch"===this.mode)return i[0]=i[1]=0,void(i[2]=3*u(this.offsetX));i[0]=u(this.offsetX)*(this.edgeSafe?l(t,e,this.width/this.height):1)*.08,i[1]=u(this.offsetY)*(this.edgeSafe?l(t,e,this.width/this.height):1)*.08,i[2]="dissolve"===this.mode?6*-u(this.ink):0}},g=class{constructor(t,e){this.rings=[],this.previous=null,this.elapsed=1,this.echoPeak=0,this.width=t,this.height=e,this.pixels=new Uint8Array(t*e*4),this.echo=new Float32Array(t*e)}get active(){return this.rings.length>0||this.echoPeak>2e-4}clear(){this.rings=[],this.previous=null,this.elapsed=1,this.pixels.fill(0),this.echo.fill(0),this.echoPeak=0}leave(){this.previous=null}move(t,e,i){const s=Math.max(1,this.width/this.height),a=Math.max(1,this.height/this.width);this.previous&&(Math.hypot((t-this.previous.x)*s,(e-this.previous.y)*a)<.065||this.elapsed<.3)||4!==this.rings.length&&(this.previous={x:t,y:e},this.elapsed=0,this.rings.push({x:t,y:e,age:0,radius:i}))}ringSample(t,e){const i=Math.max(1,this.width/this.height),s=Math.max(1,this.height/this.width);let a=0;for(const o of this.rings){const n=.015+o.age*(.15+.22*o.radius),h=.009+.014*o.radius,r=(Math.hypot((t-o.x)*i,(e-o.y)*s)-n)/h,l=Math.sin(Math.min(1,o.age/.07)*Math.PI/2)*Math.max(0,1-o.age/1.35)**2;a=Math.max(a,Math.exp(-r*r)*l)}return a}sample(t,e){const i=Math.max(0,Math.min(this.width-1,Math.round(t*(this.width-1)))),s=Math.max(0,Math.min(this.height-1,Math.round(e*(this.height-1))));return Math.max(this.ringSample(t,e),this.echo[s*this.width+i])}step(t){if(!Number.isFinite(t))return;const e=Math.max(0,Math.min(.05,t));this.elapsed+=e;for(const t of this.rings)t.age+=e;if(this.rings=this.rings.filter(t=>t.age<1.35),!this.active)return void this.pixels.fill(0);const i=Math.exp(-5.5*e);let s=0;for(let t=0;t<this.height;t++)for(let e=0;e<this.width;e++){const a=t*this.width+e;this.echo[a]=Math.max(this.ringSample(e/(this.width-1),t/(this.height-1)),this.echo[a]*i),s=Math.max(s,this.echo[a]);const o=Math.round(65535*this.echo[a]),n=4*a;this.pixels[n]=o>>>8,this.pixels[n+1]=255&o}this.echoPeak=s,this.active||(this.echo.fill(0),this.pixels.fill(0))}},p=class{constructor(t,e){this.previous=null,this.peak=0,this.width=t,this.height=e;const i=t*e;this.pixels=new Uint8Array(4*i),this.signal=new Float32Array(i),this.offsetX=new Float32Array(i),this.offsetY=new Float32Array(i),this.driftX=new Float32Array(i),this.driftY=new Float32Array(i),this.u=new Float32Array(i),this.v=new Float32Array(i),this.ink=new Float32Array(i),this.a=new Float32Array(i),this.b=new Float32Array(i),this.c=new Float32Array(i),this.pressure=new Float32Array(i),this.pressureNext=new Float32Array(i),this.divergence=new Float32Array(i),this.curl=new Float32Array(i)}get active(){return this.peak>2e-4}clear(){for(const t of[this.u,this.v,this.ink,this.a,this.b,this.c,this.pressure,this.pressureNext,this.divergence,this.curl,this.signal,this.pixels,this.offsetX,this.offsetY,this.driftX,this.driftY])t.fill(0);this.previous=null,this.peak=0}leave(){this.previous=null}move(t,e,i,s=performance.now()){if(!Number.isFinite(t+e+i+s))return;t=Math.max(0,Math.min(1,t)),e=Math.max(0,Math.min(1,e)),i=Math.max(.1,Math.min(1,i));const a=this.previous;if(this.previous={x:t,y:e,time:s},!a)return;const o=(t-a.x)*(this.width-1),n=(e-a.y)*(this.height-1),h=Math.hypot(o,n);if(h<.001)return;const r=Math.min(this.width,this.height),l=s>a.time?Math.max(1/240,(s-a.time)/1e3):1/60,c=Math.min(12,h/r/l),d=1-Math.exp(-c/2.2),u=r*(.022+.075*i)*(.65+.95*d),m=Math.min(128,Math.max(1,Math.ceil(h/(.4*u)))),f=h/u*(.32+.22*d)/m,g=o/h,p=n/h,M=-p,v=g,x=h*(2+18*d)/m;for(let i=0;i<m;i++){const s=(i+.5)/m,o=(a.x+(t-a.x)*s)*(this.width-1),n=(a.y+(e-a.y)*s)*(this.height-1),h=Math.max(1,Math.floor(o-3*u)),r=Math.min(this.width-2,Math.ceil(o+3*u)),l=Math.max(1,Math.floor(n-3*u)),c=Math.min(this.height-2,Math.ceil(n+3*u));for(let t=l;t<=c;t++)for(let e=h;e<=r;e++){const i=((e-o)*g+(t-n)*p)/u,s=((e-o)*M+(t-n)*v)/u,a=Math.exp(-i*i*.65-s*s*1.7),h=t*this.width+e;this.ink[h]=1-(1-this.ink[h])*Math.exp(-a*f);const r=1-s*s*.55,l=i*s*.7;this.u[h]=Math.max(-80,Math.min(80,this.u[h]+(g*r+M*l)*a*x)),this.v[h]=Math.max(-80,Math.min(80,this.v[h]+(p*r+v*l)*a*x))}}this.peak=1}read(t,e,i){const s=0|(e=Math.max(.5,Math.min(this.width-1.5,e))),a=0|(i=Math.max(.5,Math.min(this.height-1.5,i))),o=e-s,n=i-a,h=a*this.width+s;return(t[h]*(1-o)+t[h+1]*o)*(1-n)+(t[h+this.width]*(1-o)+t[h+this.width+1]*o)*n}sample(t,e){return this.read(this.signal,t*(this.width-1),e*(this.height-1))}displacement(t,e,i){i[0]=this.read(this.offsetX,t*(this.width-1),e*(this.height-1)),i[1]=this.read(this.offsetY,t*(this.width-1),e*(this.height-1))}step(t){if(!this.active||!Number.isFinite(t))return;let e=Math.max(0,Math.min(.05,t));const i=this.width,s=this.height;for(;e>1e-7;){const t=Math.min(1/60,e);e-=t;const a=Math.exp(-1.9*t),o=Math.exp(-3.2*t);for(let e=1;e<s-1;e++)for(let n=1;n<i-1;n++){const h=e*i+n,r=Math.max(.5,Math.min(i-1.5,n-this.u[h]*t)),l=Math.max(.5,Math.min(s-1.5,e-this.v[h]*t)),c=0|r,d=0|l,u=r-c,m=l-d,f=d*i+c,g=(1-u)*(1-m),p=u*(1-m),M=(1-u)*m,v=u*m;this.a[h]=(this.u[f]*g+this.u[f+1]*p+this.u[f+i]*M+this.u[f+i+1]*v)*a,this.b[h]=(this.v[f]*g+this.v[f+1]*p+this.v[f+i]*M+this.v[f+i+1]*v)*a,this.c[h]=(this.ink[f]*g+this.ink[f+1]*p+this.ink[f+i]*M+this.ink[f+i+1]*v)*o}[this.u,this.a]=[this.a,this.u],[this.v,this.b]=[this.b,this.v],[this.ink,this.c]=[this.c,this.ink];for(let t=1;t<s-1;t++)for(let e=1;e<i-1;e++){const s=t*i+e;this.curl[s]=.5*(this.v[s+1]-this.v[s-1]-this.u[s+i]+this.u[s-i])}for(let e=1;e<s-1;e++)for(let s=1;s<i-1;s++){const a=e*i+s,o=Math.abs(this.curl[a+1])-Math.abs(this.curl[a-1]),n=Math.abs(this.curl[a+i])-Math.abs(this.curl[a-i]),h=Math.hypot(o,n)+1e-4,r=this.curl[a]*t*10,l=Math.hypot(this.u[a],this.v[a]);this.u[a]+=n/h*r,this.v[a]-=o/h*r;const c=Math.min(1,l/(Math.hypot(this.u[a],this.v[a])+1e-6));this.u[a]*=c,this.v[a]*=c,this.divergence[a]=-.5*(this.u[a+1]-this.u[a-1]+this.v[a+i]-this.v[a-i])}this.pressure.fill(0);for(let t=0;t<4;t++){for(let t=1;t<s-1;t++)for(let e=1;e<i-1;e++){const s=t*i+e;this.pressureNext[s]=.25*(this.divergence[s]+this.pressure[s-1]+this.pressure[s+1]+this.pressure[s-i]+this.pressure[s+i])}[this.pressure,this.pressureNext]=[this.pressureNext,this.pressure]}let n=0;for(let e=1;e<s-1;e++)for(let s=1;s<i-1;s++){const a=e*i+s;this.u[a]=Math.max(-80,Math.min(80,this.u[a]-.5*(this.pressure[a+1]-this.pressure[a-1]))),this.v[a]=Math.max(-80,Math.min(80,this.v[a]-.5*(this.pressure[a+i]-this.pressure[a-i]))),this.driftX[a]+=(3.6*this.u[a]-58*this.offsetX[a]-12*this.driftX[a])*t,this.driftY[a]+=(3.6*this.v[a]-58*this.offsetY[a]-12*this.driftY[a])*t,this.offsetX[a]=Math.max(-2,Math.min(2,this.offsetX[a]+this.driftX[a]*t)),this.offsetY[a]=Math.max(-2,Math.min(2,this.offsetY[a]+this.driftY[a]*t)),this.signal[a]=1-Math.exp(.06*-Math.hypot(this.u[a],this.v[a])-.16*this.ink[a]),n=Math.max(n,this.signal[a])}this.peak=n}if(this.active)for(let t=0;t<this.ink.length;t++){const e=Math.round(65535*Math.min(1,this.signal[t]));this.pixels[4*t]=e>>>8,this.pixels[4*t+1]=255&e,this.pixels[4*t+2]=Math.round(128+63.5*this.offsetX[t]),this.pixels[4*t+3]=Math.round(128+63.5*this.offsetY[t])}else this.clear()}},M=t=>["trail","contour","dissolve","etch","ripple","bleed"].includes(t),v=t=>"etch"===t||"ripple"===t||"bleed"===t,x=t=>v(t)?3:"dissolve"===t?2:"contour"===t?1:0,y=(t,e,i,s)=>(v(i)?(256*t[e]+t[e+1]-32768)/32767:(256*t[e]+t[e+1])/65535)*s;function w(t,e,i){const s=Math.max(0,e-1),a=1-Math.exp(3.5*-Math.max(0,i));return Math.max(0,s-Math.abs(s-(t+a*s*2.1)))}function b(t,e,i,s,a,o,n,h,r,l,c,d=!1){const u=Math.min(a,o,i-1-a,s-1-o),m=Math.max(0,Math.min(1,(u-1)/4)),f=d?m*m*(3-2*m):1;n*=f;const g=1-Math.exp(3.5*-Math.max(0,n)),p=Math.max(0,Math.min(i-1,a-h*g*.026*i*f)),M=Math.max(0,Math.min(s-1,o-r*g*.026*s*f)),v=Math.floor(p),x=Math.floor(M),y=p-v,b=M-x;c.fill(0);for(let a=0;a<2;a++)for(let o=0;o<2;o++){const n=4*(Math.min(s-1,x+a)*i+Math.min(i-1,v+o)),h=(o?y:1-y)*(a?b:1-b);c[0]+=(t[n]+256*t[n+1])*h,c[1]+=t[n+2]/255*h;for(let t=0;t<4;t++)c[t+2]+=e[n+t]*h}const S=43758.5453*Math.sin(127.1*a+311.7*o),k=S-Math.floor(S);c[0]=Math.max(0,w(c[0],l,n)-g*(1-g)*k*3)}var S=[{value:"none",label:"Off",group:"Still",description:"Keep the artwork still under the pointer."},{value:"trail",label:"Trail",group:"Change characters",description:"A fluid wake changes character density and settles behind your cursor."},{value:"dissolve",label:"Dissolve",group:"Change characters",description:"Erase a soft path through the characters; the image gradually reforms."},{value:"contour",label:"Contour",group:"Change characters",description:"Fine tonal rings trace your path without moving the grid."},{value:"etch",label:"Emboss",group:"Change characters",description:"Press a light-and-shadow impression into the characters; the grid stays fixed."},{value:"ripple",label:"Ripple rings",group:"Change characters",description:"Expanding light-and-shadow rings follow your stroke. Faster movement excites stronger rings; the grid stays still."},{value:"bleed",label:"Ink bleed",group:"Change characters",description:"Ink wells up where the cursor rests, spreads along the paper grain, then dries back."},{value:"water",label:"Water",group:"Move the image",description:"Soft ripples refract the image and settle after your stroke."},{value:"silk",label:"Silk",group:"Move the image",description:"Directional folds follow the movement of your hand."},{value:"vortex",label:"Vortex",group:"Move the image",description:"A swirling wake curls through the image."},{value:"magnetic",label:"Magnetic Pull",group:"Move the image",description:"The image gathers toward your stroke, then relaxes."},{value:"scatter",label:"Scatter",group:"Move the image",description:"Characters scatter in soft drifts from your stroke, then spring back into place."},{value:"elastic",label:"Elastic",group:"Move the image",description:"Push the image with your stroke; it recoils past its rest once, then settles."},{value:"lens",label:"Lens wake",group:"Move the image",description:"A soft magnifying wake brings details closer, then relaxes back into focus."},{value:"smudge",label:"Smudge",group:"Move the image",description:"Drag the image in the direction of your stroke; a smooth, lingering pull settles without a bounce."},{value:"loupe",label:"Loupe",group:"Follow the cursor",description:"A glass loupe glides after the cursor and magnifies what it covers, easing through a soft rim."},{value:"reveal",label:"Reveal",group:"Follow the cursor",description:"A soft spotlight shows the original photograph through the characters."},{value:"repel",label:"Repel",group:"Follow the cursor",description:"Characters part around the cursor and spring back with one soft rebound when it leaves."},{value:"tilt",label:"Tilt",group:"Follow the cursor",description:"The image leans toward the cursor; bright areas sit nearer and move more, like a parallax card."},{value:"haze",label:"Heat haze",group:"Follow the cursor",description:"A rising shimmer bends the characters above the cursor, livelier while you move."}],k={rake:"silk"};function E(t,e,i,s,a){if(!(a<1))for(let o=0;o<2;o++){for(let o=0;o<s;o++){let s=0;const n=o*i;for(let e=-a;e<=a;e++)s+=t[n+Math.max(0,Math.min(i-1,e))];for(let o=0;o<i;o++)e[n+o]=s/(2*a+1),s+=t[n+Math.min(i-1,o+a+1)]-t[n+Math.max(0,o-a)]}for(let o=0;o<i;o++){let n=0;for(let t=-a;t<=a;t++)n+=e[Math.max(0,Math.min(s-1,t))*i+o];for(let h=0;h<s;h++)t[h*i+o]=n/(2*a+1),n+=e[Math.min(s-1,h+a+1)*i+o]-e[Math.max(0,h-a)*i+o]}}}function T(t,e,i,s,a){const o=e*i,n=a&&a.width===e&&a.height===i?a:{width:e,height:i,subject:new Float32Array(o),depth:new Float32Array(o),key:"none"},{subject:h,depth:r}=n;let l=0;for(let e=0;e<o;e++)t[4*e+3]<250&&l++;const c=e=>(.2126*t[4*e]+.7152*t[4*e+1]+.0722*t[4*e+2])/255;let d="none",u=0;if(l>.01*o)d="alpha";else if(s)d="mask";else{let t=0,s=0,a=0;const o=Math.max(1,Math.round(.03*Math.min(e,i)));for(let n=0;n<i;n++)for(let h=0;h<e;h++){if(h>=o&&n>=o&&h<e-o&&n<i-o)continue;const r=c(n*e+h);t+=r,s+=r*r,a++}u=t/Math.max(1,a);Math.sqrt(Math.max(0,s/Math.max(1,a)-u*u))<.07&&(d="ground")}for(let e=0;e<o;e++){const i=t[4*e+3]/255,a=c(e);let o=1;if("alpha"===d)o=i;else if("ground"===d){const t=Math.max(0,Math.min(1,(Math.abs(a-u)-.035)/.12));o=t*t*(3-2*t)}h[e]=o*(s?Math.max(0,Math.min(1,s[e])):1),r[e]=a*i}n.key=d;const m=new Float32Array(o);return E(h,m,e,i,Math.max(1,Math.round(.02*Math.min(e,i)))),E(r,m,e,i,Math.max(1,Math.round(.035*Math.min(e,i)))),n}var A=["loupe","repel","tilt","reveal","haze","bleed"],D=t=>A.includes(t),R=class{constructor(){this.value=0,this.velocity=0}step(t,e,i,s){const a=2*Math.sqrt(i)*s;for(let s=e;s>1e-6;s-=1/480){const e=Math.min(1/480,s);this.velocity+=((t-this.value)*i-this.velocity*a)*e,this.value+=this.velocity*e}}resting(t){return Math.abs(this.value-t)<.0015&&Math.abs(this.velocity)<.01}set(t){this.value=t,this.velocity=0}},z=t=>t*t*(3-2*t),F=(t,e)=>{const i=43758.5453*Math.sin(127.1*t+311.7*e);return i-Math.floor(i)};function C(t,e){const i=Math.floor(t),s=Math.floor(e),a=z(t-i),o=z(e-s),n=F(i,s),h=F(i+1,s),r=F(i,s+1);return(n+(h-n)*a)*(1-o)+(r+(F(i+1,s+1)-r)*a)*o}var X=class{constructor(t,e){this.edgeSafe=!1,this.settle=1,this.focus=[.5,.5,0,.2],this.guide=null,this.mode="loupe",this.radius=.35,this.x=new R,this.y=new R,this.tx=new R,this.ty=new R,this.presence=new R,this.target=null,this.speed=0,this.clock=0,this.inkPeak=0,this.ink=null,this.next=null,this.fiber=null,this.sampled=[0,0,0],this.width=t,this.height=e,this.pixels=new Uint8Array(t*e*4),this.sx=Math.max(1,t/e),this.sy=Math.max(1,e/t),this.clear()}setMode(t){if(t!==this.mode&&(this.mode=t,this.clear(),"bleed"===t&&!this.ink)){const t=this.width*this.height;this.ink=new Float32Array(t),this.next=new Float32Array(t),this.fiber=new Float32Array(t);for(let t=0;t<this.height;t++)for(let e=0;e<this.width;e++)this.fiber[t*this.width+e]=.35+.5*C(.09*e,.045*t)+.15*F(e,t)}}setRadius(t){this.radius=Math.max(.1,Math.min(1,t))}get reach(){return(.05+.25*this.radius)*("loupe"===this.mode?1-.14*this.speed:1)}get engaged(){return this.presence.value>.002||this.inkPeak>.002||Math.abs(this.tx.value)+Math.abs(this.ty.value)>4e-4}get active(){const t=this.target?1:0;return!this.presence.resting(t)||!!this.target&&(!this.x.resting(this.target.x)||!this.y.resting(this.target.y))||!this.tx.resting(this.tiltGoal(0))||!this.ty.resting(this.tiltGoal(1))||this.speed>.01||"haze"===this.mode&&this.presence.value>.002||this.inkPeak>.002}tiltGoal(t){return this.target&&"tilt"===this.mode?2*((t?this.target.y:this.target.x)-.5):0}clear(){for(const t of[this.x,this.y,this.tx,this.ty,this.presence])t.set(0);this.x.set(.5),this.y.set(.5),this.target=null,this.speed=0,this.inkPeak=0,this.ink?.fill(0),this.next?.fill(0),this.encode()}move(t,e,i){if(!Number.isFinite(t+e))return;t=Math.max(0,Math.min(1,t)),e=Math.max(0,Math.min(1,e));const s=Number.isFinite(i)?i/1e3:this.clock,a=this.target;if(!a&&this.presence.value<.05&&(this.x.set(t),this.y.set(e)),a){const i=Math.hypot((t-a.x)*this.sx,(e-a.y)*this.sy),o=i/Math.max(1/240,Math.min(.1,s-a.time||1/60));this.speed=Math.max(this.speed,Math.min(1,o/1.6)),"bleed"===this.mode&&this.deposit(a.x,a.y,t,e,Math.min(1,9*i))}else"bleed"===this.mode&&this.deposit(t,e,t,e,.5);this.target={x:t,y:e,time:s}}leave(){this.target=null}deposit(t,e,i,s,a){const o=this.ink,n=this.width,h=this.height,r=.55*this.reach,l=Math.hypot((i-t)*this.sx,(s-e)*this.sy),c=Math.min(48,Math.max(1,Math.ceil(l/(.5*r))));for(let l=0;l<c;l++){const d=(l+.5)/c,u=t+(i-t)*d,m=e+(s-e)*d,f=Math.max(0,Math.floor((u-2*r/this.sx)*(n-1))),g=Math.min(n-1,Math.ceil((u+2*r/this.sx)*(n-1))),p=Math.max(0,Math.floor((m-2*r/this.sy)*(h-1))),M=Math.min(h-1,Math.ceil((m+2*r/this.sy)*(h-1)));for(let t=p;t<=M;t++)for(let e=f;e<=g;e++){const i=(e/(n-1)-u)*this.sx/r,s=(t/(h-1)-m)*this.sy/r,l=t*n+e;o[l]=Math.min(1.4,o[l]+Math.exp(1.8*-(i*i+s*s))*a/c*1.6)}}this.inkPeak=Math.max(this.inkPeak,.01)}step(t){if(!Number.isFinite(t)||t<=0)return;const e=Math.min(.05,t),i=e*this.settle;this.clock+=e;const s=this.target,a=this.mode;if(s?this.presence.step(1,e,190,1):this.presence.step(0,i,"repel"===a?120:70,"repel"===a?.42:1),s){const t="tilt"===a?60:"haze"===a?90:240,i="loupe"===a||"repel"===a?.72:.9;this.x.step(s.x,e,t,i),this.y.step(s.y,e,t,i)}this.tx.step(this.tiltGoal(0),s?e:i,45,.78),this.ty.step(this.tiltGoal(1),s?e:i,45,.78),this.speed*=Math.exp(7*-e),"bleed"===a&&this.spread(e,i,!!s),this.engaged||this.active||(this.presence.set(0),this.tx.set(0),this.ty.set(0)),this.focus[0]=this.x.value,this.focus[1]=this.y.value,this.focus[2]=Math.max(0,Math.min(1,this.presence.value)),this.focus[3]=this.reach,this.encode()}spread(t,e,i){const s=this.ink,a=this.next,o=this.fiber,n=this.width,h=this.height;i&&this.target&&this.deposit(this.x.value,this.y.value,this.x.value,this.y.value,.9*t);const r=Math.max(1,Math.ceil(t/(1/240))),l=t/r,c=26*(.4+this.radius),d=(i?.02:.1)*e/t,u=Math.exp(-(i?.15:.9)*e/r);let m=0;for(let t=0;t<r;t++){m=0;for(let t=1;t<h-1;t++)for(let e=1;e<n-1;e++){const i=t*n+e,h=s[i],r=o[i],f=Math.min(r,o[i-1])*(s[i-1]-h)+Math.min(r,o[i+1])*(s[i+1]-h)+Math.min(r,o[i-n])*(s[i-n]-h)+Math.min(r,o[i+n])*(s[i+n]-h),g=Math.max(0,(h+f*Math.min(.24,c*l))*u-d*l);a[i]=g,g>m&&(m=g)}s.set(a)}this.inkPeak=m}sample(t,e,i){i[0]=i[1]=i[2]=0;const s=this.presence.value,a=this.mode;if("bleed"===a){if(this.inkPeak<=.002)return;const s=this.width,a=this.height,o=this.ink,n=Math.max(0,Math.min(s-1.001,t*(s-1))),h=Math.max(0,Math.min(a-1.001,e*(a-1))),r=Math.floor(n),l=Math.floor(h),c=n-r,d=h-l,u=l*s+r,m=u+s,f=(o[u]*(1-c)+o[u+1]*c)*(1-d)+(o[m]*(1-c)+o[m+1]*c)*d;return void(i[2]=1-Math.exp(2.4*-f))}if("tilt"===a){const s=this.guide?function(t,e,i,s){const a=t[e],o=t.width,n=t.height,h=Math.max(0,Math.min(o-1.001,i*(o-1))),r=Math.max(0,Math.min(n-1.001,s*(n-1))),l=Math.floor(h),c=Math.floor(r),d=h-l,u=r-c,m=c*o+l,f=Math.min(n-1,c+1)*o+l,g=l+1<o?1:0;return(a[m]*(1-d)+a[m+g]*d)*(1-u)+(a[f]*(1-d)+a[f+g]*d)*u}(this.guide,"depth",t,e):.6,a=.2+.8*s,o=this.edgeSafe?l(t,e,this.sx/this.sy):1;return i[0]=.024*this.tx.value*a*o/this.sx,void(i[1]=.024*this.ty.value*a*o/this.sy)}if(s<=.001)return;const o=this.reach,n=(t-this.x.value)*this.sx,h=(e-this.y.value)*this.sy,r=(n*n+h*h)/(o*o);if("reveal"===a)return void(i[2]=Math.exp(2.4*-r)*s);if(r>9)return;const c=this.edgeSafe?l(t,e,this.sx/this.sy):1;let d=0,u=0;if("loupe"===a){const t=Math.exp(-r*r)*s*.5;d=n*t,u=h*t;const e=Math.sqrt(r)-1;i[2]=Math.exp(-e*e/.018)*s*.55}else if("repel"===a){const t=Math.sqrt(r),e=.5*o*Math.exp(-r/1.7)*s,a=Math.max(1e-6,Math.sqrt(n*n+h*h));d=n/a*Math.min(e,a+.5*o*s),u=h/a*Math.min(e,a+.5*o*s);const l=Math.max(0,Math.min(1,(t-.3)/.32));i[2]=-(1-l*l*(3-2*l))*s}else if("haze"===a){const a=h/o+.45,r=Math.exp(-n*n/(o*o)-a*a/2.6)*s*(.35+.65*Math.min(1,1.4*this.speed+.25));if(r<.002)return;const l=this.clock,c=t*this.sx*9,m=e*this.sy*9+2.3*l;d=.06*(C(c,m)-.5)*r,u=.026*(C(c+17.3,1.3*m+.7*l)-.5)*r,i[2]=.35*(C(1.7*c+5,1.7*m)-.5)*r}i[0]=d*c/this.sx,i[1]=u*c/this.sy}encode(){const t=this.width,e=this.height,i=this.pixels,s=this.sampled,a=this.engaged;for(let o=0;o<e;o++)for(let n=0;n<t;n++){const h=4*(o*t+n);if(a?this.sample(n/(t-1),o/(e-1),s):s[0]=s[1]=s[2]=0,"bleed"===this.mode){const t=Math.round(32767*Math.max(-1,Math.min(1,s[2]))+32768);i[h]=t>>>8,i[h+1]=255&t,i[h+2]=128,i[h+3]=0}else{"repel"===this.mode&&(s[0]*=.3,s[1]*=.3);const t=Math.round(32767*Math.max(-1,Math.min(1,s[0]/.16))+32768),e=Math.round(32767*Math.max(-1,Math.min(1,s[1]/.16))+32768);i[h]=t>>>8,i[h+1]=255&t,i[h+2]=e>>>8,i[h+3]=255&e}}}},I=(t,e)=>"water"===t?30*e/.55:"silk"===t?28*e:"vortex"===t?38*e:["magnetic","scatter","elastic","lens","smudge"].includes(t)?32*e:["loupe","repel","tilt","haze"].includes(t)?.16*e/.55:0,_=class{constructor(t=1){this.ripple=null,this.cursor=null,this.settle=1,this.mode="water",this.amount=.55,this.radius=.2,this.lensX=.5,this.lensY=.5,this.lensStrength=0,this.previous=null,this.energy=0,this.pending=!1,this.remainder=0,this.guide=null,t=Math.max(.25,Math.min(4,t)),this.columns=Math.round(t>=1?128:128*t),this.rows=Math.round(t>=1?128/t:128),this.xScale=Math.max(1,t),this.yScale=Math.max(1,1/t),this.heights=new Float32Array(this.columns*this.rows),this.velocity=new Float32Array(this.heights.length),this.next=new Float32Array(this.heights.length),this.normals=new Float32Array(2*this.heights.length),this.edgeMask=new Float32Array(this.heights.length);for(let e=0;e<this.rows;e++)for(let i=0;i<this.columns;i++)this.edgeMask[e*this.columns+i]=l(i/(this.columns-1),e/(this.rows-1),t);this.refraction={width:this.columns,height:this.rows,pixels:new Uint8Array(4*this.heights.length),strength:30,mode:"water",focus:[.5,.5,0,.2]},this.waterPixels=this.refraction.pixels,this.trail=new p(this.columns,this.rows),this.contour=new g(this.columns,this.rows),this.afterimage=new f(this.columns,this.rows),this.clear()}get invertsDensity(){return"trail"===this.mode}get densityTrail(){return["trail","contour","dissolve","etch","ripple","bleed"].includes(this.mode)}get hasRefraction(){return this.active||this.lensStrength>15e-5}get effect(){return this.mode}get engaged(){return D(this.mode)?this.cursor.engaged:this.active}get spotlight(){return"reveal"===this.mode&&this.cursor?this.cursor.focus:null}get signature(){return D(this.mode)&&this.cursor.engaged?this.cursor.focus.map(t=>t.toFixed(4)).join(","):""}setGuide(t){this.cursor&&(this.cursor.guide=t),this.guide=t}get active(){return D(this.mode)?this.cursor.active:"ripple"===this.mode?this.ripple?.active??!1:d(this.mode)?this.afterimage.active:"contour"===this.mode?this.contour.active:"trail"===this.mode?this.trail.active:this.pending||this.energy>15e-5}setMode(t){const e="light"===t||"scan"===t?t:function(t){if("string"==typeof t)return t in k?k[t]:S.some(e=>e.value===t)?t:void 0}(t)??"none";e!==this.mode&&(this.clear(),this.mode=e,this.refraction.mode=e,d(e)&&this.afterimage.setMode(e),"ripple"!==e||this.ripple||(this.ripple=new c(this.columns,this.rows)),D(e)&&(this.cursor??(this.cursor=new X(this.columns,this.rows)),this.cursor.guide=this.guide,this.cursor.setMode(e)),this.refraction.pixels=D(e)?this.cursor.pixels:"ripple"===e?this.ripple.pixels:"trail"===e?this.trail.pixels:"contour"===e?this.contour.pixels:d(e)?this.afterimage.pixels:this.waterPixels,this.refraction.strength=I(e,this.amount),this.refraction.relative=D(e))}configure(t,e=.55,i=.2,s=!1,a=.5){this.setMode(t),this.settle=((t=.5)=>Math.pow(2,2.4*(.5-(Number.isFinite(t)?Math.max(0,Math.min(1,t)):.5))))(a),this.refraction.edgeSafe=s,this.afterimage.edgeSafe=s,this.ripple&&(this.ripple.edgeSafe=s),this.amount=Number.isFinite(e)?Math.max(0,Math.min(1,e)):.55,this.radius=Number.isFinite(i)?Math.max(.1,Math.min(1,i)):.2,this.cursor&&(this.cursor.edgeSafe=s,this.cursor.settle=this.settle,this.cursor.setRadius(this.radius)),this.refraction.strength=I(this.mode,this.amount),this.refraction.focus[2]=this.densityTrail?this.amount:this.lensStrength*this.amount,this.refraction.focus[3]=this.radius,"reveal"!==this.mode&&"repel"!==this.mode||this.syncSpot()}syncSpot(){const t=this.cursor.focus;this.refraction.focus=[t[0],t[1],t[2]*("repel"===this.mode?Math.min(1,this.amount/.55):this.amount),t[3]]}move(t,e,i){if("none"===this.mode||0===this.amount)return;if(!Number.isFinite(t+e))return;if(t=Math.max(0,Math.min(1,t)),e=Math.max(0,Math.min(1,e)),D(this.mode))return void this.cursor.move(t,e,i);if("ripple"===this.mode)return void this.ripple.move(t,e,this.radius,i);if(d(this.mode))return void this.afterimage.move(t,e,this.radius);if("contour"===this.mode)return void this.contour.move(t,e,this.radius);if("trail"===this.mode)return void this.trail.move(t,e,this.radius,i);if("water"!==this.mode)return!this.previous&&this.lensStrength<.001&&(this.lensX=t,this.lensY=e),this.previous={x:t,y:e},void(this.pending=!0);if(!this.previous)return void(this.previous={x:t,y:e});const s=this.previous;this.previous={x:t,y:e};const a=(t-s.x)*this.xScale,o=(e-s.y)*this.yScale,n=Math.hypot(a,o);if(n<1e-5)return;const h=.035+.1*this.radius,r=3*h,l=Math.min(192,Math.max(1,Math.ceil(n/(.5*h)))),c=95*Math.min(n,1)/l;for(let i=0;i<l;i++){const a=(i+.5)/l,o=s.x+(t-s.x)*a,n=s.y+(e-s.y)*a,d=Math.max(1,Math.floor((o-r/this.xScale)*(this.columns-1))),u=Math.min(this.columns-2,Math.ceil((o+r/this.xScale)*(this.columns-1))),m=Math.max(1,Math.floor((n-r/this.yScale)*(this.rows-1))),f=Math.min(this.rows-2,Math.ceil((n+r/this.yScale)*(this.rows-1)));for(let t=m;t<=f;t++)for(let e=d;e<=u;e++){const i=(e/(this.columns-1)-o)*this.xScale,s=(t/(this.rows-1)-n)*this.yScale,a=Math.exp(-(i*i+s*s)/(h*h))*c,r=t*this.columns+e;this.velocity[r]=Math.max(-12,this.velocity[r]-a)}}this.pending=!0}leave(){this.cursor?.leave(),this.ripple?.leave(),this.afterimage.leave(),this.contour.leave(),this.trail.leave(),this.previous=null,"water"!==this.mode&&this.lensStrength&&(this.pending=!0)}clear(){this.trail.clear(),this.contour.clear(),this.afterimage.clear(),this.ripple?.clear(),this.cursor?.clear(),this.heights.fill(0),this.velocity.fill(0),this.next.fill(0),this.normals.fill(0),this.refraction.focus[2]=0,this.previous=null,this.lensStrength=0,this.energy=0,this.pending=!1,this.remainder=0;for(let t=0;t<this.waterPixels.length;t+=4)this.waterPixels[t]=this.waterPixels[t+2]=128,this.waterPixels[t+1]=this.waterPixels[t+3]=0}step(t){if(!this.active||!Number.isFinite(t))return;if(D(this.mode))return this.cursor.step(t),void("reveal"!==this.mode&&"repel"!==this.mode||this.syncSpot());if(t*=this.settle,"ripple"===this.mode)return void this.ripple.step(t);if(d(this.mode))return void this.afterimage.step(t);if("contour"===this.mode)return void this.contour.step(t);if("trail"===this.mode)return void this.trail.step(t);if("water"!==this.mode)return void this.stepLight(Math.max(0,Math.min(.05,t)));this.remainder+=Math.max(0,Math.min(.05,t));const e=1/120,i=Math.exp(-3.8*e),s=.3*Math.min(this.columns,this.rows);for(;this.remainder+1e-8>=e;){this.remainder-=e;let t=0;for(let a=1;a<this.rows-1;a++)for(let o=1;o<this.columns-1;o++){const n=a*this.columns+o,h=this.heights[n],r=this.heights[n-1]+this.heights[n+1]+this.heights[n-this.columns]+this.heights[n+this.columns]-4*h,l=Math.min(o,a,this.columns-1-o,this.rows-1-a),c=l<5?.88+.024*l:1,d=(this.velocity[n]+(r*s*s-8*h)*e)*i*c;this.velocity[n]=d,this.next[n]=(h+d*e)*c,t=Math.max(t,Math.abs(this.next[n]),.1*Math.abs(d))}this.heights.set(this.next),this.energy=t,this.pending=!1}if(!this.active)return void this.clear();const a=.18*Math.min(this.columns,this.rows);for(let t=0;t<this.rows;t++)for(let e=0;e<this.columns;e++){const i=t*this.columns+e,s=(this.heights[t*this.columns+Math.max(0,e-1)]-this.heights[t*this.columns+Math.min(this.columns-1,e+1)])*a,o=(this.heights[Math.max(0,t-1)*this.columns+e]-this.heights[Math.min(this.rows-1,t+1)*this.columns+e])*a,n=Math.tanh(s)*(this.refraction.edgeSafe?this.edgeMask[i]:1),h=Math.tanh(o)*(this.refraction.edgeSafe?this.edgeMask[i]:1);this.normals[2*i]=n,this.normals[2*i+1]=h;const r=Math.round(32767*n+32768),l=Math.round(32767*h+32768);this.refraction.pixels[4*i]=r>>>8,this.refraction.pixels[4*i+1]=255&r,this.refraction.pixels[4*i+2]=l>>>8,this.refraction.pixels[4*i+3]=255&l}}stepLight(t){const e=this.previous,i=1-Math.exp(-30*t),s=1-Math.exp(-12*t);e&&(this.lensX+=(e.x-this.lensX)*i,this.lensY+=(e.y-this.lensY)*i),this.lensStrength+=((e?1:0)-this.lensStrength)*s,this.energy=Math.max(e?Math.abs(e.x-this.lensX)+Math.abs(e.y-this.lensY):0,Math.abs((e?1:0)-this.lensStrength)),this.pending=!1,e||this.active?this.refraction.focus=[this.lensX,this.lensY,this.lensStrength*this.amount,this.radius]:this.clear()}sample(t,e,i){if(D(this.mode))return this.cursor.sample(t,e,i),i[0]*=this.amount/.55,i[1]*=this.amount/.55,void(i[2]*="reveal"===this.mode||"repel"===this.mode?Math.min(1,this.amount/.55):3*this.amount);if("ripple"===this.mode)return this.ripple.sample(t,e,i),void(i[2]*=this.amount);if(d(this.mode))return this.afterimage.sample(t,e,i),i[0]*=this.amount,i[1]*=this.amount,void(i[2]*=this.amount);if("contour"===this.mode)return i[0]=i[1]=0,void(i[2]=this.contour.sample(t,e)*this.amount*3);if("trail"===this.mode)return i[0]=i[1]=0,void(i[2]=this.trail.sample(t,e)*this.amount*3);const s=Math.max(0,Math.min(this.columns-1.001,t*(this.columns-1))),a=Math.max(0,Math.min(this.rows-1.001,e*(this.rows-1))),o=Math.floor(s),n=Math.floor(a),h=s-o,r=a-n,l=2*(n*this.columns+o),c=l+2*this.columns;for(let t=0;t<2;t++)i[t]=.08*((this.normals[l+t]*(1-h)+this.normals[l+2+t]*h)*(1-r)+(this.normals[c+t]*(1-h)+this.normals[c+2+t]*h)*r);i[0]*=this.amount/.55,i[1]*=this.amount/.55,i[2]="light"===this.mode||"scan"===this.mode?function(t,e,i,s){const[a,o,n,h]=i.focus,r=(t-a)*Math.max(1,s)/h,l=(e-o)*Math.max(1,1/s)/h;return"light"===i.mode?Math.exp(-3*(r*r+l*l))*n:"scan"===i.mode?(1.3*Math.exp(-r*r*110)-.3*Math.exp(-r*r*12))*Math.exp(-l*l*1.8)*n:0}(t,e,this.refraction,this.xScale/this.yScale):0}};var P=[{value:"clean",label:"Clean",description:"Crisp characters with no optical texture."},{value:"signal",label:"Signal",description:"Broken scanlines at the edges, with a clear center."},{value:"prism",label:"Prism",description:"Soft red/cyan separation at the edges, with a crisp center."},{value:"etch",label:"Etch",description:"A fine engraved texture, keeping the ink monochrome."}];var U=(t,e,i)=>{const s=Math.max(0,Math.min(1,(i-t)/(e-t)));return s*s*(3-2*s)};function G(t,e,i=256){let s=0;for(let e=0;e<t.length;e++)t[e]>s&&(s=t[e]);if(s<=0)return 0;const a=new Uint32Array(i);for(let e=0;e<t.length;e++)a[Math.min(i-1,Math.floor(t[e]/s*i))]++;let o=0;const n=t.length*e;for(let t=0;t<i;t++)if(o+=a[t],o>=n)return(t+1)/i*s;return s}function L(t){const e=Intl.Segmenter;return e?Array.from(new e(void 0,{granularity:"grapheme"}).segment(t),t=>t.segment):Array.from(t)}function N(){const t=document.createElement("canvas"),e=document.createElement("canvas"),i=t.getContext("2d"),s=e.getContext("2d",{willReadFrequently:!0}),a=function(){const t=new WeakMap;return(e,i,s="")=>{if(!e)return i;let a=t.get(e);if(!a){const i=new Set;for(const t of e)for(const e of t)e.char&&i.add(e.char);a=[...i].sort(),t.set(e,a)}const o=i+s;return i+a.filter(t=>!o.includes(t)).join("")}}();let o,n=null;return{update(h,r,l,c,d,u,m,f){const g=m?.[0]?.length??Math.max(1,Math.round(r/c)),p=m?.length??Math.max(1,Math.round(l/d)),M=f??{charset:" .:-=+*#%@",customText:"",colorMode:"fullcolor"},v=a(m,M.charset+(M.charsetFrames?.join("")??""),M.customText),x=function(t,e,i,s,a,o,n,h=4096,r=0,l=1){if(![i,s,a,o,n,h].every(t=>Number.isFinite(t)&&t>0))throw new Error("Glyph atlas dimensions must be positive and finite.");const c=JSON.stringify([e.charset,e.customText,i,s,a,o,n,h,r,l]);if(t?.key===c)return t;const d=[...new Set([" ",...L(e.charset),...L(e.customText??"")])];if(d.length>65536)throw new Error("This character set exceeds the glyph texture capacity.");const u=i/o,m=s/n,f=.9*Math.min(u/.55,m)*l,g=Math.ceil(u*a)+6,p=Math.ceil(m*a)+6,M=Math.min(d.length,Math.floor(h/g));if(M<1||Math.ceil(d.length/M)*p>h)throw new Error("The glyph atlas exceeds the available GPU texture size.");return{key:c,glyphs:d,indices:new Map(d.map((t,e)=>[t,e])),cellWidth:u,cellHeight:m,fontSize:f,padding:3,tileWidth:g,tileHeight:p,atlasColumns:M,atlasWidth:M*g,atlasHeight:Math.ceil(d.length/M)*p}}(n,{...M,charset:v},r,l,u,g,p);if(x!==n&&(t.width=x.atlasWidth,t.height=x.atlasHeight,i.font=x.fontSize*u+'px "JetBrains Mono", monospace',i.textAlign="center",i.textBaseline="middle",i.fillStyle="#fff",x.glyphs.forEach((t,e)=>i.fillText(t,e%x.atlasColumns*x.tileWidth+x.padding+x.cellWidth*u*.5,Math.floor(e/x.atlasColumns)*x.tileHeight+x.padding+x.cellHeight*u*.5))),n=x,m)o=function(t,e,i,s,a=!1){const o=t.length,n=t[0]?.length??0,h=s?.cols===n&&s.rows===o?s:{cols:n,rows:o,indices:new Uint8Array(n*o*4),colors:new Uint8ClampedArray(n*o*4)};h.indices.fill(0),h.colors.fill(0);const r="accent"===i.colorMode?[1,3,5].map(t=>parseInt(i.accentColor.slice(t,t+2),16)):null;for(let s=0;s<o;s++){if(t[s].length!==n)throw new Error("Glyph frames must have a rectangular cell grid.");for(let o=0;o<n;o++){const l=t[s][o];if(!l.char||!a&&" "===l.char||l.a<10)continue;const c=e.get(l.char);if(void 0===c)throw new Error(`Glyph is absent from the configured character set: ${l.char}`);const d=4*(s*n+o);let u=l.r,m=l.g,f=l.b;if(r)[u,m,f]=r;else if("fullcolor"!==i.colorMode){const t=.299*u+.587*m+.114*f|0;u="matrix"===i.colorMode?0:t,m=t,f=u}(a||0!==u||0!==m||0!==f)&&(h.indices[d]=255&c,h.indices[d+1]=c>>>8,h.indices[d+2]=Math.round(.299*l.r+.587*l.g+.114*l.b),h.colors[d]=u,h.colors[d+1]=m,h.colors[d+2]=f,h.colors[d+3]=l.a)}}return h}(m,n.indices,M,o,!0);else{e.width=g,e.height=p,s.drawImage(h,0,0,g,p);const t=s.getImageData(0,0,g,p).data;o={cols:g,rows:p,indices:new Uint8Array(g*p*4),colors:new Uint8ClampedArray(t)};for(let e=0;e<t.length;e+=4){const i=Math.min(1,(.299*t[e]+.587*t[e+1]+.114*t[e+2])/255*(t[e+3]/255)*3);o.indices[e+2]=Math.round(255*i),o.indices[e]=Math.min(n.glyphs.length-1,Math.floor(i*n.glyphs.length))}}return{plan:n,packed:o,atlas:t,dots:"dots"===M.renderMode,dotSize:M.dotSizeRatio??.72}},reset(){n=null},destroy(){t.width=t.height=e.width=e.height=1,o=void 0,n=null}}}function Y(t){return!!t&&"ascii"===t.renderMode&&!/\p{Extended_Pictographic}/u.test(t.customText||t.charset)&&!t.charsetFrames?.length&&["fullcolor","grayscale","matrix","accent"].includes(t.colorMode)&&("accent"!==t.colorMode||/^#[\da-f]{6}$/i.test(t.accentColor))}function B(t){for(let e=t;e;e=e.parentElement){const t=getComputedStyle(e).backgroundColor.match(/[\d.]+/g)?.map(Number);if(t&&t.length>=3&&(t[3]??1)>.5)return(.299*t[0]+.587*t[1]+.114*t[2])/255<.4?[10/255,10/255,10/255,1]:[250/255,249/255,247/255,1]}const e=document.documentElement.dataset.theme;if("dark"!==e&&"light"!==e&&!t.isConnected&&document.body)return B(document.body);return"dark"===e||"light"!==e&&(document.documentElement.classList.contains("dark")||matchMedia("(prefers-color-scheme: dark)").matches)?[10/255,10/255,10/255,1]:[250/255,249/255,247/255,1]}function O(t,e,a){const o=e.style.visibility;let l=document.createElement("canvas"),c=null,d=null,u=null,m=null;const f=[],g=[];let p=!1,v=!1,S=4096,k=null,A=0,D=0,R=0,z=0,F=!1;const C=function(){const t=document.createElement("canvas"),e=t.getContext("2d");return{paint(i,s,a,o){s&&(t.width===i.canvas.width&&t.height===i.canvas.height||(t.width=i.canvas.width,t.height=i.canvas.height),e.globalCompositeOperation="copy",e.drawImage(i.canvas,0,0),e.globalCompositeOperation="destination-in",e.drawImage(s.canvas,0,0,t.width,t.height),e.globalCompositeOperation="source-in",e.fillStyle="#090909",e.fillRect(0,0,t.width,t.height),e.globalCompositeOperation="source-over",i.drawImage(t,0,0,a,o))},destroy(){t.width=t.height=1}}}();let X,I;const _=document.createElement("canvas"),L=document.createElement("canvas"),O=_.getContext("2d"),H=L.getContext("2d");let W=!1;const j=new h,K=N();let q=null,$=null,V=null,J=new Uint8Array(0),Z=new Uint8Array(0),Q={};const tt=()=>{l.className="ascii-hover-surface",l.style.cssText="position:absolute;inset:0;width:100%;height:100%;pointer-events:none",l.setAttribute("role","img"),l.setAttribute("aria-label","ASCII artwork preview"),l.dataset.renderer=c?"surface-gpu":"surface-canvas",t.prepend(l),e.style.visibility="hidden"},et=()=>{c&&(f.forEach(t=>c.deleteTexture(t)),g.forEach(t=>c.deleteShader(t)),u&&c.deleteProgram(u),m&&c.deleteBuffer(m))},it=()=>{p||d||(l.removeEventListener("webglcontextlost",it),et(),l.remove(),c=null,l=document.createElement("canvas"),d=l.getContext("2d"),tt(),a())};try{if(c=l.getContext("webgl",{alpha:!0,antialias:!1,depth:!1,stencil:!1,premultipliedAlpha:!0,preserveDrawingBuffer:!0}),!c)throw new Error("WebGL unavailable");S=c.getParameter(c.MAX_TEXTURE_SIZE);const t=(t,e)=>{const i=c.createShader(t);if(g.push(i),c.shaderSource(i,e),c.compileShader(i),!c.getShaderParameter(i,c.COMPILE_STATUS))throw new Error(c.getShaderInfoLog(i)||"Shader compilation failed");return i};if(u=c.createProgram(),c.attachShader(u,t(c.VERTEX_SHADER,"attribute vec2 position; void main(){gl_Position=vec4(position,0.,1.);}")),c.attachShader(u,t(c.FRAGMENT_SHADER,"\n precision highp float;\n uniform sampler2D artwork; uniform sampler2D surface;\n uniform vec2 resolution; uniform vec2 sceneSize; uniform vec2 cellSize;\n uniform vec2 surfaceSize; uniform float strength; uniform float relativeStrength;\n \nuniform float hoverMode;\nuniform vec4 hoverFocus;\nfloat surfaceLight(vec2 uv, vec2 size) {\n vec2 delta = (uv - hoverFocus.xy) * size / min(size.x, size.y) / hoverFocus.w;\n float light = exp(-3.0 * dot(delta, delta));\n if (hoverMode > 2.5) light = -smoothstep(.85, .35, length(delta));\n else if (hoverMode > 1.5) light = (exp(-delta.x * delta.x * 110.0) * 1.3 - exp(-delta.x * delta.x * 12.0) * .3) * exp(-delta.y * delta.y * 1.8);\n return hoverMode > .5 ? light * hoverFocus.z : 0.0;\n}\nvec3 illuminate(vec3 ink, float light) {\n return mix(ink, vec3(1.0), max(0.0, light) * .82) * (1.0 + min(0.0, light));\n}\n\n uniform sampler2D codes; uniform sampler2D cellColors; uniform sampler2D glyphAtlas;\n uniform vec2 gridSize; uniform vec2 atlasSize; uniform vec2 tileSize;\n uniform float atlasColumns; uniform float padding; uniform float glyphCount;\n uniform float dots; uniform float dotSize; uniform float directGlyphs; uniform vec4 backdrop;\n uniform float edgeEffect; uniform sampler2D textMask; uniform float hasTextMask; uniform sampler2D cueMap;\n \nuniform float finishMode;\nuniform float finishPixelRatio;\nuniform float finishSoftness;\nvec3 edgeCoverage(sampler2D sheet, vec2 uv, vec2 atlas, vec2 pixel, vec2 viewport, float amount) {\n float a=texture2D(sheet,uv).a;\n if(amount<=0.0 || finishMode<.5) return vec3(a);\n vec2 radial=(pixel/viewport-.5)*2.0;\n float edge=smoothstep(.3,1.05,length(radial));\n float strength=clamp(amount,0.0,1.0)*edge;\n if(strength<=0.0) return vec3(a);\n vec2 cssPixel=pixel/max(1.0,finishPixelRatio);\n // Etch is a single texture sample: linework, not an opacity wash.\n if(finishMode>1.5 && finishMode<2.5) {\n float hatch=step(.68,fract((cssPixel.y+cssPixel.x*.22)*.5));\n return vec3(a*(1.0-hatch*strength*.64));\n }\n // Samples remain inside the current atlas tile's three-pixel padding.\n if(finishMode>2.5) {\n float band=floor(cssPixel.y/3.0);\n float phase=fract(sin(band*12.9898)*43758.5453);\n float tear=(phase-.5)*3.6*strength;\n float displaced=texture2D(sheet,uv+vec2(tear,0.0)/atlas).a;\n float cut=step(.72,fract(cssPixel.y*.5));\n return vec3(mix(a,displaced,strength)*(1.0-cut*.55*strength));\n }\n // A five-tap lens: opposed red/cyan edges with a short soft shoulder.\n // The center remains untouched; all offsets stay within three atlas texels.\n vec2 direction=normalize(vec2(radial.x-radial.y*.3,radial.y+radial.x*.3)+vec2(.00001));\n vec2 shift=direction*(2.35+.45*finishSoftness)*strength/atlas;\n float left=texture2D(sheet,uv-shift).a,right=texture2D(sheet,uv+shift).a;\n float softLeft=texture2D(sheet,uv-shift*.45).a,softRight=texture2D(sheet,uv+shift*.45).a;\n float red=left*.7+softLeft*.22+a*.08;\n float cyan=right*.7+softRight*.22+a*.08;\n // Reuse the same taps for a short optical blur and peripheral fade.\n // No whole-canvas blur, extra pass or softened HTML controls.\n float soft=a*.4+(left+right+softLeft+softRight)*.15;\n vec3 fringe=mix(vec3(red,cyan,cyan),vec3(soft),finishSoftness*.45);\n return mix(vec3(a),fringe,strength)*(1.-finishSoftness*strength*.32);\n\n}\n\n \nuniform float edgeSafe;\nuniform float trailAmount;\nuniform float trailKind;\nuniform sampler2D scopeMap;\nuniform float scopeMode;\n// Hover scope: 0 everywhere, 1 on the subject, 2 around it. The map holds soft subject coverage in alpha.\nfloat hoverScope(vec2 uv) {\n if (scopeMode < .5) return 1.;\n float s=texture2D(scopeMap,uv).a;\n return scopeMode < 1.5 ? s : 1.-s;\n}\nvec4 trailField(vec2 address) {\n return texture2D(surface,(address*(surfaceSize-1.0)+.5)/surfaceSize);\n}\nfloat trailFieldDensity(vec4 field) {\n float raw=field.r*65280.0+field.g*255.0;\n return (trailKind>2.5?(raw-32768.)/32767.:raw/65535.)*trailAmount;\n}\nfloat trailDensity(vec2 address) { return trailFieldDensity(trailField(address))*hoverScope(address); }\nfloat trailPin(vec2 cell, vec2 grid) {\n vec2 edge=min(cell,grid-1.0-cell);\n return mix(1.,smoothstep(1.0,5.0,min(edge.x,edge.y)),edgeSafe);\n}\nvec2 trailSource(vec2 cell, vec2 grid, vec4 field) {\n vec2 home=(cell+.5)/grid;\n if(trailKind>.5 || trailAmount<=0.) return home;\n float pin=trailPin(cell,grid);\n float response=1.-exp(-trailFieldDensity(field)*hoverScope(home)*pin*3.5);\n vec2 drift=(field.ba*255.-128.)/127.;\n return clamp(home-drift*response*.026*pin,.5/grid,1.-.5/grid);\n}\nvec2 decodedGlyph(vec4 code) {\n return vec2(floor(code.r*255.+.5)+floor(code.g*255.+.5)*256.,code.b);\n}\n// Bilinear interpolation happens before glyph selection, never between bitmap\n// letters. Four small metadata reads replace the old four full-glyph gathers.\nvec2 flowGlyph(sampler2D codes,vec2 uv,vec2 grid) {\n vec2 p=uv*grid-.5,b=floor(p),f=fract(p);\n return mix(mix(decodedGlyph(texture2D(codes,(b+.5)/grid)),decodedGlyph(texture2D(codes,(b+vec2(1.5,.5))/grid)),f.x),\n mix(decodedGlyph(texture2D(codes,(b+vec2(.5,1.5))/grid)),decodedGlyph(texture2D(codes,(b+1.5)/grid)),f.x),f.y);\n}\nvec3 trailInk(vec3 ink, float density) {\n if (trailKind < .5) return ink;\n float peak=max(ink.r,max(ink.g,ink.b));\n float raised=trailKind>1.5?peak:max(peak,min(.44,density*.75));\n return peak>.001 ? ink*(raised/peak) : vec3(raised);\n}\nfloat flowIndex(float index,float count,float density) {\n float last=max(0.,count-1.);\n float response=1.-exp(-max(0.,density)*3.5);\n return max(0.,last-abs(last-(index+response*last*2.1)));\n}\nfloat trailTone(float tone, float density) {\n if (trailKind > 2.5) return clamp(tone+density*.35,0.,1.);\n if (trailKind < .5) return flowIndex(tone,2.,density);\n return clamp(tone + (trailKind > 1.5 ? -density : density) * .65, 0.0, 1.0);\n}\nfloat trailIndex(float index, float count, float density, vec2 cell) {\n float grain=fract((cell.x*cell.x*17.0+cell.y*cell.y*23.0+cell.x*cell.y*19.0)*.0137);\n if(trailKind>2.5) return clamp(floor(index+density*(count-1.)*.35+.5),0.,count-1.);\n if(trailKind>1.5) return max(0.0,index-floor(density*count*(1.35+grain*.9)));\n if(trailKind<.5) {\n float seed=fract(sin(dot(cell,vec2(127.1,311.7)))*43758.5453);\n float response=1.-exp(-max(0.,density)*3.5);\n return floor(max(0.,flowIndex(index,count,density)-response*(1.-response)*seed*3.)+.5);\n }\n return min(count-1.0,index+floor(density*count*.85));\n}\n\n \n uniform float motionMode; uniform float motionTime; uniform float fineDither; uniform float ditherAmount; uniform vec2 motionFocus;\n float printGrain(vec2 p) { return fract(52.9829189*fract(dot(p,vec2(.06711056,.00583715)))); }\n // cue: light, depth, glow, detail; normal: relief normal.\n vec4 ambientAt(vec2 uv, float tone, vec4 cue, vec2 normal) {\n if(motionMode<.5) return vec4(0.,0.,0.,1.);\n float p=motionTime*.5235987756, cycle=mod(motionTime,12.);\n float edge=smoothstep(0.,.12,min(min(uv.x,1.-uv.x),min(uv.y,1.-uv.y)));\n tone=clamp(tone,0.,1.);\n if(motionMode<1.5) {\n float a=p+.6; vec2 l=vec2(cos(a),sin(a));\n float form=clamp(dot(normal,l),-1.,1.)+dot(uv-.5,l)*.35;\n return vec4(0.,0.,form*.2*(.3+.7*smoothstep(.04,.3,cue.x)*(1.-.6*smoothstep(.82,1.,tone))),1.);\n }\n if(motionMode<2.5) {\n float position=-.35+1.9*smoothstep(0.,8.,cycle);\n float d=(uv.x*.75+uv.y*.45)/1.2+(normal.x*.75+normal.y*.45)*.05;\n float band=exp(-pow((d-position)/.12,2.));\n return vec4(0.,0.,band*smoothstep(.3,.8,cue.x)*(1.-.5*smoothstep(.9,1.,tone))*.45,1.);\n }\n if(motionMode<3.5) {\n float k=.5-.5*cos(p), threshold=1.-.8*k;\n return vec4(0.,0.,.2*smoothstep(threshold-.3,threshold+.02,cue.z)*sqrt(cue.z)*(1.-.65*tone)+.05*k*smoothstep(.6,.95,tone),1.);\n }\n if(motionMode<4.5) {\n float phase=printGrain(floor(uv*vec2(160.,100.)))*6.28318530718;\n float glint=pow(.5+.5*cos(p*2.+phase),28.);\n return vec4(0.,0.,glint*smoothstep(.5,.88,tone)*(.35+.65*smoothstep(.1,.55,cue.w))*.42,1.);\n }\n if(motionMode<5.5) { float near=cue.y-.45; return vec4(near*16.*sin(p)*edge,near*7.*sin(p*2.)*edge,0.,1.); }\n if(motionMode<6.5) return vec4((uv-motionFocus)*.045*(.5-.5*cos(p))*(.7+.6*cue.y)*960.,0.,1.);\n if(motionMode<7.5) {\n float s=sin(p), defocus=s*s*smoothstep(.15,.6,s>=0.?1.-cue.y:cue.y);\n return vec4(0.,0.,(cue.x-tone)*defocus*.9,1.);\n }\n if(motionMode<8.5) {\n float far=pow(1.-cue.y,1.5)*(1.-.6*cue.w);\n float veil=sin(uv.x*4.1-p+sin(uv.y*3.3+p)*.9)*.5+sin(uv.x*7.3+uv.y*2.1-p*2.+sin(uv.x*2.7-uv.y*5.1+p)*1.1)*.3+sin(uv.y*5.7-uv.x*1.9+p)*.2;\n return vec4(0.,0.,smoothstep(-.3,.9,veil)*far*.16*(1.-.55*tone),1.);\n }\n if(motionMode<9.5) {\n float fluid=(1.-.4*cue.w)*(1.-.8*cue.y)*edge, wave=uv.y*14.-p+sin(uv.x*3.+p)*1.5, still=uv.y*14.+sin(uv.x*3.)*1.5;\n return vec4((sin(wave)-sin(still)+(sin(uv.x*5.-uv.y*3.+p)-sin(uv.x*5.-uv.y*3.))*.43)*7.*fluid,(sin(uv.x*11.+p)-sin(uv.x*11.))*2.*fluid,(cos(wave)-cos(still))*.03*fluid,1.);\n }\n float fade=smoothstep(7.,8.5,cycle), k=clamp((cycle-8.5)/3.5,0.,1.);\n float shown=cycle<8.5?1.-fade:smoothstep(0.,1.,(k*1.45-(1.-tone)+(printGrain(floor(uv*vec2(160.,100.)))-.5)*.06)/.3);\n return vec4(0.,0.,-(1.-shown)*.3,1.-(1.-shown)*.9);\n }\n\n float toneAt(vec2 uv) {\n vec2 p=uv*gridSize-.5, b=floor(p), f=fract(p);\n return mix(mix(texture2D(codes,(b+.5)/gridSize).b,texture2D(codes,(b+vec2(1.5,.5))/gridSize).b,f.x),mix(texture2D(codes,(b+vec2(.5,1.5))/gridSize).b,texture2D(codes,(b+1.5)/gridSize).b,f.x),f.y);\n }\n vec4 flowingCharacter(vec2 pixel, vec2 cell, vec4 ambient) {\n if(cell.x<0. || cell.y<0. || cell.x>=gridSize.x || cell.y>=gridSize.y) return vec4(0.);\n vec2 address=(cell+.5)/gridSize;\n vec4 field=trailField(address);\n vec2 local=pixel-cell*cellSize;\n if(local.x<0. || local.y<0. || local.x>=cellSize.x || local.y>=cellSize.y) return vec4(0.);\n vec2 source=trailSource(cell,gridSize,field);\n vec2 encoded=flowGlyph(codes,source,gridSize);\n vec4 color=texture2D(cellColors,source);\n float original=encoded.x;\n float density=trailFieldDensity(field)*hoverScope(address)*trailPin(cell,gridSize),index=trailIndex(original,glyphCount,density,cell);\n if(original>.5) index=clamp(floor(index+ambient.z*(glyphCount-1.)+printGrain(cell)),0.,glyphCount-1.);\n if(index<.5 || color.a<.01) return vec4(0.);\n vec2 slot=vec2(mod(index,atlasColumns),floor(index/atlasColumns));\n float alpha=texture2D(glyphAtlas,(slot*tileSize+padding+local*resolution/sceneSize)/atlasSize).a;\n if(dots>.5) {\n vec2 d=(local-cellSize*.5)/min(cellSize.x,cellSize.y);\n float r=min(.48,(index/(glyphCount-1.))*.5*dotSize);\n alpha=1.-smoothstep(r-.06,r+.02,length(d));\n }\n vec3 coverage=dots>.5?vec3(alpha):edgeCoverage(glyphAtlas,(slot*tileSize+padding+local*resolution/sceneSize)/atlasSize,atlasSize,pixel,sceneSize,edgeEffect);\n coverage*=color.a;alpha=max(coverage.r,max(coverage.g,coverage.b));\n return vec4(trailInk(color.rgb,density)*coverage,alpha);\n }\n void main(){\n vec2 uv = vec2(gl_FragCoord.x, resolution.y - gl_FragCoord.y) / resolution;\n vec2 pixel = uv * sceneSize;\n if (strength > 0.) {\n vec4 normal = texture2D(surface, (uv * (surfaceSize - 1.) + .5) / surfaceSize) * 255.;\n vec2 slope = (vec2(normal.r * 256. + normal.g, normal.b * 256. + normal.a) - 32768.) / 32767.;\n vec2 edge = min(pixel, sceneSize - pixel) / cellSize;\n // Cursor hovers store a fraction of the scene, so they scale with it; stroke hovers store CSS pixels.\n vec2 unit = relativeStrength > .5 ? vec2(1.) : 1. / sceneSize;\n uv -= slope * strength * mix(1.,smoothstep(2.,8.,min(edge.x,edge.y)),edgeSafe) * hoverScope(uv) * unit;\n }\n vec4 ambient=vec4(0.,0.,0.,1.);\n if(motionMode>.5) {\n vec4 cue=texture2D(cueMap,uv);\n vec2 step=1./gridSize;\n vec2 slope=vec2(texture2D(cueMap,uv+vec2(step.x,0.)).x-texture2D(cueMap,uv-vec2(step.x,0.)).x,texture2D(cueMap,uv+vec2(0.,step.y)).x-texture2D(cueMap,uv-vec2(0.,step.y)).x);\n // Bright reads as raised; normals lean away from the brighter side.\n ambient=ambientAt(uv,toneAt(uv),cue,-slope/max(length(slope),1e-4)*smoothstep(0.,.06,length(slope)));\n }\n uv -= ambient.xy/sceneSize;\n pixel=uv*sceneSize;\n vec4 ink = vec4(0.);\n if (directGlyphs > .5) {\n vec2 cell=floor(pixel/cellSize), address=(cell+.5)/gridSize;\n vec4 encoded=texture2D(codes,address), color=texture2D(cellColors,address);\n float index=floor(encoded.r*255.+.5)+floor(encoded.g*255.+.5)*256.;\n if(index>.5 && color.a>.01) {\n vec2 local=(pixel-cell*cellSize)*resolution/sceneSize;\n vec2 slot=vec2(mod(index,atlasColumns),floor(index/atlasColumns));\n float alpha=texture2D(glyphAtlas,(slot*tileSize+padding+local)/atlasSize).a*color.a;\n vec3 coverage=edgeCoverage(glyphAtlas,(slot*tileSize+padding+local)/atlasSize,atlasSize,pixel,sceneSize,edgeEffect)*color.a;\n ink=vec4(color.rgb*coverage,max(coverage.r,max(coverage.g,coverage.b)));\n }\n } else ink = texture2D(artwork, uv);\n if (fineDither > .5) {\n vec2 cell=floor(pixel/cellSize),address=(cell+.5)/gridSize;\n vec4 color=texture2D(cellColors,address);\n float density=trailAmount>0.?trailDensity(address):0.;\n vec2 source=trailAmount>0. && trailKind<.5?trailSource(cell,gridSize,trailField(address)):uv;\n float tone=trailTone(clamp(toneAt(source)+ambient.z,0.,1.),density*(trailKind<.5?trailPin(cell,gridSize):1.));\n float pitch=max(1.,min(cellSize.x,cellSize.y)*.32);\n float grain=.5+(printGrain(floor(pixel/pitch))-.5)*ditherAmount;\n float alpha=(tone<.001?0.:tone>.999?1.:smoothstep(grain-.035,grain+.035,tone))*color.a*ambient.w;\n // Preserve hue; tone is carried by mark coverage rather than muddy ink.\n vec3 hue=color.rgb/max(.001,max(color.r,max(color.g,color.b)));\n if(hasTextMask>.5) hue=mix(hue,vec3(.035),texture2D(textMask,vec2(gl_FragCoord.x,resolution.y-gl_FragCoord.y)/resolution).a);\n gl_FragColor=vec4(hue*alpha,alpha); return;\n }\n if ((trailAmount > 0. && trailKind > .5) || motionMode > .5) {\n vec2 cell=floor(pixel/cellSize), address=(cell+.5)/gridSize;\n float density=trailAmount>0.?trailDensity(address):0.;\n vec4 encoded=texture2D(codes,address);\n float original=floor(encoded.r*255.+.5)+floor(encoded.g*255.+.5)*256.;\n float index=trailIndex(original,glyphCount,density,cell);\n if(original>.5) index=clamp(floor(index+ambient.z*(glyphCount-1.)+printGrain(cell)),0.,glyphCount-1.);\n vec4 color=texture2D(cellColors,address);\n if((index!=original || motionMode>.5) && color.a>.01) {\n vec2 local=(pixel-cell*cellSize)*resolution/sceneSize;\n vec2 slot=vec2(mod(index,atlasColumns),floor(index/atlasColumns));\n float alpha=texture2D(glyphAtlas,(slot*tileSize+padding+local)/atlasSize).a;\n if(dots>.5) {\n vec2 d=(pixel-(cell+.5)*cellSize)/min(cellSize.x,cellSize.y);\n float r=min(.48,(index/(glyphCount-1.))*.5*dotSize);\n alpha=1.-smoothstep(r-.06,r+.02,length(d));\n }\n // Replace the cell, rather than blending two glyphs into a ghost.\n vec3 rgb=trailInk(color.rgb,density);\n ink=vec4(rgb*alpha*color.a,alpha*color.a);\n }\n }\n if(trailAmount>0. && trailKind<.5 && trailDensity((floor(pixel/cellSize)+.5)/gridSize)>.001) {\n ink=flowingCharacter(pixel,floor(pixel/cellSize),ambient);\n }\n vec3 straightInk = ink.a > .0 ? ink.rgb / ink.a : vec3(.0);\n float value = max(straightInk.r, max(straightInk.g, straightInk.b));\n // Lighting belongs to character ink; black/transparent gaps stay empty.\n float light = surfaceLight((floor(pixel / cellSize) + .5) * cellSize / sceneSize, sceneSize) * hoverScope(pixel / sceneSize);\n vec3 lit = illuminate(straightInk, light * smoothstep(0., .22, value)) * ink.a;\n if(hasTextMask>.5) lit=mix(lit,vec3(.035)*ink.a,texture2D(textMask,vec2(gl_FragCoord.x,resolution.y-gl_FragCoord.y)/resolution).a);\n vec4 result = vec4(lit, ink.a)*ambient.w;\n gl_FragColor = directGlyphs > .5 ? result + backdrop*(1.-result.a) : result;\n }")),c.linkProgram(u),!c.getProgramParameter(u,c.LINK_STATUS))throw new Error("Surface shader link failed");c.useProgram(u),m=c.createBuffer(),c.bindBuffer(c.ARRAY_BUFFER,m),c.bufferData(c.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,1,1]),c.STATIC_DRAW);const e=c.getAttribLocation(u,"position");c.enableVertexAttribArray(e),c.vertexAttribPointer(e,2,c.FLOAT,!1,0,0),Q=Object.fromEntries(["finishSoftness","finishMode","finishPixelRatio","edgeEffect","textMask","hasTextMask","artwork","surface","resolution","sceneSize","cellSize","surfaceSize","strength","hoverMode","hoverFocus","codes","cellColors","glyphAtlas","gridSize","atlasSize","tileSize","atlasColumns","padding","glyphCount","dots","dotSize","directGlyphs","backdrop","trailKind","edgeSafe","relativeStrength","scopeMap","scopeMode","trailAmount","motionMode","motionTime","fineDither","ditherAmount","cueMap","motionFocus"].map(t=>[t,c.getUniformLocation(u,t)]));for(let t=0;t<8;t++){const e=c.createTexture();f.push(e),c.activeTexture(c.TEXTURE0+t),c.bindTexture(c.TEXTURE_2D,e),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_MIN_FILTER,c.LINEAR),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_MAG_FILTER,c.LINEAR),c.texImage2D(c.TEXTURE_2D,0,c.RGBA,1,1,0,c.RGBA,c.UNSIGNED_BYTE,new Uint8Array(4)),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_WRAP_S,c.CLAMP_TO_EDGE),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_WRAP_T,c.CLAMP_TO_EDGE)}c.uniform1i(Q.textMask,5),c.uniform1i(Q.scopeMap,6),c.uniform1i(Q.cueMap,7),c.uniform1i(Q.codes,2),c.uniform1i(Q.cellColors,3),c.uniform1i(Q.glyphAtlas,4),c.uniform1i(Q.artwork,0),c.uniform1i(Q.surface,1),c.pixelStorei(c.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1),c.disable(c.BLEND),c.disable(c.DEPTH_TEST),l.addEventListener("webglcontextlost",it),tt()}catch{it()}const st=()=>{p||(K.reset(),q=null,v=!1,a())};return document.fonts?.addEventListener("loadingdone",st),document.fonts?.ready.then(st),{get canvas(){return l},canRenderGlyphs:t=>!!c&&!p&&Y(t),render(t,a,o,h,u,m,g,N,tt=0,et={}){const st=s(N?.animationStyle),at=j.update(tt,st,N?.animationSpeed),ot=(t=>!0===t?.fineDither&&!t.customText&&!t.charsetFrames?.length&&!/[A-Za-z]/.test(t.charset??""))(N),nt=!!c&&!!g&&Y(N);if(nt!==F&&(v=!1,F=nt),p||!o||!h||!e.width||!e.height)return;const ht=Math.min(e.width/o,1.5,Math.sqrt(4e6/(o*h))),rt=Math.max(1,Math.round(o*ht)),lt=Math.max(1,Math.round(h*ht));if(l.width===rt&&l.height===lt||(l.width=rt,l.height=lt,v=!1),c&&(e.width>S||e.height>S)&&it(),(nt||M(a.mode)||"none"!==st||ot)&&(t||!v||!q)&&(q=K.update(e,o,h,u,m,ht,g,N),c)){const{plan:t,packed:e,atlas:i}=q;for(const[t,i]of[[2,e.indices],[3,e.colors]])c.activeTexture(c.TEXTURE0+t),c.bindTexture(c.TEXTURE_2D,f[t]),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_MIN_FILTER,3===t?c.LINEAR:c.NEAREST),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_MAG_FILTER,3===t?c.LINEAR:c.NEAREST),A!==e.cols||D!==e.rows?c.texImage2D(c.TEXTURE_2D,0,c.RGBA,e.cols,e.rows,0,c.RGBA,c.UNSIGNED_BYTE,i):c.texSubImage2D(c.TEXTURE_2D,0,0,0,e.cols,e.rows,c.RGBA,c.UNSIGNED_BYTE,i);A=e.cols,D=e.rows,k!==t&&(c.activeTexture(c.TEXTURE4),c.bindTexture(c.TEXTURE_2D,f[4]),c.texImage2D(c.TEXTURE_2D,0,c.RGBA,c.RGBA,c.UNSIGNED_BYTE,i),k=t),c.uniform2f(Q.gridSize,e.cols,e.rows),c.uniform2f(Q.atlasSize,t.atlasWidth,t.atlasHeight),c.uniform2f(Q.tileSize,t.tileWidth,t.tileHeight),c.uniform1f(Q.atlasColumns,t.atlasColumns),c.uniform1f(Q.padding,t.padding),c.uniform1f(Q.glyphCount,t.glyphs.length),c.uniform1f(Q.dots,q.dots?1:0),c.uniform1f(Q.dotSize,q.dotSize)}if("none"!==st&&q&&V!==q){const{packed:t}=q,e=t.cols*t.rows;J.length!==4*e&&(J=new Uint8Array(4*e));for(let i=0;i<e;i++){const e=t.indices[4*i+2];J[4*i]=J[4*i+1]=J[4*i+2]=e,J[4*i+3]=t.colors[4*i+3]}$=function(t,e,i,s){const a=e*i,o=s?.length===8*a?s:new Float32Array(8*a);if(!a)return o;const n=Math.min(e,i),h=new Float32Array(a),r=new Float32Array(a),l=new Float32Array(a),c=new Float32Array(a),d=new Float32Array(a),u=new Float32Array(a),m=new Float32Array(a);for(let e=0;e<a;e++){const i=t[4*e+3]/255;h[e]=(.299*t[4*e]+.587*t[4*e+1]+.114*t[4*e+2])/255*i}r.set(h),E(r,m,e,i,Math.max(1,Math.round(.012*n))),l.set(h),E(l,m,e,i,Math.max(1,Math.round(.03*n)));for(let t=0;t<a;t++){c[t]=Math.abs(h[t]-r[t]);const e=Math.max(0,h[t]-.55)/.45;d[t]=e*e}E(c,m,e,i,Math.max(1,Math.round(.02*n)));const f=1/Math.max(.02,G(c,.95));E(d,m,e,i,Math.max(1,Math.round(.05*n)));const g=1/Math.max(.03,G(d,.98)),p=T(t,e,i);let M=0,v=0,x=0;for(let t=0;t<i;t++)for(let s=0;s<e;s++){const a=t*e+s,o=(s+.5)/e,n=(t+.5)/i,r=.55*Math.min(1,c[a]*f)+.3*(1-Math.min(1,1.6*Math.hypot(1.3*(o-.5),n-.55)))+.15*U(.2,1,n);u[a]="none"===p.key?r:.75*p.subject[a]+.25*r;const l=u[a]*u[a]*(.25+h[a]);M+=o*l,v+=n*l,x+=l}E(u,m,e,i,Math.max(1,Math.round(.06*n)));const y=G(u,.05),w=G(u,.95),b=Math.max(.05,w-y);M=x>0?Math.max(.3,Math.min(.7,M/x)):.5,v=x>0?Math.max(.3,Math.min(.7,v/x)):.5;const S=1/Math.max(.004,G((()=>{const t=new Float32Array(a);for(let s=0;s<i;s++)for(let a=0;a<e;a++)t[s*e+a]=Math.hypot(l[s*e+Math.min(e-1,a+1)]-l[s*e+Math.max(0,a-1)],l[Math.min(i-1,s+1)*e+a]-l[Math.max(0,s-1)*e+a]);return t})(),.97));for(let t=0;t<i;t++)for(let s=0;s<e;s++){const a=t*e+s,n=8*a,h=(l[t*e+Math.min(e-1,s+1)]-l[t*e+Math.max(0,s-1)])*S,r=(l[Math.min(i-1,t+1)*e+s]-l[Math.max(0,t-1)*e+s])*S;o[n]=l[a],o[n+1]=Math.max(0,Math.min(1,(u[a]-y)/b)),o[n+2]=Math.min(1,d[a]*g),o[n+3]=Math.min(1,c[a]*f),o[n+4]=Math.max(-1,Math.min(1,-h)),o[n+5]=Math.max(-1,Math.min(1,-r)),o[n+6]=M,o[n+7]=v}return o}(J,t.cols,t.rows,$),V=q,c&&(Z=function(t,e){const i=t.length/8,s=e?.length===4*i?e:new Uint8Array(4*i);for(let e=0;e<i;e++)for(let i=0;i<4;i++)s[4*e+i]=Math.round(255*t[8*e+i]);return s}($,Z),c.activeTexture(c.TEXTURE7),c.bindTexture(c.TEXTURE_2D,f[7]),c.texImage2D(c.TEXTURE_2D,0,c.RGBA,t.cols,t.rows,0,c.RGBA,c.UNSIGNED_BYTE,Z),c.uniform2f(Q.motionFocus,$[6]??.5,$[7]??.5))}if(c){c.uniform1f(Q.motionMode,(t=>i.findIndex(e=>e.mode===t))(st)),c.uniform1f(Q.motionTime,at),c.uniform1f(Q.fineDither,ot?1:0),c.uniform1f(Q.ditherAmount,Math.max(0,Math.min(2,N?.ditherStrength??1))),c.uniform1f(Q.trailKind,x(a.mode)),c.uniform1f(Q.edgeSafe,a.edgeSafe?1:0),c.uniform1f(Q.trailAmount,M(a.mode)?a.focus[2]:0),c.viewport(0,0,rt,lt),nt||!t&&v||(c.activeTexture(c.TEXTURE0),c.bindTexture(c.TEXTURE_2D,f[0]),c.pixelStorei(c.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!0),c.texImage2D(c.TEXTURE_2D,0,c.RGBA,c.RGBA,c.UNSIGNED_BYTE,e),c.pixelStorei(c.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1)),c.uniform1f(Q.directGlyphs,nt?1:0),!nt||!t&&v||c.uniform4fv(Q.backdrop,et.textMask||function(t){const e=t.length,i=t[0]?.length??0;for(let s=0;s<e;s+=Math.max(1,e>>2))for(let e=0;e<i;e+=Math.max(1,i>>2))if(t[s][e].a<200)return!0;return!1}(g)?[0,0,0,0]:B(e)),v=!0,c.activeTexture(c.TEXTURE1),c.bindTexture(c.TEXTURE_2D,f[1]),R!==a.width||z!==a.height?(c.texImage2D(c.TEXTURE_2D,0,c.RGBA,a.width,a.height,0,c.RGBA,c.UNSIGNED_BYTE,a.pixels),R=a.width,z=a.height):c.texSubImage2D(c.TEXTURE_2D,0,0,0,a.width,a.height,c.RGBA,c.UNSIGNED_BYTE,a.pixels),c.uniform2f(Q.resolution,rt,lt),c.uniform2f(Q.sceneSize,o,h),c.uniform2f(Q.cellSize,u,m),c.uniform2f(Q.surfaceSize,a.width,a.height),c.uniform1f(Q.strength,a.strength),c.uniform1f(Q.relativeStrength,a.relative?1:0);const s=et.scope;c.uniform1f(Q.scopeMode,s&&"all"!==s.mode?"subject"===s.mode?1:2:0),s&&"all"!==s.mode&&s.pixels!==I&&(c.activeTexture(c.TEXTURE6),c.bindTexture(c.TEXTURE_2D,f[6]),c.texImage2D(c.TEXTURE_2D,0,c.RGBA,s.width,s.height,0,c.RGBA,c.UNSIGNED_BYTE,s.pixels),I=s.pixels),c.uniform1f(Q.hoverMode,"light"===a.mode||"reveal"===a.mode?1:"scan"===a.mode?2:"repel"===a.mode?3:0),c.uniform4fv(Q.hoverFocus,a.focus),et.textMask&&et.textMask!==X&&(c.activeTexture(c.TEXTURE5),c.bindTexture(c.TEXTURE_2D,f[5]),c.texImage2D(c.TEXTURE_2D,0,c.RGBA,c.RGBA,c.UNSIGNED_BYTE,et.textMask.canvas),X=et.textMask),c.uniform1f(Q.hasTextMask,et.textMask?1:0);const n=function(t={}){const e=P.some(e=>e.value===t.filter)?t.filter:"prism",i=Number.isFinite(t.edgeEffect)?Math.max(0,Math.min(1,t.edgeEffect)):t.filter?1:0,s=Number.isFinite(t.edgeSoftness)?Math.max(0,Math.min(1,t.edgeSoftness)):0;return{mode:["clean","prism","etch","signal"].indexOf(e),amount:"clean"===e?0:i,softness:"prism"===e?s:0}}(et);c.uniform1f(Q.edgeEffect,n.amount),c.uniform1f(Q.finishMode,n.mode),c.uniform1f(Q.finishSoftness,n.softness),c.uniform1f(Q.finishPixelRatio,1),c.drawArrays(c.TRIANGLE_STRIP,0,4)}else if(d){if(d.setTransform(ht,0,0,ht,0,0),d.clearRect(0,0,o,h),d.drawImage(e,0,0,o,h),("none"!==st||ot)&&q){d.clearRect(0,0,o,h);const{plan:t,packed:e,atlas:i}=q,s=[0,0,0,1],l=[0,0,0,0,0,0],c=Math.max(1,.32*Math.min(u,m));for(let o=0;o<e.rows;o++)for(let h=0;h<e.cols;h++){const f=(h+.5)/e.cols,g=(o+.5)/e.rows,p=4*(o*e.cols+h);if(e.colors[p+3]<10)continue;r(st,f,g,at,s,e.indices[p+2]/255,$??void 0,8*(o*e.cols+h));const v=4*(Math.min(a.height-1,Math.round(g*(a.height-1)))*a.width+Math.min(a.width-1,Math.round(f*(a.width-1)))),S=M(a.mode)?y(a.pixels,v,a.mode,a.focus[2]):0,k=e.indices[p]+256*e.indices[p+1];"trail"===a.mode&&b(e.indices,e.colors,e.cols,e.rows,h,o,S,(a.pixels[v+2]-128)/127,(a.pixels[v+3]-128)/127,t.glyphs.length,l,a.edgeSafe);let E="trail"===a.mode?l[0]:3===x(a.mode)?Math.round(k+S*(t.glyphs.length-1)*.35):"dissolve"===a.mode?k-Math.floor(S*t.glyphs.length*1.8):k+Math.floor(S*t.glyphs.length*.85);k>0&&(E+=s[2]*(t.glyphs.length-1)),E=Math.max(0,Math.min(t.glyphs.length-1,Math.floor(E+n(h,o))));let T=s[0],A=s[1];a.strength>0&&(T+=(256*a.pixels[v]+a.pixels[v+1]-32768)/32767*a.strength,A+=(256*a.pixels[v+2]+a.pixels[v+3]-32768)/32767*a.strength);const D=h*u+T,R=o*m+A,z=("trail"===a.mode?l[5]:e.colors[p+3])/255*s[3],F="trail"===a.mode?l[2]:e.colors[p],C="trail"===a.mode?l[3]:e.colors[p+1],X="trail"===a.mode?l[4]:e.colors[p+2],I=ot?Math.max(1,F,C,X):255,_=`rgb(${F/I*255},${C/I*255},${X/I*255})`;if(d.fillStyle=_,ot){const t="trail"===a.mode?w(Math.max(0,Math.min(1,l[1]+s[2])),2,S):Math.max(0,Math.min(1,e.indices[p+2]/255+s[2]+(3===x(a.mode)?.35*S:.65*("dissolve"===a.mode?-S:S))));d.globalAlpha=z;for(let e=0;e<m;e+=c)for(let i=0;i<u;i+=c)t>.5+(n(Math.floor((D+i)/c),Math.floor((R+e)/c))-.5)*Math.max(0,Math.min(2,N?.ditherStrength??1))&&d.fillRect(D+i,R+e,Math.min(c,u-i),Math.min(c,m-e))}else if(E>0)if(d.globalAlpha=z,q.dots){const e=Math.min(.48,E/(t.glyphs.length-1)*.5*q.dotSize)*Math.min(u,m);d.beginPath(),d.arc(D+.5*u,R+.5*m,e,0,2*Math.PI),d.fill()}else d.drawImage(i,E%t.atlasColumns*t.tileWidth+t.padding,Math.floor(E/t.atlasColumns)*t.tileHeight+t.padding,t.cellWidth*ht,t.cellHeight*ht,D,R,u,m),d.globalAlpha=1,d.globalCompositeOperation="source-atop",d.fillRect(D,R,u,m),d.globalCompositeOperation="source-over"}d.globalAlpha=1}else if(M(a.mode)&&q){const{plan:t,packed:e,atlas:i}=q,s=[0,0,0,0,0,0];for(let o=0;o<e.rows;o++)for(let n=0;n<e.cols;n++){const h=4*(Math.min(a.height-1,Math.round((o+.5)/e.rows*(a.height-1)))*a.width+Math.min(a.width-1,Math.round((n+.5)/e.cols*(a.width-1)))),r=y(a.pixels,h,a.mode,a.focus[2]),l=4*(o*e.cols+n),c=.0137*(n*n*17+o*o*23+n*o*19),f=c-Math.floor(c);"trail"===a.mode&&b(e.indices,e.colors,e.cols,e.rows,n,o,r,(a.pixels[h+2]-128)/127,(a.pixels[h+3]-128)/127,t.glyphs.length,s,a.edgeSafe);const g=e.indices[l]+256*e.indices[l+1],p="trail"===a.mode?Math.round(s[0]):3===x(a.mode)?Math.max(0,Math.min(t.glyphs.length-1,Math.round(g+r*(t.glyphs.length-1)*.35))):"dissolve"===a.mode?Math.max(0,g-Math.floor(r*t.glyphs.length*(1.35+.9*f))):Math.min(t.glyphs.length-1,g+Math.floor(r*t.glyphs.length*.85));if(p===g&&("trail"!==a.mode||r<1e-4)||e.colors[l+3]<10)continue;const M=n*u,v=o*m;if(d.clearRect(M,v,u,m),d.globalAlpha=("trail"===a.mode?s[5]:e.colors[l+3])/255,q.dots){const e=Math.min(.48,p/(t.glyphs.length-1)*.5*q.dotSize)*Math.min(u,m);d.fillStyle="#fff",d.beginPath(),d.arc(M+.5*u,v+.5*m,e,0,2*Math.PI),d.fill()}else d.drawImage(i,p%t.atlasColumns*t.tileWidth+t.padding,Math.floor(p/t.atlasColumns)*t.tileHeight+t.padding,t.cellWidth*ht,t.cellHeight*ht,M,v,u,m);d.globalAlpha=1,d.globalCompositeOperation="source-atop";const w="trail"===a.mode?s[2]:e.colors[l],S="trail"===a.mode?s[3]:e.colors[l+1],k="trail"===a.mode?s[4]:e.colors[l+2],E=Math.max(w,S,k),T="dissolve"===a.mode||"trail"===a.mode||3===x(a.mode)?E:Math.max(E,Math.min(112,191*r)),A=E?T/E:0;d.fillStyle=`rgb(${E?w*A:T},${E?S*A:T},${E?k*A:T})`,d.fillRect(M,v,u,m),d.globalCompositeOperation="source-over"}}else if("light"===a.mode||"scan"===a.mode||"reveal"===a.mode){!t&&W&&_.width===rt&&_.height===lt||(_.width=rt,_.height=lt,L.width=rt,L.height=lt,O.filter="brightness(3)",O.drawImage(e,0,0,rt,lt),O.filter="none",W=!0);const[i,s,n,r]=a.focus;if(n>.001){H.setTransform(1,0,0,1,0,0),H.clearRect(0,0,rt,lt),H.drawImage(_,0,0),H.globalCompositeOperation="destination-in",H.save(),H.translate(i*rt,s*lt),H.scale("scan"===a.mode?.13:1,1);const t=r*Math.min(rt,lt),e=H.createRadialGradient(0,0,0,0,0,t);e.addColorStop(0,`rgba(255,255,255,${n})`),e.addColorStop(.5,`rgba(255,255,255,${.4*n})`),e.addColorStop(1,"rgba(255,255,255,0)"),H.fillStyle=e,H.fillRect(10*-rt,10*-lt,20*rt,20*lt),H.restore(),H.globalCompositeOperation="source-over",d.globalCompositeOperation="source-atop",d.drawImage(L,0,0,o,h),d.globalCompositeOperation="source-over"}}else if(a.strength>0){const t=32,i=18,s=o/t,n=h/i;for(let r=0;r<i;r++)for(let l=0;l<t;l++){const c=(l+.5)/t,f=(r+.5)/i,g=Math.min(a.width-1,Math.round(c*(a.width-1))),p=4*(Math.min(a.height-1,Math.round(f*(a.height-1)))*a.width+g),M=a.pixels,v=Math.min(l*s/u,(o-(l+1)*s)/u,r*n/m,(h-(r+1)*n)/m),x=Math.max(0,Math.min(1,(v-2)/6)),y=a.edgeSafe?x*x*(3-2*x):1,w=et.scope&&"all"!==et.scope.mode?et.scope.pixels[4*(Math.min(et.scope.height-1,Math.floor(f*et.scope.height))*et.scope.width+Math.min(et.scope.width-1,Math.floor(c*et.scope.width)))+3]/255:1,b=("outside"===et.scope?.mode?1-w:w)*y,S=(256*M[p]+M[p+1]-32768)/32767*a.strength*b*(a.relative?o:1),k=(256*M[p+2]+M[p+3]-32768)/32767*a.strength*b*(a.relative?h:1);Math.abs(S)+Math.abs(k)<.02||(d.clearRect(l*s,r*n,s,n),d.drawImage(e,Math.max(0,Math.min(o-s,l*s-S))*e.width/o,Math.max(0,Math.min(h-n,r*n-k))*e.height/h,s*e.width/o,n*e.height/h,l*s,r*n,s,n))}}C.paint(d,et.textMask,o,h)}},destroy(){p=!0,C.destroy(),document.fonts?.removeEventListener("loadingdone",st),K.destroy(),l.removeEventListener("webglcontextlost",it),et(),l.remove(),_.width=_.height=L.width=L.height=1,e.style.visibility=o,c?.getExtension("WEBGL_lose_context")?.loseContext()}}}function H(e,i,a={}){let o,n,h,r={effect:"trail",scope:"all",decay:.5,touch:!0,strength:.55,radius:a.effect&&"trail"!==a.effect?.2:.45,animated:!1,fps:30,fontSize:7,charAspect:.58,charSpacing:1,paused:!1,charset:" .:-=+*#%@",customText:"",renderMode:"ascii",dotSizeRatio:.72,...a},l=new _(i.clientWidth/Math.max(1,i.clientHeight)),c=!1,d=!1,u=!0,m=0,f=0,g=-1/0,p=0,M=null,v=!1,x=[],y=null,w=!0;const b=document.createElement("canvas"),S=b.getContext("2d",{willReadFrequently:!0}),k=matchMedia("(prefers-reduced-motion: reduce)"),E=()=>{c||!d||document.hidden||m||(m=requestAnimationFrame(D))},A=O(e,i,()=>queueMicrotask(()=>{u=!0,E()}));function D(t){if(m=0,c||!d||document.hidden)return;const e=i.clientWidth,a=i.clientHeight;if(!e||!a)return;const D=k.matches||r.paused;l.configure(r.effect,r.strength,r.radius,r.edgeSafe,r.decay),w&&(()=>{if(w=!1,"all"===r.scope&&"tilt"!==r.effect)return h=void 0,y=null,void l.setGuide(null);if(!S||!i.width||!i.height)return;b.width=l.columns,b.height=l.rows,S.clearRect(0,0,b.width,b.height),S.drawImage(i,0,0,b.width,b.height);try{y=T(S.getImageData(0,0,b.width,b.height).data,b.width,b.height,null,y)}catch{y=null}if(l.setGuide(y),!y||"all"===r.scope||"none"===y.key)return void(h=void 0);const t=new Uint8Array(y.width*y.height*4);for(let e=0;e<y.subject.length;e++)t[4*e+3]=255*y.subject[e];h={pixels:t,width:y.width,height:y.height,mode:r.scope}})();const R=l.active;if(!D){if(p+=f?Math.min(.1,(t-f)/1e3):0,v){const t=i.getBoundingClientRect();if(t.width&&t.height)for(const e of x)l.move((e.x-t.left)/t.width,(e.y-t.top)/t.height,e.time);x=[],M||l.leave(),v=!1}l.step(f?(t-f)/1e3:1/60)}const z=u||r.animated&&!D&&t-g>=1e3/Math.max(1,Math.min(60,r.fps))-.5;(z||R||l.active||!D&&"none"!==s(r.motion))&&(z&&(o=r.getFrame?.()),A.render(z,l.refraction,e,a,r.fontSize*r.charSpacing,r.fontSize/r.charAspect*r.charSpacing,o,{charset:r.charset,customText:r.customText,renderMode:r.renderMode,dotSizeRatio:r.dotSizeRatio,colorMode:r.colorMode??"fullcolor",accentColor:r.accentColor??"#888888",animationStyle:r.motion,animationSpeed:r.motionSpeed??1,artStyle:"classic",fineDither:r.fineDither,ditherStrength:r.ditherStrength??1},p,{filter:r.filter,edgeEffect:r.edgeEffect,edgeSoftness:r.edgeSoftness,textMask:n?.read(),scope:h}),z&&(g=t),u=!1),f=t,D||!r.animated&&!l.active&&"none"===s(r.motion)||E()}r.textMask&&(n=t(e,r.textMask,()=>{u=!0,E()}));const R=()=>{l.clear(),M=null,x=[],v=!1,f=0,u=!0,cancelAnimationFrame(m),m=0,E()},z=t=>{if("touch"===t.pointerType&&!r.touch||r.paused||k.matches)return;if(t.target instanceof Element&&t.target.closest("button,a,input,summary,select"))return void F();const e=i.getBoundingClientRect();t.clientX<e.left||t.clientX>e.right||t.clientY<e.top||t.clientY>e.bottom?F():(M={x:t.clientX,y:t.clientY,time:t.timeStamp},x.push(M),x.length>6&&x.shift(),v=!0,E())},F=()=>{M=null,v=!0,E()},C=t=>{"touch"===t.pointerType&&F()},X=new IntersectionObserver(([t])=>{d=t.isIntersecting,R()});X.observe(e);const I=new ResizeObserver(()=>{l=new _(i.clientWidth/Math.max(1,i.clientHeight)),w=!0,R()});return I.observe(i),e.addEventListener("pointerdown",z),e.addEventListener("pointerup",C),e.addEventListener("pointermove",z),e.addEventListener("pointerleave",F),e.addEventListener("pointercancel",F),document.addEventListener("visibilitychange",R),k.addEventListener("change",R),{get canvas(){return A.canvas},invalidate(){n?.invalidate(),u=!0,w=!0,E()},update(i){"textMask"in i&&(n?.destroy(),n=i.textMask?t(e,i.textMask,()=>{u=!0,E()}):void 0),r={...r,...i},w=!0,R()},destroy(){c||(c=!0,cancelAnimationFrame(m),X.disconnect(),I.disconnect(),A.destroy(),n?.destroy(),e.removeEventListener("pointerdown",z),e.removeEventListener("pointerup",C),e.removeEventListener("pointermove",z),e.removeEventListener("pointerleave",F),e.removeEventListener("pointercancel",F),document.removeEventListener("visibilitychange",R),k.removeEventListener("change",R))}}}exports.DEPRECATED_STUDIO_HOVERS=k,exports.DEPRECATED_STUDIO_MOTIONS=e,exports.HOVER_SCOPES=[{value:"all",label:"Everywhere",description:"The hover acts on the whole artwork."},{value:"subject",label:"Subject",description:"The hover stays on the subject and fades softly at its edge."},{value:"outside",label:"Around",description:"The hover acts only on the ground around the subject."}],exports.STUDIO_ANIMATION_PRESETS=[{id:"quiet-light",label:"Quiet light",description:"A key light circles the subject’s own relief, with a soft magnifying wake.",motion:{type:"relight",speed:.6,amount:.8},hover:{effect:"lens",radius:.45,strength:.5}},{id:"living-paper",label:"Memory",description:"A slow push toward the subject; the image leans toward your cursor.",motion:{type:"push",speed:.5,amount:.8},hover:{effect:"tilt",radius:.5,strength:.45}},{id:"silver-grain",label:"Silver light",description:"Sparse glints on bright detail, with a fluid character trail.",motion:{type:"shimmer",speed:.6,amount:.9},hover:{effect:"trail",radius:.35,strength:.55}},{id:"ripple-study",label:"Still water",description:"Smooth water and sky drift like a cinemagraph; your stroke sends out rings.",motion:{type:"current",speed:.6,amount:.8},hover:{effect:"ripple",radius:.45,strength:.7}},{id:"unfolding-print",label:"Darkroom",description:"The print develops from its highlights; your cursor dissolves a soft path.",motion:{type:"develop",speed:.7,amount:1},hover:{effect:"dissolve",radius:.35,strength:.55}},{id:"silk-flow",label:"Morning mist",description:"Mist drifts through the distance; directional folds follow your hand.",motion:{type:"mist",speed:.6,amount:1},hover:{effect:"silk",radius:.4,strength:.5}},{id:"depth-study",label:"Depth study",description:"A slow parallax drift from the photo’s own depth, with a loupe under your cursor.",motion:{type:"parallax",speed:.6,amount:.9},hover:{effect:"loupe",radius:.35,strength:.55}}],exports.STUDIO_HOVERS=S,exports.STUDIO_MOTIONS=i,exports.createTextMask=t,exports.mountHover=H,exports.mountStudioHover=H;//# sourceMappingURL=hover.cjs.map