PageSourceSearch

https://adrolab.ai/j-site.js?v=eb45afab

js adrolab.ai collected 2026-10-02 01:10:38 UTC 219,469 bytes, 1,829 lines download raw bytes

1(()=>{
2/* URL helpers: ?t=<sec> fast-forward the intros; ?static reduced motion; ?freeze no animation loop; ?f=<phase> jump to a
3   point of the landing breakdown (0 whole, 1..5 pieces, 6 big, 7 last screen); ?scroll=<px|N%|Nvh>; ?quiz=q2|q3|suite|team|team51; ?fit=q1.q2a-q2b.q3; ?menu */
4const Q=new URLSearchParams(location.search);
5const RM=matchMedia('(prefers-reduced-motion: reduce)').matches||Q.has('static');
6if(RM)document.documentElement.classList.add('rm'); // CSS hook, so ?static also stops the CSS loops (QA 29 Sep 2026)
7const FREEZE=Q.has('freeze');
8const FT=Q.has('t')?parseFloat(Q.get('t'))||0:0;
9const VIEW=Q.get('view');
10if(Q.get('vh')) document.documentElement.style.setProperty('--vh',Q.get('vh')+'px');
11
12const C30=Math.cos(Math.PI/6), TAU=Math.PI*2;
13const clamp=(v,a,b)=>v<a?a:v>b?b:v;
14const lerp=(a,b,t)=>a+(b-a)*t;
15const ss=(a,b,x)=>{const t=clamp((x-a)/(b-a),0,1);return t*t*(3-2*t)};
16const easeIO=p=>p<.5?4*p*p*p:1-Math.pow(-2*p+2,3)/2;
17const hash=(i,j)=>{const s=Math.sin(i*127.1+j*311.7)*43758.5453;return s-Math.floor(s)};
18// seeded random so renders are stable
19let SEED=7; const srnd=()=>{SEED=(SEED*16807)%2147483647;return (SEED-1)/2147483646};
20
21const hex=h=>{h=h.replace('#','');return [parseInt(h.slice(0,2),16),parseInt(h.slice(2,4),16),parseInt(h.slice(4,6),16)]};
22const rgb=c=>`rgb(${c[0]|0},${c[1]|0},${c[2]|0})`;
23const mixC=(a,b,k)=>{const A=hex(a),B=hex(b);return rgb([lerp(A[0],B[0],k),lerp(A[1],B[1],k),lerp(A[2],B[2],k)])};
24const pal=(t,l,r)=>({top:rgb(hex(t)),left:rgb(hex(l)),right:rgb(hex(r))});
25const P_HI    =pal('#8AA59C','#6E8C83','#57756C');   // L: a highlighted block, light sage
26// 1 Oct 2026, founder: "just a small touch of orange" (J's orange, used sparingly): the chosen piece of the mark, a few
27// agents in every swarm, the moving or approved block in each small scene, a few cubes in the dividers
28const ORANGE='#EE6A33',ORG=[238,106,51],OR_FRAC=.07;
29const P_OR   =pal('#F47F4A','#E0602B','#B24A1E');
30const P_LIFT_OR=pal('#F6DECE','#EBC9B2','#DDB59A');   // the cursor's lifted world cells, at the very centre
31const P_COB   =pal('#46675F','#324E48','#26403B');
32// L: the world on cream, tone on tone
33const P_GROUND=pal('#EAE2D3','#DDD4C2','#D0C6B2');
34const P_LIFT  =pal('#F5F0E7','#E3DACA','#D4CAB6');
35const P_PLINTH=pal('#EFE7DA','#EFE7DA','#EFE7DA');
36const P_FLOOR =pal('#DCD4C2','#C8BEA8','#AFA48E');
37const P_INK   =pal('#3A5650','#253B36','#121D1A');
38const P_FOG   =pal('#B7C2BB','#95A49C','#74857D');
39const INK='#121D1A', BONE='#E4DCCA', COBALT='#324E48', CREAM='#EFE7DA', WORLD_BG='#EFE7DA';
40// L: highlights: dark sage on the light grounds, a bright cream on the dark ones
41const HI_L='#324E48', HI_D='#F6F1E7';
42const FONT='"Switzer", system-ui, sans-serif';
43
44// isometric box (from "Isometric World"), with optional face culling
45function box(ctx,P,x,y,z,w,d,h,p,stroke,skipL,skipR){
46  const u=P.u,ox=P.ox,oy=P.oy;
47  const X=(a,b)=>ox+(a-b)*u*C30, Y=(a,b,c)=>oy+(a+b)*u*.5-c*u;
48  const x2=x+w,y2=y+d,z2=z+h;
49  const ax=X(x,y),ay=Y(x,y,z2),bx=X(x2,y),by=Y(x2,y,z2),cx=X(x2,y2),cy=Y(x2,y2,z2),dx=X(x,y2),dy=Y(x,y2,z2);
50  const ey=Y(x2,y,z),fy=Y(x2,y2,z),gy=Y(x,y2,z);
51  if(stroke) ctx.strokeStyle=stroke;
52  if(!skipR){ctx.beginPath();ctx.moveTo(bx,by);ctx.lineTo(cx,cy);ctx.lineTo(cx,fy);ctx.lineTo(bx,ey);ctx.closePath();ctx.fillStyle=p.right;ctx.fill();if(stroke)ctx.stroke();}
53  if(!skipL){ctx.beginPath();ctx.moveTo(dx,dy);ctx.lineTo(cx,cy);ctx.lineTo(cx,fy);ctx.lineTo(dx,gy);ctx.closePath();ctx.fillStyle=p.left;ctx.fill();if(stroke)ctx.stroke();}
54  ctx.beginPath();ctx.moveTo(ax,ay);ctx.lineTo(bx,by);ctx.lineTo(cx,cy);ctx.lineTo(dx,dy);ctx.closePath();ctx.fillStyle=p.top;ctx.fill();if(stroke)ctx.stroke();
55}
56const projPt=(P,x,y,z)=>[P.ox+(x-y)*P.u*C30,P.oy+(x+y)*P.u*.5-z*P.u];
57// canvas sizing: DPR capped at 2, and the backing store capped (~7 MP) so large retina screens stay fast and never hit canvas limits
58const MAXPX=7e6;
59function setupCanvas(cv){
60  const r=cv.getBoundingClientRect();const W=Math.max(1,r.width),H=Math.max(1,r.height);
61  const dpr=Math.max(1,Math.min(window.devicePixelRatio||1,2,Math.sqrt(MAXPX/(W*H))));
62  cv.width=Math.max(1,Math.round(W*dpr));cv.height=Math.max(1,Math.round(H*dpr));
63  const ctx=cv.getContext('2d');ctx.setTransform(dpr,0,0,dpr,0,0);
64  return {ctx,W,H,dpr};
65}
66function fit(W,H,b,pad,shiftY){
67  let minx=1e9,maxx=-1e9,miny=1e9,maxy=-1e9;
68  for(const x of [b[0],b[1]])for(const y of [b[2],b[3]])for(const z of [b[4],b[5]]){
69    const sx=(x-y)*C30,sy=(x+y)*.5-z;minx=Math.min(minx,sx);maxx=Math.max(maxx,sx);miny=Math.min(miny,sy);maxy=Math.max(maxy,sy);}
70  const u=Math.min((W-2*pad)/(maxx-minx),(H-2*pad)/(maxy-miny));
71  return {u,ox:W/2-(minx+maxx)/2*u,oy:H/2-(miny+maxy)/2*u+(shiftY||0)};
72}
73function label(ctx,text,x,y,color,align,size){
74  ctx.font=`500 ${size||10}px ${FONT}`;ctx.fillStyle=color;ctx.textAlign=align||'left';ctx.textBaseline='middle';
75  ctx.fillText(text.toUpperCase(),x,y);
76}
77// a label on a small solid tag, so it stays readable over any surface
78function chip(ctx,text,x,y,align,size,bg,fg){
79  const T=text.toUpperCase();ctx.font=`500 ${size||10}px ${FONT}`;const w=ctx.measureText(T).width,px=5,h=(size||10)+8;
80  const x0=align==='right'?x-w-px*2:align==='center'?x-w/2-px:x;ctx.fillStyle=bg;ctx.fillRect(x0,y-h/2,w+px*2,h);
81  ctx.fillStyle=fg;ctx.textAlign='left';ctx.textBaseline='middle';ctx.fillText(T,x0+px,y+.5);
82}
83// a label with a halo in the background colour
84function haloLabel(ctx,text,x,y,color,halo,align,size){
85  ctx.font=`500 ${size||10}px ${FONT}`;ctx.textAlign=align||'left';ctx.textBaseline='middle';
86  ctx.lineWidth=4;ctx.lineJoin='round';ctx.strokeStyle=halo;ctx.strokeText(text.toUpperCase(),x,y);ctx.fillStyle=color;ctx.fillText(text.toUpperCase(),x,y);
87}
88// curl-noise flow (from "Living System")
89function psi(x,y,t){return Math.sin(x*.0031+t*.13)*Math.cos(y*.0043-t*.09)+Math.sin((x+y)*.0017-t*.07+1.3)*.8+Math.cos(x*.0071-y*.0023+t*.19)*.35}
90function field(x,y,t){const e=2,dx=psi(x+e,y,t)-psi(x-e,y,t),dy=psi(x,y+e,t)-psi(x,y-e,t);return Math.atan2(-dx,dy)}
91function chev(c,x,y,a,r){const cc=Math.cos(a),s=Math.sin(a);c.moveTo(x+cc*r,y+s*r);c.lineTo(x-cc*r*.8-s*r*.75,y-s*r*.8+cc*r*.75);c.lineTo(x-cc*r*.3,y-s*r*.3);c.lineTo(x-cc*r*.8+s*r*.75,y-s*r*.8-cc*r*.75);c.closePath()}
92const angWrap=d=>((d%TAU)+TAU*1.5)%TAU-Math.PI;
93const lineWrap=d=>{d=((d%Math.PI)+Math.PI*1.5)%Math.PI-Math.PI/2;return d};
94
95/* ============================================================
96   THE MARK (Website L): the OFFICIAL ADROLAB icon, traced to vectors from Adrolab_Icon_black.png (1083x1111) with potrace,
97   in mark units (946 wide, y 25..975). ll/logo/ has the tracing scripts and the fidelity check. The mark is split along its
98   own outlines into five pieces; together they give back the official mark exactly. Each piece: ink (the outline, filled,
99   even-odd), face (its enclosed faces, for the subtle tint of the active piece) and poly (its silhouette, for hit tests and
100   label placement). caps: the short closing edges where a piece was cut free of its neighbour (the wing strips and the stem);
101   they are drawn only while the pieces are apart, so the whole mark never shows them.
102   ============================================================ */
103const MARK_D={"parts":{"core":{"ink":"M467.7 386.6C452 384.7 441.3 381.4 431.1 375.2C428.7 373.7 418.1 367.3 407.6 360.9C397.1 354.5 386.6 348.1 384.3 346.7C375.4 341.2 371.9 339.1 361.5 332.8C351.4 326.6 349 325.2 338.6 318.8C336.4 317.4 330.2 313.6 324.9 310.4C319.6 307.2 312.5 302.8 309.2 300.8C305.9 298.8 299.6 294.9 295.2 292.2C290.7 289.5 284.3 285.6 280.9 283.5C277.5 281.4 267.3 275.1 258.3 269.6C249.3 264.1 239.2 257.9 235.7 255.8C232.3 253.7 225.9 249.8 221.4 247.1C217 244.4 210.6 240.4 207.2 238.3C203.7 236.2 197.3 232.3 192.9 229.6C188.4 226.8 182 222.9 178.6 220.8C175.2 218.7 165 212.5 156 207C147 201.5 136.9 195.3 133.5 193.2C130 191.1 123.6 187.1 119.2 184.4C114.7 181.7 108.3 177.8 104.9 175.7C101.5 173.6 91.3 167.3 82.3 161.8C73.3 156.3 63.2 150.1 59.7 148C56.3 145.9 49.8 141.9 45.2 139.1C33.3 131.9 29.4 128.8 25.8 124.1C23.9 121.6 22.8 119.6 22.6 118.5C22.3 116.9 22.2 116.8 21.1 117.1C19.3 117.6 16.4 116.8 15.1 115.4C11.6 111.6 18 96.5 27.2 86.8C37.3 76.3 105 33.3 118.2 29C127.7 25.9 141.9 24.3 148.6 25.4C150 25.7 152.6 26.1 154.4 26.4C159.3 27.3 166.8 29.5 170.5 31.2C173.3 32.4 188.9 41.9 212.2 56.3C215.4 58.3 224 63.6 231.2 68C238.5 72.5 250.3 79.7 257.5 84.1C264.7 88.5 276.4 95.8 283.6 100.2C290.9 104.6 302.8 111.9 310.1 116.4C317.5 120.9 326.1 126.2 329.3 128.2C347.5 139.5 361.7 148.1 362.7 148.5C364.7 149.2 366.8 148.5 373.4 144.9C378.8 141.8 394.8 131.5 402.6 125.9C427.5 108.2 445.2 98.5 457.3 96C462.4 94.9 477.1 94.6 482 95.6C488.8 96.9 498.6 99.4 499.5 100.2C499.7 100.4 500.2 100.6 500.5 100.6C501.8 100.6 511.6 105.4 518.9 109.6C523.2 112 534.6 119.6 549.9 130.1C561 137.7 572 144.1 579.4 147.3C584.1 149.4 582.2 150.1 597.9 140.4C604.3 136.4 610.5 132.6 611.7 131.9C612.8 131.2 618.2 127.9 623.7 124.5C633.4 118.5 655.8 104.7 667 97.8C670.1 95.8 677.6 91.2 683.7 87.5C689.7 83.8 699.5 77.7 705.5 74C711.6 70.3 719.1 65.6 722.2 63.7C725.4 61.8 735.2 55.7 743.9 50.4C752.6 45 762.9 38.6 766.8 36.2C775.3 31 778.1 29.8 787.2 27.5C803.7 23.5 820 24.9 833.5 31.5C847.5 38.3 869.2 51.4 895 68.7C920.4 85.7 926.7 92.6 931.8 108.4C932.6 110.9 932.5 113.6 931.4 114.9C930.5 116.1 926.2 117.4 924.9 116.9C924.3 116.7 924.1 117 923.9 118.2C923.4 120.7 918.2 127.2 914.3 130.1C910.7 132.7 909.9 133.2 884 149.2C875 154.7 861.7 162.8 854.6 167.2C847.4 171.6 835.7 178.8 828.4 183.3C821.2 187.7 8
10309.4 194.9 802.2 199.4C795 203.8 783.1 211.1 775.8 215.6C768.5 220 760 225.3 756.8 227.3C753.6 229.2 746.4 233.6 740.8 237.1C735.2 240.6 725.9 246.2 720.3 249.7C714.7 253.1 707.4 257.5 704.3 259.5C701.1 261.5 692.6 266.7 685.3 271.2C671.4 279.7 652.5 291.3 635.6 301.7C621.2 310.5 611.5 316.5 590.1 329.5C559.9 347.9 534.1 363.7 521.5 371.3C509.7 378.5 505.4 380.8 500.3 382.5C490.4 385.7 475.7 387.6 467.7 386.6ZM482 358C485.6 357.4 492.9 355.1 495.2 353.9C496.2 353.4 503.7 348.9 511.8 343.9C520 338.9 532.6 331.1 539.8 326.7C558.2 315.4 576.9 303.9 592.4 294.4C599.7 289.9 611.6 282.6 618.8 278.2C626 273.7 637.9 266.5 645.1 262C652.3 257.6 664.2 250.3 671.5 245.9C678.7 241.4 687.3 236.1 690.5 234.2C693.6 232.2 700.8 227.8 706.5 224.3C712.1 220.9 721.3 215.2 727 211.8C732.6 208.3 739.8 203.9 743 201.9C746.1 200 754.7 194.7 762 190.2C769.3 185.8 781.1 178.5 788.2 174.1C809.9 160.8 827.1 150.3 840.8 141.9C847.9 137.5 861.1 129.4 870.1 123.8C879.1 118.3 889 112.2 892.1 110.3C895.2 108.4 897.8 106.6 897.8 106.2C897.8 105.2 885.8 97 860.3 80.6C844 70.1 825.9 60 817.6 56.8C815.5 56 813.3 55.1 812.8 54.9C807 52.4 794.7 54 787.7 58C785.8 59.1 673.5 128.3 552.7 202.6C503.1 233.2 507 230.8 502.7 232.5C497.1 234.8 493.8 235.7 486.6 237.2C482.1 238.1 466.7 238.2 462.6 237.4C447.8 234.4 440.9 231.7 427.8 223.5C423.7 220.8 417.9 217.2 414.9 215.4C412 213.7 397.5 204.7 382.7 195.5C367.9 186.3 353.2 177.1 350 175.2C346.8 173.2 336.5 166.8 327 160.9C317.5 155 307.1 148.6 303.9 146.6C300.7 144.7 286 135.6 271.2 126.4C256.4 117.2 241.7 108 238.5 106.1C235.3 104.1 225 97.7 215.5 91.8C206 85.9 195.6 79.5 192.4 77.5C189.3 75.6 184.2 72.4 181.1 70.5C160.7 57.7 158.4 56.6 150.3 55C148.7 54.7 146.5 54.2 145.3 54C141.5 53.3 136.5 53.7 133.2 55C132.7 55.2 130.5 56 128.4 56.8C117.1 61.2 97.5 72.7 68.3 92C54 101.5 48.6 105.5 48.8 106.6C48.9 107.3 51.8 109.4 59 113.8C64.6 117.2 71.6 121.5 74.5 123.2C77.4 125 87.1 131 96.1 136.5C105.1 142 115.3 148.2 118.7 150.3C122.1 152.4 128.6 156.4 133 159.1C137.4 161.8 143.9 165.7 147.3 167.8C150.7 169.9 160.9 176.1 169.8 181.6C178.8 187.1 189 193.4 192.4 195.5C195.8 197.6 202.3 201.5 206.7 204.2C211.1 206.9 217.6 210.9 221 213C224.4 215.1 230.8 219 235.3 221.7C239.7 224.4 246.1 228.4 249.6 230.5C253 232.6 263.1 238.8 272.1 244.3C281.1 249.8 291.3 256 294.7 258.1C298.1 260.2 304.6 264.2 309 266.9C313.4 269.6 319.8 273.5 323.3 275.6C326.7 277.7 336.8 283.9 345.8 289.5C354.8 295 365 301.2 368.4 303.3C371.8 305.4 378.3 309.3 382.7 312C387.1 314.7 393.6 318.7 397 320.8C400.4 322.9 410.6 329.1 419.6 334.6C428.6 340.1 438.7 346.3 442.1 348.4C448.1 352.1 452 354 458 356.2C463.6 358.3 475.7 359.2 482 358ZM483.4 208.3C487.7 207.3 490.1 206.5 490.7 206C491 205.8 491.4 205.6 491.8 205.6C492.9 205.6 501.9 201.1 507.2 197.9C513 194.5 517.7 191.4 527.1 184.8C535.7 178.9 544.2 173.5 549.4 170.8C556.3 167.2 556.3 165.9 549.4 162.5C544 159.9 536.2 155.1 527.4 148.9C514.8 140.2 512.8 138.9 508 135.9C481.2 119.4 467.2 119.1 441.2 134.7C436.9 137.3 431.3 141 413.8 152.9C407 157.5 400 161.7 395.4 164C393.5 164.9 391.8 166 391.7 166.4C391.4 167.2 392.5 169 393.9 169.8C394.4 170.2 400.7 174.2 407.8 178.7C439.3 198.9 450.7 205.3 460.8 208.2C465.9 209.6 477.8 209.7 483.4 208.3Z","face":"M466.5 358.1C466.2 357.9 465.3 357.6 464.5 357.6C463.8 357.6 462.4 357.4 461.5 357.1C460.6 356.9 459.1 356.4 458.3 356.2C457.4 355.9 456.2 355.6 455.7 355.4C450.3 353.1 447.4 351.6 442.1 348.4C438.7 346.3 428.6 340.1 419.6 334.6C410.6 329.1 400.4 322.9 397 320.8C393.6 318.7 387.1 314.7 382.7 312C378.3 309.3 371.8 305.4 368.4 303.3C365 301.2 354.8 295 345.8 289.5C336.8 283.9 326.7 277.7 323.3 275.6C319.8 273.5 313.4 269.6 309 266.9C304.6 264.2 298.1 260.2 294.7 258.1C291.3 256 281.1 249.8 272.1 244.3C263.1 238.8 253 232.6 249.6 230.5C246.1 228.4 239.7 224.4 235.3 221.7C230.8 219 224.4 215.1 221 213C217.6 210.9 211.1 206.9 206.7 204.2C202.3 201.5 195.8 197.6 192.4 195.5C189 193.4 178.8 187.1 169.8 181.6C160.9 176.1 150.7 169.9 147.3 167.8C143.9 165.7 137.4 161.8 133 159.1C128.6 156.4 122.1 152.4 118.7 150.3C115.3 148.2 105.1 142 96.1 136.5C87.1 131 77.4 125 74.5 123.2C71.6 121.5 64.6 117.2 59 113.8C51.8 109.4 48.9 107.3 48.8 106.6C48.6 105.5 54 101.5 68.3 92C97.5 72.7 117.1 61.2 128.4 56.8C130.5 56 132.7 55.1 133.2 54.9C134.2 54.5 136 54.2 139.6 53.7C143.1 53.3 150.7 54.8 158.6 57.4C161.1 58.2 166.3 61.3 181.1 70.5C184.2 72.4 189.3 75.6 192.4 77.5C195.6 79.5 206 85.9 215.5 91.8C225 97.7 235.3 104.1 238.5 106.1C241.7 108 256.4 117.2 271.2 126.4C286 135.6 300.7 144.7 303.9 146.6C307.1 148.6 317.5 155 327 160.9C336.5 166.8 346.8 173.2 350 175.2C353.2 177.1 367.9 186.3 382.7 195.5C397.5 204.7 412 213.7 414.9 215.4C417.9 217.2 423.7 220.8 427.8 223.5C439 230.4 448.7 234.7 457.6 236.4C459.1 236.7 461.3 237.2 462.5 237.5C465.4 238.1 483.9 238.1 485.6 237.4C486.3 237.2 487.4 236.9 488.1 236.9C488.7 236.9 489.9 236.7 490.7 236.5C491.5 236.3 493.1 235.8 494.2 235.5C495.3 235.2 496.7 234.8 497.2 234.6C497.7 234.4 499.9 233.6 502 232.7C507.1 230.8 500.9 234.5 566.5 194.1C735.3 90.2 788.3 57.6 789.6 57.1C790.4 56.7 792.1 56.2 793.2 55.9C794.3 55.6 795.8 55.1 796.4 54.9C797.1 54.8 798.4 54.3 799.4 54C802.2 53.2 809.9 53.7 812.8 54.9C813.3 55.1 815.5 56 817.6 56.8C825.9 60 844 70.1 860.3 80.6C885.8 97 897.8 105.2 897.8 106.2C897.8 106.6 895.2 108.4 892.1 110.3C889 112.2 879.1 118.3 870.1 123.8C861.1 129.4 847.9 137.5 840.8 141.9C827.1 150.3 809.9 160.8 788.2 174.1C781.1 178.5 769.3 185.8 762 190.2C754.7 194.7 746.1 200 743 201.9C739.8 203.9 732.6 208.3 727 211.8C721.3 215.2 712.1 220.9 706.5 224.3C700.8 227.8 693.6 232.2 690.5 234.2C687.3 236.1 678.7 241.4 671.5 245.9C664.2 250.3 652.3 257.6 645.
1031 262C637.9 266.5 626 273.7 618.8 278.2C611.6 282.6 599.7 289.9 592.4 294.4C576.9 303.9 558.2 315.4 539.8 326.7C532.6 331.1 520 338.9 511.8 343.9C494 354.8 493.5 355.1 487.3 356.8C482.3 358.2 468 359.1 466.5 358.1ZM463.3 208.8C462.7 208.6 461.2 208.2 460.1 207.9C451.3 205.6 438.6 198.5 407.8 178.7C400.7 174.2 394.4 170.2 393.9 169.8C392.5 169 391.4 167.2 391.7 166.4C391.8 166 393.5 164.9 395.4 164C400 161.7 407 157.5 413.8 152.9C446.4 130.7 449.9 128.7 465.2 124C467.8 123.2 479 123.2 481.8 124C482.9 124.3 484.6 124.8 485.4 125C491.3 126.7 499.5 130.7 508 135.9C512.8 138.9 514.8 140.2 527.4 148.9C536.2 155.1 544 159.9 549.4 162.5C556.3 165.9 556.3 167.2 549.4 170.8C544.2 173.5 535.7 178.9 527.1 184.8C510.4 196.5 494.9 205.6 491.7 205.6C491.3 205.6 490.9 205.8 490.7 206.1C490.6 206.3 490 206.5 489.4 206.5C488.8 206.5 488.1 206.7 488 206.9C487.9 207.2 487.2 207.5 486.5 207.6C485.8 207.7 484.2 208.1 482.9 208.4C480.2 209.1 465 209.4 463.3 208.8Z","poly":[[14.4,111.4],[38.3,134.9],[446.0,382.3],[471.4,386.9],[511.0,377.7],[910.9,132.6],[924.3,116.5],[932.1,113.7],[925.7,94.8],[904.9,75.4],[837.7,33.5],[809.6,25.2],[772.7,32.6],[583.8,149.2],[509.6,104.5],[480.1,95.3],[460.8,95.3],[443.3,100.8],[363.6,149.2],[174.2,33.1],[147.0,25.2],[115.7,29.8],[30.9,83.3],[19.4,97.1]]},"frame":{"ink":"M465.9 527.8C461.6 526.9 460.1 526.4 459.1 525.5C458.4 524.8 456.7 523.9 455.3 523.5C452.3 522.5 453.1 522.9 415.5 499.7C412.9 498.1 408.7 495.5 406.1 493.9C403.5 492.3 393.5 486 383.9 480C374.2 473.9 364.2 467.7 361.5 466C358.8 464.4 354.8 461.8 352.5 460.4C342.2 453.8 339.1 451.9 332 447.5C327.8 444.9 321.1 440.6 317 438.1C313 435.5 308.2 432.5 306.5 431.4C302.9 429.3 303.1 429.4 286.2 418.7C279.8 414.7 268.3 407.5 260.6 402.7C252.9 397.8 245.8 393.4 244.9 392.9C244.1 392.3 240.3 390 236.7 387.8C233 385.6 227.2 382.1 223.8 380C220.3 377.9 204.5 368.3 188.5 358.8C172.5 349.2 155.8 339.1 151.3 336.4C146.9 333.7 142.5 331.3 141.7 331.1C139.9 330.8 138.6 330.1 123.8 321.4C117.6 317.7 112.2 314.6 111.8 314.4C111.4 314.2 110.8 313.1 110.4 312.1C109.7 310.1 108.9 309.5 92.2 299.1C90.7 298.2 77.2 289.8 62.3 280.4C47.3 271 33.6 262.4 31.9 261.3C26.4 258 23.5 254.3 19 245.3C13.1 233.6 12.6 229.6 11.8 197C10.9 159.1 12.3 117.8 14.5 115.1C15.8 113.6 20.4 114 21.7 115.8C22.3 116.8 22.4 116.7 25.4 114.4C27.1 113.1 28.7 112.1 28.8 112.1C29 112.1 31.9 111.5 35.4 110.7L41.8 109.4L48.3 110.8C54.9 112.1 54.9 112.1 61.1 115.8C67 119.4 100.3 139.7 109.3 145.1C118.2 150.6 121.6 152.7 132.1 159.1C138 162.7 144.8 166.8 147 168.2C156.9 174.2 160 176.1 177.9 187C188.3 193.4 205.3 203.7 215.7 210.1C226.1 216.4 243.1 226.8 253.5 233.1C300.3 261.6 311.7 268.6 336.2 283.6C350.5 292.3 365 301.2 368.4 303.3C371.8 305.4 378.3 309.3 382.7 312C387.1 314.7 393.6 318.7 397 320.8C402.4 324.1 422.8 336.6 442.8 348.8C459.3 358.9 473.2 361.1 489.1 356.2C494.7 354.4 494.5 354.5 511.8 343.9C520 338.9 532.6 331.1 539.9 326.6C547.2 322.1 559.1 314.8 566.3 310.4C573.5 306 585.3 298.8 592.4 294.4C599.6 290 611.7 282.6 619.3 277.9C632.3 270 634.9 268.4 645.1 262.2C647.4 260.8 654.1 256.7 660.1 253.1C666 249.4 672.7 245.4 674.9 244C677.1 242.7 680.8 240.4 683 239.1C685.2 237.7 691.9 233.6 697.8 230C703.8 226.4 710.5 222.3 712.7 221C714.9 219.6 718.5 217.4 720.8 216C723 214.7 729.7 210.6 735.6 207C745.9 200.7 749 198.8 758.4 193.1C760.7 191.7 767.4 187.6 773.4 183.9C783.7 177.7 786.8 175.8 796.2 170C798.5 168.6 805.2 164.5 811.2 160.9C817.1 157.3 823.8 153.2 826 151.9C828.2 150.5 831.9 148.3 834.1 146.9C836.3 145.6 843 141.5 848.9 137.9C859.2 131.7 861.8 130 871.8 124C874 122.6 879.3 119.3 883.5 116.8L891.1 112.1L897.9 110.7L904.7 109.3L911.5 110.7C915.2 111.5 918.5 112.3 918.7 112.4C919 112.6 920.3 113.5 921.6 114.5L924.1 116.4L925.1 115.4C927.4 113.3 930.8 112.7 932.3 114.1C934.1 115.9 934.8 133.4 934.8 175.7C934.8 226.6 934.5 229.5 927.7 243.7C920.5 258.8 914.2 264.7 887.9 280.9C867.5 293.5 841.8 308.5 839.3 309.3C837.6 309.8 836.7 310.4 836.2 311.3C835.9 311.9 835.1 312.7 834.5 313C833.4 313.6 815.7 324.4 809.3 328.4C807.1 329.8 805 330.9 804.6 330.9C802.9 330.9 727.7 377.1 670.2 413.5C663.1 417.9 652.6 424.5 635.2 435.1C619.8 444.6 607.3 452.2 597.4 458.3C591.7 461.8 585.3 465.8 583.1 467.1C581 468.4 576.4 471.2 573 473.3C569.6 475.4 563.1 479.3 558.7 482C554.3 484.7 547.8 488.7 544.4 490.8
103C541 492.9 534.6 496.8 530.1 499.5C525.7 502.3 519.4 506.1 516.1 508.1C498.2 519.1 491 523.4 489.6 524C488.7 524.3 487.5 525 487 525.4C486.1 526.3 474.2 529.1 472.2 529C471.7 529 468.9 528.4 465.9 527.8ZM480.5 496.2C487 492.1 490.3 490.1 509.2 478.6C513.1 476.2 519.5 472.2 523.4 469.8C527.4 467.4 533.8 463.5 537.7 461.1C541.7 458.7 548.1 454.7 552 452.3C559.1 448 563.4 445.3 583.6 433C595 425.9 610.5 416.5 621.2 409.9C625.1 407.5 631.5 403.6 635.4 401.2C639.3 398.8 645.8 394.8 649.7 392.4C653.6 390 660 386.1 664 383.7C667.9 381.3 674.3 377.3 678.2 374.9C682.2 372.5 688.6 368.6 692.5 366.2C696.5 363.8 702.9 359.8 706.9 357.3C718.1 350.5 727.8 344.6 735.4 339.9C743 335.2 752.7 329.3 763.8 322.5C767.8 320 774.2 316.1 778.1 313.7C790.8 305.9 799.2 300.8 813.7 291.9C821.6 287.1 834 279.4 841.3 274.9C848.7 270.4 857.8 264.8 861.6 262.5C865.4 260.2 873.2 255.4 878.9 251.9C892.8 243.4 896.3 240.5 901.6 233.3C905.9 227.5 905.6 231.4 905.6 181.6C905.6 143.6 905.5 137.9 904.9 137.9C904.3 137.9 899.9 140.5 877.7 154.1C875.5 155.5 868.7 159.6 862.8 163.2C852.4 169.5 849.3 171.4 840 177.1C837.7 178.5 830.9 182.6 825 186.3C819 189.9 812.3 194 810 195.4C800.6 201.1 797.5 203 787.2 209.3C781.3 212.9 774.5 217 772.2 218.4C762.8 224.2 759.8 226 749.4 232.3C743.5 235.9 736.8 240 734.6 241.4C732.4 242.7 728.7 245 726.5 246.3C724.3 247.7 717.6 251.7 711.7 255.4C705.7 259 699 263.1 696.7 264.5C687.3 270.2 684.2 272.1 673.9 278.4C667.9 282 661.2 286.1 658.9 287.5C649.8 293.1 646.5 295.1 636.1 301.4C630.1 305.1 621.6 310.2 617.2 313C612.8 315.7 600.6 323.1 590.3 329.4C564.4 345.2 535.2 363 522.8 370.6C517.1 374 511.4 377.5 510.1 378.2C500.4 383.9 480.7 387.8 468.3 386.6C459.1 385.8 453.2 384.6 446.3 382.4C438.9 380 440.2 380.8 402.3 357.7C389.2 349.7 386.5 348 376.5 341.9C374.2 340.5 367.5 336.4 361.5 332.8C355.6 329.1 348.9 325.1 346.6 323.7C344.4 322.4 340.8 320.1 338.6 318.8C336.4 317.4 326 311.1 315.5 304.7C305.1 298.3 288 287.9 277.5 281.5C267.1 275.2 250.1 264.8 239.8 258.5C229.4 252.2 212.5 241.9 202.2 235.6C191.9 229.3 174.8 218.9 164.3 212.5C146.2 201.5 143.1 199.6 133.2 193.5C130.9 192.1 124.2 188 118.2 184.4C112.3 180.8 105.6 176.7 103.4 175.4C101.2 174 97.5 171.8 95.3 170.4C90.9 167.7 57 147 47.6 141.3C43.8 139 41.5 137.9 41.3 138.2C41 138.5 40.9 155.2 40.9 183.3L41.1 227.9L42.4 230.4C44.3 233.7 47.9 237.6 52.7 241.4C56.3 244.2 59.3 246.4 73.6 256C86.4 264.8 109.7 279.3 131.8 292.5C149.7 303.1 152.2 304.6 174.9 317.8C191.3 327.3 191.5 327.4 210.9 338.7C226.1 347.7 244 358.5 265.9 372.2C270.7 375.1 276.7 378.9 279.3 380.4C283.6 383.1 308.3 398.6 314.3 402.4C315.8 403.3 318.5 405 320.3 406.1C322 407.2 326.8 410.2 330.9 412.7C337.8 417.1 354.2 427.4 362 432.3C369.2 436.8 377.2 441.8 382.9 445.4C386.2 447.5 396.2 453.7 405 459.2C413.9 464.7 422.7 470.2 424.6 471.4C426.5 472.6 432.5 476.4 438 479.7C456.9 491.4 459.9 493.3 465.6 496.8C468.8 498.8 471.9 500.4 472.5 500.4C473 500.5 476.7 498.5 480.5 496.2Z","face":"M465.6 496.8C459.9 493.3 456.9 491.4 438 479.7C432.5 476.4 426.5 472.6 424.6 471.4C422.7 470.2 413.9 464.7 405 459.2C396.2 453.7 386.2 447.5 382.9 445.4C377.2 441.8 369.2 436.8 362 432.3C354.2 427.4 337.8 417.1 330.9 412.7C326.8 410.2 322 407.2 320.3 406.1C318.5 405 315.8 403.3 314.3 402.4C308.3 398.6 283.6 383.1 279.3 380.4C276.7 378.9 270.7 375.1 265.9 372.2C244 358.5 226.1 347.7 210.9 338.7C191.5 327.4 191.3 327.3 174.9 317.8C152.2 304.6 149.7 303.1 131.8 292.5C109.7 279.3 86.4 264.8 73.6 256C59.3 246.4 56.3 244.2 52.7 241.4C47.9 237.6 44.3 233.7 42.4 230.4L41.1 227.9L40.9 183.3C40.9 155.2 41 138.5 41.3 138.2C41.5 137.9 43.8 139 47.6 141.3C57 147 90.9 167.7 95.3 170.4C97.5 171.8 101.2 174 103.4 175.4C105.6 176.7 112.3 180.8 118.2 184.4C124.2 188 130.9 192.1 133.2 193.5C143.1 199.6 146.2 201.5 164.3 212.5C174.8 218.9 191.9 229.3 202.2 235.6C212.5 241.9 229.4 252.2 239.8 258.5C250.1 264.8 267.1 275.2 277.5 281.5C288 287.9 305.1 298.3 315.5 304.7C326 311.1 336.4 317.4 338.6 318.8C340.8 320.1 344.4 322.4 346.6 323.7C348.9 325.1 355.6 329.1 361.5 332.8C367.5 336.4 374.2 340.5 376.5 341.9C378.8 343.3 382.4 345.5 384.5 346.8C390.2 350.3 425.2 371.6 432 375.7C442.2 381.9 453.6 385.5 466.5 386.5C481.6 387.8 499.7 384.3 510.1 378.2C511.4 377.5 517.1 374 522.8 370.6C535.2 363 564.4 345.2 590.3 329.4C600.6 323.1 612.8 315.7 617.2 313C621.6 310.2 630.1 305.1 636.1 301.4C646.5 295.
1031 649.8 293.1 658.9 287.5C661.2 286.1 667.9 282 673.9 278.4C684.2 272.1 687.3 270.2 696.7 264.5C699 263.1 705.7 259 711.7 255.4C717.6 251.7 724.3 247.7 726.5 246.3C728.7 245 732.4 242.7 734.6 241.4C736.8 240 743.5 235.9 749.4 232.3C759.8 226 762.8 224.2 772.2 218.4C774.5 217 781.3 212.9 787.2 209.3C797.5 203 800.6 201.1 810 195.4C812.3 194 819 189.9 825 186.3C830.9 182.6 837.7 178.5 840 177.1C849.3 171.4 852.4 169.5 862.8 163.2C868.7 159.6 875.5 155.5 877.7 154.1C899.9 140.5 904.3 137.9 904.9 137.9C905.5 137.9 905.6 143.6 905.6 181.6C905.6 231.5 905.9 227.6 901.5 233.5C896.1 240.6 892.1 243.8 878.9 251.9C873.2 255.4 865.4 260.2 861.6 262.5C857.8 264.8 848.7 270.4 841.3 274.9C834 279.4 821.6 287.1 813.7 291.9C799.2 300.8 790.8 305.9 778.1 313.7C774.2 316.1 767.8 320 763.8 322.5C752.7 329.3 743 335.2 735.4 339.9C727.8 344.6 718.1 350.5 706.9 357.3C702.9 359.8 696.5 363.8 692.5 366.2C688.6 368.6 682.2 372.5 678.2 374.9C674.3 377.3 667.9 381.3 664 383.7C660 386.1 653.6 390 649.7 392.4C645.8 394.8 639.3 398.8 635.4 401.2C631.5 403.6 625.1 407.5 621.2 409.9C610.5 416.5 595 425.9 583.6 433C563.4 445.3 559.1 448 552 452.3C548.1 454.7 541.7 458.7 537.7 461.1C533.8 463.5 527.4 467.4 523.4 469.8C519.5 472.2 513.1 476.2 509.2 478.6C490.3 490.1 487 492.1 480.5 496.2C476.7 498.5 473 500.5 472.5 500.4C471.9 500.4 468.8 498.8 465.6 496.8Z","poly":[[13.0,121.1],[13.0,227.0],[28.6,259.3],[112.0,314.6],[244.3,392.0],[444.7,517.7],[471.4,528.8],[492.1,522.8],[899.4,273.6],[920.6,256.0],[933.9,223.8],[933.0,115.5],[923.8,116.5],[905.4,109.5],[891.1,112.3],[497.2,353.3],[467.2,358.8],[447.9,352.3],[56.7,113.2],[41.5,109.5],[22.6,116.9],[15.3,114.6]]},"wingL":{"ink":"M333.7 798.6C333 798.5 328 795.7 286.9 772.8C278.1 767.9 263.9 759.9 255.3 755.1C214.7 732.4 207.7 728.5 192.9 720.2C169.6 707.2 170.8 707.8 168.7 707.8C165.6 707.8 144 720 122.8 733.8C89.6 755.5 71 765.6 59 768.6C53.5 770 45.2 769.9 39.9 768.4C26.7 764.8 16.7 754.1 13.5 740.5C12.9 737.9 12.9 728.5 13.6 695.8C14.2 663.6 13.6 613.7 11.8 553.2C11 526.1 10.9 436.5 11.7 421C13.1 395.1 13.5 391.6 16.3 383.3C19.8 373.2 25.1 366.6 34.4 361C40.5 357.3 42.1 356.3 80.6 332.6C100.7 320.2 110.1 314.7 111.1 314.6C112 314.5 115.6 312.8 119.3 310.8C128.6 305.5 128 305.8 135.6 304.2L142.5 302.8L147.8 303.9C152.2 304.8 153.7 305.4 156.9 307.3C159.1 308.6 167.2 313.3 174.9 317.8C191.2 327.3 191.4 327.4 210.9 338.7C226.1 347.7 244 358.5 265.9 372.2C270.7 375.1 276.7 378.9 279.3 380.4C285.9 384.5 294.9 390.2 298.4 392.4C309.5 399.6 312.8 401.7 330.4 413.1C337.2 417.5 347.9 424.4 354 428.4C360.2 432.4 365.6 436.2 366.1 436.8C367.5 438.5 367.5 465.2 366.2 466.8C364.8 468.5 362.5 468.2 358.9 465.8C357.1 464.7 349.7 459.9 342.3 455.1C334.8 450.3 323.2 442.7 316.5 438.4C301.2 428.5 297 425.7 291.7 422.4C289.4 420.9 286.2 418.8 284.6 417.8C277.9 413.5 265.8 405.8 256.5 400C251 396.7 245.8 393.4 244.9 392.9C244.1 392.3 240.4 390.1 236.9 387.9C233.3 385.8 227.5 382.3 224 380.1C220.4 378 204.5 368.3 188.5 358.8C172.5 349.2 155.8 339.1 151.3 336.4C146.8 333.6 142.7 331.4 142.3 331.4C141.6 331.4 128.6 338.5 102.1 353.5C98.2 355.7 86.2 362.4 75.4 368.5C49.8 382.8 50.8 382.3 50.8 382.9C50.8 383.3 57.3 387.6 65.3 392.5C73.2 397.5 82.9 403.4 86.7 405.8C90.5 408.1 98.1 412.8 103.5 416.1C113.3 422.2 119.5 426 130.9 433.1C134.2 435.1 141.4 439.5 146.8 442.9C152.3 446.2 159.3 450.6 162.5 452.5C165.6 454.5 172.7 458.9 178.1 462.2C187.8 468.2 193.5 471.7 205.6 479.1C208.8 481.2 216 485.6 221.4 488.9C226.9 492.3 233.9 496.7 237.1 498.6C240.3 500.6 250 506.6 258.8 512C267.5 517.4 277.3 523.4 280.4 525.3C283.6 527.3 290.6 531.7 296.1 535C301.5 538.4 308.6 542.7 311.7 544.7C314.9 546.6 324 552.3 332 557.2C347.8 567 348.4 567.4 354.4 575.8C359.7 583.2 364.1 592.9 366.1 601.5C366.4 603 366.6 628.5 366.6 687.2C366.6 768.9 366.6 770.9 365.7 775.3C364.6 780.4 364.9 779.8 361.5 784.3C358.2 788.6 352.7 792.9 349.3 793.7C348.1 794.1 345.1 794.7 342.8 795.1C339.5 795.8 338.5 796.2 337.5 797.2C336.4 798.5 335.2 799 333.7 798.6ZM338.9 686.7L338.9 606.3L337.9 603.5C336.8 600.3 335.1 597.4 331.8 592.8C327.7 587.4 324.6 584.8 315.4 579.2C305.6 573.2 297.8 568.5 254 541.9C227.5 525.7 219.4 520.7 192.6 504.4C187.7 501.5 183.4 499.1 183 499.1C181.8 499.1 181.9 682 183.1 683C183.5 683.4 188.8 686.4 194.7 689.6C200.7 692.8 207.7 696.6 210.4 698.1C213.1 699.6 221.2 704 228.5 708C235.8 711.9 247.7 718.4 255 722.3C262.3 726.3 270.4 730.7 273 732.2C277.6 734.7 312 753.4 326.3 761.1C329.8 763.1 333.9 765.3 335.4 766.1C337 767 338.4 767.5 338.6 767.4C338.8 767.3 338.9 731 338.9 686.7ZM57.3 738.2C74.3 730.7 114.3 707.5 146.8 686.4C149.2 684.8 151.8 683.2 152.5 682.8L153.7 682L153.7 581.8L153.7 481.6L151.8 479.6C150.6 478.4 146.3 475.5 141.3 472.5C123.5 462 93.4 444 89.2 441.5C86.8 440 76 433.6 65.3 427.2C54.5 420.8 44.9 415.1 44 414.5C43 413.9 41.9 413.4 41.5 413.4C40.4 413.4 40.5 735.2 41.6 736.7C42 737.3 43.4 738.5 44.7 739.4C48.2 741.7 49.7 741.5 57.3 738.2Z","face":"M335.4 766.1C333.9 765.3 329.8 763.1 326.3 761.1C312 753.4 277.6 734.7 273 732.2C270.4 730.7 262.3 726.3 255 722.3C247.7 718.4 235.8 711.9 228.5 708C221.2 704 213.1 699.6 210.4 698.1C207.7 696.6 200.7 692.8 194.7 689.6C188.8 686.4 183.5 683.4 183.1 683C181.9 682 181.8 499.1 183 499.1C183.4 499.1 187.7 501.5 192.6 504.4C219.4 520.7 227.5 525.7 254 541.9C297.8 568.5 305.6 573.2 315.4 579.2C324.6 584.8 327.7 587.4 331.8 592.8C335.1 597.4 336.8 600.3 337.9 603.5L338.9 606.3L338.9 686.7C338.9 731 338.8 767.3 338.6 767.4C338.4 767.5 337 767 335.4 766.1ZM44.7 739.4C43.4 738.5 42 737.3 41.6 736.7C40.5 735.2 40.4 413.4 41.5 413.4C41.9 413.4 43 413.9 44 414.5C44.9 415.1 54.5 420.8 65.3 427.2C76 433.6 86.8 440 89.2 441.5C93.1 443.8 117.8 458.5 141.3 472.5C146.5 475.6 150.6 478.4 151.8 479.6L153.7 481.7L153.7 581.8L153.7 682L152.5 682.8C151.8 683.2 149.2 684.8 146.8 686.4C114.3 707.5 74.3 730.7 57.3 738.2C49.7 741.5 48.2 741.7 44.7 739.4ZM350 570C347.2 566.5 350.4 568.6 311.7 544.7C308.6 542.7 301.5 538.4 296.1 535C290.6 531.7 283.6 527.3 280.4 525.3C277.3 523.4 267.5 517.4 258.8 512C250 506.6 240.3 500.6 237.1 498.6C233.9 496.7 226.9 492.3 221.4 488.9C216 485.6 208.8 481.2 205.6 479.1C193.5 471.7 187.8 468.2 178.1 462.2C172.7 458.9 165.6 454.5 162.5 452.5C159.3 450.6 152.3 446.2 146.8 442.9C141.4 439.5 134.3 435.1 131 433.1C127.8 431.1 122.5 427.9 119.3 425.9C116.1 423.9 109 419.5 103.5 416.1C98.1 412.8 90.5 408.1 86.7 405.8C82.9 403.4 73.2 397.5 65.3 392.5C57.3 387.6 50.8 383.3 50.8 382.9C50.8 382.3 49.8 382.8 75.4 368.5C86.2 362.4 98.2 355.7 102.1 353.5C128.6 338.5 141.6 331.4 142.3 331.4C142.7 331.4 146.8 333.6 151.3 336.4C155.8 339.1 172.5 349.2 188.5 358.8C204.5 368.3 220.4 377.9 223.9 380C227.4 382.2 233.2 385.7 236.8 387.9C240.4 390.1 244.1 392.3 244.9 392.9C245.8 393.4 251 396.7 256.5 400C265.8 405.8 277.9 413.5 284.6 417.8C286.2 418.8 289.4 420.9 291.7 422.4C297.2 425.9 301.8 428.9 316.6 438.4C323.4 442.9 334.4 449.9 340.9 454.2L352.8 461.9L352.8 517.6C352.8 548.2 352.7 573.3 352.6 573.2C352.6 573.2 351.4 571.8 350 570Z","poly":[[29.1,364.3],[14.8,388.3],[11.1,433.0],[13.0,738.0],[20.8,755.9],[39.7,768.4],[63.2,767.4],[170.5,707.6],[332.7,798.3],[357.6,788.6],[366.3,771.6],[366.3,603.0],[352.5,573.5],[352.5,462.5],[366.3,466.6],[366.3,437.1],[240.6,356.5],[150.3,304.4],[128.6,305.8]]},"wingR":{"ink":"M611.3 797.8C610.8 797.7 610 797.2 609.7 796.8C609.4 796.3 608.6 795.9 607.9 795.9C606.3 795.8 595.5 793.5 594.9 793.1C590 790.3 582.7 782.6 581.2 778.9C579.9 7
10375.6 579 746.2 578.7 700.9C578.4 664.4 579.2 607.7 579.9 604.6C580.2 603.4 580.6 601.7 580.9 600.7C583.1 591.4 588.1 581.1 594.5 572.5C598.4 567.3 599.1 566.8 612.6 558.5C619.3 554.4 629.1 548.4 634.3 545.2C645.9 537.9 658.3 530.2 678.9 517.5C687.7 512.1 699.1 505.1 704.3 501.8C709.5 498.6 720.9 491.6 729.6 486.2C749.5 473.9 762.7 465.7 772.5 459.7C776.6 457.1 788.9 449.5 799.6 442.9C810.4 436.2 822.7 428.6 826.9 426C831.2 423.4 838.1 419.1 842.4 416.4C846.6 413.8 858.9 406.2 869.7 399.6C880.4 392.9 890.8 386.5 892.6 385.4C894.5 384.3 895.9 383.1 895.8 382.8C895.7 382.5 893.5 381 890.8 379.5C888.2 377.9 882.7 374.7 878.5 372.2C874.4 369.8 867.7 365.8 863.7 363.4C859.6 361 853 357.1 848.9 354.6C844.9 352.2 838.2 348.3 834.1 345.8C811 332.1 811.2 332.2 808 331.6C806.3 331.2 804.7 330.9 804.4 330.9C802.8 330.9 721.9 380.7 667.4 415.2C661.7 418.8 653.1 424.3 648.3 427.3C643.5 430.3 634 436.3 627.1 440.7C580.1 470.3 581.8 469.3 579.8 466.8C578.6 465.4 578.5 438.6 579.7 437C580 436.5 585.6 432.7 592 428.7C598.3 424.7 608.5 418.3 614.5 414.5C632.4 403.3 656.1 388.5 664 383.7C670.9 379.4 675.1 376.9 692.5 366.2C696.5 363.8 702.9 359.8 706.8 357.4C710.7 355 717.2 351.1 721.1 348.7C725 346.2 731.5 342.3 735.5 339.8C739.5 337.4 745.9 333.4 749.8 331.1C756.8 326.8 762.2 323.5 781 311.9C786.6 308.5 791.5 305.5 792 305.2C792.6 305 795.7 304.2 799 303.5L805 302.3L814.2 304.2L823.4 306.1L829.5 309.6C832.9 311.5 835.8 313.4 835.9 313.7C836.2 314.4 844.4 319.9 851.5 324.1C857.4 327.6 860.4 329.3 877.3 338.7C919.8 362.2 927.8 370.1 932.8 393.6C933.2 395.5 933.3 399.3 933 407.7C932.4 427 932.6 514.4 933.2 535.5C935.8 617.2 936.3 662.2 935.2 695.6C933.9 730.9 933.4 736.8 930.6 745.9C929.5 749.3 925.7 755.8 923.1 758.6C914.2 768.3 901.4 771.5 885.8 767.9C883.9 767.5 870.1 759.9 830.5 737.5L777.8 707.7L776.2 708.5C775.3 708.9 763.1 715.6 749 723.4C734.9 731.2 713.6 743 701.6 749.6C689.7 756.3 670.1 767.1 658.1 773.7C646.1 780.4 631.6 788.4 625.7 791.6C615.4 797.4 613.5 798.2 611.3 797.8ZM614 764.5C616.9 762.9 621.5 760.4 624.1 758.9C629.2 756.1 663.3 737.6 681.5 727.7C687.3 724.5 694.2 720.8 696.9 719.3C699.6 717.8 713.7 710.2 728.2 702.2C762.8 683.5 763.6 683 763.9 682.2C764 681.8 764.2 640.5 764.2 590.5C764.2 510.7 764.1 499.5 763.5 499.2C762.7 498.9 762 499.3 748 507.8C737.8 514.1 730.6 518.4 705.7 533.6C699.7 537.3 693.9 540.8 692.8 541.5C691.6 542.1 685.9 545.6 680.1 549.2C674.3 552.7 664.7 558.5 658.9 562.1C653.1 565.6 643.4 571.5 637.5 575.1C623.8 583.4 623.7 583.5 619.5 587.8C613 594.5 608.9 601.2 607.7 606.9C607.2 609.3 607.1 623.9 607.1 688.2C607.1 765.3 607.1 768.2 608.4 767.5C608.6 767.4 611.1 766 614 764.5ZM900.5 740.1C901.4 739.6 903 738.4 903.9 737.4L905.6 735.7L905.6 574.5C905.6 432.2 905.5 413.4 904.9 413.4C904.3 413.4 896.8 417.9 873.4 432.3C869.9 434.4 859.7 440.7 850.6 446.2C829.7 459 827.5 460.3 819.9 465C816.5 467.1 812 469.8 810 471C808 472.3 803.2 475.2 799.4 477.5L792.5 481.8L792.4 581.4C792.3 675.7 792.3 681 793.1 681.6C793.5 682 816.7 695.6 844.6 711.9C900.8 744.7 896.1 742.3 900.5 740.1Z","face":"M607.6 767.1C606.8 766.3 606.8 611 607.7 606.9C608.9 601.2 613 594.5 619.5 587.8C623.7 583.5 623.8 583.4 637.5 575.1C643.4 571.5 653.1 565.6 658.9 562.1C664.7 558.5 674.3 552.7 680.1 549.2C685.9 545.6 691.6 542.1 692.8 541.5C693.9 540.8 699.7 537.3 705.7 533.6C730.6 518.4 737.8 514.1 748 507.8C762 499.3 762.7 498.9 763.5 499.2C764.1 499.5 764.2 510.7 764.2 590.5C764.2 640.5 764 681.8 763.9 682.2C763.6 683 762.8 683.5 728.2 702.2C713.7 710.2 699.6 717.8 696.9 719.3C694.2 720.8 687.3 724.5 681.5 727.7C663.3 737.6 629.2 756.1 624.1 758.9C620.3 761 609.9 766.7 608.4 767.5C608.3 767.6 607.9 767.4 607.6 767.1ZM844.6 711.9C816.7 695.6 793.5 682 793.1 681.6C792.3 681 792.3 675.7 792.4 581.4L792.5 481.8L799.4 477.5C803.2 475.2 808 472.3 810 471C812 469.8 816.5 467.1 819.9 465C827.5 460.3 829.7 459 850.6 446.2C859.7 440.7 869.9 434.4 873.4 432.3C896.8 417.9 904.3 413.4 904.9 413.4C905.5 413.4 905.6 432.2 905.6 574.5L905.6 735.7L903.9 737.4C901.9 739.5 899.5 740.8 897 741.2C895.3 741.5 892.9 740.1 844.6 711.9ZM593.2 517.8L593.2 461.9L605.1 454.5C611.6 450.4 622.1 443.8 628.3 439.9C634.5 436 643.5 430.3 648.3 427.3C653.1 424.3 661.7 418.8 667.4 415.2C706.2 390.7 760 357.3 787 341.1C802.9 331.5 804.1 330.9 805.6 331.1C806.5 331.3 808.1 331.6 809.1 331.8
103C810.2 332 814.2 334 818.8 336.7C823.1 339.3 830 343.4 834.1 345.8C838.2 348.3 844.9 352.2 848.9 354.6C853 357.1 859.6 361 863.7 363.4C867.7 365.8 874.4 369.8 878.5 372.2C882.7 374.7 888.2 377.9 890.8 379.5C893.5 381 895.7 382.5 895.8 382.8C895.9 383.1 894.5 384.3 892.6 385.4C890.8 386.5 880.4 392.9 869.7 399.6C858.9 406.2 846.6 413.8 842.4 416.4C838.1 419.1 831.2 423.4 826.9 426C822.7 428.6 810.4 436.2 799.6 442.9C788.9 449.5 776.6 457.1 772.5 459.7C762.7 465.7 749.5 473.9 729.6 486.2C720.9 491.6 709.5 498.6 704.3 501.8C699.1 505.1 687.7 512.1 678.9 517.5C658 530.4 645.8 538 634.3 545.2C629.1 548.4 619.2 554.5 612.4 558.7C598.9 567 598.4 567.4 595.5 571.2C594.5 572.6 593.5 573.7 593.4 573.7C593.3 573.7 593.2 548.6 593.2 517.8Z","poly":[[823.4,306.3],[805.9,302.6],[791.1,305.8],[579.2,438.0],[579.7,466.6],[593.5,462.5],[593.5,574.4],[582.4,595.1],[579.2,615.9],[581.5,779.4],[595.3,793.2],[615.1,797.4],[777.3,707.6],[883.7,767.4],[901.2,769.3],[914.1,765.6],[930.3,746.7],[935.8,677.6],[932.1,390.1],[926.1,374.9],[914.6,362.0]]},"spine":{"ink":"M372.1 975C355.1 971.2 343.9 959.4 339.4 940.5C338.5 936.5 338.6 567.5 339.6 566.1C341.3 563.5 344.9 564.4 348.6 568.5C357 577.8 363.4 590 366.1 601.5C366.4 603 366.6 651.2 366.6 769C366.6 910.8 366.7 934.8 367.2 936.9C368.4 941.6 370.2 943.9 374 946C376.7 947.4 377.1 947.5 381.6 947.5C388.5 947.5 386 948.7 413.8 932.3C419.4 929 431.2 922.1 440 916.9C452.9 909.4 456.4 907.1 457.4 905.7L458.7 903.9L458.7 714.4C458.7 526.5 458.7 524.9 457.8 524.4C457.3 524.2 455.7 523.6 454.2 523.1C448.3 521.2 394.6 489.1 361.5 467.5C359.9 466.4 354.6 463 349.8 459.9C338.1 452.5 338.9 453.6 338.9 445.2C338.9 436.4 338.6 436.8 346.9 435.1L353.2 433.8L359.8 435.1C367.1 436.6 365.8 436 380.9 445.7C404.7 461 436.4 480.4 456 491.5C461.7 494.7 467.5 498 468.9 498.9C472.1 500.8 473.2 500.8 475.5 499.2C476.5 498.6 478.2 497.7 479.4 497.2C482.6 496.1 527.5 468.9 568.8 443.2L579.7 436.5L586.2 435.1L592.8 433.8L599.1 435.1C607.4 436.8 607.1 436.3 607.1 445.3C607.1 449 606.9 452.5 606.6 453C606.1 453.9 579.5 470.7 543.4 492.8C512.8 511.5 494.4 522.3 491 523.5L487.5 524.7L487.4 712.4L487.3 900.1L488.4 903.8C489.5 907.2 489.8 907.6 491.6 908.7C495.1 910.9 554.3 945.4 556.3 946.4C559.1 947.8 568.3 947.8 571.3 946.4C576.6 943.9 578.5 939.7 579.5 928.2C581.7 900.9 582.1 839.9 580.3 778.7C580.1 770.8 579.7 757.6 579.5 749.5C577.4 675.9 577.7 614.6 580.3 603.2C580.5 602.1 580.9 600.3 581.2 599.3C583.9 587.4 596.4 567 601.8 565.5C603.9 564.9 605.6 565.4 606.5 566.6C607.1 567.6 607.1 572.4 606.8 613.9C606.4 675.7 606.4 668.5 607 736.1C607.6 799.5 607.4 938.1 606.6 940.7C606.4 941.4 606 943 605.7 944.1C600.3 964.1 584.6 975.9 563.4 975.9C557.3 975.9 552.3 975.2 549.9 973.9C546 971.8 528 961.9 520.1 957.5C515 954.7 506.6 950.1 501.5 947.3C496.3 944.5 488.2 940 483.4 937.3C476.5 933.5 474.3 932.5 472.9 932.5C470 932.6 460.7 937.6 439.3 950.6C414.3 965.9 404.7 971.3 402.9 971.3C402.5 971.3 402 971.5 401.8 971.7C401.3 972.2 392.1 974.5 387.5 975.4C383.5 976.1 376.6 976 372.1 975Z","face":"M378.3 947.4C374.3 946.9 370.3 944.1 368.6 940.7C366.5 936.7 366.6 944.4 366.6 769C366.6 651.2 366.4 603 366.1 601.5C364.2 593.4 360.5 585 355.6 577.5L352.8 573.3L352.8 517.8C352.8 487.2 352.9 462.2 353.1 462.2C353.3 462.3 354.9 463.3 356.8 464.4C393.8 488.5 448.2 521.3 453.9 523C454.9 523.3 456.4 523.9 457.2 524.2L458.7 524.9L458.7 714.4L458.7 903.9L457.4 905.7C456.4 907.1 452.9 909.4 440 916.9C431.2 922.1 419.4 929 413.8 932.3C390.4 946.1 390.7 945.9 388 946.8C386.1 947.5 381.1 947.8 378.3 947.4ZM556.2 946.3C554.1 945.3 494.6 910.6 491.6 908.7C489.8 907.6 489.5 907.2 488.4 903.8L487.3 900.1L487.4 712.4L487.5 524.7L489.6 524.1C493.5 522.8 495.3 521.8 512.9 511.3C535.6 497.7 552.5 487.3 580.5 469.9C587.2 465.7 592.9 462.2 593 462.2C593.1 462.2 593.2 487.4 593.2 518.2L593.2 574.1L591.2 577.3C586.5 584.7 582.7 592.9 581.2 599.3C580 604.2 579.8 605.7 579.4 610.8C577.6 636 577.6 684.5 579.5 749.5C579.7 757.6 580.1 770.8 580.3 778.7C582.4 852.6 581.3 929.9 578.1 937.9C576.5 941.9 575.9 942.9 574.8 944.1C572.2 946.7 569.9 947.4 563.7 947.3C558.8 947.3 557.7 947.2 556.2 946.3Z","poly":[[340.5,436.7],[339.2,451.9],[353.0,462.0],[353.0,573.0],[344.7,565.2],[339.2,567.0],[339.2,939.3],[342.8,950.8],[360.8,971.1],[385.7,975.7],[407.8,969.2],[473.7,931.9],[550.6,974.3],[582.9,972.0],[598.1,960.0],[606.8,939.8],[606.8,567.5],[601.3,565.7],[593.0,573.5],[593.0,462.0],[606.4,453.2],[606.8,438.5],[593.0,433.9],[579.7,436.7],[471.4,500.7],[366.3,436.7]]}},"full":"M374.6 975.5C374.1 975.3 373.1 975.1 372.4 975C369.6 975 362.7 972.3 358.5 969.7C349.5 964.1 342.6 953.6 339.5 940.8C339.1 939.2 338.9 923.5 338.9 883.4C338.9 853 338.8 822.2 338.6 814.9L338.3 801.7L336.7 800.7C335.8 800.1 327.8 795.6 318.9 790.6C310 785.7 295.7 777.7 287 772.8C278.3 768 264.2 760.1 255.5 755.2C246.9 750.4 232.8 742.5 224.2 737.7C215.6 732.9 201.4 725 192.8 720.2C169.6 707.2 170.8 707.8 168.7 707.8C165.6 707.8 144 720 122.8 733.8C102.6 747 89 755.2 78.4 760.6C68.6 765.5 64.2 767.3 57.6 769C55.3 769.6 43.8 769.7 42 769.1C41.2 768.9 39.7 768.4 38.5 768.1C28.2 765.1 18.6 755.8 14.8 744.9C12.8 739.1 12.8 738 13.6 695.8C14.2 663.6 13.6 613.7 11.8 553.2C11 526.1 10.9 436.5 11.7 421C13.1 395.1 13.5 391.6 16.3 383.3C19.8 373.1 25.1 366.7 34.6 360.9C43.6 355.4 47.5 353 79 333.6C108 315.7 109.1 315 109.7 313.2C110.7 310.5 112.1 311.6 92.9 299.6C91 298.4 77.2 289.8 62.3 280.4C47.3 271 33.6 262.4 31.9 261.3
103C30.1 260.2 28 258.8 27.3 258.2C24.4 255.5 18 244.4 16.2 239C15.8 237.7 15.3 236.3 15.1 235.8C14.7 234.9 14.1 232.7 13.4 229.3C12 222.5 11.1 182.6 11.8 153.7C12.8 111.9 13.7 105.8 20.8 94.8C22.5 92.2 27 86.7 28.9 85C39.1 75.7 97.5 38.3 113.2 31.1C118.8 28.4 124.5 26.9 132.5 25.8C139.7 24.8 150.1 25.3 156.9 26.8C158.1 27 159.6 27.4 160.4 27.5C161.2 27.7 162.2 28 162.7 28.2C163.2 28.4 164.5 28.8 165.5 29.1C170.5 30.7 177.4 34.8 212.2 56.3C215.4 58.3 224 63.6 231.2 68C238.5 72.5 250.4 79.8 257.6 84.2C264.8 88.6 276.7 95.9 283.9 100.3C291.1 104.8 303 112 310.3 116.5C317.5 121 326.1 126.2 329.3 128.2C347.5 139.5 361.7 148.1 362.7 148.5C364.7 149.2 366.8 148.5 373.4 144.9C378.8 141.8 394.8 131.5 402.6 125.9C426.6 108.9 446.3 98 457.1 95.8C459.8 95.3 480.1 95.1 483.1 95.6C484.3 95.8 486 96.2 487.1 96.5C488.1 96.7 489.7 97.2 490.7 97.4C494.2 98.3 499 99.8 499.5 100.2C499.7 100.4 500.2 100.6 500.5 100.6C501.8 100.6 511.6 105.4 518.9 109.6C523.2 112 534.6 119.6 549.9 130.1C561 137.7 572 144.1 579.4 147.3C584.1 149.4 582.2 150.1 597.9 140.4C604.3 136.4 610.5 132.6 611.7 131.9C612.8 131.2 618.2 127.9 623.7 124.5C633.4 118.5 655.8 104.7 667 97.8C670.1 95.8 677.6 91.2 683.7 87.5C689.7 83.8 699.5 77.7 705.5 74C711.6 70.3 719.1 65.6 722.2 63.7C725.4 61.8 735.2 55.7 743.9 50.4C752.6 45 762.9 38.6 766.8 36.3C770.6 33.9 775.1 31.4 776.9 30.7C778.6 30.1 780.5 29.4 781 29.2C781.9 28.8 784.7 28 787.7 27.3C801.1 24.1 814.9 24.6 827.5 28.8C841.7 33.5 905.5 73.8 918.3 86.1C922.1 89.7 928.6 98.5 928.6 99.9C928.6 100 929 101 929.5 102.1C934 112.1 934.8 123.7 934.8 175.7C934.8 224.2 934.5 228.4 929.3 240.5C925.8 248.6 921.2 256 917 260.2C910.6 266.5 896.9 275.7 871.1 291.1C865.6 294.3 857 299.4 851.9 302.5C842.6 308.1 839.4 309.7 837.9 309.7C836.8 309.7 836.7 309.9 836.3 312.3L836 314.2L841.2 317.6C849.3 323 854.2 325.8 877.3 338.7C919.9 362.3 927.8 370.1 932.8 393.6C933.2 395.5 933.3 399.3 933 407.7C932.4 427 932.6 514.4 933.2 535.5C935.8 617.2 936.3 662.2 935.2 695.6C933.9 731.1 933.4 736.8 930.5 746C928.1 753.9 921.2 762 914.1 765.5C906 769.6 893.1 770.6 884.7 767.8C883.9 767.5 859.5 753.9 830.5 737.5L777.8 707.7L776.2 708.5C775.3 708.9 763.1 715.6 749 723.4C734.9 731.2 713.6 743.1 701.5 749.7C689.5 756.4 670.2 767 658.7 773.4C647.1 779.8 631.1 788.7 622.9 793.2L608.1 801.4L607.8 824C607.7 836.5 607.4 867.4 607.3 892.8C607.1 919.4 606.8 939.6 606.6 940.6C603.6 952.4 600.7 957.8 594.1 964.4C589.7 968.7 582.2 973.1 576.9 974.3C576.1 974.4 574.5 974.8 573.2 975.1C570.6 975.7 556.8 975.8 553.6 975.3C552.5 975.1 550.9 974.5 550.2 974.1C549.4 973.6 544.4 970.9 539.1 968C533.8 965.1 525.2 960.4 520.1 957.5C515 954.7 506.6 950.1 501.5 947.3C496.3 944.5 488.2 940 483.4 937.3C476.5 933.5 474.3 932.5 472.9 932.5C470 932.6 460.7 937.6 439.3 950.6C414 966 404.7 971.3 402.8 971.3C402.4 971.3 402 971.5 401.9 971.7C401.7 971.9 401.1 972.2 400.6 972.4C400 972.5 398.7 972.9 397.7 973.2C394.6 974 392.3 974.6 389.6 975.1C386 975.7 375.7 976 374.6 975.5ZM388 946.8C390.7 945.9 390.4 946.1 413.8 932.3C419.4 929 431.2 922.1 440 916.9C452.9 909.4 456.4 907.1 457.4 905.7L458.7 903.9L458.7 714.4L458.7 524.9L457.2 524.2C456.4 523.9 454.9 523.3 453.9 523C448.4 521.3 395.2 489.3 360.4 466.8C357.8 465.1 349.7 459.9 342.3 455.1C304.4 430.5 298.2 426.5 291.7 422.4C289.4 420.9 286.2 418.8 284.6 417.8C277.9 413.5 265.8 405.8 256.5 400C251 396.7 245.8 393.4 244.9 392.9C244.1 392.3 240.4 390.1 236.8 387.9C233.2 385.7 227.4 382.2 223.9 380C220.4 377.9 204.5 368.3 188.5 358.8C172.5 349.2 155.8 339.1 151.3 336.4C146.8 333.6 142.7 331.4 142.3 331.4C141.6 331.4 128.6 338.5 102.1 353.5C98.2 355.7 86.2 362.4 75.4 368.5C49.8 382.8 50.8 382.3 50.8 382.9C50.8 383.3 57.3 387.6 65.3 392.5C73.2 397.5 82.9 403.4 86.7 405.8C90.5 408.1 98.1 412.8 103.5 416.1C109 419.5 116.1 423.9 119.4 426C131.5 433.4 137.1 436.9 146.8 442.9C152.3 446.2 159.3 450.6 162.5 452.5C165.6 454.5 172.7 458.9 178.1 462.2C183.6 465.6 190.7 470 193.9 472C197.2 473.9 202.4 477.2 205.7 479.2C208.9 481.2 216 485.6 221.4 488.9C226.9 492.3 233.9 496.7 237.1 498.6C240.3 500.6 250 506.6 258.8 512C267.5 517.4 277.3 523.4 280.4 525.3C283.6 527.3 290.6 531.7 296.1 535C306.3 541.3 307.2 541.9 332.5 557.5C347.3 566.7 347.5 566.9 350.4 570.5C358.5 580.7 363.6 590.8 366.1 601.5C366.4 603 366.6 651.2 366.6 769C366.6 944.4 366.5 936.7 368.6 940.7C370.3 944.1 374.3 946.9 378.3 947.4C381.1 947.8 386.1 947.5 388 946.8ZM571.4 946.4C572.5 945.9 574 944.8 574.8 944.1C575.9 942.9 576.5 941.9 578.1 937.9C581.3 929.9 582.4 852.6 580.3 778.7C580.1 770.8 579.7 757.6 579.5 749.5C577.4 
103675.9 577.7 614.6 580.3 603.2C581.4 598.2 581.7 597.3 582.4 595.1C585.1 587.4 589.7 578.9 594.9 572C598.6 567.1 597.9 567.6 612.6 558.6C619.3 554.4 629.1 548.4 634.3 545.2C645.8 538 658 530.4 678.9 517.5C687.7 512.1 699.1 505.1 704.3 501.8C709.5 498.6 720.9 491.6 729.6 486.2C749.5 473.9 762.7 465.7 772.5 459.7C776.6 457.1 788.9 449.5 799.6 442.9C810.4 436.2 822.7 428.6 826.9 426C831.2 423.4 838.1 419.1 842.4 416.4C846.6 413.8 858.9 406.2 869.7 399.6C880.4 392.9 890.8 386.5 892.6 385.4C894.5 384.3 895.9 383.1 895.8 382.8C895.7 382.5 893.5 381 890.8 379.5C888.2 377.9 882.7 374.7 878.5 372.2C874.4 369.8 867.7 365.8 863.7 363.4C859.6 361 853 357.1 848.9 354.6C844.9 352.2 838.2 348.3 834.1 345.8C830 343.4 823.1 339.3 818.8 336.7C814.2 334 810.2 332 809.1 331.8C808.1 331.6 806.5 331.3 805.6 331.1C804.1 330.9 802.9 331.5 787 341.1C760 357.3 706.2 390.7 667.4 415.2C661.7 418.8 653.1 424.3 648.3 427.3C643.5 430.3 634.5 436 628.3 439.9C580.7 469.9 543.5 492.9 512.9 511.3C495.3 521.8 493.5 522.8 489.6 524.1L487.5 524.7L487.4 712.4L487.3 900.1L488.4 903.8C489.5 907.2 489.8 907.6 491.6 908.7C494.6 910.6 554.1 945.3 556.2 946.3C558.9 947.8 568.3 947.8 571.4 946.4ZM338.9 686.7L338.9 606.3L337.9 603.5C336.8 600.3 335.1 597.4 331.8 592.8C327.7 587.4 324.6 584.8 315.4 579.2C305.6 573.2 297.8 568.5 254 541.9C227.5 525.7 219.4 520.7 192.6 504.4C187.7 501.5 183.4 499.1 183 499.1C181.8 499.1 181.9 682 183.1 683C183.5 683.4 188.8 686.4 194.7 689.6C200.7 692.8 207.7 696.6 210.4 698.1C213.1 699.6 221.2 704 228.5 708C235.8 711.9 247.7 718.4 255 722.3C262.3 726.3 270.4 730.7 273 732.2C277.6 734.7 312 753.4 326.3 761.1C329.8 763.1 333.9 765.3 335.4 766.1C337 767 338.4 767.5 338.6 767.4C338.8 767.3 338.9 731 338.9 686.7ZM614 764.5C616.9 762.9 621.5 760.4 624.1 758.9C629.2 756.1 663.3 737.6 681.5 727.7C687.3 724.5 694.2 720.8 696.9 719.3C699.6 717.8 713.7 710.2 728.2 702.2C762.8 683.5 763.6 683 763.9 682.2C764 681.8 764.2 640.5 764.2 590.5C764.2 510.7 764.1 499.5 763.5 499.2C762.7 498.9 762 499.3 748 507.8C737.8 514.1 730.6 518.4 705.7 533.6C699.7 537.3 693.9 540.8 692.8 541.5C691.6 542.1 685.9 545.6 680.1 549.2C674.3 552.7 664.7 558.5 658.9 562.1C653.1 565.6 643.4 571.5 637.5 575.1C623.8 583.4 623.7 583.5 619.5 587.8C613 594.5 608.9 601.2 607.7 606.9C607.2 609.3 607.1 623.9 607.1 688.2C607.1 765.3 607.1 768.2 608.4 767.5C608.6 767.4 611.1 766 614 764.5ZM900.5 740.1C901.4 739.6 903 738.4 903.9 737.4L905.6 735.7L905.6 574.5C905.6 432.2 905.5 413.4 904.9 413.4C904.3 413.4 896.8 417.9 873.4 432.3C869.9 434.4 859.7 440.7 850.6 446.2C829.7 459 827.5 460.3 819.9 465C816.5 467.1 812 469.8 810 471C808 472.3 803.2 475.2 799.4 477.5L792.5 481.8L792.4 581.4C792.3 675.7 792.3 681 793.1 681.6C793.5 682 816.7 695.6 844.6 711.9C900.8 744.7 896.1 742.3 900.5 740.1ZM57.3 738.2C74.3 730.7 114.3 707.5 146.8 686.4C149.2 684.8 151.8 683.2 152.5 682.8L153.7 682L153.7 581.8L153.7 481.7L151.8 479.6C150.6 478.4 146.5 475.6 141.3 472.5C117.8 458.5 93.1 443.8 89.2 441.5C86.8 440 76 433.6 65.3 427.2C54.5 420.8 44.9 415.1 44 414.5C43 413.9 41.9 413.4 41.5 413.4C40.4 413.4 40.5 735.2 41.6 736.7C42 737.3 43.4 738.5 44.7 739.4C48.2 741.7 49.7 741.5 57.3 738.2ZM480.5 496.2C487 492.1 490.3 490.1 509.2 478.6C513.1 476.2 519.5 472.2 523.4 469.8C527.4 467.4 533.8 463.5 537.7 461.1C541.7 458.7 548.1 454.7 552 452.3C559.1 448 563.4 445.3 583.6 433C595 425.9 610.5 416.5 621.2 409.9C625.1 407.5 631.5 403.6 635.4 401.2C639.3 398.8 645.8 394.8 649.7 392.4C653.6 390 660 386.1 664 383.7C667.9 381.3 674.3 377.3 678.2 374.9C682.2 372.5 688.6 368.6 692.5 366.2C696.5 363.8 702.9 359.8 706.9 357.3C718.1 350.5 727.8 344.6 735.4 339.9C743 335.2 752.7 329.3 763.8 322.5C767.8 320 774.2 316.1 778.1 313.7C790.8 305.9 799.2 300.8 813.7 291.9C821.6 287.1 834 279.4 841.3 274.9C848.7 270.4 857.8 264.8 861.6 262.5C865.4 260.2 873.2 255.4 878.9 251.9C892.1 243.8 896.1 240.6 901.5 233.5C905.9 227.6 905.6 231.5 905.6 181.6C905.6 143.6 905.5 137.9 904.9 137.9C904.3 137.9 899.9 140.5 877.7 154.1C875.5 155.5 868.7 159.6 862.8 163.2C852.4 169.5 849.3 171.4 840 177.1C837.7 178.5 830.9 182.6 825 186.3C819 189.9 812.3 194 810 195.4C800.6 201.1 797.5 203 787.2 209.3C781.3 212.9 774.5 217 772.2 218.4C762.8 224.2 759.8 226 749.4 232.3C743.5 235.9 736.8 240 734.6 241.4C732.4 242.7 728.7 245 726.5 246.3C724.3 247.7 717.6 251.7 711.7 255.4C705.7 259 699 263.1 696.7 264.5C687.3 270.2 684.2 272.1 673.9 278.4C667.9 282 661.2 286.1 658.9 287.5C649.8 293.1 646.5 295.
1031 636.1 301.4C630.1 305.1 621.6 310.2 617.2 313C612.8 315.7 600.6 323.1 590.3 329.4C564.4 345.2 535.2 363 522.8 370.6C508.7 379.1 508.2 379.4 504.6 380.9C487.4 387.8 467.1 388.7 449.3 383.4C442.8 381.4 437.6 379.1 432 375.7C425.2 371.6 390.2 350.3 384.5 346.8C382.4 345.5 378.8 343.3 376.5 341.9C374.2 340.5 367.5 336.4 361.5 332.8C355.6 329.1 348.9 325.1 346.6 323.7C344.4 322.4 340.8 320.1 338.6 318.8C336.4 317.4 326 311.1 315.5 304.7C305.1 298.3 288 287.9 277.5 281.5C267.1 275.2 250.1 264.8 239.8 258.5C229.4 252.2 212.5 241.9 202.2 235.6C191.9 229.3 174.8 218.9 164.3 212.5C146.2 201.5 143.1 199.6 133.2 193.5C130.9 192.1 124.2 188 118.2 184.4C112.3 180.8 105.6 176.7 103.4 175.4C101.2 174 97.5 171.8 95.3 170.4C90.9 167.7 57 147 47.6 141.3C43.8 139 41.5 137.9 41.3 138.2C41 138.5 40.9 155.2 40.9 183.3L41.1 227.9L42.4 230.4C44.3 233.7 47.9 237.6 52.7 241.4C56.3 244.2 59.3 246.4 73.6 256C86.4 264.8 109.7 279.3 131.8 292.5C149.7 303.1 152.2 304.6 174.9 317.8C191.3 327.3 191.5 327.4 210.9 338.7C226.1 347.7 244 358.5 265.9 372.2C270.7 375.1 276.7 378.9 279.3 380.4C283.6 383.1 308.3 398.6 314.3 402.4C315.8 403.3 318.5 405 320.3 406.1C322 407.2 326.8 410.2 330.9 412.7C337.8 417.1 354.2 427.4 362 432.3C369.2 436.8 377.2 441.8 382.9 445.4C386.2 447.5 396.2 453.7 405 459.2C413.9 464.7 422.7 470.2 424.6 471.4C426.5 472.6 432.5 476.4 438 479.7C456.9 491.4 459.9 493.3 465.6 496.8C468.8 498.8 471.9 500.4 472.5 500.4C473 500.5 476.7 498.5 480.5 496.2ZM483.1 357.7C491.6 356.1 492.6 355.7 511.8 343.9C520 338.9 532.6 331.1 539.8 326.7C558.2 315.4 576.9 303.9 592.4 294.4C599.7 289.9 611.6 282.6 618.8 278.2C626 273.7 637.9 266.5 645.1 262C652.3 257.6 664.2 250.3 671.5 245.9C678.7 241.4 687.3 236.1 690.5 234.2C693.6 232.2 700.8 227.8 706.5 224.3C712.1 220.9 721.3 215.2 727 211.8C732.6 208.3 739.8 203.9 743 201.9C746.1 200 754.7 194.7 762 190.2C769.3 185.8 781.1 178.5 788.2 174.1C809.9 160.8 827.1 150.3 840.8 141.9C847.9 137.5 861.1 129.4 870.1 123.8C879.1 118.3 889 112.2 892.1 110.3C895.2 108.4 897.8 106.6 897.8 106.2C897.8 105.2 885.8 97 860.3 80.6C844 70.1 825.9 60 817.6 56.8C815.5 56 813.3 55.1 812.8 54.9C809.9 53.7 802.2 53.2 799.4 54C798.4 54.3 797.1 54.8 796.4 54.9C795.8 55.1 794.3 55.6 793.2 55.9C792.1 56.2 790.4 56.7 789.6 57.1C788.3 57.6 735.3 90.2 566.5 194.1C500.9 234.5 507.1 230.8 502 232.7C499.9 233.6 497.7 234.4 497.2 234.6C496.7 234.8 495.3 235.2 494.2 235.5C493.1 235.8 491.5 236.3 490.7 236.5C489.9 236.7 488.7 236.9 488.1 236.9C487.4 236.9 486.3 237.2 485.6 237.4C483.9 238.1 465.4 238.1 462.5 237.5C461.3 237.2 459.1 236.7 457.6 236.4C448.7 234.7 439 230.4 427.8 223.5C423.7 220.8 417.9 217.2 414.9 215.4C412 213.7 397.5 204.7 382.7 195.5C367.9 186.3 353.2 177.1 350 175.2C346.8 173.2 336.5 166.8 327 160.9C317.5 155 307.1 148.6 303.9 146.6C300.7 144.7 286 135.6 271.2 126.4C256.4 117.2 241.7 108 238.5 106.1C235.3 104.1 225 97.7 215.5 91.8C206 85.9 195.6 79.5 192.4 77.5C189.3 75.6 184.2 72.4 181.1 70.5C166.3 61.3 161.1 58.2 158.6 57.4C150.7 54.8 143.1 53.3 139.6 53.7C136 54.2 134.2 54.5 133.2 54.9C132.7 55.1 130.5 56 128.4 56.8C117.1 61.2 97.5 72.7 68.3 92C54 101.5 48.6 105.5 48.8 106.6C48.9 107.3 51.8 109.4 59 113.8C64.6 117.2 71.6 121.5 74.5 123.2C77.4 125 87.1 131 96.1 136.5C105.1 142 115.3 148.2 118.7 150.3C122.1 152.4 128.6 156.4 133 159.1C137.4 161.8 143.9 165.7 147.3 167.8C150.7 169.9 160.9 176.1 169.8 181.6C178.8 187.1 189 193.4 192.4 195.5C195.8 197.6 202.3 201.5 206.7 204.2C211.1 206.9 217.6 210.9 221 213C224.4 215.1 230.8 219 235.3 221.7C239.7 224.4 246.1 228.4 249.6 230.5C253 232.6 263.1 238.8 272.1 244.3C281.1 249.8 291.3 256 294.7 258.1C298.1 260.2 304.6 264.2 309 266.9C313.4 269.6 319.8 273.5 323.3 275.6C326.7 277.7 336.8 283.9 345.8 289.5C354.8 295 365 301.2 368.4 303.3C371.8 305.4 378.3 309.3 382.7 312C387.1 314.7 393.6 318.7 397 320.8C400.4 322.9 410.6 329.1 419.6 334.6C428.6 340.1 438.7 346.3 442.1 348.4C447.4 351.6 450.3 353.1 455.7 355.4C456.2 355.6 457.4 355.9 458.3 356.2C459.1 356.4 460.6 356.9 461.5 357.1C462.4 357.4 463.8 357.6 464.5 357.6C465.3 357.6 466.2 357.9 466.5 358.1C467.4 358.7 479.6 358.4 483.1 357.7ZM482.9 208.4C484.2 208.1 485.8 207.7 486.5 207.6C487.2 207.5 487.9 207.2 488 206.9C488.1 206.7 488.8 206.5 489.4 206.5C490 206.5 490.6 206.3 490.7 206.1C490.9 205.8 491.3 205.6 491.7 205.6C494.9 205.6 510.4 196.5 527.1 184.8C535.7 178.9 544.2 173.5 549.4 170.8C556.3 167.2 556.3 165.9 549.4 162.5C544 159.9 536.2 155.1 527.4 148.9C514.8 140.2 512.8 138.9 508 135.9C499.5 130.7 491.3 126.7 485.4 125C484.6 124.8 482.9 124.3 481.8 124C479 123.2 467.8 123.2 465.2 124C458.4 126.1 454.3 127.6 450.4 129.6C442.3 133.7 438 136.4 413.8 152.9C407 157.5 400 161.7 395.4 164C393.5 164.9 391.8 166 391.7 166.4C391.4 167.2 392.5 169 393.9 169.8C394.4 170.2 400.7 174.2 407.8 178.7C438.6 198.5 451.3 205.6 460.1 207.9C461.2 208.2 462.7 208.6 463.3 208.8C465 209.4 480.2 209.1 482.9 208.4Z","caps":{"L":[338.9,443.3,366.6,637.8],"R":[578.5,444.7,607.1,637.8]}};
104/* ---- THE SWARM MARK (Website L, round 3, 30 Sep 2026) ----
105   J's style (agents trace the outlines and hatch the faces in orderly rows, and stream in), on the OFFICIAL geometry: SW is
106   made by ll/logo/swarm.py from the traced logo. SW.pieces[id]: e = the centre lines of the piece's strokes (rounded corners
107   and all), r = strokes another piece already traces, and the cuts through the wing strips (shown only while apart),
108   f = faces [tone, hatch angle, polygon], poly = silhouette. SW.loz = the lozenge in the top V band's notch (sharp corners
109   T R B L and the corner radii): flat and in place when the mark is whole; while apart it rises and extrudes into a cube.
110   Pieces (founder, round 3): core = the lozenge -> About; frame = the top and lower V bands -> Our rules; left panel ->
111   Agentic systems; right panel -> AI products; stem -> AI Audit. */
112const SW={"pieces":{"frame":{"e":[[132.3,40.9,124.7,43.3,108.7,50.7,77.5,69.5,44.0,92.3,37.7,98.5,34.9,103.4,32.8,113.5,32.9,116.2,33.9,118.6,35.8,120.1,46.2,123.8,53.7,127.4,70.6,137.4,96.6,154.2,115.3,164.7,152.0,187.2,161.3,193.6,307.5,282.3,340.9,303.3,359.5,313.9,438.3,362.6,451.6,369.1,467.3,372.4,482.1,372.2,494.1,369.9,502.7,366.6,530.6,349.3,543.6,342.1,555.3,334.0,596.8,308.7,604.2,305.0,631.9,287.3,690.3,252.2,774.7,199.8,810.0,178.8,851.3,153.0,866.7,144.3,892.8,127.7,901.9,123.1,912.2,119.5,913.8,117.6,914.4,115.0,914.4,109.0,913.6,105.4,910.2,98.8,907.1,95.2,897.2,87.6,881.5,77.2,847.4,55.8,827.7,45.3,818.2,41.6,810.7,40.5,798.7,40.5,794.0,41.3,784.3,44.4,768.1,53.5,744.1,68.1,731.6,76.6,600.1,157.3,589.5,163.1,575.4,167.4,569.3,175.5,561.9,181.5,509.6,214.6,497.1,220.6,485.4,223.9,480.7,224.5,466.7,224.4,458.3,223.0,453.0,221.3,435.6,212.7,387.7,183.3,379.6,176.5,372.1,167.1,361.5,164.1,352.3,159.6,207.5,69.9,168.9,47.0,162.2,43.9,150.5,40.7],[30.7,124.7,28.3,131.8,27.5,137.4,27.5,226.4,30.3,237.3,34.8,244.4,38.1,248.0,51.9,258.1,77.9,275.1,114.5,297.7,122.6,304.6,130.3,313.8,145.6,317.3,158.0,323.6,278.1,395.7,285.0,400.8,376.0,458.8,422.1,487.5,460.7,510.2,474.1,515.8,477.4,514.8,487.3,509.8,533.0,482.4,650.8,409.5,659.3,403.5,672.3,396.2,713.4,370.0,730.4,360.2,744.5,350.9,789.3,323.7,803.2,316.4,817.5,312.6,824.2,304.8,829.4,300.3,878.1,270.7,894.7,259.9,906.7,250.6,912.8,243.5,916.8,236.1,919.2,228.1,920.4,217.6,921.5,200.0,921.5,149.1,919.2,131.0,916.9,124.9]],"r":[],"f":[["T",0.0,[49.5,107.5,444.5,350.5,459.5,357.5,484.5,358.5,496.5,354.5,522.5,337.5,533.5,332.5,548.5,321.5,573.5,307.5,590.5,295.5,598.5,292.5,626.5,273.5,668.5,249.5,675.5,243.5,684.5,239.5,766.5,187.5,873.5,123.5,898.5,106.5,873.5,88.5,822.5,58.5,811.5,54.5,798.5,54.5,791.5,56.5,546.5,206.5,518.5,225.5,503.5,233.5,485.5,238.5,463.5,238.5,453.5,236.5,428.5,224.5,424.5,220.5,384.5,197.5,319.5,155.5,305.5,148.5,159.5,57.5,148.5,54.5,135.5,54.5,120.5,60.5,64.5,94.5]],["U",0.0,[41.5,138.5,41.5,229.5,49.5,239.5,100.5,274.5,254.5,364.5,471.5,500.5,477.5,499.5,488.5,491.5,512.5,478.5,514.5,475.5,528.5,468.5,589.5,429.5,644.5,397.5,646.5,394.5,763.5,324.5,767.5,320.5,776.5,316.5,845.5,272.5,861.5,264.5,865.5,260.5,886.5,248.5,902.5,234.5,905.5,228.5,9
11205.5,138.5,784.5,212.5,770.5,219.5,696.5,266.5,675.5,277.5,609.5,319.5,529.5,366.5,515.5,376.5,503.5,382.5,492.5,385.5,467.5,387.5,447.5,383.5,432.5,376.5,306.5,298.5,162.5,212.5,122.5,186.5,88.5,167.5,65.5,151.5]]],"poly":[[12.5,134.5],[13.5,230.5],[21.5,250.5],[29.5,260.5],[106.5,307.5],[120.5,326.5],[149.5,335.5],[269.5,407.5],[451.5,522.5],[473.5,531.5],[497.5,521.5],[796.5,336.5],[809.5,329.5],[826.5,325.5],[838.5,310.5],[884.5,284.5],[918.5,260.5],[929.5,242.5],[934.5,224.5],[935.5,139.5],[925.5,94.5],[906.5,76.5],[850.5,40.5],[824.5,27.5],[810.5,25.5],[791.5,26.5],[774.5,32.5],[592.5,145.5],[566.5,154.5],[575.5,166.5],[567.5,177.5],[510.5,214.5],[484.5,224.5],[452.5,221.5],[414.5,200.5],[377.5,174.5],[372.5,166.5],[381.5,154.5],[363.5,149.5],[167.5,29.5],[137.5,25.5],[113.5,31.5],[36.5,79.5],[20.5,97.5]]},"spine":{"e":[[353.5,646.8,354.5,665.4,354.5,762.4,353.5,774.0,353.7,936.9,356.9,947.6,359.8,952.4,362.5,955.1,370.3,960.2,378.5,962.3,384.3,962.4,394.5,960.3,402.9,956.5,447.8,929.7,469.7,917.5,472.3,915.0,473.3,912.4,473.5,522.5],[478.0,918.3,532.6,948.3,544.5,955.7,555.5,960.6,563.6,961.9,571.2,961.3,577.6,959.7,584.6,955.2,590.1,947.5,592.1,941.4,592.5,936.5,592.5,645.5]],"r":[[355.1,454.6,353.9,457.0,353.5,461.8,353.5,646.8],[473.5,522.5,472.3,518.0,469.7,515.5,460.6,511.1,436.4,497.0,367.2,454.0,361.3,452.1,356.3,453.4],[592.5,645.5,592.3,458.6,590.8,455.3,588.6,453.8,585.3,453.0,579.4,454.7,533.2,483.3,488.4,510.2,477.8,515.8]],"f":[["L",-0.8472,[353.5,462.5,353.5,574.5,364.5,594.5,366.5,601.5,367.5,938.5,375.5,947.5,384.5,948.5,389.5,947.5,454.5,909.5,459.5,904.5,459.5,525.5,419.5,504.5]],["R",-2.2944,[592.5,463.5,501.5,519.5,488.5,525.5,487.5,902.5,489.5,907.5,558.5,947.5,570.5,947.5,576.5,943.5,579.5,934.5,581.5,911.5,581.5,793.5,578.5,710.5,579.5,607.5,582.5,595.5,592.5,576.5]]],"poly":[[351.5,438.5],[341.5,446.5],[338.5,453.5],[338.5,808.5],[341.5,947.5],[348.5,961.5],[360.5,971.5],[375.5,976.5],[387.5,976.5],[411.5,968.5],[473.5,932.5],[552.5,975.5],[568.5,976.5],[585.5,972.5],[599.5,959.5],[607.5,942.5],[606.5,451.5],[594.5,439.5],[575.5,439.5],[472.5,500.5],[374.5,440.5],[361.5,436.5]]},"wingL":{"e":[[33.7,396.3,28.7,406.5,27.7,413.1,26.5,430.6,26.5,526.6,27.5,536.2,27.5,621.2,28.5,631.8,28.5,697.8,27.5,707.4,27.5,731.3,29.5,742.5,31.1,745.8,33.4,748.5,41.9,754.2,47.4,755.4,53.4,755.5,58.9,754.3,70.7,749.1,93.6,736.6,150.0,701.6,165.7,693.5,168.0,690.9,168.5,686.1,168.5,498.1,172.1,482.8,169.4,477.1,166.3,473.5,156.3,466.1,139.7,455.5,60.4,408.0,49.4,400.8,37.3,396.1],[174.0,694.5,324.7,777.3,332.2,781.0,338.3,783.0,344.8,784.3,348.0,783.6],[347.8,593.7,343.9,586.3,339.2,580.3,334.9,576.2,325.5,569.9,244.0,520.4,200.4,492.9,185.5,485.4,178.2,483.8],[126.6,319.9,122.0,324.9,115.0,329.8,42.0,373.3,38.8,376.8,35.4,383.4,34.6,387.0,34.9,390.8]],"r":[[348.0,783.6,349.9,780.9,351.8,775.6,353.5,764.3,353.5,665.3,352.5,654.7,352.4,605.8,351.4,601.2,347.8,593.7],[348.6,581.2,351.7,578.2,352.5,573.5,352.5,450.5,352.0,446.7,350.8,444.3,339.4,436.1,284.7,401.6,277.8,396.5,157.7,324.4,147.0,318.9,135.3,316.0,130.1,316.9,126.6,319.9]],"f":[["L",-0.8472,[42.5,413.5,41.5,729.5,42.5,738.5,47.5,741.5,52.5,741.5,84.5,725.5,154.5,682.5,154.5,482.5,132.5,466.5,102.5,450.5,95.5,444.5,89.5,442.5]],["l",-0.8472,[183.5,499.5,183.5,683.5,314.5,755.5,339.5,767.5,339.5,606.5,335.5,597.5,329.5,589.5,318.5,580.5,304.5,573.5,192.5,503.5]],["T",0.0,[51.5,382.5,63.5,392.5,74.5,397.5,141.5,440.5,165.5,453.5,167.5,456.5,345.5,565.5,352.5,573.5,352.5,461.5,271.5,408.5,142.5,331.5]]],"poly":[[124.5,302.5],[105.5,318.5],[29.5,364.5],[20.5,378.5],[19.5,390.5],[13.5,402.5],[11.5,531.5],[14.5,744.5],[20.5,755.5],[31.5,765.5],[40.5,769.5],[59.5,769.5],[102.5,748.5],[158.5,712.5],[172.5,708.5],[327.5,795.5],[343.5,799.5],[353.5,798.5],[361.5,792.5],[368.5,770.5],[367.5,600.5],[362.5,589.5],[367.5,580.5],[366.5,440.5],[358.5,430.5],[287.5,384.5],[167.5,312.5],[140.5,301.5]]},"wingR":{"e":[[911.6,396.0,901.2,399.7,889.9,406.1,835.9,439.9,799.8,461.3,784.9,471.2,778.6,477.6,775.6,482.8,778.5,492.2,779.5,498.8,779.5,686.8,780.0,691.4,782.4,693.6,875.4,746.6,890.2,754.3,895.7,755.5,901.6,755.2,907.0,753.8,914.5,748.2,918.7,74
1120.9,920.4,728.7,920.5,711.7,921.5,701.1,921.5,588.1,920.5,577.5,920.5,439.5,919.6,431.9,919.0,408.1,915.7,398.5,913.9,396.6],[769.3,483.9,760.6,486.1,754.7,488.8,624.7,568.4,615.3,574.8,608.0,580.8,604.0,585.9,598.7,594.7],[599.6,783.6,602.1,784.4,605.8,784.0,613.1,782.2,618.1,779.9,755.8,704.2,775.5,694.0],[912.4,389.5,912.1,383.7,909.4,377.8,907.4,374.8,902.3,370.5,885.6,360.0,836.4,331.4,827.1,325.0,821.1,318.5]],"r":[[598.7,594.7,596.2,601.6,594.7,609.0,594.4,624.9,593.5,631.5,593.5,705.5,594.5,716.1,594.6,767.1,596.4,777.3,598.0,781.6,599.6,783.6],[821.1,318.5,819.1,316.6,815.9,315.3,809.0,315.8,800.2,318.7,789.6,324.5,744.1,352.1,730.7,361.0,713.0,371.2,672.7,397.0,658.9,404.7,651.2,410.3,607.3,437.0,595.6,444.8,593.9,448.0,593.5,451.8,593.5,575.8,594.9,581.2,596.8,582.9,599.4,583.6]],"f":[["R",-2.2944,[905.5,413.5,793.5,482.5,793.5,682.5,896.5,741.5,900.5,741.5,906.5,736.5]],["r",-2.2944,[764.5,499.5,728.5,519.5,708.5,533.5,623.5,584.5,609.5,601.5,607.5,610.5,608.5,768.5,764.5,683.5]],["T",0.0,[896.5,383.5,815.5,334.5,804.5,331.5,716.5,384.5,687.5,404.5,668.5,414.5,658.5,422.5,593.5,462.5,593.5,575.5,598.5,568.5,619.5,554.5,641.5,542.5,669.5,523.5,678.5,519.5,682.5,515.5,772.5,461.5,823.5,428.5,832.5,424.5,842.5,416.5,853.5,411.5]]],"poly":[[823.5,301.5],[803.5,301.5],[781.5,311.5],[587.5,431.5],[579.5,441.5],[578.5,582.5],[583.5,591.5],[579.5,604.5],[578.5,625.5],[579.5,772.5],[585.5,791.5],[594.5,798.5],[618.5,796.5],[778.5,708.5],[887.5,769.5],[905.5,769.5],[916.5,765.5],[927.5,754.5],[932.5,744.5],[936.5,689.5],[933.5,401.5],[927.5,388.5],[926.5,377.5],[916.5,363.5],[842.5,319.5]]}},"loz":{"T":[473.9,98.4],"R":[582.7,167.5],"B":[474.9,234.7],"L":[363.9,168.5],"rT":60.0,"rL":12.5,"loop":[374.6,167.2,379.8,175.7,388.0,182.4,436.6,212.3,450.7,219.3,457.6,221.8,467.0,223.4,483.8,223.2,494.7,220.4,510.6,213.0,562.9,179.8,569.3,174.2,572.9,168.8,573.5,166.5,570.2,161.2,562.1,154.3,550.8,147.7,519.6,126.6,510.7,121.4,498.3,115.2,486.9,111.3,478.5,109.7,472.6,109.5,462.1,110.8,454.0,113.1,445.6,116.9,427.0,127.6,402.9,144.2,383.7,155.8,378.1,161.2,375.3,164.9]}};
113const XY=a=>{const o=[];for(let i=0;i<a.length;i+=2)o.push([a[i],a[i+1]]);return o};
114const LZ=SW.loz,CUBE_H=Math.hypot(LZ.R[0]-LZ.B[0],LZ.R[1]-LZ.B[1]);   // an iso cube: the vertical edge as long as a top edge
115const PARTS=[
116  {id:'core',off:[0,-400],side:'r'},
117  {id:'frame',off:[0,-100],side:'l'},
118  {id:'wingL',off:[-178,98],side:'l'},
119  {id:'wingR',off:[178,98],side:'r'},
120  {id:'spine',off:[0,210],side:'s'}
121];
122// a rounded polyline: each inner corner (or every corner, closed) becomes an arc of radius r[i] (capped by the room there).
123// Returns the dense points and, per input vertex, the index of its arc's middle point.
124function roundPts(pts,rad,closed){const n=pts.length,out=[],vi=[];
125  for(let i=0;i<n;i++){const p=pts[i];
126    if(!closed&&(i===0||i===n-1)){vi.push(out.length);out.push(p);continue}
127    const a=pts[(i-1+n)%n],b=pts[(i+1)%n],la=Math.hypot(a[0]-p[0],a[1]-p[1]),lb=Math.hypot(b[0]-p[0],b[1]-p[
1271]);
128    if(la<.5||lb<.5){vi.push(out.length);out.push(p);continue}
129    const ua=[(a[0]-p[0])/la,(a[1]-p[1])/la],ub=[(b[0]-p[0])/lb,(b[1]-p[1])/lb],cs=clamp(ua[0]*ub[0]+ua[1]*ub[1],-1,1),th=Math.acos(cs);
130    if(th>3.1){vi.push(out.length);out.push(p);continue}
131    const t=Math.min(rad[i]/Math.tan(th/2),la*.48,lb*.48),r=t*Math.tan(th/2);
132    const p0=[p[0]+ua[0]*t,p[1]+ua[1]*t],p1=[p[0]+ub[0]*t,p[1]+ub[1]*t];
133    // the arc from p0 to p1 around the corner (a quadratic through p is close enough to a circle at these sizes)
134    const m=Math.max(2,Math.round(r/4));for(let j=0;j<=m;j++){const q=j/m,iq=1-q;if(j===Math.round(m/2))vi.push(out.length);
135      out.push([iq*iq*p0[0]+2*iq*q*p[0]+q*q*p1[0],iq*iq*p0[1]+2*iq*q*p[1]+q*q*p1[1]]);}}
136  return{pts:out,vi}}
137// cumulative lengths of a dense polyline, and a point + direction at fraction f of it
138function chain(pts){const c=[0];for(let i=1;i<pts.length;i++)c.push(c[i-1]+Math.hypot(pts[i][0]-pts[i-1][0],pts[i][1]-pts[i-1][1]));return{pts,c,L:c[c.length-1]}}
139function atChain(C,f){const d=clamp(f,0,1)*C.L,c=C.c;let lo=0,hi=c.length-1;while(hi-lo>1){const m=(lo+hi)>>1;if(c[m]<=d)lo=m;else hi=m}
140  const a=C.pts[lo],b=C.pts[hi],q=(d-c[lo])/Math.max(1e-6,c[hi]-c[lo]);return[a[0]+(b[0]-a[0])*q,a[1]+(b[1]-a[1])*q,Math.atan2(b[1]-a[1],b[0]-a[0])]}
141// the core at rise h (0: the flat lozenge, 1: a cube): its corners, and its outline chains, all in mark units
142//   O: L -> T -> R (the top edges, always drawn)   S: R -> R' -> B' -> L' -> L (the sides' outline)   I: L -> B -> R and B -> B' (inner)
143const LZr={T:LZ.rT,R:16,B:LZ.rT,L:16,d:LZ.rT*.55};
144function cubeGeo(rise){const h=CUBE_H*rise,T=LZ.T,R=LZ.R,B=LZ.B,L=LZ.L,R2=[R[0],R[1]+h],B2=[B[0],B[1]+h],L2=[L[0],L[1]+h];
145  const hex=h<1?[T,R,B,L]:[T,R,R2,B2,L2,L],rr=h<1?[LZr.T,LZr.R,LZr.B,LZr.L]:[LZr.T,LZr.R,LZr.R,LZr.d,LZr.L,LZr.L];
146  const H=roundPts(hex,rr,true),n=H.pts.length,vi=H.vi,iR=vi[1],iL=vi[hex.length-1];
147  const O=[],Sd=[];for(let i=iL;i!==iR;i=(i+1)%n)O.push(H.pts[i]);O.push(H.pts[iR]);for(let i=iR;i!==iL;i=(i+1)%n)Sd.push(H.pts[i]);Sd.push(H.pts[iL]);
148  const Ir=roundPts([L,B,R],[0,LZr.B,0],false),I=Ir.pts,Bi=I[Ir.vi[1]],Bo=h<1?Bi:H.pts[vi[3]];
149  // the inner vertical edge runs between the two rounded corners (not the sharp ones), so it never pokes out below the cube
150  return{rh:H.pts,T,R,B,L,R2,B2,L2,h,hex:h<1?[T,R,B,L]:[T,R,R2,B2,L2,L],O:chain(O),S:chain(Sd),I:chain(I),V:chain([Bi,[Bi[0],Math.max(Bi[1],Bo[1])]])}}
151const CUBE1=cubeGeo(1);
152PARTS.forEach(P=>{
153  if(P.id==='core'){P.faces=[{p:CUBE1.hex}];}
154  else{const D=SW.pieces[P.id];P.edges=D.e.map(XY);P.rev=D.r.map(XY);P.fc=D.f.map(([c,ang,p])=>({c,ang,p:XY(p)}));P.faces=[{p:D.poly}];}
155  const xs=P.faces[0].p.map(p=>p[0]),ys=P.faces[0].p.map(p=>p[1]);P.cx=(Math.min(...xs)+Math.max(...xs))/2;P.cy=(Math.min(...ys)+Math.max(...ys))/2;});
156const MARK_FULL=new Path2D(MARK_D.full);
157const PI={};PARTS.forEach((p,k)=>PI[p.id]=k);
158// which part each breakdown step lights up, in the order of the site's pages: About, Our rules, Agentic systems, AI products,
159// AI Audit. Step 6 puts the mark back together.
160const STEP_PART={1:'core',2:'frame',3:'wingL',4:'wingR',5:'spine'};
161const NSTEP=5;
162// how much the active part grows (a small scale-up)
163const GROW=.07;
164// intro: each piece's agents stream in from one side, bottom-up (as J)
165const ENTRY={spine:{t0:.15,dir:[.1,1],bow:.12},wingL:{t0:.45,dir:[-.45,1],bow:-.16},wingR:{t0:.6,dir:[1,.5],bow:.16},frame:{t0:.9,dir:[.75,-1],bow:-.14},core:{t0:1.45,dir:[.05,-1],bow:.1}};
166const INTRO_END=4.8;
167// agent tones, on cream: the outline dark green, the faces hatched in sage (light tops, mid left faces, dark right faces).
168// E edges, V strokes shown only while apart; T/U tops, L/l left-facing, R/r right-facing; CE/CS/CI the core's outline,
169// side outline and inner edges, CL/CR its side faces. _D: the same on a sage ground (cream agents).
170const DKG=[18,29,26],SGE=[50,78,72],CRM=[239,231,218];
171const mix3=(a,b,k)=>[lerp(a[0],b[0],k),lerp(a[1],b[1],k),lerp(a[2],b[2],k)];
172const TONE_BASE={E:DKG,V:DKG,T:mix3(SGE,CRM,.42),U:mix3(SGE,CRM,.3),L:mix3(SGE,CRM,.16),l:mix3(SGE,DKG,.15),R:SGE,r:mix3(SGE,DKG,.35)};
173const TONE_ACTV={E:DKG,V:DKG,T:mix3(SGE,CRM,.12),U:SGE,L:SGE,l:mix3(SGE,DKG,.45),R:mix3(SGE,DKG,.5),r:mix3(SGE,DKG,.7)};
174const TONE_DIM=[176,184,174];
175// the chosen piece turns orange (1 Oct 2026): edges a deep orange, tops light, left faces full, right faces darker
176const TONE_OR={E:mix3(ORG,DKG,.22),V:mix3(ORG,DKG,.22),T:mix3(ORG,CRM,.38),U:mix3(ORG,CRM,.22),L:ORG,l:mix3(ORG,DKG,.14),R:mix3(ORG,DKG,.12),r:mix3(ORG,DKG,.3)}
176;
177const TONE_MAP={CE:'E',CS:'E',CI:'E',CL:'L',CR:'R'};
178const TONE_A={E:1,V:.9,T:1,U:1,L:1,l:1,R:1,r:1};
179const DENS={T:1.25,U:1.15,L:1,l:.95,R:.95,r:.9};
180const polyLen=l=>{let s=0;for(let i=1;i<l.length;i++)s+=Math.hypot(l[i][0]-l[i-1][0],l[i][1]-l[i-1][1]);return s};
181const polyArea=p=>{let s=0;for(let i=0;i<p.length;i++){const a=p[i],b=p[(i+1)%p.length];s+=a[0]*b[1]-b[0]*a[1]}return Math.abs(s)/2};
182const inPoly=(x,y,p)=>{let c=false;for(let i=0,j=p.length-1;i<p.length;j=i++){if(((p[i][1]>y)!==(p[j][1]>y))&&(x<(p[j][0]-p[i][0])*(y-p[i][1])/(p[j][1]-p[i][1])+p[i][0]))c=!c}return c};
183const segD=(x,y,a,b)=>{const dx=b[0]-a[0],dy=b[1]-a[1],q=clamp(((x-a[0])*dx+(y-a[1])*dy)/(dx*dx+dy*dy),0,1);return Math.hypot(x-a[0]-dx*q,y-a[1]-dy*q)};
184const edgeD=(x,y,p)=>{let m=1e9;for(let i=0;i<p.length;i++)m=Math.min(m,segD(x,y,p[i],p[(i+1)%p.length]));return m};
185// sample the mark into about N agent homes, in tidy patterns (as J):
186//  - edges: the stroke centre lines, traced end to end at one even spacing, each stroke laid along its edge
187//  - faces: a regular lattice fitted to the face, every stroke on a face the same length and direction (hatching, not noise)
188//  - the core: parametric homes (a chain and a fraction, or a face and u, v) so they follow the lozenge as it becomes a cube
189function sampleMark(N,edgeFrac){
190  SEED=11;const out=[];const CD=1.6;   // the core is small: a denser weave, so it still reads
191  let TL=0;PARTS.forEach(P=>{if(P.id==='core')TL+=CUBE1.O.L+CUBE1.S.L;
191else P.edges.forEach(l=>TL+=polyLen(l))});
192  const es=TL/(N*edgeFrac);
193  const cubeA=polyArea([LZ.T,LZ.R,LZ.B,LZ.L])*(1+1.7);
194  let WA=cubeA*CD;PARTS.forEach(P=>{if(P.fc)P.fc.forEach(F=>WA+=polyArea(F.p)*DENS[F.c])});
195  const sp0=Math.sqrt(WA*.866/(N*(1-edgeFrac)));
196  const HW=14;   // the logo's stroke half width (mark units): the hatching starts inside the strokes
197  PARTS.forEach((P,k)=>{
198    if(P.id==='core'){const G0=cubeGeo(0),G1=CUBE1;
199      const ch=(c,tone,step)=>{const n=Math.max(2,Math.round(G1[c].L/step));for(let j=0;j<n;j++){const f=(j+.5)/n,[x,y,a]=atChain(G0[c],f);out.push({k,core:1,ch:c,f,hx:x,hy:y,a0:a,tone,edge:1})}};
200      ch('O','CE',es*.8);ch('S','CS',es*.8);ch('I','CI',es*.9);ch('V','CI',es*.9);
201      const sp=sp0/Math.sqrt(CD*1.2),E=CUBE_H;
202      const quad=(fc,tone,ang,Lu,Lv)=>{const n=Math.max(1,Math.round(Lu/sp)),m=Math.max(1,Math.round(Lv/(sp*.866))),mu=Math.min(.3,(HW+sp*.45)/Lu),mv=Math.min(.3,(HW+sp*.45)/Lv);
203        for(let j=0;j<m;j++)for(let i=0;i<n;i++)out.push({k,core:1,fc,u:mu+(1-2*mu)*(i+.5)/n,v:mv+(1-2*mv)*(j+.5)/m,hx:0,hy:0,a0:ang,tone,edge:0});};
204      quad('T','T',0,E,E);quad('CL','CL',Math.PI/6-Math.PI/2+.2,E,E);quad('CR','CR',-Math.PI/6-Math.PI/2-.2,E,E);
205      out.forEach(a=>{if(a.k===k&&a.fc)cubeHome(a,G0)});
206      return;}
207    const edge=(l,step,tone)=>{const C=chain(l),n=Math.max(1,Math.round(C.L/step));for(let j=0;j<n;j++){const [x,y,a]=atChain(C,(j+.5)/n);out.push({k,hx:x,hy:y,a0:a,tone,edge:1})}};
208    P.edges.forEach(l=>edge(l,es,'E'));
209    P.rev.forEach(l=>edge(l,es*1.1,'V'));
210    P.fc.forEach(F=>{
211      const sp=sp0/Math.sqrt(DENS[F.c]);
212      // lattice axes: tops run along the two iso top-face directions; side faces along the vertical and their slope
213      const side=F.ang!==0,sg=F.ang>-Math.PI/2?1:-1,ax=side?[0,1]:[C30,.5],bx=side?[C30,.5*sg]:[-C30,.5],o=F.p[0];
214      let x0=1e9,x1=-1e9,y0=1e9,y1=-1e9;F.p.forEach(p=>{x0=Math.min(x0,p[0]);x1=Math.max(x1,p[0]);y0=Math.min(y0,p[1]);y1=Math.max(y1,p[1])});
215      const R=Math.ceil(Math.hypot(x1-x0,y1-y0)/sp)+2;
216      for(let i=-R;i<R;i++)for(let j=-R;j<R;j++){const x=o[0]+(i+.5)*sp*ax[0]+(j+.5)*sp*bx[0],y=o[1]+(i+.5)*sp*ax[1]+(j+.5)*sp*bx[1];
217        if(x<x0||x>x1||y<y0||y>y1)continue;
218        if(inPoly(x,y,F.p)&&edgeD(x,y,F.p)>=Math.max(sp*.42,3))out.push({k,hx:x,hy:y,a0:F.ang,tone:F.c,edge:0});}
219    });
220  });
221  // intro timing: each part assembles from the side it arrives on, in order (nearest first), with a little personality
222  PARTS.forEach((P,k)=>{const E=ENTRY[P.id],dl=Math.hypot(E.dir[0],E.dir[1]),dx=E.dir[0]/dl,dy=E.dir[1]/dl;
223    const Lk=out.filter(a=>a.k===k);let mn=1e9,mx=-1e9;Lk.forEach(a=>{a.pr=a.hx*dx+a.hy*dy;mn=Math.min(mn,a.pr);mx=Math.max(mx,a.pr)});
224    Lk.forEach(a=>{a.del=E.t0+(1-(a.pr-mn)/Math.max(1,mx-mn))*.95+srnd()*.25;});});
225  out.forEach((a,i)=>{
226    a.dur=1.45+srnd()*.5;a.spr=(srnd()-.5)*2;a.bn=(srnd()-.5)*.04;a.ph=srnd()*TAU;a.back=srnd();
227    a.acc=!a.edge&&!a.core&&(i%67===23);
228    a.x=0;a.y=0;a.a=a.a0;a.ox=0;a.oy=0;a.on=false;});
229  return out;
230}
231// a core agent's home in the cube geometry G (mark units)
232function cubeHome(a,G){
233  if(a.ch){const [x,y,an]=atChain(G[a.ch],a.f);a.hx=x;a.hy=y;a.a0=an;return}
234  const q=a.fc==='T'?[G.T,G.R,G.B,G.L]:a.fc==='CL'?[G.L,G.B,G.B2,G.L2]:[G.B,G.R,G.R2,G.B2],u=a.u,v=a.v;
235  // T: u along T->R, v along T->L; the sides: u along the top edge, v down
236  const [A,B,C,D]=q;
237  if(a.fc==='T'){a.hx=A[0]+(B[0]-A[0])*u+(D[0]-A[0])*v;a.hy=A[1]+(B[1]-A[1])*u+(D[1]-A[1])*v;return}
238  a.hx=A[0]*(1-u)*(1-v)+B[0]*u*(1-v)+C[0]*u*v+D[0]*(1-u)*v;a.hy=A[1]*(1-u)*(1-v)+B[1]*u*(1-v)+C[1]*u*v+D[1]*(1-u)*v;}
239
240/* ============================================================
241   THE STAGE: one canvas: a calm isometric world + the mark, made of agents
242   ============================================================ */
243
244/* ---- LOGO STYLE OPTIONS (round 5, 30 Sep 2026; ?mk=1|2|3, the default unchanged until the founder picks) ----
245   Founder: the swarm logos looked like wobbly pencil drawings, the core's hatching was denser than the rest, the halo too
246   strong. All three options: the official stroke geometry (SW centre lines at the official weight), the core sampled and
247   toned exactly like every other piece, a gentle halo.
248     1  crisp official outline (a clean stroke) + faces in neat, evenly spaced parallel rows of agents that sway along their rows
249     2  no drawn stroke: the outline is formed by dense agents in tight lanes along the smooth paths; faces fine and lighter
250     3  the clean mark at rest (outline + a very light even tint); agents only while it assembles or breaks apart
251   Small marks (under ~230 px wide) fall back to the clean mark (3's rest state) in every option. */
252const MK=Q.has('mk')&&[0,1,2,3,4,5,6].includes(+Q.get('mk'))?+Q.get('mk'):4;   // round 7: the founder picked 4 (?mk=0 the old default)
253const MK_LW=24;   // the official stroke width, in mark units (the traced logo's strokes are ~24-28)
254PARTS.forEach(P=>{if(P.id==='core')return;const pth=L=>{const p=new Path2D();L.forEach(l=>l.forEach((q,i)=>i?p.lineTo(q[0],q[1]):p.moveTo(q[0],q[1])));return p};
255  P.ep=pth(P.edges);P.rp=pth(P.rev);});
256// sp: face row spacing, es: edge spacing (mark units); lanes: agent lanes across the stroke (option 2)
257function sampleMarkMK(sp,es,lanes){
258  SEED=11;const out=[];const hw=MK_LW/2;
259  const lo=li=>lanes>1?(-hw+hw*2*(li+.5)/lanes)*.86:0;
260  const edgeC=(C,k,tone,extra)=>{const n=Math.max(2,Math.round(C.L/es));for(let li=0;li<lanes;li++)for(let j=0;j<n;j++){const f=((j+.5+(li%2)*.5)/n)%1,[x,y,a]=atChain(C,f);
261    out.push(Object.assign({k,C,f,hx:x,hy:y,a0:a,tone,edge:1,lo:lo(li)},extra||{}))}};
262  PARTS.forEach((P,k)=>{
263    if(P.id==='core'){const G1=CUBE1,G0=cubeGeo(0);
264      [['O','CE'],['S','CS'],['I','CI'],['V','CI']].forEach(([c,tn])=>edgeC(G1[c],k,tn,{core:1,ch:c}));
265      // faces: the same rows and spacing as every other piece (top: rows along the iso direction; sides: vertical rows)
266      const quad=(fc,tone,Lu,Lv,ang)=>{const n=Math.max(1,Math.round(Lu/sp)),m=Math.max(1,Math.round(Lv/sp)),mu=Math.min(.35,(hw+sp*.35)/Lu),mv=Math.min(.35,(hw+sp*.35)/Lv);
267        for(let j=0;j<m;j++)for(let i=0;i<n;i++)out.push({k,core:1,fc,u:mu+(1-2*mu)*(i+.5)/n,v:mv+(1-2*mv)*(j+.5)/m,hx:0,hy:0,a0:ang,tone,edge:0,row:fc==='T'?j:i});};
268      const E=CUBE_H,LT=Math.hypot(LZ.R[0]-LZ.T[0],LZ.R[1]-LZ.T[1]),LV=Math.hypot(LZ.L[0]-LZ.T[0],LZ.L[1]-LZ.T[1]);
269      quad('T','T',LT,LV,Math.PI/6);quad('CL','CL',E,E,Math.PI/2);quad('CR','CR',E,E,Math.PI/2);
270      out.forEach(a=>{if(a.k===k&&a.fc)cubeHome(a,G0)});
271      out.forEach(a=>{if(a.k===k&&a.fc)a.a0=a.fc==='T'?Math.PI/6:Math.PI/2});
272      return;}
273    P.edges.forEach(l=>edgeC(chain(l),k,'E'));
274    P.rev.forEach(l=>edgeC(chain(l),k,'V'));
275    P.fc.forEach(F=>{
276      const side=F.ang!==0,sg=F.ang>-Math.PI/2?1:-1,A=side?[0,1]:[C30,.5],B=side?[C30,.5*sg]:[-C30,.5],o=F.p[0],ang=Math.atan2(A[1],A[0]);
277      let x0=1e9,x1=-1e9,y0=1e9,y1=-1e9;F.p.forEach(p=>{x0=Math.min(x0,p[0]);x1=Math.max(x1,p[0]);y0=Math.min(y0,p[1]);y1=Math.max(y1,p[1])});
278      const R=Math.ceil(Math.hypot(x1-x0,y1-y0)/sp)+2;
279      for(let i=-R;i<R;i++)for(let j=-R;j<R;j++){const x=o[0]+(i+.5)*sp*A[0]+(j+.5)*sp*B[0],y=o[1]+(i+.5)*sp*A[1]+(j+.5)*sp*B[1];
280        if(x<x0||x>x1||y<y0||y>y1)continue;
281        if(inPoly(x,y,F.p)&&edgeD(x,y,F.p)>=hw+sp*.35)out.push({k,hx:x,hy:y,a0:ang,tone:F.c,edge:0,row:j});}
282    });
283  });
284  PARTS.forEach((P,k)=>{const E=ENTRY[P.id],dl=Math.hypot(E.dir[0],E.dir[1]),dx=E.dir[0]/dl,dy=E.dir[1]/dl;
285    const Lk=out.filter(a=>a.k===k);let mn=1e9,mx=-1e9;Lk.forEach(a=>{a.pr=a.hx*dx+a.hy*dy;mn=Math.min(mn,a.pr);mx=Math.max(mx,a.pr)});
286    Lk.forEach(a=>{a.del=E.t0+(1-(a.pr-mn)/Math.max(1,mx-mn))*.95+srnd()*.25;});});
287  out.forEach(a=>{a.dur=1.45+srnd()*.5;a.spr=(srnd()-.5)*2;a.bn=(srnd()-.5)*.04;a.ph=srnd()*TAU;a.back=srnd();a.acc=false;a.p=0;
288    a.x=0;a.y=0;a.a=a.a0;a.ox=0;a.oy=0;a.on=false;});
289  return out;
290}
291
292/* ---- THE SWARM OPTIONS (round 6, 30 Sep 2026; ?mk=4|5|6; round 7: the founder picked 4, now the default everywhere) ----
293   Founder on 1-3: "it needs more swarm, this looks like our logo with just some lines inside it". So in 4-6 nothing is drawn:
294   every mark is an agent, thousands of them, and the mark's shape comes from where they crowd and how they flow. The
295   outline is the official 28-unit stroke band around SW's centre lines (rounded corners and all); the core is sampled and
296   toned exactly like the rest, flat in the notch and rising into a cube of agents in the breakdown.
297     4  dense particle swarm: short dashes fill the stroke bands, drift slowly along the path and now and then swap lanes;
298        the faces hold a sparser, lighter drifting population
299     5  flowing traffic: chevron-headed agents stream along the strokes in evenly spaced lanes, both directions, each lane at
300        its own speed; the faces are hatching agents sliding along their hatch
301     6  J's swarm on the official geometry: J's orderly face hatching, the edges traced by 3 lanes of dashes (2-3x J's edge
302        agents) that crawl along the path, with arrow heads among them; no jitter
303   Sizes are in px at the stage's largest fit (g scales them down for small marks: a denser, finer swarm, never a drawn
304   outline). Every agent is drawn batched: one path per piece, tone and shape. */
305const SWM=MK>=4,SW_HW=14;   // half the official stroke width (mark units)
306TONE_BASE.E2=mix3(DKG,SGE,.8);TONE_ACTV.E2=mix3(DKG,SGE,.55);TONE_A.E2=1;TONE_OR.E2=mix3(ORG,DKG,.1);
307const SWP={
308  4:{eArea:12,rim:4.6,eL:3.3,elw:1.45,fSp:12.5,fL:3.2,flw:1.25,fa:.6,v:5,sage:.38},
309  5:{lanePx:6.8,eSp:7.6,chr:3.2,fSp:7.4,fL:3,flw:1.2,fa:.85,v:15,vs:3.2,sage:.3},
310  6:{lanes:4,eSp:7.2,eL:5.4,elw:3.2,chr:3.3,fSp:7.2,fL:4.2,flw:1.6,fa:1,v:4.5,sage:.3}};
311/* ---- option 4's strokes, sampled from the official INK (round 7, 30 Sep 2026) ----
312   Founder: the band had gaps where strokes meet (the stem stopped short of the lower V's point). Cause: each stroke's agents
313   were laid in a 28-unit band around its own centre line, so the band stopped with a square end wherever a centre line ends:
314   at every T and Y junction, the corners where a branch leaves a band, the loop's start/end, the panels' window corners.
315   Now the agents' homes come from the official ink itself (MARK_FULL rasterised, 2 px per unit): a stratified (jittered grid)
316   sample of the ink, so the band is continuous by construction, junctions and corners included. Each ink point belongs to
317   one piece: the piece whose centre line it lies across (a branch's last half-stroke, inside the band it runs into, goes to
318   that band; points no band claims, like junction wedges, go to the nearest centre line). Its flow direction is that
319   centre line's tangent. The strokes a piece shows only while apart (rev: a neighbour's shared stroke, the cuts through the
320   wing strips) are sampled the same way over their band, minus what the piece already owns, so a separated piece's cut
321   ends are closed by a full band. The rims: agents on the ink's real edge (found by marching out from the centre line), so
322   the silhouette stays the official one. Motion: each agent slides along its tangent inside the ink over a short run
323   (about a spacing each way), shrinking to a dot as it wraps; nothing ever leaves the ink, so the coverage holds while it flows. */
324let SWI=null;
325function swInk(){if(SWI)return SWI;const R=2,W=950*R,H=1000*R,cv=document.createElement('canvas');cv.width=W;cv.height=H;
326  const c=cv.getContext('2d');c.setTransform(R,0,0,R,0,0);c.fillStyle='#000';c.fill(MARK_FULL,'evenodd');
327  const d=c.getImageData(0,0,W,H).data,M=new Uint8Array(W*H);for(let i=0;i<W*H;i++)M[i]=d[i*4+3]>127?1:0;
328  const ink=(x,y)=>{const i=Math.round(x*R-.5),j=Math.round(y*R-.5);return i>=0&&j>=0&&i<W&&j<H&&M[j*W+i]===1};
329  // the centre lines: non-core pieces' own strokes (e) and their apart-only strokes (r), plus the flat lozenge (core)
330  const mk=(pts,k,rev)=>{const C=chain(pts);let x0=1e9,x1=-1e9,y0=1e9,y1=-1e9;
330pts.forEach(p=>{x0=Math.min(x0,p[0]);x1=Math.max(x1,p[0]);y0=Math.min(y0,p[1]);y1=Math.max(y1,p[1])});
331    return{k,rev,C,bb:[x0-40,x1+40,y0-40,y1+40],closed:Math.hypot(pts[0][0]-pts[pts.length-1][0],pts[0][1]-pts[pts.length-1][1])<1}};
332  const E=[],RV=[];PARTS.forEach((P,k)=>{if(P.id==='core')return;P.edges.forEach(l=>E.push(mk(l,k,0)));P.rev.forEach(l=>RV.push(mk(l,k,1)))});
333  const G0=cubeGeo(0),CO=[mk(G0.O.pts,PI.core,0)],FP=PARTS[PI.frame].faces[0].p;
334  // nearest point of a chain: distance, arc position, tangent angle
335  const near=(ch,x,y)=>{const P=ch.C.pts,c=ch.C.c;let bd=1e9,bs=0,ba=0;for(let i=1;i<P.length;i++){const a=P[i-1],b=P[i],dx=b[0]-a[0],dy=b[1]-a[1],l2=dx*dx+dy*dy||1e-9,
336      q=clamp(((x-a[0])*dx+(y-a[1])*dy)/l2,0,1),ex=x-a[0]-dx*q,ey=y-a[1]-dy*q,dd=ex*ex+ey*ey;if(dd<bd){bd=dd;bs=c[i-1]+(c[i]-c[i-1])*q;ba=Math.atan2(dy,dx)}}
337    return{d:Math.sqrt(bd),s:bs,a:ba}};
338  const inBB=(ch,x,y)=>x>=ch.bb[0]&&x<=ch.bb[1]&&y>=ch.bb[2]&&y<=ch.bb[3];
339  // who owns an ink point: -1 not ink, PI.core the lozenge's band (the core's own agents cover it), else the piece + tangent
340  const own=(x,y)=>{if(!ink(x,y))return null;
341    let bi=null,bo=null;for(const ch of E){if(!inBB(ch,x,y))continue;const r=near(ch,x,y);r.ch=ch;
342      if(!bo||r.d<bo.d)bo=r;const Z=ch.closed?0:SW_HW;if(r.d<SW_HW+2&&r.s>=Z&&r.s<=ch.C.L-Z&&(!bi||r.d<bi.d))bi=r;}
343    // a point across a piece's own stroke band is that piece's. Otherwise, the lozenge's arch (the flat core shows only its
344    // top edges O, clipped to outside the frame's silhouette) is the core's where O is the nearest centre line
345    if(!(bi&&bi.d<SW_HW)&&!inPoly(x,y,FP))for(const ch of CO){if(inBB(ch,x,y)&&near(ch,x,y).d<(bo?bo.d:1e9))return{k:PI.core}}
346    const b=bi||bo;return b?{k:b.ch.k,a:b.a,d:b.d}:null};
347  const revD=(k,x,y)=>{let b=null;for(const ch of RV){if(ch.k!==k||!inBB(ch,x,y))continue;const r=near(ch,x,y);if(!b||r.d<b.d)b=r}return b};
348  return SWI={ink,own,revD,E,RV};}
349function swInkAgents(out,P0,u,g,sR,mul,G0){
350  const I=swInk(),hw=SW_HW,m=u(P0.elw)*.6,sp=Math.sqrt(P0.eArea*g*g/(sR*sR)*mul),vel0=u(P0.v)/g,Rm=sp*1.1;out.esp=sp;
351  const add=(k,x,y,an,rev,rim,runOK,endOK)=>{
352    // the run along the tangent: as far as the ink (or the apart-only band) reaches, up to Rm each way
353    const cx=Math.cos(an),sn=Math.sin(an),lim=rim?Math.min(Rm,u(P0.rim)*mul):Rm;let ra=0,rb=0;
354    for(const sd of[-1,1]){let r=0;while(r<lim&&runOK(x+cx*(r+1)*sd,y+sn*(r+1)*sd))r+=1;
355      // the run's far end must still be this piece's (checked once, not per step)
356      for(let i=0;i<3&&r>0&&endOK&&!endOK(x+cx*r*sd,y+sn*r*sd);i++)r=Math.floor(r*.5);if(sd<0)ra=r;else rb=r;}
357    out.push({k,hx:x,hy:y,bx:x,by:y,a0:an,b0:an,ra,rb,tone:rev?'V':'E',edge:1,mk:1,sh:0,dir:1,lo:0,v:srnd()<(rim?.15:P0.sage)?1:0,
358      vel:vel0*(rim?.8:(.7+srnd()*.6)),rim:rim?1:0});};
359  // 1) the fill: one jittered point per sp x sp cell of the whole mark; ink points go to their owner (not the core's band)
360  for(let y=22;y<980;y+=sp)for(let x=8;x<942;x+=sp){const px=x+(.12+.76*srnd())*sp,py=y+(.12+.76*srnd())*sp,o=I.own(px,py);
361    if(!o||o.k===PI.core)continue;const k=o.k;add(k,px,py,o.a,0,0,I.ink,(qx,qy)=>{const q=I.own(qx,qy);return q&&q.k===k});}
362  // 2) the rims: from points along each centre line, march out both ways to the ink's edge; an agent just inside it
363  const rs=u(P0.rim)*mul;
364  for(const ch of I.E){const n=Math.max(2,Math.round(ch.C.L/rs));for(let j=0;j<n;j++){const [x,y,an]=atChain(ch.C,(j+.5)/n),nx=-Math.sin(an),ny=Math.cos(an);
365    for(const sd of[-1,1]){let r=0;while(r<hw+6&&I.ink(x+nx*sd*(r+.5),y+ny*sd*(r+.5)))r+=.5;if(r>=hw+6||r<m+1)continue;
366      const px=x+nx*sd*(r-m),py=y+ny*sd*(r-m),o=I.own(px,py);if(!o||o.k!==ch.k)continue;
367      add(ch.k,px,py,an,0,1,(qx,qy)=>I.ink(qx,qy)&&!I.ink(qx+nx*sd*(m+1.5),qy+ny*sd*(m+1.5)),(qx,qy)=>{const q=I.own(qx,qy);return q&&q.k===ch.k});}}}
368  // 3) the apart-only strokes: the band around each r line (on the ink: the ink it covers, a little wider to meet the piece's
369  //    own ink; off the ink, the cuts: 28 units), minus what the piece owns; rims along both sides
370  const inRev=(k,x,y)=>{const r=I.revD(k,x,y);if(!r)return null;const ik=I.ink(x,y);if(r.d>=(ik?hw+2.5:hw))return null;if(ik){const o=I.own(x,y);if(o&&o.k===k)return null}return r};
371  PARTS.forEach((P,k)=>{if(P.id==='core')return;const R=I.RV.filter(c=>c.k===k);if(!R.length)return;
372    let x0=1e9,x1=-1e9,y0=1e9,y1=-1e9;R.forEach(c=>{x0=Math.min(x0,c.bb[0]+24);x1=Math.max(x1,c.bb[1]-24);y0=Math.min(y0,c.bb[2]+24);y1=Math.max(y1,c.bb[3]-24)});
373    for(let y=y0;y<y1;y+=sp)for(let x=x0;x<x1;x+=sp){const px=x+(.12+.76*srnd())*sp,py=y+(.12+.76*srnd())*sp,r=inRev(k,px,py);if(!r)continue;add(k,px,py,r.a,1,0,(qx,qy)=>!!inRev(k,qx,qy));}
374    for(const ch of R){const n=Math.max(2,Math.round(ch.C.L/rs));for(let j=0;j<n;j++){const [x,y,an]=atChain(ch.C,(j+.5)/n),nx=-Math.sin(an),ny=Math.cos(an);
375      for(const sd of[-1,1]){const px=x+nx*sd*(hw-m),py=y+ny*sd*(hw-m);if(!inRev(k,px,py))continue;add(k,px,py,an,1,1,(qx,qy)=>!!inRev(k,qx,qy));}}}
376  });
377}
378function sampleSwarm(opt,g,sR,cap,mul){
379  SEED=13;const out=[],P0=SWP[opt],u=v=>v*g/sR,hw=SW_HW;mul=mul||1;
380  const G0=cubeGeo(0),chs=[];
381  PARTS.forEach((P,k)=>{if(P.id==='core'){[['O','CE'],['S','CS'],['I','CI'],['V','CI']].forEach(([c,tn])=>chs.push({k,core:1,ch:c,L:CUBE1[c].L,tone:tn}));return}
382    P.edges.forEach(l=>{const C=chain(l);chs.push({k,C,L:C.L,tone:'E'})});P.rev.forEach(l=>{const C=chain(l);chs.push({k,C,L:C.L,tone:'V'})});});
383  const home0=(c,f)=>atChain(c.core?G0[c.ch]:c.C,f);
384  const push=(c,f0,lo0,extra)=>{const [x,y,an]=home0(c,f0);out.push(Object.assign({k:c.k,C:c.C,core:c.core,ch:c.ch,Lc:c.L,f0,lo0,hx:x-Math.sin(an)*lo0,hy:y+Math.cos(an)*lo0,a0:an,tone:c.tone,edge:1,sh:0,v:0,dir:1,vel:0,lo:0},extra))};
385  chs.forEach((c,ci)=>{
386    if(opt===4){if(!c.core)return;   // option 4's strokes (all but the core's): sampled from the official ink itself, below
387      const m=u(P0.elw)*.6,wB=2*(hw-m),n=Math.max(2,Math.round(c.L*wB*sR*sR/(P0.eArea*g*g)/mul));
388      for(let j=0;j<n;j++)push(c,(j+srnd())/n,(srnd()*2-1)*(hw-m),{v:srnd()<P0.sage?1:0,vel:u(P0.v)/g*(.7+srnd()*.6),tsw:5+srnd()*9,m});
389      // the rims: two files of agents walk the band's edges in order, so the silhouette stays the official one, crisp
390      const nr=Math.max(2,Math.round(c.L/(u(P0.rim)*mul)));for(const sd of[-1,1])for(let j=0;j<nr;j++)push(c,((j+(sd>0?.5:0)+srnd()*.3)/nr)%1,sd*(hw-m),{v:srnd()<.15?1:0,vel:u(P0.v)/g*.8,tsw:0,m,rim:1});}
391    else if(opt===5){const nl=clamp(Math.round(2*hw*sR/(P0.lanePx*g)),2,4),sp=u(P0.eSp)*mul;
392      for(let li=0;li<nl;li++){const lo=(-hw+2*hw*(li+.5)/nl)*.9,dir=li%2?-1:1,vel=u(P0.v)/g*(1+(hash(li+ci*.37,3.1)-.5)*.3),n=Math.max(2,Math.round(c.L/sp)),outer=li===0||li===nl-1;
393        for(let j=0;j<n;j++)push(c,((j+.5+(li%2)*.5)/n)%1,lo,{sh:1,dir,vel,v:srnd()<(outer?.12:.5)?1:0});}}
394    else{const nl=clamp(Math.round(2*hw*.88*sR/(P0.elw*g*1.4)),2,P0.lanes),sp=u(P0.eSp)*mul;
395      for(let li=0;li<nl;li++){const lo=(-hw+2*hw*(li+.5)/nl)*.88,n=Math.max(2,Math.round(c.L/sp));
396        for(let j=0;j<n;j++)push(c,((j+.5+li/nl)/n)%1,lo,{sh:(j+li*3)%8===5?1:0,vel:u(P0.v)/g,v:srnd()<(li>0&&li<nl-1?.5:.1)?1:0});}}
397  });
398  if(opt===4)swInkAgents(out,P0,u,g,sR,mul,G0);
399  // faces
400  const fsp=u(P0.fSp)*Math.sqrt(mul);
401  PARTS.forEach((P,k)=>{
402    if(P.id==='core'){
403      const quad=(fc,tone,Lu,Lv,ang)=>{const n=Math.max(1,Math.round(Lu/fsp)),m=Math.max(1,Math.round(Lv/fsp)),mu=Math.min(.35,(hw+fsp*.35)/Lu),mv=Math.min(.35,(hw+fsp*.35)/Lv);
404        for(let j=0;j<m;j++)for(let i=0;i<n;i++){const ji=opt===4?(srnd()-.5)*.8:0,jj=opt===4?(srnd()-.5)*.8:0;
405          out.push({k,core:1,fc,u:mu+(1-2*mu)*clamp((i+.5+ji)/n,0,1),v:0,vv:mv+(1-2*mv)*clamp((j+.5+jj)/m,0,1),hx:0,hy:0,bx:0,by:0,a0:ang,b0:ang,tone,edge:0,sh:0,row:fc==='T'?j:i,lo:0});}};
406      const E=CUBE_H,LT=Math.hypot(LZ.R[0]-LZ.T[0],LZ.R[1]-LZ.T[1]),LV=Math.hypot(LZ.L[0]-LZ.T[0],LZ.L[1]-LZ.T[1]);
407      const tA=opt===6?0:Math.PI/6;quad('T','T',LT,LV,tA);quad('CL','CL',E,E,Math.PI/2);quad('CR','CR',E,E,Math.PI/2);
408      out.forEach(a=>{if(a.k===k&&a.fc){a.v=a.vv;const uu=a.u;cubeHome(a,G0);a.bx=a.hx;a.by=a.hy;a.v=0;a.u=uu}});
409      return;}
410    P.fc.forEach(F=>{
411      const side=F.ang!==0,sg=F.ang>-Math.PI/2?1:-1,A=side?[0,1]:[C30,.5],B=side?[C30,.5*sg]:[-C30,.5],o=F.p[0];
412      const sp=opt===6?fsp/Math.sqrt(DENS[F.c]):fsp,ang=opt===6?F.ang:Math.atan2(A[1],A[0]);
413      let x0=1e9,x1=-1e9,y0=1e9,y1=-1e9;F.p.forEach(p=>{x0=Math.min(x0,p[0]);x1=Math.max(x1,p[0]);y0=Math.min(y0,p[1]);y1=Math.max(y1,p[1])});
414      const R=Math.ceil(Math.hypot(x1-x0,y1-y0)/sp)+2;let n=0;
415      for(let i=-R;i<R;i++)for(let j=-R;j<R;j++){let x=o[0]+(i+.5)*sp*A[0]+(j+.5)*sp*B[0],y=o[1]+(i+.5)*sp*A[1]+(j+.5)*sp*B[1];
416        if(opt===4){x+=(srnd()-.5)*sp*.7;y+=(srnd()-.5)*sp*.7;}
417        if(x<x0||x>x1||y<y0||y>y1)continue;
418        if(inPoly(x,y,F.p)&&edgeD(x,y,F.p)>=hw+sp*.4){n++;out.push({k,hx:x,hy:y,bx:x,by:y,a0:ang,b0:ang,tone:F.c,edge:0,sh:opt===6&&n%67===23?1:0,row:j,lo:0});}}
419    });
420  });
421  if(out.length>cap*1.05&&mul<8)return sampleSwarm(opt,g,sR,cap,mul*out.length/cap);
422  PARTS.forEach((P,k)=>{const E=ENTRY[P.id],dl=Math.hypot(E.dir[0],E.dir[1]),dx=E.dir[0]/dl,dy=E.dir[1]/dl;
423    const Lk=out.filter(a=>a.k===k);let mn=1e9,mx=-1e9;Lk.forEach(a=>{a.pr=a.hx*dx+a.hy*dy;mn=Math.min(mn,a.pr);mx=Math.max(mx,a.pr)});
424    Lk.forEach(a=>{a.del=E.t0+(1-(a.pr-mn)/Math.max(1,mx-mn))*.95+srnd()*.25;});});
425  out.forEach((a,i)=>{a.or=hash(i*.0173+.31,9.13)<OR_FRAC?1:0;a.id=i;a.dur=1.45+srnd()*.5;a.spr=(srnd()-.5)*2;a.bn=(srnd()-.5)*.04;a.ph=srnd()*TAU;a.back=srnd();a.acc=false;a.p=0;a.rp=srnd();
426    a.x=0;a.y=0;a.a=a.a0;a.ox=0;a.oy=0;a.on=false;});
427  out.fsp=fsp;return out;
428}
429// an agent's home right now (mark units): edges flow along their chain (the core's from the cube as it is now), faces drift
430function swHome(a,t,G,opt,fsp){
431  if(a.mk){const D=a.ra+a.rb;let d=0,ls=1;if(D>
431.5&&!RM){const q=((a.ph/TAU+t*a.vel/D)%1+1)%1;d=-a.ra+q*D;ls=Math.min(1,Math.sin(Math.PI*q)*2.4);}
432    a.hx=a.bx+Math.cos(a.b0)*d;a.hy=a.by+Math.sin(a.b0)*d;a.a0=a.b0;a.ls=ls;return}
433  if(a.edge){const C=a.core?G[a.ch]:a.C;let f=a.f0+a.dir*a.vel*t/a.Lc;f-=Math.floor(f);let lo=a.lo0;
434    if(opt===4&&!a.rim){const T=a.tsw,q=t/T+a.ph,i0=Math.floor(q),fr=q-i0,lim=SW_HW-a.m,r=i=>(hash(a.id*.0131+i*1.71,a.id*.0077+.3)*2-1)*lim;
435      lo=clamp(lerp(r(i0),r(i0+1),ss(.72,1,fr))+Math.sin(t*.9+a.ph*5)*.8,-lim,lim);}
436    const [x,y,an]=atChain(C,f);a.hx=x-Math.sin(an)*lo;a.hy=y+Math.cos(an)*lo;
437    a.a0=an+(a.dir<0?Math.PI:0)+(opt===4&&!a.rim?Math.sin(t*.7+a.ph)*.16:0);return}
438  if(a.core){const u=a.u,v=a.vv;a.v=v;cubeHome(a,G);a.v=0;a.bx=a.hx;a.by=a.hy;}
439  const b=a.b0;let d=0,px=0,py=0;
440  if(opt===4){px=Math.sin(t*.31+a.ph)*fsp*.3;py=Math.cos(t*.27+a.ph*1.3)*fsp*.3;a.a0=b+Math.sin(t*.4+a.ph)*.35;}
441  else if(opt===5){d=((t*.35+hash(a.row*.73+a.k,a.k*1.9+(a.fc?5:0)))%1-.5)*fsp;a.a0=b;}   // a row slides one spacing and repeats: the lattice looks like it flows
442  else{d=Math.sin(t*.8+a.row*.45+a.k*1.7)*fsp*.14;a.a0=b;}
443  a.hx=a.bx+Math.cos(b)*d+px;a.hy=a.by+Math.sin(b)*d+py;
444}
445
446
447/* ============================================================
448   THE STAGE: one canvas, a calm isometric world on cream + the official mark (or one piece of it), made of agents.
449   Website L, round 3: J's swarm (agents trace the outlines, hatch the faces, stream in) on the traced OFFICIAL geometry
450   (SW in p1-helpers.js), so the silhouette and its rounded corners are the official mark's. The core is the lozenge in the
451   notch of the top V band: flat and in place when the mark is whole; while the pieces are apart it rises from behind the
452   band and extrudes into a small cube (this.rise), and sinks back and flattens when they come together.
453   Options:
454     fits(W,H,mob,S)   named placements of the mark: {A, E?, B?, C?} (from FB(box, region))
455     phase(f)          scroll phase -> {e: how far apart, w: weight per step, fit: [from, to, k]}
456     only              a part id: draw just that piece (the core: as its cube)
457     cards             the breakdown labels (HTML links), placed beside their pieces
458   ============================================================ */
459const FB=(b,r)=>{const s=Math.min((r[2]-r[0])/(b[1]-b[0]),(r[3]-r[1])/(b[3]-b[2]));return{s,ox:(r[0]+r[2])/2-(b[0]+b[1])/2*s,oy:(r[1]+r[3])/2-(b[2]+b[3])/2*s,b}};
460// the same placement at another scale, around the same centre
461const rescale=(F,s)=>{const b=F.b,cx=F.ox+(b[0]+b[1])/2*F.s,cy=F.oy+(b[2]+b[3])/2*F.s;return{s,ox:cx-(b[0]+b[1])/2*s,oy:cy-(b[2]+b[3])/2*s,b}};
462const partBox=(id,m)=>{const P=PARTS[PI[id]],xs=P.faces.flatMap(F=>F.p.map(p=>p[0])),ys=P.faces.flatMap(F=>F.p.map(p=>p[1]));
463  return[Math.min(...xs)-m,Math.max(...xs)+m,Math.min(...ys)-m,Math.max(...ys)+m]};
464const PHASE0={e:0,w:[0,0,0,0,0,0],fit:['A','A',0]};
465// stack labels on one side without overlaps, keeping each as close to where it wants to be as the room allows
466function stack(L,top,bot,gap){let c=top;for(const i of L){i.y=Math.max(i.y,c);c=i.y+i.h+gap;}
467  let lim=bot;for(let j=L.length-1;j>=0;j--){const i=L[j];i.y=Math.min(i.y,lim-i.h);lim=i.y-gap;}
468  c=top;for(const i of L){i.y=Math.max(i.y,c);c=i.y+i.h+gap;}}
469// the x-extent of a polygon inside a horizontal band [y0,y1]
470function bandX(q,y0,y1,cb){for(let i=0;i<q.length;i++){const a=q[i],b=q[(i+1)%q.length];
471  const lo=Math.min(a[1],b[1]),hi=Math.max(a[1],b[1]);if(hi<y0||lo>y1)continue;
472  if(Math.abs(b[1]-a[1])<1e-6){cb(a[0]);cb(b[0]);continue}
473  const ya=clamp(y0,lo,hi),yb=clamp(y1,lo,hi);[ya,yb].forEach(y=>cb(a[0]+(b[0]-a[0])*(y-a[1])/(b[1]-a[1])));}}
474
475class Stage{
476  constructor(cv,o){this.cv=cv;this.o=o;this.t=0;this.f=0;this.fT=0;this.e=0;this.mouse=null;this.mt=-1e9;this.cur=0;this.shiftY=0;this.hot=null;
477    this.started=!o.lazy;this.st=PARTS.map(()=>({act:0,sc:1,hv:0,tk:0,or:0}));this.lift=0;this.rise=o.only==='core'?1:0;this.G=cubeGeo(this.rise);this.ph=PHASE0;this.resize();}
478  resize(){
479    const s=setupCanvas(this.cv);Object.assign(this,s);this.mob=this.W<760;this.below=this.W<1024;this.ok=this.mob?.7:1;
480    const o=this.o,W=this.W,H=this.H,mob=this.mob;
481    if(o.cards){if(this.below)o.cards.forEach(el=>{el.style.width='';el.style.transform='';el._w=el._tr=null});this.measureCards();}
482    this.FT=o.fits(W,H,mob,this);
483    this.applyFit();
484    // the drawing's centre and size in mark units (the halo and the calm patch of world sit under it)
485    if(o.only){const b=partBox(o.only,0);this.mcU=[(b[0]+b[1])/2,(b[2]+b[3])/2,Math.max(b[1]-b[0],b[3]-b[2])*1.1];}else this.mcU=[473,520,946];
486    // the world: fixed blocks, drawn once into a cache
487    const u=o.unit(W,H,mob);this.u=u;this.P={u,ox:W*.5,oy:H*.5-5*u+(o.oyAdj?o.oyAdj(this,u):0)};
488    const FA=this.FT.A,plinthScr=o.plinth?o.plinth.map(p=>[FA.ox+p[0]*FA.s,FA.oy+p[1]*FA.s]):null;
489    const calm=Object.values(this.FT).map(F=>[F.ox+this.mcU[0]*F.s,F.oy+this.mcU[1]*F.s,this.mcU[2]*F.s*.62]);
490    this.cells=[];this.grid=new Map();
491    for(let i=-80;i<90;i++)for(let j=-80;j<90;j++){
492      const sx=this.P.ox+(i-j)*u*C30,sy=this.P.oy+(i+j+1)*u*.5;
493      if(sx<-u*2||sx>W+u*2||sy<-u*1.5||sy>H+u*4)continue;
494      const r=hash(i,j);let base=r<.76?0:r<.9?.25:r<.97?.5:r<.993?1:1.5;
495      let f=o.flat?o.flat(sx,sy,W,H,mob):1,q=1;
496      // near the mark the world goes flat and its grid lines fade right down, so the swarm is read against a quiet plate
497      for(const [x,y,R] of calm){const d=Math.hypot((sx-x)*.8,sy-y);f*=ss(R*.8,R*1.35,d);q=Math.min(q,ss(R*.55,R*1.25,d));}
498      base*=f;
499      const plinth=!!plinthScr&&inPoly(sx,sy,plinthScr);if(plinth)base=0;
500      const c={i,j,base,h:base,lift:0,plinth,gs:plinth?`rgba(18,29,26,${(.03).toFixed(3)})`:`rgba(18,29,26,${(.085*(.2+.8*q)).toFixed(3)})`};this.cells.push(c);this.grid.set(i*1000+j,c);
501    }
502    this.cells.sort((a,b)=>(a.i+a.j)-(b.i+b.j));
503    if(!this.wc)this.wc=document.createElement('canvas');
504    this.wc.width=this.cv.width;this.wc.height=this.cv.height;
505    const wx=this.wc.getContext('2d');wx.setTransform(this.dpr,0,0,this.dpr,0,0);this.drawWorld(wx,true);
506    // agents: as many as it takes to read the mark at this size (the lattice rounding adds ~29%, so the real count lands on n)
507    const n=o.count?o.count(this):mob?o.nMob:Math.round(clamp(946*FA.s*3.4,o.nMin,o.nMax));
508    let mkKey=null;if(SWM){const sR=Math.max(...Object.values(this.FT).map(F=>F.s));this.swsR=sR;
508this.swg=clamp(946*sR/760,this.mob?.55:.42,1);
509      mkKey='sw'+MK+':'+Math.round(sR*1000)+':'+(this.mob?1:0);}
510    else if(MK){const sR=Math.max(...Object.values(this.FT).map(F=>F.s)),mob=this.mob;
511      this.spU=(MK===2?(mob?6:7.2):(mob?6.4:8))/sR;this.esU=(MK===2?(mob?2.6:3.2):9)/sR;this.lanes=MK===2?clamp(Math.round(MK_LW*sR/6),1,3):1;mkKey='mk'+Math.round(this.spU*10)+':'+Math.round(this.esU*10)+':'+this.lanes;}
512    if(o.noMark){this.agents=[];this.bk=[];}
513    else if(REFM)this.refInit();
514    else if(!this.agents||this.nWant!==(mkKey||n)){this.nWant=mkKey||n;let A=SWM?sampleSwarm(MK,this.swg,this.swsR,this.mob?4200:9800):MK?sampleMarkMK(this.spU,this.esU,this.lanes):sampleMark(Math.round(n/1.29),o.edgeFrac||.42);
515      if(o.only){const k=PI[o.only],t0=ENTRY[o.only].t0-.1;const esp=A.esp,fsp=A.fsp;A=A.filter(a=>a.k===k);A.esp=esp;A.fsp=fsp;A.forEach(a=>a.del-=t0);}
516      this.agents=A;if(SWM)this.swFsp=A.fsp||8;this.bucket();}
517    if(o.cards)this.cardW();
518    if(o.onResize)o.onResize(this);
519  }
520  bucket(){const m=new Map();this.acc=[];
521    if(SWM){for(const a of this.agents){const key=a.k+':'+a.tone+':'+(a.v|0)+':'+a.sh+':'+a.edge+':'+(a.or|0);if(!m.has(key))m.set(key,{k:a.k,tone:a.tone,v:a.v|0,sh:a.sh,edge:a.edge,or:a.or|0,list:[]});m.get(key).list.push(a);}
522      this.bk=[...m.values()].sort((a,b)=>(a.edge-b.edge)||(a.sh-b.sh));return}
523    for(const a of this.agents){if(a.acc){this.acc.push(a);continue}const key=a.k+':'+a.tone;if(!m.has(key))m.set(key,{k:a.k,tone:a.tone,list:[]});m.get(key).list.push(a);}
524    const ord={r:0,l:0,R:1,L:1,CL:1,CR:1,U:2,T:2,V:3,CI:3,E:4,CS:4,CE:4};
525    this.bk=[...m.values()].sort((a,b)=>ord[a.tone]-ord[b.tone]);}
526  // sy: on the landing hero the mark rides up with the first bit of scroll (as G), only while it is in its hero place A
527  applyFit(){const [a,b,k]=this.ph.fit,A=this.FT[a]||this.FT.A,B=this.FT[b]||A,sy=this.sy||0;
528    this.s=lerp(A.s,B.s,k);this.ox=lerp(A.ox,B.ox,k);this.oy=lerp(A.oy-(a==='A'?sy:0),B.oy-(b==='A'?sy:0),k)+this.shiftY;}
529  PX(p,P,e,sc){return this.ox+(P.cx+(p[0]-P.cx)*sc+P.off[0]*e*this.ok)*this.s}
530  PY(p,P,e,sc){return this.oy+(P.cy+(p[1]-P.cy)*sc+P.off[1]*e*this.ok)*this.s}
531  update(dt){
532    if(!this.started)return;
533    const o=this.o;
534    if(!RM)this.t+=dt;const t=RM?INTRO_END+5:this.t;const K=(typeof o.introK==='function'?o.introK(this):o.introK)||1;const eD=o.entryD?o.entryD(this):0;
535    this.f+=(this.fT-this.f)*(RM||dt===0?1:Math.min(1,dt*10));if(Math.abs(this.fT-this.f)<.0005)this.f=this.fT;
536    this.ph=o.phase?o.phase(this.f):PHASE0;this.e=this.ph.e;this.applyFit();
537    // the active part grows a little and its agents turn to the strong sage/dark green; a part whose label is hovered does so a little
538    const kh=RM||dt===0?1:Math.min(1,dt*8);
539    PARTS.forEach((P,k)=>{const Z=this.st[k];
540      if(o.only){Z.act=P.id===o.only?1:0;Z.tk=P.id===o.only?.35:0;Z.sc=1;Z.or=0;return}   // a page's own piece: a touch stronger than at rest, not the full highlight
541      let act=0;for(let q=1;q<=NSTEP;q++){if(STEP_PART[q]===P.id)act=Math.max(act,this.ph.w[q]);}
542      Z.act=act*ss(.35,.8,this.e);Z.sc=1+GROW*easeIO(Z.act);
543      Z.hv+=((this.hot===P.id&&this.e>.5?1:0)-Z.hv)*kh;Z.tk=Math.max(Z.act,Z.hv*.5);Z.or=Math.max(Z.act,Z.hv*.75);});
544    // the core: flat lozenge while whole, a cube while apart (scroll-driven, with the pieces' separation)
545    this.rise=o.only?(o.only==='core'?1:0):easeIO(ss(.04,.62,this.e));this.G=cubeGeo(this.rise);
546    // cursor
547    const now=performance.now(),mOn=!RM&&this.mouse&&now-this.mt<2500;
548    this.cur=mOn?1:Math.max(0,this.cur-dt*.8);
549    if(!RM){const u=this.u;let mi=0,mj=0;if(mOn){const a=(this.mouse[0]-this.P.ox)/(u*C30),b=(this.mouse[1]-this.P.oy)/(u*.5);mi=(a+b)/2;mj=(b-a)/2;}
550      let any=0;const kk=Math.min(1,dt*5);
551      if(mOn||this.lift>0)for(const c of this.cells){let tg=0;if(mOn&&!c.plinth){const d=Math.hypot(c.i+.5-mi,c.j+.5-mj);if(d<3)tg=Math.pow(1-d/3,1.6)*1.1;}
552        c.lift+=(tg-c.lift)*kk;if(c.lift<.002)c.lift=0;c.h=c.base+c.lift;any=Math.max(any,c.lift);}
553      this.lift=any;}
554    if(o.noMark)return;
555    if(REFM){this.refUpdate(dt);return}
556    // agents
557    const s=this.s,e=this.e,W=this.W,H=this.H,ox=this.ox,oy=this.oy,G=this.G;
558    if(MK){const de=Math.abs(e-(this._pe==null?e:this._pe));this._pe=e;const tg=RM||dt<=0?0:clamp(de/dt*2.5,0,1);this.mv=(this.mv||0);this.mv+=(tg-this.mv)*Math.min(1,dt*(tg>this.mv?8:2.2));if(this.mv<.005)this.mv=0;
559      this.arrN=PARTS.map(()=>0);this.arrC=PARTS.map(()=>0);}
560    const cx=ox+this.mcU[0]*s,cy=oy+(o.only?this.mcU[1]:500)*s;const jit=RM?0:.6;const R=this.mob?70:110;const kc=Math.min(1,dt*6);
561    for(const a of this.agents){const P=PARTS[a.k],sc=this.st[a.k].sc;
562      if(SWM)swHome(a,t,G,MK,this.swFsp);else if(a.core)cubeHome(a,G);
563      if(MK===3&&a.C&&!a.core&&this.mv>.01&&!RM){const r=atChain(a.C,(a.f+t*.07)%1);a.hx=r[0];a.hy=r[1];a.a0=r[2];}
564      const lo=a.lo||0,hx=a.hx-Math.sin(a.a0)*lo,hy=a.hy+Math.cos(a.a0)*lo;
565      const tx=ox+(P.cx+(hx-P.cx)*sc+P.off[0]*e*this.ok)*s,ty=oy+(P.cy+(hy-P.cy)*sc+P.off[1]*e*this.ok)*s;
566      const p=(RM||o.noIntro)?1:clamp((t-a.del*K)/(a.dur*K),0,1);
567      a.p=p;if(MK){this.arrN[a.k]++;if(p>=1)this.arrC[a.k]++;}
568      if(p<=0){a.on=false;continue}
569      a.on=true;let x,y,ang;
570      if(p>=1&&SWM){x=tx;y=ty;ang=a.a0;}
571      else if(p>=1&&MK){let wv=0;if(!a.edge&&!RM)wv=Math.sin(t*.8+a.row*.45+a.k*1.7)*this.spU*s*.16;x=tx+Math.cos(a.a0)*wv;y=ty+Math.sin(a.a0)*wv;ang=a.a0;}
572      else if(p>=1){x=tx+Math.sin(t*1.3+a.ph)*jit;y=ty+Math.cos(t*1.1+a.ph*1.3)*jit;ang=a.a0+(RM?0:Math.sin(t*.9+a.ph)*.05);}
573      else{
574        const E=ENTRY[P.id],dl=Math.hypot(E.dir[0],E.dir[1]),dx0=E.dir[0]/dl,dy0=E.dir[1]/dl;
575        const D=eD?eD*(1+a.back*.35):Math.max(W,H)*.75+a.back*Math.max(W,H)*.25;
576        const sx=cx+dx0*D-dy0*a.spr*26,sy=cy+dy0*D+dx0*a.spr*26;
577        const vx=tx-sx,vy=ty-sy,len=Math.hypot(vx,vy)+1,nx=-vy/len,ny=vx/len,bow=len*(E.bow+a.bn);
578        const mx=(sx+tx)/2+nx*bow,my=(sy+ty)/2+ny*bow;
579        const q=1-Math.pow(1-p,2.6),iq=1-q;
580        const wig=Math.sin(p*7+a.ph)*6*iq;
581        x=iq*iq*sx+2*iq*q*mx+q*q*tx+nx*wig;y=iq*iq*sy+2*iq*q*my+q*q*ty+ny*wig;
582        const ddx=2*iq*(mx-sx)+2*q*(tx-mx),ddy=2*iq*(my-sy)+2*q*(ty-my),am=Math.atan2(ddy,ddx);
583        const w=ss(.62,1,p);ang=am+((a.acc||a.sh)?angWrap(a.a0-am):lineWrap(a.a0-am))*w;
584      }
585      if(this.cur>0){let gx=0,gy=0;if(mOn){const dx=x-this.mouse[0],dy=y-this.mouse[1],d=Math.hypot(dx,dy);
586          if(d<R&&d>.5){const k=Math.pow(1-d/R,2);gx=(dx/d*13-dy/d*4)*k;gy=(dy/d*13+dx/d*4)*k;}}
587        a.ox+=(gx-a.ox)*kc;a.oy+=(gy-a.oy)*kc;}
588      else{a.ox=0;a.oy=0;}
589      a.x=x+a.ox;a.y=y+a.oy;a.a=ang;
590    }
591  }
592  drawWorld(ctx,base){
593    ctx.fillStyle=WORLD_BG;ctx.fillRect(0,0,this.W,this.H);
594    ctx.lineWidth=1;const gs=this.o.gridStroke,P0=this.P;
595    for(const c of this.cells){const h=base?c.base:c.h;
596      const nl=this.grid.get(c.i*1000+c.j+1),nr=this.grid.get((c.i+1)*1000+c.j);
597      const hl=nl?(base?nl.base:nl.h):-1,hr=nr?(base?nr.base:nr.h):-1;
598      box(ctx,P0,c.i,c.j,-3,1,1,3+h,c.plinth?P_PLINTH:(!base&&c.lift>.15?P_LIFT:P_GROUND),c.gs||gs,hl>=h,hr>=h);
599    }
600  }
601  // a soft cream halo under the drawing, wherever it is right now: lowers the contrast of the world where the swarm is read
602  drawHalo(ctx){if(this.o.halo===false)return;const hk=this.o.haloK?this.o.haloK(this):1;if(hk<.01)return;const s=this.s,x=this.ox+this.mcU[0]*s,y=this.oy+this.mcU[1]*s,r=this.mcU[2]*s*.62;
603    if(!(r>0)||!isFinite(x+y))return;
603 // a zero-size or collapsed stage (tiny window, mid-resize): nothing to draw
604    const g=ctx.createRadialGradient(x,y,r*.1,x,y,r*1.05);
605    g.addColorStop(0,'rgba(239,231,218,.9)');g.addColorStop(.55,'rgba(239,231,218,.7)');g.addColorStop(1,'rgba(239,231,218,0)');
606    ctx.save();const hk2=MK?hk*.38:hk;if(hk2<1)ctx.globalAlpha=hk2;ctx.translate(x,y);ctx.scale(1.25,1);ctx.translate(-x,-y);ctx.fillStyle=g;ctx.fillRect(x-r*1.2,y-r*1.1,r*2.4,r*2.2);ctx.restore();}
607  // a piece's silhouette on screen now (the core: its lozenge or cube as it is right now)
608  partPolys(k){const P=PARTS[k],e=this.e,sc=this.st[k].sc,F=P.id==='core'?[{p:this.G.hex}]:P.faces;return F.map(F=>F.p.map(p=>[this.PX(p,P,e,sc),this.PY(p,P,e,sc)]));}
609  hitPart(x,y){for(let k=0;k<PARTS.length;k++){if(this.partPolys(k).some(q=>inPoly(x,y,q)))return PARTS[k].id}return null}
610  draw(){
611    const ctx=this.ctx,e=this.e,mob=this.mob,s=this.s,o=this.o,W=this.W,H=this.H;
612    ctx.setTransform(1,0,0,1,0,0);
613    if(this.lift>0){ctx.setTransform(this.dpr,0,0,this.dpr,0,0);this.drawWorld(ctx,false);}
614    else ctx.drawImage(this.wc,0,0);
615    ctx.setTransform(this.dpr,0,0,this.dpr,0,0);
616    this.drawHalo(ctx);
617    // markA (concept I's chat screen): the swarm mark fades out once the conversation starts, and the crisp logo takes over
618    const MA=o.markA?o.markA():1;
619    if(REFM&&!o.noMark){this.refDraw(MA);ctx.globalAlpha=1;
619if(o.cards){this.placeCards();this.drawLead(ctx);}return}
620    if(MA<.01){ctx.globalAlpha=1;if(o.cards)this.placeCards();return}
621    ctx.globalAlpha=MA;
622    if(MK){if(SWM)this.drawSW(ctx);else this.drawMK(ctx);ctx.globalAlpha=1;if(o.cards)this.placeCards();return}
623    // ---- the mark: agents only. Stroke length and weight follow the mark's size, so it reads the same big or small ----
624    const Ls=clamp((o.lsK||7.4)*s,mob?2.6:3.1,o.lsMax||5.2),rv=o.only?1:ss(.3,.8,e),rc=ss(.02,.3,this.rise),apart=o.only?0:ss(.02,.3,e);ctx.lineCap='round';
625    const LW=mob?(o.lwMob||1.7):clamp(1.3+s*1.1,o.lwMin||1.9,o.lwMax||2.5);
626    const segs=b=>{const tn=TONE_MAP[b.tone]||b.tone,Lb=(tn==='E'||tn==='V'?Ls*1.25:Ls)*this.st[b.k].sc;
627      ctx.beginPath();for(const a of b.list){if(!a.on)continue;const c=Math.cos(a.a)*Lb,sn=Math.sin(a.a)*Lb;ctx.moveTo(a.x-c,a.y-sn);ctx.lineTo(a.x+c,a.y+sn);}};
628    const alphaOf=b=>{const tn=TONE_MAP[b.tone]||b.tone;let al=TONE_A[tn];if(b.tone==='V')al*=rv;if(b.tone==='CS'||b.tone==='CI'||b.tone==='CL'||b.tone==='CR')al*=rc;return al};
629    const colOf=b=>{const tn=TONE_MAP[b.tone]||b.tone,Z=this.st[b.k],dim=apart*(1-Z.tk)*(tn==='E'||tn==='V'?.55:.7);
630      const c=mix3(mix3(TONE_BASE[tn],TONE_DIM,dim),TONE_ACTV[tn],Z.tk);return`rgba(${c[0]|0},${c[1]|0},${c[2]|0},`};
631    const core=PI.core,pass=list=>{
632      // a cream under-stroke first, so the agents read crisply over the world's grid lines
633      ctx.lineWidth=LW+1.6;for(const b of list){const al=alphaOf(b);if(al<.02||b.tone==='V')continue;ctx.strokeStyle=`rgba(239,231,218,${(.45*al).toFixed(3)})`;segs(b);ctx.stroke();}
634      ctx.lineWidth=LW;for(const b of list){const al=alphaOf(b);if(al<.02)continue;ctx.strokeStyle=colOf(b)+al.toFixed(3)+')';segs(b);ctx.stroke();}};
635    pass(this.bk.filter(b=>b.k!==core));
636    const cb=this.bk.filter(b=>b.k===core);
637    if(cb.length){ctx.save();
638      // the core sits BEHIND the frame: whatever of it overlaps the frame's silhouette is hidden, so as it rises its sides
639      // come out from behind the top V band
640      if(!o.only){const fp=this.partPolys(PI.frame)[0];ctx.beginPath();ctx.rect(-10,-10,W+20,H+20);fp.forEach((p,i)=>i?ctx.lineTo(p[0],p[1]):ctx.moveTo(p[0],p[1]));ctx.closePath();ctx.clip('evenodd');}
641      pass(cb);ctx.restore();}
642    const cs=o.chevS?clamp(s/.5,.9,1.6):1;
643    for(const a of this.acc){if(!a.on)continue;const Z=this.st[a.k];ctx.fillStyle=Z.act>.5?'#324E48':(apart>.5&&Z.tk<.2?'rgb(120,138,130)':'#121D1A');ctx.beginPath();chev(ctx,a.x,a.y,a.a,(mob?4.2:5.2)*Z.sc*cs);ctx.fill();}
644    ctx.globalAlpha=1;
645    if(o.cards){this.placeCards();this.drawLead(ctx);}
646  }
647  drawLead(ctx){const Ld=this._lead;{
648      if(Ld&&Ld[3]>.02){ctx.globalAlpha=Ld[3];ctx.strokeStyle=ORANGE;ctx.lineWidth=1.5;ctx.beginPath();ctx.moveTo(Ld[0],Ld[1]);ctx.lineTo(Ld[2],Ld[1]);ctx.stroke();
649        ctx.fillStyle=ORANGE;ctx.beginPath();ctx.arc(Ld[0],Ld[1],3,0,TAU);ctx.fill();ctx.globalAlpha=1;}}
650  }
651  // ---- the swarm options (?mk=4|5|6; see p1-helpers.js): agents only, batched per piece, tone, shade and shape ----
652  drawSW(ctx){
653    const o=this.o,W=this.W,H=this.H,e=this.e,MA=ctx.globalAlpha,P0=SWP[MK],g=this.swg,z=this.s/this.swsR;
654    const rv=o.only?1:ss(.3,.8,e),rc=ss(.02,.3,this.rise),apart=o.only?0:ss(.02,.3,e);
655    const col=b=>{const tn=TONE_MAP[b.tone]||b.tone,Z=this.st[b.k],dim=apart*(1-Z.tk)*(b.edge?.55:.7);
656      const B0=b.edge&&b.v?TONE_BASE.E2:TONE_BASE[tn],A0=b.edge&&b.v?TONE_ACTV.E2:TONE_ACTV[tn],O0=b.edge&&b.v?TONE_OR.E2:TONE_OR[tn];
657      // the sprinkled orange agents stay orange (a touch lighter while their piece is dimmed); the chosen piece turns orange
658      const c=b.or?mix3(b.edge?TONE_OR.E2:ORG,CRM,dim*.45):mix3(mix3(mix3(B0,TONE_DIM,dim),A0,Z.tk),O0,easeIO(Z.or));return`rgba(${c[0]|0},${c[1]|0},${c[2]|0},`};
659    const alphaOf=b=>{let al=b.edge?1:P0.fa*(1+.35*this.st[b.k].tk);if(b.tone==='V')al*=rv;if(b.tone==='CS'||b.tone==='CI'||b.tone==='CL'||b.tone==='CR')al*=rc;return Math.min(1,al)*MA};
660    const draw=b=>{const al=alphaOf(b);if(al<.02)return;const sc=this.st[b.k].sc;
661      if(b.sh){const r=Math.max(1.45,(P0.chr||3)*g*z)*sc;ctx.fillStyle=col(b)+al.toFixed(3)+')';ctx.beginPath();
662        for(const a of b.list){if(a.on)chev(ctx,a.x,a.y,a.a,r)}ctx.fill();return}
663      const Lh=Math.max(.8,(b.edge?P0.eL:P0.fL)*g*z*.5)*sc;ctx.lineWidth=Math.max(.9,(b.edge?P0.elw:P0.flw)*g*z);ctx.lineCap='round';
664      ctx.strokeStyle=col(b)+al.toFixed(3)+')';ctx.beginPath();
665      for(const a of b.list){if(!a.on)continue;const L2=a.ls===undefined?Lh:Lh*a.ls,c=Math.cos(a.a)*L2,sn=Math.sin(a.a)*L2;ctx.moveTo(a.x-c,a.y-sn);ctx.lineTo(a.x+c,a.y+sn);}ctx.stroke();};
666    const core=PI.core;
667    for(const b of this.bk)if(b.k!==core)draw(b);
668    if(!o.only||o.only==='core'){ctx.save();
669      if(!o.only){const fp=this.partPolys(PI.frame)[0];ctx.beginPath();ctx.rect(-10,-10,W+20,H+20);fp.forEach((p,i)=>i?ctx.lineTo(p[0],p[1]):ctx.moveTo(p[0],p[1]));ctx.closePath();ctx.clip('evenodd');}
670      for(const b of this.bk)if(b.k===core)draw(b);
670ctx.restore();}
671  }
672  // ---- the logo style options (?mk=1|2|3; see p1-helpers.js) ----
673  drawMK(ctx){
674    const o=this.o,s=this.s,e=this.e,dpr=this.dpr,W=this.W,H=this.H,G=this.G,MA=ctx.globalAlpha;
675    const small=!o.only&&946*s<230,rest=MK===3||small,mv=rest&&!small?(this.mv||0):0;
676    const rv=o.only?1:ss(.3,.8,e),rc=ss(.02,.3,this.rise),apart=o.only?0:ss(.02,.3,e);
677    const col=(tone,k)=>{const tn=TONE_MAP[tone]||tone,Z=this.st[k],dim=apart*(1-Z.tk)*(tn==='E'||tn==='V'?.55:.7);
678      const c=mix3(mix3(TONE_BASE[tn],TONE_DIM,dim),TONE_ACTV[tn],Z.tk);return`rgba(${c[0]|0},${c[1]|0},${c[2]|0},`};
679    const arr=k=>this.arrN&&this.arrN[k]?this.arrC[k]/this.arrN[k]:1;
680    const setT=k=>{const P=PARTS[k],sc=this.st[k].sc,a=s*sc;ctx.setTransform(dpr*a,0,0,dpr*a,dpr*(this.ox+(P.cx*(1-sc)+P.off[0]*e*this.ok)*s),dpr*(this.oy+(P.cy*(1-sc)+P.off[1]*e*this.ok)*s));};
681    const scr=()=>ctx.setTransform(dpr,0,0,dpr,0,0);
682    const spPx=this.spU*s,esPx=this.esU*s;
683    const plate=k=>{const P=PARTS[k],Z=this.st[k],A=arr(k),core=P.id==='core';setT(k);
684      const polys=core?[G.rh]:P.fc.map(F=>F.p);ctx.beginPath();polys.forEach(q=>{q.forEach((p,i)=>i?ctx.lineTo(p[0],p[1]):ctx.moveTo(p[0],p[1]));ctx.closePath()});
685      ctx.globalAlpha=MA*A*(core?Math.max(rc,1-rc):1);ctx.fillStyle='rgba(239,231,218,.9)';ctx.fill();
686      if(rest){ctx.fillStyle=`rgba(50,78,72,${(.075+.09*Z.tk).toFixed(3)})`;ctx.fill();}
687      ctx.globalAlpha=MA;scr();};
688    const agentsOf=(k,edge)=>{
689      for(const b of this.bk){if(b.k!==k)continue;const isE=b.tone==='E'||b.tone==='V'||b.tone==='CE'||b.tone==='CS'||b.tone==='CI';if(isE!==edge)continue;
690        let al=1;
690if(b.tone==='V')al*=rv;if(b.tone==='CS'||b.tone==='CI'||b.tone==='CL'||b.tone==='CR')al*=rc;
691        const settledOK=edge?(MK===2&&!small):!rest,sc=this.st[k].sc;
692        let Lh,lw;if(edge){Lh=(MK===2&&!small?esPx*.4:Math.max(2.2,spPx*.35))*sc;lw=MK===2&&!small?Math.max(1.1,MK_LW*s/this.lanes*.62):1.6;}
693        else{Lh=spPx*(MK===2?.24:.3)*sc;lw=MK===2?1.15:clamp(spPx*.19,1,1.6);if(MK===2)al*=.8;}
694        ctx.lineWidth=lw;ctx.lineCap=edge&&MK===2?'butt':'round';
695        // settled agents
696        if(settledOK&&al>.02){ctx.strokeStyle=col(b.tone,k)+al.toFixed(3)+')';ctx.beginPath();let any=0;
697          for(const a of b.list){if(!a.on||a.p<1)continue;any=1;const c=Math.cos(a.a)*Lh,sn=Math.sin(a.a)*Lh;ctx.moveTo(a.x-c,a.y-sn);ctx.lineTo(a.x+c,a.y+sn);}if(any)ctx.stroke();}
698        // agents still flying in (all options), and in option 3 the edge agents streaming while the mark moves
699        const fl=edge&&mv>.01&&!settledOK;const fa=al*(fl?mv:1);if(fa<.02)continue;
700        ctx.strokeStyle=col(b.tone,k)+fa.toFixed(3)+')';ctx.lineCap='round';if(edge&&!settledOK)ctx.lineWidth=Math.max(1.6,MK_LW*s*.3);ctx.beginPath();let any=0;
701        for(const a of b.list){if(!a.on||(a.p>=1&&!fl))continue;any=1;const c=Math.cos(a.a)*Lh,sn=Math.sin(a.a)*Lh;ctx.moveTo(a.x-c,a.y-sn);ctx.lineTo(a.x+c,a.y+sn);}if(any)ctx.stroke();}};
702    const outline=k=>{if(MK===2&&!small)return;const P=PARTS[k],A=arr(k)*(1-.8*mv);if(A<.01)return;setT(k);
703      ctx.lineWidth=MK_LW;ctx.lineJoin='round';ctx.lineCap='round';const c=col('E',k);
704      if(P.id==='core'){const pl=(C,al)=>{if(al<.01)return;ctx.strokeStyle=c+(al*A).toFixed(3)+')';ctx.beginPath();C.pts.forEach((p,i)=>i?ctx.lineTo(p[0],p[1]):ctx.moveTo(p[0],p[1]));ctx.stroke()};
705        pl(G.O,1);pl(G.S,1);pl(G.I,rc);if(G.h>1)pl(G.V,rc);}
706      else{ctx.strokeStyle=c+A.toFixed(3)+')';ctx.stroke(P.ep);if(rv>.01){ctx.strokeStyle=c+(A*rv).toFixed(3)+')';ctx.stroke(P.rp);}}
707      scr();};
708    const core=PI.core,others=PARTS.map((P,k)=>k).filter(k=>k!==core&&(!o.only||PARTS[k].id===o.only));
709    others.forEach(plate);others.forEach(k=>agentsOf(k,false));others.forEach(outline);others.forEach(k=>agentsOf(k,true));
710    if(!o.only||o.only==='core'){ctx.save();
711      if(!o.only){const fp=this.partPolys(PI.frame)[0];ctx.beginPath();ctx.rect(-10,-10,W+20,H+20);fp.forEach((p,i)=>i?ctx.lineTo(p[0],p[1]):ctx.moveTo(p[0],p[1]));ctx.closePath();ctx.clip('evenodd');}
712      plate(core);agentsOf(core,false);outline(core);agentsOf(core,true);ctx.restore();}
713  }
714  /* ---- the breakdown labels (HTML links). Each has two measured states: the label and the full card. ---- */
715  measureCards(){const C=this.o.cards,below=this.W<1024,cw=this._cw||340;
716    for(const el of C){const body=el._b||(el._b=el.querySelector('.pc-body'));const s0=el.style.cssText,b0=body.style.cssText;
717      el.style.setProperty('--a','0');el.style.width=below?'':'max-content';body.style.height='0px';body.style.display='none';
718      const lw=el.offsetWidth+4,lh=el.offsetHeight;body.style.display='';
719      el.style.setProperty('--a','1');el.style.width=below?'':cw+'px';body.style.height='auto';
720      const ch=el.offsetHeight,bh=body.offsetHeight;
721      el.style.cssText=s0;body.style.cssText=b0;el._a=el._bh=el._w=null;el._m={lw,lh,ch,bh};}
722    if(below){let sum=0,mx=0;C.forEach(el=>{sum+=el._m.lh;mx=Math.max(mx,el._m.ch-el._m.lh)});this.cardH=sum+6*(C.length-1)+mx;}
723    else this.cardH=Math.max(...C.map(el=>el._m.ch));
724    document.documentElement.style.setProperty('--pch',this.cardH+'px');}
725  // card width on wide screens: whatever room the broken-apart mark leaves at the side
726  cardW(){if(this.below)return;const F=this.FT.E;let mn=1e9,mx=-1e9;
727    PARTS.forEach(P=>P.faces.forEach(Fc=>Fc.p.forEach(p=>{const x=F.ox+(P.cx+(p[0]-P.cx)*(1+GROW)+P.off[0])*F.s;mn=Math.min(mn,x);mx=Math.max(mx,x);})));
728    const room=Math.min(mn,this.W-mx)-30-22;const w=Math.round(clamp(room,250,400));
729    if(this._cw!==w){this._cw=w;this.measureCards();}
730    // where each label sits across: just outside the mark, within the rows its full card will cover (mark units, apart and grown)
731    const s=F.s,polys=PARTS.map(P=>P.faces.map(Fc=>Fc.p.map(p=>[P.cx+(p[0]-P.cx)*(1+GROW)+P.off[0],P.cy+(p[1]-P.cy)*(1+GROW)+P.off[1]])));
732    for(const el of this.o.cards){const k=PI[el.dataset.part],P=PARTS[k],m=el._m,l=P.side==='l';let y0=1e9,y1=-1e9,own=l?1e9:-1e9;
733      polys[k].forEach(q=>q.forEach(p=>{y0=Math.min(y0,p[1]);y1=Math.max(y1,p[1]);own=l?Math.min(own,p[0]):Math.max(own,p[0]);}));
734      let best=null;
735      for(const fy of [.5,.4,.6,.3,.7,.2,.8]){const top=y0+(y1-y0)*fy-m.lh/2/s,b0=top-26/s,b1=top+(m.ch+26)/s;let ext=own;
736        const take=x=>{ext=l?Math.min(ext,x):Math.max(ext,x)};polys.forEach(pp=>pp.forEach(q=>bandX(q,b0,b1,take)));
737        const cost=Math.abs(ext-own)+Math.abs(fy-.5)*(y1-y0)*.35;if(!best||cost<best.cost)best={cost,ext,fy};}
738      m.xm=best.ext;m.fy=best.fy;}}
739  placeCards(){
740    const C=this.o.cards,vis=RM?(this.e>.5?1:0):ss(.45,.9,this.e),items=[];this._lead=null;
741    for(const el of C){const k=PI[el.dataset.part],P=PARTS[k],Z=this.st[k],m=el._m;if(!m)continue;
742      const a=RM?(Z.act>.5?1:0):ss(.25,.9,Z.act);
743      const av=a.toFixed(3);if(el._a!==av){el.style.setProperty('--a',av);el._a=av;}
744      const bh=(m.bh*a).toFixed(1);if(el._bh!==bh){el._b.style.height=bh+'px';el._bh=bh;}
745      const op=vis.toFixed(3);if(el._op!==op){el.style.opacity=op;el._op=op;}
746      el.classList.toggle('live',vis>.5);el.classList.toggle('on',a>.5&&vis>.5);el.classList.toggle('hot',this.hot===P.id);
747      if(!this.below){let y0=1e9,y1=-1e9;this.partPolys(k).forEach(q=>q.forEach(p=>{y0=Math.min(y0,p[1]);y1=Math.max(y1,p[1]);}));
748        items.push({el,a,side:P.side,w:lerp(m.lw,this._cw,a),h:lerp(m.lh,m.ch,a),y:y0+(y1-y0)*(m.fy==null?.5:m.fy)-m.lh/2,xm:m.xm});}
749    }
750    if(this.below)return;
751    // the spine (AI Audit): its label sits right beside the spine's lower part and its card opens from there, below the
752    // right wing and clear of every other piece (as G). The labels on the right then stack above it, never over it.
753    const S=items.find(i=>i.side==='s');let rBot=this.H-16;
754    if(S){const k=PI.spine;let x1=-1e9,y0=1e9,y1=-1e9;this.partPolys(k).forEach(q=>q.forEach(p=>{x1=Math.max(x1,p[0]);y0=Math.min(y0,p[1]);y1=Math.max(y1,p[1]);}));
755      S.x=Math.min(x1+22,this.W-S.w-16);
756      let yb=-1e9;for(let j=0;j<PARTS.length;j++){if(j===k)continue;this.partPolys(j).forEach(q=>{let a0=1e9,a1=-1e9,b1=-1e9;q.forEach(p=>{a0=Math.min(a0,p[0]);a1=Math.max(a1,p[0]);b1=Math.max(b1,p[1]);});
757        if(a1>S.x-6&&a0<S.x+S.w+6)yb=Math.max(yb,b1);});}
758      const bot=this.H-16,ty=y1-(y1-y0)*.3-S.el._m.lh/2;
759      if(yb+16+S.h<=bot)S.y=Math.max(Math.min(ty,bot-S.h),yb+16);
760      else{// short screen (as G): no room under the wing, so the card rises and steps right just past the wing,
761        // and a short ink leader ties it back to the spine, under the wing
762        S.y=Math.min(ty,bot-S.h);let x=x1+22;
763        for(let j=0;j<PARTS.length;j++){if(j===k)continue;this.partPolys(j).forEach(q=>{let a0=1e9,a1=-1e9,b0=1e9,b1=-1e9;q.forEach(p=>{a0=Math.min(a0,p[0]);a1=Math.max(a1,p[0]);b0=Math.min(b0,p[1]);b1=Math.max(b1,p[1]);});
764          if(a1>x1&&b1>S.y-12&&b0<S.y+S.h+12)x=Math.max(x,a1+22);});}
765        S.x=Math.min(x,this.W-S.w-16);
766        const ly=clamp(Math.max(ty+S.el._m.lh/2,yb+14),S.y+18,S.y+S.h-18);if(S.x>x1+30)this._lead=[x1+6,ly,S.x,S.a*vis];}
767      rBot=S.y-12;}
768    for(const side of['l','r'])stack(items.filter(i=>i.side===side).sort((a,b)=>a.y-b.y),16,side==='r'?rBot:this.H-16,12);
769    for(const it of items){const X=this.ox+it.xm*this.s;let lx=it.side==='s'?it.x:it.side==='l'?X-30-it.w:X+30;lx=clamp(lx,16,this.W-it.w-16);
770      const w=it.w.toFixed(1);if(it.el._w!==w){it.el.style.width=w+'px';it.el._w=w;}
771      const tr=`translate(${lx.toFixed(1)}px,${it.y.toFixed(1)}px)`;if(it.el._tr!==tr){it.el.style.transform=tr;it.el._tr=tr;}}
772  }
773}
774
775/* ============================================================
776   THE REFERENCE SWARM (founder, 1 Oct 2026): "use the same swarms and effect, exactly the same" as the concept page
777   share3/swarm.html, in the site's green. Short dashes ride a slow curl-noise flow, gather into a shape, hold it with a
778   faint tremor and leave trails (each frame only fades the last one, as the reference paints its paper at 34%); about
779   one agent in 97 is an orange chevron. The physics, timings, sampling and sizes below are the reference's own.
780   It drives (1) the landing: the icon, "Where intelligence begins." and the ADROLAB wordmark in turn, in the band
781   between the nav and "Ask the lab anything" (RefHero), and (2) every Stage, the breakdown and the pages' pieces: the official
782   mark's pieces, each piece's ink sampled the way the reference samples its shapes (Stage.ref*). ?mk=<n> shows the
783   earlier swarms instead.
784   ============================================================ */
785const REFM=!Q.has('mk');
786const RS_INK=[50,78,72];
787const rrnd=(a,b)=>a+Math.random()*(b-a);
788const rsEase=t=>t<.5?4*t*t*t:1-Math.pow(-2*t+2,3)/2;
789function rsPsi(x,y,t){
790  return Math.sin(x*.0031+t*.13)*Math.cos(y*.0043-t*.09)*1.0
791       + Math.sin((x+y)*.0017-t*.07+1.3)*0.8
792       + Math.cos(x*.0071-y*.0023+t*.19)*0.35;
793}
794function rsField(x,y,t){ // angle of a divergence-free (curl) flow: agents stream without collapsing
795  const e=2,dx=rsPsi(x+e,y,t)-rsPsi(x-e,y,t),dy=rsPsi(x,y+e,t)-rsPsi(x,y-e,t);return Math.atan2(-dx,dy);
796}
797// the reference's sampler: n points (x, y, angle) of a shape drawn in alpha on cv, mapped into box
798function rsSample(cv,n,box,hatch){
799  const w=cv.width,h=cv.height,d=cv.getContext('2d').getImageData(0,0,w,h).data;
800  const bc=document.createElement('canvas');bc.width=w;bc.height=h;
801  const bx=bc.getContext('2d');bx.filter='blur(4px)';bx.drawImage(cv,0,0);
802  const bd=bx.getImageData(0,0,w,h).data;
803  let area=0;for(let i=3;i<d.length;i+=4)if(d[i]>110)area++;
804  const step=Math.max(1,Math.sqrt(area/n)*0.94);
805  let pts=[];
806  for(let y=step/2;y<h;y+=step)for(let x=step/2;x<w;x+=step){
807    const jx=clamp(Math.round(x+rrnd(-.3,.3)*step),0,w-1),jy=clamp(Math.round(y+rrnd(-.3,.3)*step),0,h-1);
808    if(d[(jy*w+jx)*4+3]>110){
809      let a;
810      if(hatch!=null)a=hatch;
811      else{const g=(xx,yy)=>bd[(clamp(yy,0,h-1)*w+clamp(xx,0,w-1))*4+3];
812        const gx=g(jx+2,jy)-g(jx-2,jy),gy=g(jx,jy+2)-g(jx,jy-2);a=Math.atan2(gy,gx)+Math.PI/2;}
813      pts.push([jx,jy,a]);
814    }
815  }
816  for(let i=pts.length-1;i>0;i--){const j=(Math.random()*(i+1))|0;[pts[i],pts[j]]=[pts[j],pts[i]]}
817  if(pts.length>n)pts.length=n;
818  const base=pts.length;while(pts.length<n&&base)pts.push(pts[(Math.random()*base)|0]);
819  const sx=box.w/w,sy=box.h/h;
820  return pts.map(p=>({x:box.x+p[0]*sx,y:box.y+p[1]*sy,a:p[2]}));
821}
822// the accent agent: a small chevron
823function rsChev(c,x,y,a,r){const co=Math.cos(a),s=Math.sin(a);c.moveTo(x+co*r,y+s*r);c.lineTo(x-co*r*.8-s*r*.75,y-s*r*.8+co*r*.75);
824  c.lineTo(x-co*r*.3,y-s*r*.3);c.lineTo(x-co*r*.8+s*r*.75,y-s*r*.8-co*r*.75);c.closePath();}
825// trails on a see-through layer: fade what is there by k (the reference paints its paper at 34% each frame)
826function rsFade(c,k){c.save();c.setTransform(1,0,0,1,0,0);if(k>=1)c.clearRect(0,0,c.canvas.width,c.canvas.height);
827  else{c.globalCompositeOperation='destination-out';c.globalAlpha=k;c.fillStyle='#000';c.fillRect(0,0,c.canvas.width,c.canvas.height);}c.restore();}
828const rsCol=c=>`rgb(${c[0]|0},${c[1]|0},${c[2]|0})`;
829
830/* ---- (1) THE LANDING: the reference's hero, in the band between the nav and the headline ---- */
831function RefHero(land,head){
832  const cv=document.createElement('canvas');cv.className='land-swarm';cv.setAttribute('aria-hidden','true');
833  land.insertBefore(cv,land.querySelector('.land-in'));const ctx=cv.getContext('2d');
834  let W=0,H=0,DPR=1,agents=[],targets={},wordImg=null;const mouse={x:-1e4,y:-1e4,t:-1e4};
835  // the band: from just under the nav to just above the headline, the full width
836  const place=()=>{const L=land.getBoundingClientRect(),nav=land.querySelector('.nav'),nb=nav?nav.getBoundingClientRect().bottom-L.top:60;
837    const top=Math.round(nb+4),bot=Math.round(head.getBoundingClientRect().top-L.top-6);cv.style.top=top+'px';cv.style.height=Math.max(60,bot-top)+'px';};
838  function size(){place();DPR=Math.min(2,devicePixelRatio||1);W=cv.clientWidth;H=cv.clientHeight;cv.width=W*DPR;cv.height=H*DPR;ctx.setTransform(DPR,0,0,DPR,0,0);}
839  const nWant=()=>W<700?Math.round(clamp(W*H/80,700,1200)):Math.round(clamp(W*H/380,520,1700));
840  function makeAgents(){const n=nWant();agents=[];
841    for(let i=0;i<n;i++)agents.push({x:rrnd(0,W),y:rrnd(0,H),vx:rrnd(-1,1),vy:rrnd(-1,1),a:rrnd(0,TAU),acc:i%97===0,d:Math.random(),s:rrnd(.7,1.3),ph:rrnd(0,TAU),tx:0,ty:0,ta:0});}
842  function buildTargets(){
843    const n=agents.length,mob=W<700,cy=H/2;
844    // the icon: the official mark, as tall as the band allows
845    {const R=.8,oc=document.createElement('canvas');oc.width=Math.round(930*R);oc.height=Math.round(956*R);const o=oc.getContext('2d');
846      o.setTransform(R,0,0,R,-8*R,-22*R);o.fillStyle='#000';o.fill(MARK_FULL,'evenodd');
847      const mh=Math.min(H*.7,W*(mob?.62:.5)),mw=mh*930/956;targets.mark=rsSample(oc,n,{x:W/2-mw/2,y:cy-mh/2,w:mw,h:mh});}
848    // the line: in the site's type, on one, two or three lines, whichever sets it biggest in the band
849    {const F=fs=>`500 ${fs}px "Switzer", system-ui, sans-serif`,o0=document.createElement('canvas').getContext('2d');o0.font=F(100);
850      const bw=Math.min(W*.92,1240),bh=H*.66;let best=null;
851      for(const lines of [['Where intelligence begins.'],['Where intelligence','begins.'],['Where','intelligence','begins.']]){
852        const w100=Math.max(...lines.map(l=>o0.measureText(l).width)),fs=Math.min(bw/w100*100,bh/(lines.length*1.02+.12));if(!best||fs>best.fs)best={lines,fs,w100};}
853      const R=2,fs=best.fs,lh=fs*1.02,tw=best.w100*fs/100,th=lh*best.lines.length+fs*.12,oc=document.createElement('canvas');
854      oc.width=Math.ceil(tw*R+12);oc.height=Math.ceil(th*R+12);const o=oc.getContext('2d');o.font=F(fs*R);o.textAlign='center';o.fillStyle='#000';
855      best.lines.forEach((l,i)=>o.fillText(l,oc.width/2,6+R*(lh*i+fs*.86)));
856      const w=oc.width/R,h=oc.height/R;targets.words=rsSample(oc,n,{x:W/2-w/2,y:cy-h/2,w,h},-1.05);}
857    // the wordmark
858    if(wordImg){const oc=document.createElement('canvas');oc.width=wordImg.width*2;oc.height=wordImg.height*2;oc.getContext('2d').drawImage(wordImg,0,0,oc.width,oc.height);
859      let bw=Math.min(W*(mob?.86:.62),980),bh=bw*wordImg.height/wordImg.width;if(bh>H*.5){bh=H*.5;bw=bh*wordImg.width/wordImg.height;}
860      targets.word=rsSample(oc,n,{x:W/2-bw/2,y:cy-bh/2,w:bw,h:bh},Math.PI/2);}
861    for(const k in targets){const T=targets[k],cx=W/2;T.sort((p,q)=>Math.atan2(p.y-cy,p.x-cx)-Math.atan2(q.y-cy,q.x-cx));}
862  }
863  const SEQ=[
864    ['working',.1],['gathering',1.7,'mark'],['formed',2.6,'mark'],['dispersing',.8],
865    ['working',1.8],['gathering',1.7,'words'],['formed',2.8,'words'],['dispersing',.8],
866    ['working',1.8],['gathering',1.6,'word'],['formed',2.2,'word'],['dispersing',.8],['working',1.4]];
867  const SEQT=SEQ.reduce((s,p)=>s+p[1],0);
868  function phaseAt(t){t%=SEQT;for(const p of SEQ){if(t<p[1])return{name:p[0],shape:p[2],t,dur:p[1]};t-=p[1];}return{name:'working',t:0,dur:1};}
869  let lastPhase='',lastShape='',T0=performance.now();
870  function assign(shape){const T=targets[shape];if(!T)return;const cx=W/2,cy=H/2;
871    const idx=agents.map((a,i)=>[Math.atan2(a.y-cy,a.x-cx),i]).sort((p,q)=>p[0]-q[0]);
872    idx.forEach(([_,i],k)=>{const p=T[k%T.length],a=agents[i];a.tx=p.x;a.ty=p.y;a.ta=p.a;});}
873  function step(now){
874    const t=(now-T0)/1000,ph=phaseAt(t);
875    if(ph.name!==lastPhase||ph.shape!==lastShape){
876      if(ph.name==='gathering')assign(ph.shape);
877      if(ph.name==='dispersing')for(const a of agents){const dx=a.x-W/2,dy=a.y-H/2,dd=Math.hypot(dx,dy)+1,f=rrnd(3,8);a.vx+=dx/dd*f+rrnd(-1.5,1.5);a.vy+=dy/dd*f+rrnd(-1.5,1.5);}
878      lastPhase=ph.name;lastShape=ph.shape;}
879    const mouseOn=(now-mouse.t)<2500,R=Math.min(260,W*.3);
880    for(const a of agents){
881      let ax=0,ay=0;
882      if(ph.name==='gathering'||ph.name==='formed'){
883        const p=ph.name==='formed'?1:clamp((ph.t/ph.dur-a.d*.3)/.5,0,1);
884        const k=.006+rsEase(p)*.07,damp=.84-p*.05;
885        const bx=a.tx+Math.sin(t*1.3+a.ph)*.5,by=a.ty+Math.cos(t*1.1+a.ph)*.5;
886        if(p<=0){const f=rsField(a.x,a.y,t);ax+=(Math.cos(f)*1.1*a.s-a.vx)*.04;ay+=(Math.sin(f)*1.1*a.s-a.vy)*.04;}
887        else{ax+=(bx-a.x)*k;ay+=(by-a.y)*k;a.vx*=damp;a.vy*=damp;}
888      }else{
889        const f=rsField(a.x,a.y,t)+Math.sin(t*.7+a.ph)*.35,sp=(ph.name==='dispersing'?.8:1.15)*a.s;
890        ax+=(Math.cos(f)*sp-a.vx)*.035;ay+=(Math.sin(f)*sp-a.vy)*.035;
891      }
892      if(mouseOn){const dx=mouse.x-a.x,dy=mouse.y-a.y,d=Math.hypot(dx,dy);
893        if(d<R&&d>1){const f=(1-d/R),g=ph.name==='formed'?.35:1;ax+=(dx/d*.22*f-dy/d*.18*f)*g;ay+=(dy/d*.22*f+dx/d*.18*f)*g;if(d<26){ax-=dx/d*.3;ay-=dy/d*.3}}}
894      a.vx+=ax;a.vy+=ay;
895      const vmax=ph.name==='gathering'?16:7,v=Math.hypot(a.vx,a.vy);if(v>vmax){a.vx*=vmax/v;a.vy*=vmax/v}
896      a.x+=a.vx;a.y+=a.vy;
897      if(ph.name==='working'||ph.name==='dispersing'){if(a.x<-20)a.x+=W+40;if(a.x>W+20)a.x-=W+40;if(a.y<-20)a.y+=H+40;if(a.y>H+20)a.y-=H+40;}
898      const settle=(ph.name==='formed'||ph.name==='gathering')?clamp(1-v/1.6,0,1):0,va=Math.atan2(a.vy,a.vx);
899      let target=v>.05?va:a.a;if(settle>0)target=a.ta;
900      let da=((target-a.a)%TAU+TAU*1.5)%TAU-Math.PI;
901      if(settle>0&&Math.abs(da)>Math.PI/2)da=((da+Math.PI)%TAU+TAU*1.5)%TAU-Math.PI;   // lines are symmetric
902      a.a+=da*(settle>0?.12:.2);
903    }
904  }
905  function draw(){
906    rsFade(ctx,RM?1:.34);ctx.setTransform(DPR,0,0,DPR,0,0);ctx.lineCap='round';
907    const mob=W<700;ctx.strokeStyle=rsCol(RS_INK);ctx.lineWidth=mob?1.1:1.35;ctx.beginPath();const L=mob?2.4:3.6;
908    for(const a of agents){if(a.acc)continue;const c=Math.cos(a.a)*L,s=Math.sin(a.a)*L;ctx.moveTo(a.x-c,a.y-s);ctx.lineTo(a.x+c,a.y+s);}
909    ctx.stroke();ctx.fillStyle=ORANGE;ctx.beginPath();for(const a of agents){if(a.acc)rsChev(ctx,a.x,a.y,a.a,mob?4.2:5.5);}ctx.fill();
910  }
911  const settleAll=()=>{assign('mark');for(const a of agents){a.x=a.tx;a.y=a.ty;a.a=a.ta;}draw();};
912  let vis=true;new IntersectionObserver(es=>{vis=es[0].isIntersecting}).observe(cv);
913  addEventListener('pointermove',e=>{const r=cv.getBoundingClientRect();mouse.x=e.clientX-r.left;mouse.y=e.clientY-r.top;mouse.t=performance.now();},{passive:true});
914  const talking=()=>land.classList.contains('talking');
915  const rebuild=()=>
915{if(talking())return;const n0=agents.length;size();if(Math.abs(nWant()-n0)>n0*.25)makeAgents();buildTargets();
916    if(RM)settleAll();else if(lastPhase==='gathering'||lastPhase==='formed')assign(lastShape);};
917  size();makeAgents();buildTargets();
918  const wsrc=(document.querySelector('.brand img.w')||{}).src;
919  if(wsrc)loadImg(wsrc).then(i=>{wordImg=i;buildTargets();if(lastShape==='word'&&(lastPhase==='gathering'||lastPhase==='formed'))assign('word');});
920  if(document.fonts)document.fonts.load('500 100px "Switzer"').then(()=>{if(!(lastShape==='words'&&(lastPhase==='gathering'||lastPhase==='formed')))buildTargets();if(RM)settleAll();});
921  let rt;addEventListener('resize',()=>{clearTimeout(rt);rt=setTimeout(rebuild,160)});
922  if(RM||FREEZE){settleAll();return}
923  T0=performance.now();
924  const loop=now=>{if(vis&&!talking()){if(Math.abs(cv.clientWidth-W)>2)rebuild();step(now);draw();}requestAnimationFrame(loop)};requestAnimationFrame(loop);
925}
926function loadImg(src){return new Promise(r=>{const i=new Image();i.onload=()=>r(i);i.src=src})}
927
928/* ---- (2) THE STAGES: the official mark's pieces, sampled and moved as the reference does ---- */
929let RS_RAS=null;
930// the official ink at 1 px per mark unit, and a blurred copy for the dashes' angles (the reference's tangent trick)
931function rsRaster(){if(RS_RAS)return RS_RAS;const W=950,H=1000,c=document.createElement('canvas');c.width=W;c.height=H;const x=c.getContext('2d');
932  x.fillStyle='#000';x.fill(MARK_FULL,'evenodd');const d=x.getImageData(0,0,W,H).data;
933  const b=document.createElement('canvas');b.width=W;b.height=H;const bx=b.getContext('2d');bx.filter='blur(5px)';bx.drawImage(c,0,0);
934  return RS_RAS={W,H,d,bd:bx.getImageData(0,0,W,H).data};}
935// agents at spacing sp (mark units): the ink, each point to the piece that owns it; the strokes a piece shows only while
936// apart (V); and the core, laid along its cube's outline so it can rise and flatten
937function rsMarkAgents(sp){
938  const I=swInk(),K=rsRaster(),out=[],W=K.W,H=K.H,hw=SW_HW;
939  const g=(x,y)=>K.bd[(clamp(Math.round(y),0,H-1)*W+clamp(Math.round(x),0,W-1))*4+3];
940  const ang=(x,y)=>{const gx=g(x+2,y)-g(x-2,y),gy=g(x,y+2)-g(x,y-2);return Math.atan2(gy,gx)+Math.PI/2};
941  for(let y=22+sp/2;y<980;y+=sp)for(let x=8+sp/2;x<940;x+=sp){
942    const px=clamp(x+rrnd(-.3,.3)*sp,0,W-1),py=clamp(y+rrnd(-.3,.3)*sp,0,H-1);
943    if(K.d[(Math.round(py)*W+Math.round(px))*4+3]<=110)continue;
944    const o=I.own(px,py);if(!o||o.k===PI.core)continue;out.push({k:o.k,hx:px,hy:py,a0:ang(px,py),cat:'E'});}
945  // apart-only strokes: the band round each such line, minus the ink the piece already has
946  PARTS.forEach((P,k)=>{if(P.id==='core')return;const RV=I.RV.filter(c=>c.k===k);if(!RV.length)return;
947    let x0=1e9,x1=-1e9,y0=1e9,y1=-1e9;RV.forEach(c=>{x0=Math.min(x0,c.bb[0]+24);x1=Math.max(x1,c.bb[1]-24);y0=Math.min(y0,c.bb[2]+24);y1=Math.max(y1,c.bb[3]-24)});
948    for(let y=y0+sp/2;y<y1;y+=sp)for(let x=x0+sp/2;x<x1;x+=sp){const px=x+rrnd(-.3,.3)*sp,py=y+rrnd(-.3,.3)*sp,r=I.revD(k,px,py);if(!r)continue;
949      const ik=I.ink(px,py);if(r.d>=(ik?hw+2.5:hw))continue;if(ik){const o=I.own(px,py);if(o&&o.k===k)continue}
950      out.push({k,hx:px,hy:py,a0:hw-r.d<6?r.a:Math.PI/2,cat:'V'});}});
951  // the core: its outline chains as 28-unit bands; dashes near a band's edge follow it, inside they stand upright (as the
952  // reference's blurred-tangent angles do in a thick stroke)
953  const k=PI.core;[['O','C'],['S','X'],['I','X'],['V','X']].forEach(([ch,cat])=>{const L=CUBE1[ch].L,n=Math.max(2,Math.round(L*2*hw/(sp*sp)));
954    for(let j=0;j<n;j++){const lo=rrnd(-hw+1,hw-1);out.push({k,core:1,ch,f0:Math.random(),lo,vert:Math.abs(lo)<hw-6,hx:0,hy:0,a0:0,cat});}});
955  out.forEach((a,i)=>{a.acc=i%97===0;a.d=Math.random();a.s=rrnd(.7,1.3);a.ph=rrnd(0,TAU);a.on=true;a.edge=1;a.tone=a.cat==='V'?'V':'E';});
956  return out;
957}
958Object.assign(Stage.prototype,{
959  refInit(){
960    const o=this.o,cv=this.cv;
961    if(!this.ov){const v=document.createElement('canvas');v.setAttribute('aria-hidden','true');v.style.cssText='position:absolute;pointer-events:none;margin:0';cv.after(v);this.ov=v;this.oc=v.getContext('2d');}
962    const v=this.ov,zi=getComputedStyle(cv).zIndex;v.style.left=cv.offsetLeft+'px';v.style.top=cv.offsetTop+'px';v.style.width=cv.clientWidth+'px';v.style.height=cv.clientHeight+'px';
963    v.style.zIndex=zi==='auto'?'':zi;v.width=cv.width;v.height=cv.height;
964    // spacing: ~5 px between agents at the largest size this stage shows the mark (the reference's letters are this dense;
965    // the mark's strokes are thinner than its letters, so at its 7.6 px they read as dashed outlines)
966    const sR=Math.max(...Object.values(this.FT).map(F=>F.s));let sp=(this.mob?4.4:5)/sR;
967    const key='ref:'+Math.round(sp*10)+':'+(o.only||'');
968    if(this.refKey!==key){this.refKey=key;let A=rsMarkAgents(sp);
969      const cap=o.only?2800:4200,nk=o.only?A.filter(a=>a.k===PI[o.only]).length:A.length;if(nk>cap){sp*=Math.sqrt(nk/cap);A=rsMarkAgents(sp);}
970      if(o.only)A=A.filter(a=>a.k===PI[o.only]);this.agents=A;this.bk=[];
971      const m=new Map();for(const a of A){if(a.acc)continue;const kk=a.k+':'+a.cat;if(!m.has(kk))m.set(kk,{k:a.k,cat:a.cat,list:[]});m.get(kk).list.push(a);}
972      this.rg=[...m.values()];}
973  },
974  refUpdate(dt){
975    const o=this.o,s=this.s,e=this.e,ox=this.ox,oy=this.oy,G=this.G,W=this.W,H=this.H;
976    const now=performance.now(),mOn=!RM&&this.mouse&&now-this.mt<2500,R=Math.min(260,W*.3),tt=RM?99:this.t;
977    // the pages: the agents flow for a moment, then gather (the reference's "working" then "gathering"); the breakdown
978    // starts formed (the landing above has had the intro)
979    const T1=o.refFormed?-99:.4,TG=1.7,nsub=RM?1:clamp(Math.round(dt*60),1,3);
980    for(const a of this.agents){const P=PARTS[a.k],sc=this.st[a.k].sc;
981      if(a.core){const r=atChain(G[a.ch],a.f0);a.hx=r[0]-Math.sin(r[2])*a.lo;a.hy=r[1]+Math.cos(r[2])*a.lo;a.a0=a.vert?Math.PI/2:r[2];}
982      const tx=ox+(P.cx+(a.hx-P.cx)*sc+P.off[0]*e*this.ok)*s,ty=oy+(P.cy+(a.hy-P.cy)*sc+P.off[1]*e*this.ok)*s;
983      if(!a.init){a.init=1;if(o.refFormed||RM){a.x=tx;a.y=ty;a.a=a.a0;}else{a.x=rrnd(0,W);a.y=rrnd(0,H);a.a=rrnd(0,TAU);}a.vx=rrnd(-1,1);a.vy=rrnd(-1,1);}
984      if(RM){a.x=tx;a.y=ty;a.a=a.a0;continue}
985      const p=clamp(((tt-T1)/TG-a.d*.3)/.5,0,1);
986      for(let i=0;i<nsub;i++){
987        let ax=0,ay=0;
988        if(p<=0){const f=rsField(a.x,a.y,tt)+Math.sin(tt*.7+a.ph)*.35;ax+=(Math.cos(f)*1.15*a.s-a.vx)*.035;ay+=(Math.sin(f)*1.15*a.s-a.vy)*.035;}
989        else{const k=.006+rsEase(p)*.07,damp=.84-p*.05,bx=tx+Math.sin(tt*1.3+a.ph)*.5,by=ty+Math.cos(tt*1.1+a.ph)*.5;ax+=(bx-a.x)*k;ay+=(by-a.y)*k;a.vx*=damp;a.vy*=damp;}
990        if(mOn){const dx=this.mouse[0]-a.x,dy=this.mouse[1]-a.y,d=Math.hypot(dx,dy);
991          if(d<R&&d>1){const f=(1-d/R),gg=p>=1?.35:1;ax+=(dx/d*.22*f-dy/d*.18*f)*gg;ay+=(dy/d*.22*f+dx/d*.18*f)*gg;if(d<26){ax-=dx/d*.3;ay-=dy/d*.3}}}
992        a.vx+=ax;a.vy+=ay;const vmax=p>0&&p<1?16:7,v=Math.hypot(a.vx,a.vy);if(v>vmax){a.vx*=vmax/v;a.vy*=vmax/v}
993        a.x+=a.vx;a.y+=a.vy;
994        if(p<=0){if(a.x<-20)a.x+=W+40;if(a.x>W+20)a.x-=W+40;if(a.y<-20)a.y+=H+40;if(a.y>H+20)a.y-=H+40;}
995        const settle=p>0?clamp(1-v/1.6,0,1):0,va=Math.atan2(a.vy,a.vx);let target=v>.05?va:a.a;if(settle>0)target=a.a0;
996        let da=((target-a.a)%TAU+TAU*1.5)%TAU-Math.PI;if(settle>0&&Math.abs(da)>Math.PI/2)da=((da+Math.PI)%TAU+TAU*1.5)%TAU-Math.PI;
997        a.a+=da*(settle>0?.12:.2);}
998    }
999  },
1000  refDraw(MA){
1001    const c=this.oc,o=this.o,e=this.e;if(!c)return;rsFade(c,RM?1:.34);c.setTransform(this.dpr,0,0,this.dpr,0,0);if(MA<.01)return;
1002    const rv=o.only?1:ss(.3,.8,e),rc=ss(.02,.3,this.rise),apart=o.only?0:ss(.02,.3,e),mob=this.mob,L=mob?3.2:3.6;
1003    c.lineCap='round';c.lineWidth=mob?1.3:1.35;
1004    const alphaOf=g=>(g.cat==='V'?rv:g.cat==='X'?rc:1)*MA;
1005    // a piece: green; a little paler while another piece is chosen; orange when it is the chosen one
1006    const colOf=k=>{const Z=this.st[k];return rsCol(mix3(mix3(RS_INK,TONE_DIM,apart*(1-Z.tk)*.45),ORG,easeIO(Z.or||0)))};
1007    const pass=g=>{const al=alphaOf(g);if(al<.02)return;c.globalAlpha=al;c.strokeStyle=colOf(g.k);c.beginPath();
1008      for(const a of g.list){const x=Math.cos(a.a)*L,y=Math.sin(a.a)*L;c.moveTo(a.x-x,a.y-y);c.lineTo(a.x+x,a.y+y);}c.stroke();};
1009    const core=PI.core;this.rg.forEach(g=>{if(g.k!==core)pass(g)});
1010    const cg=this.rg.filter(g=>g.k===core);
1011    if(cg.length){c.save();
1012      // the core sits BEHIND the frame: as it rises its sides come out from behind the top V band
1013      if(!o.only){const fp=this.partPolys(PI.frame)[0];c.beginPath();c.rect(-10,-10,this.W+20,this.H+20);fp.forEach((p,i)=>i?c.lineTo(p[0],p[1]):c.moveTo(p[0],p[1]));c.closePath();c.clip('evenodd');}
1014      cg.forEach(pass);
1014c.restore();}
1015    c.fillStyle=ORANGE;c.beginPath();let any=0;
1016    for(const a of this.agents){if(!a.acc)continue;const al=a.cat==='V'?rv:a.cat==='X'?rc:1;if(al<.5)continue;any=1;rsChev(c,a.x,a.y,a.a,5.5);}
1017    c.globalAlpha=MA;if(any)c.fill();c.globalAlpha=1;
1018  }
1019});
1020/* ============================================================
1021   SMALL SCENES: dividers (slow), belief glyphs (move on hover), the lab floor (slow)
1022   ============================================================ */
1023// Dividers. L, round 3 (30 Sep 2026): the colour fades from one section into the next through a skyline of plain isometric
1024// cubes (ported from "Isometric World": light top, mid left, dark right, thin darker edges, bobbing gently).
1025// Founder's round-3 notes: "back to cubes, but less" and "they look like they are suddenly cutting off". So the default
1026// ('cubes') is calmer than round 2 ('old'): smaller cubes, a lower maximum height, and a sparse skyline with gaps (the back
1027// rows are thinned out in clusters, so about half the blocks of round 2 show), and long eased fades at both edges: the cubes
1028// rise out of the colour above over the top ~75% and sink into the colour below over the bottom ~55% (smoothstep, not linear),
1029// so neither edge has a line.
1030// ?div=cubes (the default) | old (round 2's dense skyline and short fade, for comparison) | mix | logos (the logo blocks of
1031// round 2's second pass; ?divr=<0..1> sets the mix share).
1032// Every block is a sprite pre-rendered per size, palette and DPR, then blitted: no path is traced per frame. Static under
1033// reduced motion (t = 0); the loop only draws a divider while it is on screen.
1034const DIV_MODE=({cubes:1,old:1,mix:1,logos:1})[Q.get('div')]?Q.get('div'):'cubes';   // founder's decision, 30 Sep 2026 (round 3): plain cubes
1035const DIV_RATIO=DIV_MODE==='cubes'||DIV_MODE==='old'?0:DIV_MODE==='logos'?1:Q.has('divr')?clamp(parseFloat(Q.get('divr'))||0,0,1):.2;
1036// the mark's faces, in mark units: T top, U the lower top step, L / R left- / right-facing, l / r the recessed wing panels
1037const DIV_FACES=[["T",[31,102,33,116,44,126,434,365,450,373,464,376,484,376,496,374,883,138,906,123,916,110,915,99,905,88,851,53,817,37,792,37,754,57,508,210,484,220,465,220,438,209,185,51,165,40,154,37,129,37,106,47,43,87]],["T",[374,162,375,175,385,186,430,214,462,227,493,225,513,216,562,185,573,171,572,160,562,149,514,118,495,109,477,105,459,107,432,119,383,150]],["U",[23,134,23,233,29,244,39,254,90,289,244,379,457,513,471,518,487,514,871,279,907,254,918,243,923,232,923,134,915,123,905,120,899,121,505,361,486,368,467,369,442,361,48,121,35,121,25,129]],["L",[136,314,41,367,33,378,34,390,53,407,333,579,340,587,348,605,349,942,353,951,369,964,379,966,395,964,464,924,475,913,477,908,476,518,468,509,450,501,385,462,281,393,155,317,142,313]],["L",[42,395,32,398,26,404,23,416,23,733,25,745,32,753,41,758,52,759,67,754,118,726,163,698,171,689,172,478,160,463,54,399]],["l",[173,484,165,495,164,668,167,692,174,699,331,784,339,785,349,782,357,771,357,602,351,588,328,565,202,488,183,481]],["R",[819,316,804,313,786,320,531,480,478,510,471,518,469,906,473,916,480,923,549,963,570,965,583,961,592,952,597,938,599,915,599,789,596,632,597,611,606,587,629,569,904,400,913,390,913,376,904,366]],["R",[905,395,899,396,872,411,833,437,784,466,775,478,774,675,776,689,783,697,854,739,890,758,896,759,906,758,913,754,924,740,923,409,915,398]],["r",[774,484,764,481,757,482,718,504,613,569,593,592,589,606,589,768,592,777,598,783,608,786,614,785,768,702,780,692,782,687,782,495]]].map(([c,a])=>{const p=new Path2D();for(let i=0;i<a.length;i+=2)p[i?'lineTo':'moveTo'](a[i],a[i+1]);p.closePath();return[c,p]});
1038let DIV_PAL={bone:{top:'#E4DCCA',left:'#D2C9B5',right:'#BCB19B',edge:'rgba(18,29,26,.12)',ink:'#A99E86'},
1039               cobalt:{top:'#324E48',left:'#2E4A44',right:'#253E39',edge:'rgba(18,32,29,.45)',ink:'#213833'},
1040               // 1 Oct 2026: a few cubes in every divider are orange
1041               orange:{top:'#EE6A33',left:'#D65A27',right:'#B24A1E',edge:'rgba(18,29,26,.22)',ink:'#B24A1E'}};
1042const DIV_HL=.78;   // how far the mark stands above its column's top face (units of u): the mark is 1.78 u tall, a cube top 1 u
1043const DIV_SPR=new Map();
1044// round 5 (30 Sep 2026): divider options for the founder to pick from (?dv=1|2|3; the default unchanged until then).
1045//   1 an even low skyline: a continuous band of small cubes, no holes, gentle height variation (1 to 3 cubes)
1046//   2 a single soft row of small cubes along the boundary, slight height jitter
1047//   3 J's original density and look, lighter (lower contrast) and lower
1048// founder's pick (30 Sep 2026, round 6): 1, the even low skyline, is the default;
1048 ?dv=0 shows the old default, ?dv=2|3 the others
1049const DV=Q.has('dv')&&[0,1,2,3].includes(+Q.get('dv'))?+Q.get('dv'):1;
1050if(DV===3){const mh=(a,b,k)=>{const A=hex(a),B=hex(b);return'#'+A.map((v,i)=>Math.round(lerp(v,B[i],k)).toString(16).padStart(2,'0')).join('')};
1051  const L=(q,k)=>Object.assign({},q,{left:mh(q.top,q.left,k),right:mh(q.top,q.right,k)});
1052  DIV_PAL.bone=Object.assign(L(DIV_PAL.bone,.55),{edge:'rgba(18,29,26,.06)'});DIV_PAL.cobalt=Object.assign(L(DIV_PAL.cobalt,.5),{edge:'rgba(18,32,29,.22)'});}
1053// a column: a 1 x 1 block whose sides run far enough down to leave the canvas (it is always drawn behind the next row)
1054function divCol(u,dpr,pk,H,one){const k='c|'+u+'|'+dpr+'|'+pk+'|'+H+'|'+(one?1:0);let s=DIV_SPR.get(k);if(s)return s;
1055  const q=DIV_PAL[pk],m=2,hz=one?1:Math.ceil(H/u)+3,w=Math.ceil(2*u*C30)+m*2,h=Math.ceil(u+hz*u)+m*2,c=document.createElement('canvas');
1056  c.width=Math.ceil(w*dpr);c.height=Math.ceil(h*dpr);const x=c.getContext('2d');x.setTransform(dpr,0,0,dpr,0,0);
1057  box(x,{u,ox:m+u*C30,oy:m},0,0,-hz,1,1,hz,pal(q.top,q.left,q.right),q.edge);
1058  s={c,w:c.width/dpr,h:c.height/dpr,ax:m+u*C30,ay:m};DIV_SPR.set(k,s);return s;}
1059// a logo block: the mark as wide as a cube's top face, its faces in the cube tones, its outline as the edges
1060function divLogo(u,dpr,pk){const k='m|'+u+'|'+dpr+'|'+pk;let s=DIV_SPR.get(k);if(s)return s;
1061  const q=DIV_PAL[pk],m=2,sc=2*u*C30/930,w=930*sc+m*2,h=956*sc+m*2,c=document.createElement('canvas');
1062  c.width=Math.ceil(w*dpr);c.height=Math.ceil(h*dpr);const x=c.getContext('2d');x.setTransform(dpr*sc,0,0,dpr*sc,dpr*(m-8*sc),dpr*(m-22*sc));
1063  const T={T:q.top,U:mixC(q.top,q.left,.45),L:q.left,l:mixC(q.left,q.right,.5),R:q.right,r:mixC(q.right,'#121D1A',.18)};
1064  for(const[cl,p]of DIV_FACES){x.fillStyle=T[cl];x.fill(p);}
1065  x.fillStyle=q.ink;x.fill(MARK_FULL,'evenodd');
1066  s={c,w:c.width/dpr,h:c.height/dpr,ax:m+465*sc,ay:m};DIV_SPR.set(k,s);return s;}
1067// a smooth value noise along one axis (clusters for the skyline's gaps)
1068const vnoise=(x,sd)=>{const i=Math.floor(x),f=x-i,a=hash(i,sd),b=hash(i+1,sd),q=f*f*(3-2*f);return a+(b-a)*q};
1069// an eased vertical fade: colour c from alpha a0 at y0 to a1 at y1, smoothstep through many stops (no visible band edges)
1070function divFade(ctx,W,y0,y1,c,a0,a1,soft){const g=ctx.createLinearGradient(0,y0,0,y1);
1071  // soft (1 Oct 2026): smootherstep, flat at both ends, so there is no visible start or end to the fade
1072  for(let k=0,N=soft?24:12;k<=N;k++){const q=k/N,e=soft?q*q*q*(q*(6*q-15)+10):q*q*(3-2*q),a=a0+(a1-a0)*e;g.addColorStop(q,c+Math.round(clamp(a,0,1)*255).toString(16).padStart(2,'0'));}
1073  ctx.fillStyle=g;ctx.fillRect(0,Math.min(y0,y1),W,Math.abs(y1-y0));}
1074function drawDivider(D,t){
1075  const {ctx,W,H,dpr}=D; const from=D.from, to=D.to, pk=D.pal==='bone'?'bone':'cobalt', OLD=DV===3||(!DV&&DIV_MODE==='old');
1076  if(!D.P){ const u=DV===1?worldU(innerWidth<760):DV===2?(W<700?11:15):DV===3?(W<700?13:19):OLD?(W<700?16:24):(W<700?12:17); D.P={u,ox:W/2,oy:30}; D.cols=[]; /* oy 30 (B: 18) so the tallest bobbing block never clips on the canvas top edge */
1077    const n=Math.ceil(W/(u*C30))+8;let lift=0,raised=0;
1078    // the calm skyline: its back rows start lower in the band (the top fade has room), and are thinned out in clusters
1079    if(!OLD||DV)D.P.oy=Math.round(H*(DV===1?.36:DV===2?.4:DV===3?.3:(W<700?.36:.33)));
1080    for(let i=-n;i<n;i++)for(let r=0;r<Math.ceil(H/(u*.5))+8;r+=1){ const j=r-i; const sx=D.P.ox+(i-j)*u*C30; if(sx<-u*2||sx>W+u*2) continue;
1081      const hh=hash(i,j*1.7);
1082      let base;
1083      if(DV===1){ if(r<3)base=-2; else{const k=r-3;if(k>7)continue;const q=vnoise(sx/(u*4.5),3.1+(D.ix||0)*1.3)*.55+hh*.45;
1084          base=(k<2?(q<.5?0:q<.82?1:2):k<4?(q<.6?0:.5):0)+k*-.05;
1085          if(D.P.oy+u*(1.05*r-2.2-base)>H*.95)continue;} }
1086      else if(DV===2){ if(r<3)base=-2; else if(r===3)base=hh*.35; else continue; }
1087      else if(OLD) base = r<3?-2: ((hh<.4?0:hh<.75?.5:hh<.93?1:1.5)*(DV===3?.6:1)) + (r-3)*-.05;
1088      else{ if(r<3){base=-2;}
1089        else{ // presence: clusters of gaps (a slow noise across the band) plus a light random thinning, strongest in the back rows
1090          const k=r-3,keep=vnoise(sx/(u*5.5),7.3+(D.ix||0)*1.9)*.8+hash(i*1.3,j*.7+4.1)*.45-.4+k*.1;
1091          if(keep<.5)continue;
1092          base=(hh<.6?0:hh<.87?.28:hh<.97?.5:.72)+k*-.06; } }
1093      const logo=r>=3&&hash(i*.37+(D.ix||0)*5.1,j*1.13+2.7)<DIV_RATIO;
1094      if(logo){const y=D.P.oy+r*u*.5-(2.2-r*.55+base+.45+DIV_HL)*u;   // the mark's top at the highest bob
1095        if(y<2){if(DIV_RATIO<1){D.cols.push({i,j,r,base,logo:false});continue;} lift=Math.max(lift,2-y);}}
1096      if(r>=3&&r<9)raised++;
1097      D.cols.push({i,j,r,base,logo,or:false}   /* founder 1 Oct 2026: no orange cubes in the dividers */); }
1098    D.P.oy+=Math.ceil(lift);   // only with ?div=logos: the whole band moves down so the tallest mark never clips
1099    D.cols.sort((a,b)=>(a.i+a.j)-(b.i+b.j)); D.nBlocks=raised; }
1100  const P=D.P,u=P.u; ctx.setTransform(D.dpr,0,0,D.dpr,0,0); ctx.fillStyle=from; ctx.fillRect(0,0,W,H);
1101  // round 3: the ground behind the cubes already turns from the colour above to the colour below (eased), so the gaps in the
1102  // skyline never show a line where the solid band starts
1103  if(!OLD||DV)divFade(ctx,W,DV===1?H*.42:H*.18,H*.9,to,0,1,DV===1);
1104  if(DV){ctx.fillStyle=to;ctx.fillRect(0,H*.9-.5,W,H*.1+1);}
1105  const col=divCol(u,dpr,pk,H,DV===2),colO=divCol(u,dpr,'orange',H,DV===2),lg=DIV_RATIO>0?divLogo(u,dpr,pk):null;
1106  for(const c of D.cols){ const wob = RM?0:Math.max(0,Math.sin(t*.7+c.i*.9+c.j*.4)-.85)*(DV?3:OLD?6:4);
1107    const top=(c.r<3?-40:(2.2 - c.r*.55 + c.base + wob*.5));
1108    if(top<-30) continue;
1109    const X=P.ox+(c.i-c.j)*u*C30,Y=P.oy+(c.i+c.j)*u*.5-top*u;
1110    const cc=c.or?colO:col;ctx.drawImage(cc.c,X-cc.ax,Y-cc.ay,cc.w,cc.h);
1111    if(c.logo)ctx.drawImage(lg.c,X-lg.ax,Y-DIV_HL*u-lg.ay,lg.w,lg.h); }
1112  if(OLD&&!DV){const g=ctx.createLinearGradient(0,H*.55,0,H); g.addColorStop(0,to+'00'); g.addColorStop(1,to+'ff');
1113    ctx.fillStyle=g; ctx.fillRect(0,H*.55,W,H*.45);}
1114  else{
1115    // the long, eased fades: the colour above lies over the skyline's top and thins out slowly (the cubes rise out of it);
1116    // the colour below rises from the middle and covers everything by the bottom edge
1117    if(DV===2){divFade(ctx,W,0,H*.4,from,1,0);divFade(ctx,W,H*.5,H,to,0,1);}
1118    // DV 1 (1 Oct 2026, founder: "the fade is like a cut"): longer, smootherstep fades; the cubes come out of the colour above
1119    // gradually and sink into the colour below just as gradually
1120    // founder, 1 Oct 2026 (late): no wash of the colour above over the cubes: the skyline stands solid on it; below, the cubes
1121    // show for longer and only then sink smoothly into the colour below
1122    else if(DV===1){divFade(ctx,W,H*.6,H*.97,to,0,1,1);ctx.fillStyle=to;ctx.fillRect(0,H*.97-.5,W,H*.03+1);}
1123    else{divFade(ctx,W,0,H*.72,from,1,0);divFade(ctx,W,H*.46,H,to,0,1);}}
1124  // the last rows are solid "to", so the canvas edge never shows a seam
1125  ctx.fillStyle=to; ctx.fillRect(0,H-3,W,3);
1126}
1127function drawGlyph(G,t,still){
1128  const {ctx,W,H,kind}=G;ctx.setTransform(G.dpr,0,0,G.dpr,0,0);ctx.clearRect(0,0,W,H);
1129  const S=RM||still;
1130  const P=fit(W,H,[0,3,0,3,0,2.8],8,0);const st='rgba(18,29,26,.6)';ctx.lineWidth=1;
1131  const it=[];const b=(x,y,z,w,d,h,p)=>it.push([x+w/2+y+d/2,z,()=>box(ctx,P,x,y,z,w,d,h,p,st)]);
1132  const k=S?.5:(Math.sin(t*1.4)*.5+.5);
1133  if(kind==='lab'){b(0,0,0,3,3,.3,P_FLOOR);b(1,1,.3+k*.5,1,1,1,P_OR);
1134    [[0,0],[2.7,0],[0,2.7],[2.7,2.7]].forEach(([x,y])=>b(x,y,.3,.3,.3,1.9,P_COB));
1135    b(0,0,2.2,3,.3,.3,P_COB);b(0,2.7,2.2,3,.3,.3,P_COB);b(0,.3,2.2,.3,2.4,.3,P_COB);b(2.7,.3,2.2,.3,2.4,.3,P_COB);}
1136  else if(kind==='train'){b(0,2,0,3,1,.7,P_COB);b(0,1,0,3,1,1.4,P_COB);b(0,0,0,3,1,2.1,P_COB);
1137    const q=S?1:((t*.45)%1),st2=Math.min(2,Math.floor(q*3.2)),z=[.7,1.4,2.1][st2];b(1,2.1-st2+.1,z,.8,.8,.8,P_OR);}
1138  else if(kind==='evidence'){b(0,0,0,3,3,.3,P_FLOOR);const g1=S?1:ss(0,.5,(t*.3)%1.2);b(.3,2,.3,.8,.8,.2+.4*g1,P_FOG);b(1.1,1.1,.3,.8,.8,.3+.9*g1,P_FOG);b(2,.3,.3,.8,.8,.4+1.7*g1,P_OR);}
1139  else{b(0,0,0,3,3,.3,P_FLOOR);b(.2,.2,.3,1.2,1.2,1.4,P_COB);b(1.6,1.6,.3,1.2,1.2,1.4,P_INK);
1140    const q=S?.5:(Math.sin(t*.9)*.5+.5);b(.4+q*1.4,.4+q*1.4,1.7+Math.sin(q*Math.PI)*.8,.8,.8,.4,P_OR);}
1141  it.sort((a,b)=>a[0]-b[0]||a[1]-b[1]);it.forEach(o=>o[2]());
1142  ctx.strokeStyle=INK;ctx.lineWidth=1.3;ctx.lineCap='round';ctx.beginPath();
1143  for(let n=0;n<5;n++){const q=((S?n*.2:t*.12+n*.2)%1)*12;const side=Math.floor(q/3),f=q%3;
1144    const pts=[[f,-.35],[3.35,f],[3-f,3.35],[-.35,3-f]][side];const d=[[1,0],[0,1],[-1,0],[0,-1]][side];
1145    const a=projPt(P,pts[0]-d[0]*.13,pts[1]-d[1]*.13,0),c=projPt(P,pts[0]+d[0]
1145*.13,pts[1]+d[1]*.13,0);ctx.moveTo(a[0],a[1]);ctx.lineTo(c[0],c[1]);}
1146  ctx.stroke();
1147}
1148// Fig. 2 (ported from "Isometric World", same scene, same speed): work moves through the rooms and passes a human check.
1149// One change from B: every item passes. The gate lights as each item goes under it, the item turns sage there,
1150// then leaves along the dark lane (it sinks away at the end instead of hanging off the edge and vanishing).
1151const P_BONE=pal('#E9E2D2','#CCC3B0','#9FB0A8');
1152function FloorScene(cv){
1153  const S={cv,t:0};
1154  S.resize=()=>{Object.assign(S,setupCanvas(cv));S.mob=S.W<700;
1155    if(S.mob)S.P=fit(S.W,S.H,[0,11,0,7,-.4,2.2],14,0);
1156    else{const P=fit(S.W*.62,S.H*.9,[0,11,0,7,-.4,2.2],24,0);P.ox+=S.W*.36;P.oy+=S.H*.06;S.P=P;}};
1157  S.resize();
1158  S.nAg=4;
1159  S.step=dt=>{S.t+=dt;};
1160  const path=[[.2,1],[2,2.5],[3.5,2.5],[5,4.5],[7,4.5],[8.3,3.1],[9.6,3.1],[9.6,3.1],[11.4,3.1]];
1161  const seglen=[];let total=0;for(let k=0;k<path.length-1;k++){const l=Math.hypot(path[k+1][0]-path[k][0],path[k+1][1]-path[k][1])||1.1;seglen.push(l);total+=l;}
1162  S.draw=()=>{const {ctx,W,H,P}=S,t=RM?4:S.t;ctx.setTransform(S.dpr,0,0,S.dpr,0,0);ctx.clearRect(0,0,W,H);
1163    const it=[];
1164    for(let i=0;i<11;i++)for(let j=0;j<7;j++)it.push([i+j+1,-1,()=>box(ctx,P,i,j,-.4,1,1,.4,i===10?P_INK:P_FLOOR,'rgba(18,29,26,.14)')]);
1165    const wall=(x,y)=>it.push([x+.12+y+.5+.05,0,()=>box(ctx,P,x,y,0,.24,1,.95,P_COB,'rgba(18,29,26,.25)')]);
1166    for(let j=0;j<7;j++){if(j!==2)wall(3.4,j);if(j!==4)wall(6.8,j);}
1167    let gateGlow=0;const N=4,period=7.2;
1168    for(let n=0;n<N;n++){
1169      const ph=((t/period)+n/N)%1;let s=ph*total,k=0;while(k<seglen.length-1&&s>seglen[k]){s-=seglen[k];k++;}
1170      const f=Math.min(1,s/seglen[k]);const A=path[k],B=path[k+1];const x=A[0]+(B[0]-A[0])*f,y=A[1]+(B[1]-A[1])*f;
1171      // every item passes: it lights the gate while it stands under it, and turns sage as it is approved
1172      if(k===6)gateGlow=Math.max(gateGlow,Math.sin(Math.PI*f));
1173      const ok=k>6||(k===6&&f>=.5);
1174      // grow in at the start, sink away at the end of the out lane
1175      const sc=clamp(ph*total/.6,0,1)*clamp((11.3-x)/.9,0,1);if(sc<=.01)continue;const sz=.6*sc;
1176      it.push([x+y+.7,1,()=>box(ctx,P,x-sz/2,y-sz/2,0,sz,sz,sz,ok?P_OR:P_COB,'rgba(18,29,26,.35)')]);
1177    }
1178    const gp=gateGlow>.01?{top:mixC('#3A5650','#F47F4A',gateGlow),left:mixC('#121D1A','#E0602B',gateGlow),right:mixC('#121D1A','#B24A1E',gateGlow)}:P_INK;
1179    it.push([9.4+2.3+.2,0,()=>box(ctx,P,9.3,2.2,0,.26,.26,1.6,P_INK,null)]);
1180    it.push([9.4+4.0+.2,0,()=>box(ctx,P,9.3,3.8,0,.26,.26,1.6,P_INK,null)]);
1181    it.push([9.4+4.1+.3,2,()=>box(ctx,P,9.3,2.2,1.6,.26,1.86,.3,gp,'rgba(18,29,26,.3)')]);
1182    it.sort((a,b)=>a[0]-b[0]||a[1]-b[1]);it.forEach(o=>o[2]());
1183    // labels: B's leaders, set on small solid tags so they read on this section's dark ground
1184    const fs=S.mob?10:12;ctx.strokeStyle=BONE;ctx.lineWidth=1;
1185    const g=projPt(P,9.43,2.2,2.3);ctx.beginPath();ctx.moveTo(g[0],g[1]);ctx.lineTo(g[0],g[1]-30);ctx.stroke();
1186    chip(ctx,'Human check',g[0],g[1]-40,'center',fs,INK,BONE);
1187    const e=projPt(P,0,1.5,.1);ctx.beginPath();ctx.moveTo(e[0],e[1]);ctx.lineTo(e[0]-12,e[1]-30);ctx.lineTo(e[0]-26,e[1]-30);ctx.stroke();
1188    chip(ctx,'Work in',e[0]-26,e[1]-30,'right',fs,INK,BONE);
1189    const o=projPt(P,10.5,3.1,.1);ctx.beginPath();ctx.moveTo(o[0],o[1]);ctx.lineTo(o[0]+16,o[1]+26);ctx.lineTo(o[0]+30,o[1]+26);ctx.stroke();
1190    chip(ctx,'Out',o[0]+30,o[1]+26,'left',fs,CREAM,INK);
1191  };
1192  return S;
1193}
1194
1195/* ============================================================
1196   OUT OF THE LAB: three small isometric scenes, each drawn by an organized swarm.
1197   Every block is traced by agents at one even spacing along its visible edges and hatched on a lattice across its
1198   visible faces (same stroke length and direction per face). Moving work runs on lanes at even spacing.
1199   ============================================================ */
1200const isoAng=(dx,dy,dz)=>Math.atan2((dx+dy)*.5-dz,(dx-dy)*C30);
1201function boxSwarm(w,d,h,es,sp,faces){
1202  const out=[],E=(p,q)=>{const L=Math.hypot(q[0]-p[0],q[1]-p[1],q[2]-p[2]),n=Math.max(1,Math.round(L/es)),a=isoAng(q[0]-p[0],q[1]-p[1],q[2]-p[2]);
1203    for(let j=0;j<n;j++){const f=(j+.5)/n;
1203out.push({l:[p[0]+(q[0]-p[0])*f,p[1]+(q[1]-p[1])*f,p[2]+(q[2]-p[2])*f],a0:a,tone:'E'})}};
1204  E([0,0,h],[w,0,h]);E([w,0,h],[w,d,h]);E([w,d,h],[0,d,h]);E([0,d,h],[0,0,h]);
1205  if(h>.12){E([w,0,0],[w,0,h]);E([w,d,0],[w,d,h]);E([0,d,0],[0,d,h]);E([w,0,0],[w,d,0]);E([w,d,0],[0,d,0]);}
1206  const F=(tone,A,B,pt,a0)=>{const n1=Math.round(A/sp),n2=Math.round(B/sp);if(n1<1||n2<1)return;const m1=Math.min(.2,es*.45/A),m2=Math.min(.2,es*.45/B);
1207    for(let j=0;j<n2;j++)for(let i=0;i<n1;i++)out.push({l:pt(m1+(1-2*m1)*(i+.5)/n1,m2+(1-2*m2)*(j+.5)/n2),a0,tone});};
1208  if(faces.includes('t'))F('T',w,d,(u,v)=>[u*w,v*d,h],0);
1209  if(faces.includes('r'))F('R',d,h,(u,v)=>[w,u*d,v*h],.846);
1210  if(faces.includes('l'))F('L',w,h,(u,v)=>[u*w,d,v*h],-.846);
1211  return out;
1212}
1213const SC_TONES={
1214  light:{E:[18,29,26,1],T:[18,29,26,.72],L:[18,29,26,.6],R:[18,29,26,.5],OE:[18,29,26,1],OT:[50,78,72,.95],OL:[50,78,72,.85],OR:[43,70,65,.85],FE:[18,29,26,.6],FT:[18,29,26,.38],FL:[18,29,26,.32],FR:[18,29,26,.28]},
1215  dark:{E:[246,240,228,1],T:[240,234,220,.95],L:[218,210,193,.85],R:[178,200,190,.9],OE:[252,249,242,1],OT:[250,246,238,1],OL:[240,234,222,1],OR:[226,219,204,1],FE:[228,220,202,.45],FT:[228,220,202,.3],FL:[228,220,202,.25],FR:[228,220,202,.2]}
1216};
1217const SC_FILL={light:pal('#E9E2D2','#DCD4C2','#CFC5B0'),dark:pal('#3A5953','#33504A','#2B4641'),lightO:pal('#C9D6CF','#B3C3BA','#9FB2A8'),darkO:pal('#3A5953','#33504A','#2B4641')};
1218const SCENES={
1219  products:{mode:'light',plate:false,bounds:[.4,9.1,.5,6.8,0,4.3],sbox:[-5.7,7.6,-1.6,7.1],es:.24,sp:.32,
1220    build(){return[{id:'p1',x:.5,y:3.7,z:0,w:3,d:3,h:.35,faces:'tlr'},{id:'tower',x:1.3,y:4.5,z:.35,w:1.4,d:1.4,h:3.8,faces:'tlr',sp:.9,order:1},
1221      {id:'p2',x:5.4,y:.6,z:0,w:3.6,d:3,h:.35,faces:'tlr',order:2},{id:'screen',x:5.8,y:1.0,z:.35,w:2.8,d:.2,h:2.1,faces:'t',sp:.8,order:3}];},
1222    flows(){return[{ring:[1.12,4.32,1.76],n:24,v:.55,tone:'O',zf:t=>.6+(RM?.55:(Math.sin(t*1.25)*.5+.5))*3.3}];},
1223    bars:{x:6.02,y:1.0,n:8,dx:.31,w:.2,d:.2,z:.5,max:1.55},
1224    labels:[{at:[3.5,6.7,0],dx:12,dy:22,t:'RealSight',al:'left'},{at:[9,3.6,0],dx:12,dy:22,t:'ADRODUB',al:'left'}]},
1225  audit:{mode:'light',bounds:[0,10,0,6.5,-.2,3.8],es:.27,sp:.4,
1226    stacks:[{x:5.0,y:4.4,tone:'O',t:'AI fully'},{x:6.9,y:2.5,tone:'',t:'With approval'},{x:8.8,y:.6,tone:'F',t:'Not at all'}],
1227    build(){const B=[],order=[0,1,0,2,0,1,0,1,2,0,1,2],cnt=[0,0,0];
1228      order.forEach((g,k)=>B.push({id:'c'+k,cube:1,g,w:.8,d:.8,h:.8,faces:'tlr',nx:.3+hash(k,1)*3.4,ny:.3+hash(k,2)*5,nz:.5+hash(k,3)*2.2,sz:cnt[g]++*.8,ph:hash(k,4)*6,order:k*.5}));return B;},
1229    flows(){return[];},
1230    labels:[{at:[1.9,5.9,0],dx:0,dy:26,t:'Every recurring task',al:'center'}]}
1231};
1232function SwarmScene(cv){
1233  const kind=cv.dataset.scene,def=SCENES[kind],S={cv,kind,def,t:0,started:RM,dark:def.mode==='dark'};
1234  S.resize=()=>{Object.assign(S,setupCanvas(cv));S.mob=S.W<560;const pd=S.mob?8:14;
1235    // sbox: the drawing's real extent on screen (iso units), so it fills the canvas instead of its much taller bounding box
1236    if(def.sbox){const b=def.sbox,u=Math.min((S.W-2*pd)/(b[1]-b[0]),(S.H-2*pd)/(b[3]-b[2]));S.P={u,ox:S.W/2-(b[0]+b[1])/2*u,oy:S.H/2-(b[2]+b[3])/2*u};}
1237    else S.P=fit(S.W,S.H,def.bounds,pd,0);};
1238  S.resize();
1239  SEED=41+kind.length*7;
1240  S.boxes=def.build().map((b,i)=>Object.assign(b,{ag:boxSwarm(b.w,b.d,b.h,def.es,def.sp*(b.sp||1),b.faces)}));
1241  S.boxes.forEach((b,i)=>{const o=b.order!=null?b.order:i;b.ag.forEach(a=>{a.del=.1+o*.16+srnd()*.55;a.dur=.8+srnd()*.4;a.sx=-.35-srnd()*.5;a.sy=(srnd()-.5)*.5;})});
1242  S.flows=def.flows();S.n=S.boxes.reduce((q,b)=>q+b.ag.length,0)+S.flows.reduce((q,f)=>q+f.n,0)+(def.bars?def.bars.n*8:0);
1243  const TN=SC_TONES[def.mode];
1244  const col=(tone,al)=>{const c=TN[tone]||TN.E;return`rgba(${c[0]},${c[1]},${c[2]},${(c[3]*(al==null?1:al)).toFixed(3)})`};
1245  S.step=dt=>{if(S.started&&!RM)S.t+=dt;};
1246  // where a box is right now (the audit cubes travel between the pile and their stack)
1247  const place=(b,t)=>{if(!b.cube)return;const st=def.stacks[b.g],cyc=RM?7:(t+3)%10,k=S.boxes.indexOf(b);
1248    let f=clamp((cyc-(.6+k*.38))/.9,0,1);if(cyc>8.6)f=1-clamp((cyc-8.6)/.9,0,1);const e=easeIO(f);
1249    const j=RM?0:1-e;b.x=b.nx+(st.x-b.nx)*e+Math.sin(t*2.3+b.ph)*.12*j;b.y=b.ny+(st.y-b.ny)*e+Math.cos(t*1.9+b.ph)*.12*j;b.z=b.nz+(b.sz-b.nz)*e+Math.sin(Math.PI*e)*1.1;
1250    b.tone=e>.98?st.tone:'F';};
1251  const lw=()=>S.mob?1.25:(S.dark?1.7:1.5);
1252  S.draw=()=>{const {ctx,W,H,P}=S,t=S.t;ctx.setTransform(S.dpr,0,0,S.dpr,0,0);ctx.clearRect(0,0,W,H);ctx.lineCap='round';
1253    const u=P.u,Lf=def.sp*u*.3,Le=def.es*u*.36;
1254    // the plate + a faint floor grid
1255    const bd=def.bounds;if(def.plate!==false){box(ctx,P,bd[0],bd[2],-.2,bd[1]-bd[0],bd[3]-bd[2],.2,S.dark?pal('#38564F','#314E48','#2A4540'):pal('#DED6C5','#D0C7B3','#BEB39E'),S.dark?'rgba(20,36,32,.5)':'rgba(18,29,26,.14)');
1256    ctx.strokeStyle=S.dark?'rgba(20,36,32,.35)':'rgba(18,29,26,.07)';ctx.lineWidth=1;ctx.beginPath();
1257    for(let i=bd[0]+1;i<bd[1];i++){const a=projPt(P,i,bd[2],0),c=projPt(P,i,bd[3],0);ctx.moveTo(a[0],a[1]);ctx.lineTo(c[0],c[1]);}
1258    for(let j=bd[2]+1;j<bd[3];j++){const a=projPt(P,bd[0],j,0),c=projPt(P,bd[1],j,0);ctx.moveTo(a[0],a[1]);ctx.lineTo(c[0],c[1]);}ctx.stroke();}
1259    if(def.stacks){ctx.save();ctx.setLineDash([4,4]);ctx.strokeStyle='rgba(18,29,26,.45)';def.stacks.forEach(st=>{ctx.beginPath();
1259[[0,0],[.8,0],[.8,.8],[0,.8],[0,0]].forEach(([dx,dy],i)=>{const p=projPt(P,st.x+dx,st.y+dy,0);i?ctx.lineTo(p[0],p[1]):ctx.moveTo(p[0],p[1])});ctx.stroke();});ctx.restore();}
1260    const items=[];
1261    // blocks: a quiet fill (so nearer blocks hide farther agents), then their agents
1262    for(const b of S.boxes){place(b,t);
1263      items.push([b.x+b.w/2+b.y+b.d/2,b.z,()=>{
1264        const tn=b.tone||'',fp=S.dark?SC_FILL.dark:(tn==='O'?SC_FILL.lightO:SC_FILL.light);
1265        box(ctx,P,b.x,b.y,b.z,b.w,b.d,b.h,fp,null);
1266        const groups={};for(const a of b.ag){const p=RM?1:clamp((t-a.del)/a.dur,0,1);if(p<=0)continue;
1267          const h=projPt(P,b.x+a.l[0],b.y+a.l[1],b.z+a.l[2]);let x=h[0],y=h[1],ang=a.a0;
1268          if(p<1){const q=1-Math.pow(1-p,3),sx=h[0]+a.sx*W,sy=h[1]+a.sy*H;x=sx+(h[0]-sx)*q;y=sy+(h[1]-sy)*q+Math.sin(p*Math.PI)*-18;ang=a.a0+lineWrap(Math.atan2(h[1]-sy,h[0]-sx)-a.a0)*(1-ss(.55,1,p));}
1269          const g=tn+a.tone;(groups[g]||(groups[g]=[])).push([x,y,ang,a.tone==='E'?Le:Lf]);}
1270        for(const g in groups){const L=groups[g],path=()=>{ctx.beginPath();for(const [x,y,a,l] of L){const c=Math.cos(a)*l,s=Math.sin(a)*l;ctx.moveTo(x-c,y-s);ctx.lineTo(x+c,y+s)}};
1271          if(S.dark){ctx.lineWidth=lw()+1.6;ctx.strokeStyle='rgba(16,30,27,.45)';path();ctx.stroke();}
1272          ctx.lineWidth=lw();ctx.strokeStyle=col(g in TN?g:g.slice(-1));path();ctx.stroke();}
1273      }]);}
1274    // lanes: work moving at even spacing; past a gate it turns to the highlight (approved)
1275    for(const f of S.flows){let pts=f.pts;const z=f.zf?f.zf(t):null;
1276      if(f.ring){const [x0,y0,s]=f.ring;pts=[[x0,y0,z],[x0+s,y0,z],[x0+s,y0+s,z],[x0,y0+s,z],[x0,y0,z]];}
1277      let L=0;const segs=[];for(let i=1;i<pts.length;i++){const a=pts[i-1],b=pts[i],l=Math.hypot(b[0]-a[0],b[1]-a[1],b[2]-a[2]);segs.push([a,b,l]);L+=l;}
1278      for(let i=0;i<f.n;i++){let d=(((RM?0:t)*f.v+i*L/f.n)%L+L)%L;let k=0;while(k<segs.length-1&&d>segs[k][2]){d-=segs[k][2];k++;}
1279        const [a,b,l]=segs[k],q=d/l,x=a[0]+(b[0]-a[0])*q,y=a[1]+(b[1]-a[1])*q,zz=a[2]+(b[2]-a[2])*q;
1280        const ok=f.tone==='O'||(f.gate!=null&&y>f.gate);const ang=isoAng(b[0]-a[0],b[1]-a[1],b[2]-a[2]);
1281        const vis=RM?1:clamp((t-.2-i*.03)/.5,0,1);if(vis<=0)continue;
1282        items.push([x+y+(f.ring?.001:.3),zz+.01,()=>{const p=projPt(P,x,y,zz),l2=Le*.95;const c=Math.cos(ang)*l2,s=Math.sin(ang)*l2;
1283          ctx.beginPath();ctx.moveTo(p[0]-c,p[1]-s);ctx.lineTo(p[0]+c,p[1]+s);
1284          if(S.dark){ctx.lineWidth=lw()+2.4;ctx.strokeStyle='rgba(16,30,27,.45)';ctx.stroke();}
1285          ctx.lineWidth=lw()+1;ctx.strokeStyle=ok?ORANGE:col('E');ctx.globalAlpha=vis;ctx.stroke();ctx.globalAlpha=1;}]);}}
1286    // audio bars: stacks of level dashes that rise and fall
1287    if(def.bars){const B=def.bars;for(let k=0;k<B.n;k++){const x=B.x+k*B.dx,h=.2+(RM?.5:Math.abs(Math.sin(t*3.1+k*1.1)*Math.cos(t*1.7+k*.6)))*B.max,n=Math.max(1,Math.round(h/.15));
1288      items.push([1e3,1,()=>{ctx.beginPath();for(let j=0;j<n;j++){const a=projPt(P,x,B.y+B.d,B.z+j*.15),c=projPt(P,x+B.w,B.y+B.d,B.z+j*.15);ctx.moveTo(a[0],a[1]);ctx.lineTo(c[0],c[1]);}
1289        ctx.lineWidth=lw()+2.4;ctx.strokeStyle='rgba(16,30,27,.45)';ctx.stroke();ctx.lineWidth=lw()+.8;ctx.strokeStyle=k===3?ORANGE:col('E');ctx.stroke();}]);}}
1290    items.sort((a,b)=>a[0]-b[0]||a[1]-b[1]);items.forEach(o=>o[2]());
1291    // labels with short leaders
1292    const lc=S.dark?BONE:INK,fs=S.mob?10:12;ctx.lineWidth=1;ctx.strokeStyle=lc;
1293    const lab=(at,dx,dy,txt,al)=>{const p=projPt(P,at[0],at[1],at[2]);ctx.beginPath();ctx.moveTo(p[0],p[1]);ctx.lineTo(p[0]+dx,p[1]+dy);ctx.stroke();
1294      const tx=p[0]+dx+(al==='left'?4:al==='center'?0:-4),ls=txt.split('\n'),y0=p[1]+dy+(dy>0?8:-6-(ls.length-1)*(fs+5));
1295      ls.forEach((t2,i)=>haloLabel(ctx,t2,tx,y0+i*(fs+5),lc,S.dark?COBALT:BONE,al==='left'?'left':al==='center'?'center':'right',fs));ctx.lineWidth=1;};
1296    const la=RM?1:ss(1.2,2,t);ctx.globalAlpha=la;
1297    (def.labels||[]).forEach(L=>{if(L.desk&&S.mob)return;lab(L.at,L.dx,L.dy,L.t,L.al)});
1298    if(def.stacks)def.stacks.forEach(st=>{const p=projPt(P,st.x+.8,st.y+.8,0);haloLabel(ctx,st.t,p[0],p[1]+20,INK,BONE,'center',fs)});
1299    ctx.globalAlpha=1;
1300  };
1301  return S;
1302}
1303
1304/* ============================================================
1305   OUTPUT A: two sibling drawings, one per product, in the site's swarm style.
1306   Laid out on screen axes: a = to the right, b = down, z = up (a box at (a,b) is still a true isometric box).
1307   ADROSUITE (ready-made): one compact unit. Messages from WhatsApp, Instagram, web chat and phone come in; its modules
1308     (chat replies, calls, bookings, follow-up, marketing, reviews) light up as they work, and everything lands on one
1309     dashboard on top, where a member of staff approves.
1310   ADROTEAM (built for you): a four-floor building, one department per floor, agents working on each floor;
1311     finished work drops out of its floor, stops at one person who checks it, and only then goes on to Done.
1312   ============================================================ */
1313function OfficeScene(cv){
1314  const kind=cv.dataset.office||'suite',S={cv,kind,t:0,started:RM};
1315  const K=.5/C30,es=.2,sp=.3;
1316  const TN=SC_TONES.light,col=(tone,al)=>{const c=TN[tone]||TN.E;return`rgba(${c[0]},${c[1]},${c[2]},${(c[3]*(al==null?1:al)).toFixed(3)})`};
1317  // a box centred at (a,b) on screen axes, w along world x, d along world y
1318  const wbox=(a,b,z,w,d,h,faces,tone,order)=>{const o={x:a*K+b-w/2,y:-a*K+b-d/2,z,w,d,h,faces,tone:tone||'',order};o.ag=boxSwarm(w,d,h,es,sp,faces);return o};
1319  const blk=(a,b,z,s,h,faces,tone,order)=>wbox(a,b,z,s,s,h,faces,tone,order);
1320  const toAB=(x,y)=>[(x-y)*C30,(x+y)*.5];
1321  const pt=(P,a,b,z)=>[P.ox+a*P.u,P.oy+b*P.u-z*P.u];
1322  // --- ADROSUITE geometry: one compact, ready-made unit ---
1323  // channel tiles on the left; the unit itself is one cabinet with a module per row on its right face,
1324  // and the dashboard (where staff approve) stands on its top
1325  const CH=['WhatsApp','Instagram','Web chat','Phone'],chB=[-1.35,-.45,.45,1.35];
1326  const MODS=['Chat replies','Calls','Bookings','Follow-up','Marketing','Reviews'];
1327  const UH=2.7,UA=3.0,UB=.6;
1328  const A=kind!=='suite'?null:(()=>{
1329    const unit=blk(UA,UB,0,1.3,UH,'tlr','',1.4);
1330    const scr={x:unit.x+.12,y:unit.y+.05,z:unit.h,w:unit.w-.24,d:.1,h:1.0,order:2.2,tone:''};scr.ag=boxSwarm(scr.w,scr.d,scr.h,es,sp,'tlr');
1331    const tiles=chB.map((b,k)=>blk(0,b,0,.52,.18,'tlr','',k*.4));
1332    return{unit,scr,tiles,all:[...tiles,unit,scr]};})();
1333  // --- ADROTEAM geometry (unchanged) ---
1334  const FH=1.0,BS=1.25,BA=0;
1335  const Bt=kind!=='team'?null:{slabs:[0,1,2,3,4].map(k=>blk(BA,0,k*FH,BS,.1,k===4?'t':'','',k*.5)),check:blk(BA+4.3,.35,0,.8,.1,'t','',3),done:blk(BA+6.5,.35,0,.95,.22,'tlr','',3.5)};
1336  const DEP=['Sales','Marketing','Customer service','Operations'];// top floor first
1337  const all=A?A.all:[...Bt.slabs,Bt.check,Bt.done];
1338  SEED=kind==='suite'?97:131;all.forEach(b=>b.ag.forEach(g=>{g.del=.1+(b.order||0)*.16+srnd()*.5;g.dur=.8+srnd()*.4;g.sx=-.35-srnd()*.5;g.sy=(srnd()-.5)*.5;}));
1339  // layout: fit the drawing and its measured labels into the canvas (measured on every draw, so it follows the web font)
1340  let fsL=0;const layout=()=>{const {W,H}=S;S.mob=W<560;S.fs=S.mob?10:12;const ctx=S.ctx;ctx.font=`500 ${S.fs}px ${FONT}`;
1341    const pad=S.mob?6:14;
1342    if(kind==='suite'){
1343      const lw=Math.max(...CH.map(s=>ctx.measureText(s.toUpperCase()).width))+12,rw=Math.max(...MODS.map(s=>ctx.measureText(s.toUpperCase()).width))+14;
1344      // drawing spans a in [-.3, 4.15], screen-b in [-3.85, 2.2]; plus the channel labels at the left, the module labels at the right,
1345      // and the dashboard label above (in px)
1346      fsL=S.fs+14;
1346const u=Math.max(8,Math.min((W-2*pad-lw-rw)/4.45,(H-2*pad-fsL-S.fs-12)/6.05,S.mob?44:62));
1347      const w=lw+4.45*u+rw,left=(W-w)/2;S.P={u,ox:left+lw+.3*u,oy:(H-6.05*u-fsL)/2+fsL+3.85*u};
1348    }else{
1349      S.dep=S.mob?DEP.map(d=>d.replace(' ','\n')):DEP;
1350      const lw=Math.max(...S.dep.flatMap(s=>s.split('\n')).map(s=>ctx.measureText(s.toUpperCase()).width))+14;
1351      // drawing spans a in [-1.2, 7.35], screen-b in [-4.85, 1.25]
1352      const u=Math.max(8,Math.min((W-2*pad-lw)/8.55,(H-2*pad)/6.35,S.mob?46:74));
1353      const w=lw+8.55*u,left=(W-w)/2;S.P={u,ox:left+lw+1.2*u,oy:(H-6.35*u)/2+5.0*u};
1354    }};
1355  S.resize=()=>{Object.assign(S,setupCanvas(cv));layout();};
1356  S.resize();
1357  S.step=dt=>{if(S.started&&!RM)S.t+=dt;};
1358  // one block in the site's swarm style: a quiet fill, then its agents (dashes on the edges, hatching on the faces)
1359  const drawBlk=(P,b,t,tint)=>{const {ctx,W,H}=S,u=P.u,Lf=sp*u*.3,Le=es*u*.36;
1360    box(ctx,P,b.x,b.y,b.z,b.w,b.d,b.h,b.tone==='O'||tint?SC_FILL.lightO:SC_FILL.light,null);
1361    const groups={};for(const a of b.ag){const p=RM?1:clamp((t-a.del)/a.dur,0,1);if(p<=0)continue;
1362      const h=projPt(P,b.x+a.l[0],b.y+a.l[1],b.z+a.l[2]);let x=h[0],y=h[1],ang=a.a0;
1363      if(p<1){const q=1-Math.pow(1-p,3),sx=h[0]+a.sx*W,sy=h[1]+a.sy*H;x=sx+(h[0]-sx)*q;y=sy+(h[1]-sy)*q+Math.sin(p*Math.PI)*-18;ang=a.a0+lineWrap(Math.atan2(h[1]-sy,h[0]-sx)-a.a0)*(1-ss(.55,1,p));}
1364      const g=(b.tone||'')+a.tone;(groups[g]||(groups[g]=[])).push([x,y,ang,a.tone==='E'?Le:Lf]);}
1365    ctx.lineCap='round';
1366    for(const g in groups){const L=groups[g];ctx.beginPath();for(const [x,y,a,l] of L){const c=Math.cos(a)*l,s=Math.sin(a)*l;ctx.moveTo(x-c,y-s);ctx.lineTo(x+c,y+s)}
1367      ctx.lineWidth=S.mob?1.2:1.5;ctx.strokeStyle=col(g in TN?g:g.slice(-1));ctx.stroke();}};
1368  // a message (envelope) and a piece of finished work (a sheet with lines), drawn flat on screen
1369  const env=(x,y,e,ok,al)=>{const {ctx}=S,w=e*1.45,h=e;ctx.globalAlpha=al;ctx.lineWidth=1.3;ctx.lineJoin='round';
1370    ctx.fillStyle=ok?'#9DB3AA':BONE;ctx.strokeStyle=INK;ctx.beginPath();ctx.rect(x-w/2,y-h/2,w,h);ctx.fill();ctx.stroke();
1371    ctx.beginPath();ctx.moveTo(x-w/2,y-h/2);ctx.lineTo(x,y+h*.08);ctx.lineTo(x+w/2,y-h/2);ctx.stroke();ctx.globalAlpha=1;};
1372  const sheet=(x,y,e,ok,al)=>{const {ctx}=S,w=e*.95,h=e*1.25;ctx.globalAlpha=al;ctx.lineWidth=1.3;
1373    ctx.fillStyle=ok===true||ok>=1?'#9DB3AA':ok>0?mixC('#E4DCCA','#9DB3AA',ok):BONE;ctx.strokeStyle=INK;ctx.beginPath();ctx.rect(x-w/2,y-h/2,w,h);ctx.fill();ctx.stroke();
1374    ctx.beginPath();for(let i=0;i<3;i++){const yy=y-h/2+h*(.3+i*.2);ctx.moveTo(x-w*.28,yy);ctx.lineTo(x+w*(i===2?.05:.28),yy);}ctx.stroke();ctx.globalAlpha=1;};
1375  // the person who approves: a small ink figure; a check mark appears beside them as each item passes
1376  const person=(P,a,b,glow)=>{const {ctx}=S,u=P.u,[x,y]=pt(P,a,b,0),s=u*.95;
1377    ctx.fillStyle=INK;ctx.beginPath();ctx.moveTo(x-s*.2,y);ctx.lineTo(x-s*.2,y-s*.42);ctx.quadraticCurveTo(x-s*.2,y-s*.62,x,y-s*.62);ctx.quadraticCurveTo(x+s*.2,y-s*.62,x+s*.2,y-s*.42);ctx.lineTo(x+s*.2,y);ctx.closePath();ctx.fill();
1378    ctx.beginPath();ctx.arc(x,y-s*.8,s*.13,0,TAU);ctx.fill();
1379    if(glow>.02){const cx=x+s*.42,cy=y-s*.78,r=s*.17;ctx.globalAlpha*=glow;ctx.fillStyle=ORANGE;ctx.beginPath();ctx.arc(cx,cy,r,0,TAU);ctx.fill();
1380      ctx.strokeStyle=INK;ctx.lineWidth=1.6;ctx.lineCap='round';ctx.lineJoin='round';ctx.beginPath();ctx.moveTo(cx-r*.45,cy+r*.02);ctx.lineTo(cx-r*.1,cy+r*.38);ctx.lineTo(cx+r*.5,cy-r*.35);ctx.stroke();ctx.globalAlpha=1;}};
1381  // a faint dashed track with small arrowheads, so direction reads even in the still frame
1382  const track=(P,pts,arrows)=>{const {ctx}=S;ctx.save();ctx.setLineDash([3,5]);ctx.strokeStyle='rgba(18,29,26,.32)';ctx.lineWidth=1.2;ctx.beginPath();
1383    pts.forEach((p,i)=>{const q=pt(P,p[0],p[1],p[2]||0);i?ctx.lineTo(q[0],q[1]):ctx.moveTo(q[0],q[1])});ctx.stroke();ctx.restore();
1384    (arrows||[]).forEach(([i,f])=>{const p0=pts[i],p1=pts[i+1],a=pt(P,p0[0],p0[1],p0[2]||0),c=pt(P,p1[0],p1[1],p1[2]||0),x=a[0]+(c[0]-a[0])*f,y=a[1]+(c[1]-a[1])*f,an=Math.atan2(c[1]-a[1],c[0]-a[0]),r=S.mob?4:5.5;
1385      ctx.strokeStyle='rgba(18,29,26,.6)';ctx.lineWidth=1.5;ctx.lineCap='round';ctx.beginPath();ctx.moveTo(x-Math.cos(an-.6)*r,y-Math.sin(an-.6)*r);ctx.lineTo(x,y);ctx.lineTo(x-Math.cos(an+.6)*r,y-Math.sin(an+.6)*r);ctx.stroke();});};
1386  // position along a polyline, by distance
1387  const along=(pts,d)=>{for(let i=1;i<pts.length;i++){const a=pts[i-1],b=pts[i],l=Math.hypot(b[0]-a[0],b[1]-a[1],(b[2]||0)-(a[2]||0));
1387if(d<=l||i===pts.length-1){const q=clamp(d/l,0,1);return[a[0]+(b[0]-a[0])*q,a[1]+(b[1]-a[1])*q,(a[2]||0)+((b[2]||0)-(a[2]||0))*q]}d-=l;}};
1388  const plen=pts=>{let L=0;for(let i=1;i<pts.length;i++)L+=Math.hypot(pts[i][0]-pts[i-1][0],pts[i][1]-pts[i-1][1],(pts[i][2]||0)-(pts[i-1][2]||0));return L};
1389  const lab=(P,a,b,z,txt,al,dy)=>{const p=pt(P,a,b,z);haloLabel(S.ctx,txt,p[0],p[1]+(dy||0),INK,BONE,al||'center',S.fs);};
1390  // a quad on a box's front (+y) face, from face coordinates (fx along x, fz up), in world units
1391  const faceQuad=(P,b,x0,x1,z0,z1)=>{const y=b.y+b.d;return[[b.x+x0,y,b.z+z0],[b.x+x1,y,b.z+z0],[b.x+x1,y,b.z+z1],[b.x+x0,y,b.z+z1]].map(p=>projPt(P,p[0],p[1],p[2]));};
1392  const fillQ=(q,c)=>{const {ctx}=S;ctx.fillStyle=c;ctx.beginPath();q.forEach((p,n)=>n?ctx.lineTo(p[0],p[1]):ctx.moveTo(p[0],p[1]));ctx.closePath();ctx.fill();};
1393
1394  /* ---------- ADROSUITE ---------- */
1395  const DT=1.45,ORD=[0,3,1,2,0,1,3,2],T1=1.7;
1396  // label above an object: centred on its top face, clear of its highest corner
1397  const topOf=(P,b)=>{const c=[[0,0],[b.w,0],[b.w,b.d],[0,b.d]].map(([dx,dy])=>projPt(P,b.x+dx,b.y+dy,b.z+b.h));return[(Math.min(...c.map(p=>p[0]))+Math.max(...c.map(p=>p[0])))/2,Math.min(...c.map(p=>p[1]))];};
1398  const quadX=(P,b,y0,y1,z0,z1)=>{const x=b.x+b.w;return[[x,b.y+y0,b.z+z0],[x,b.y+y1,b.z+z0],[x,b.y+y1,b.z+z1],[x,b.y+y0,b.z+z1]].map(p=>projPt(P,p[0],p[1],p[2]));};
1399  // the modules: one row each on the unit's right face, top to bottom
1400  const rowZ=k=>{const m=.16,g=.07,rh=(UH-2*m-g*5)/6,z1=UH-m-k*(rh+g);return[z1-rh,z1]};
1401  const drawSuite=(t,la)=>{const {ctx}=S,P=S.P,u=P.u,e=clamp(u*.26,7,14),{unit,scr,tiles}=A;
1402    const inF=toAB(unit.x+unit.w*.5,unit.y+unit.d),inZ=UH*.45;                    // where messages enter: the unit's left face
1403    const per=[4.4,1.75];                                                          // the member of staff, in front of the unit
1404    chB.forEach(b=>track(P,[[.45,b,.1],[1.2,b,.1],[inF[0]-.35,inF[1]+(b-inF[1])*.12,inZ]],[[0,.72]]));
1405    const ft=RM?30:t,n0=Math.floor(ft/DT);const hot=[0,0,0,0,0,0];let glow=0,dash=0;const inbound=[];
1406    for(let i=n0;i>n0-Math.ceil((T1+2.6)/DT)-2;i--){const age=ft-i*DT;if(age<0)continue;const c=ORD[((i%8)+8)%8];
1407      const pin=[[.45,chB[c],.28],[1.2,chB[c],.28],[inF[0]-.12,inF[1],inZ]];
1408      if(age<T1){const q=easeIO(clamp(age/T1,0,1)),p=along(pin,q*plen(pin));inbound.push([pt(P,p[0],p[1],p[2]),clamp(age/.3,0,1)*clamp((T1-age)/.18,0,1)]);continue;}
1409      const a2=age-T1,pulse=(t0,d)=>a2>t0&&a2<t0+d?Math.sin(Math.PI*(a2-t0)/d):0;
1410      // first the module that answers (chat or call), then the one that follows up (a booking or a follow-up)
1411      const m1=c===3?1:0,m2=((i%2)+2)%2?3:2;
1412      hot[m1]=Math.max(hot[m1],pulse(0,.8));hot[m2]=Math.max(hot[m2],pulse(.55,.8));
1413      // then it lands on the dashboard, and a member of staff approves it
1414      dash=Math.max(dash,pulse(1.15,1.1));glow=Math.max(glow,pulse(1.3,1.2));}
1415    // marketing posts and review replies run on their own rhythm
1416    if(!RM){const mk=(ft%5.8)/5.8,rv=((ft+2.9)%5.8)/5.8;hot[4]=Math.max(hot[4],mk<.16?Math.sin(Math.PI*mk/.16):0);hot[5]=Math.max(hot[5],rv<.16?Math.sin(Math.PI*rv/.16):0);}
1417    tiles.forEach(b=>drawBlk(P,b,t));
1418    drawBlk(P,unit,t);
1419    ctx.globalAlpha=la;
1420    for(let k=0;k<6;k++){const [z0,z1]=rowZ(k),q=quadX(P,unit,.12,unit.d-.12,z0,z1);fillQ(q,hot[k]>.05?mixC('#CFC5B0','#324E48',hot[k]):'rgba(18,29,26,.13)');}
1421    // the unit's little "on" light
1422    fillQ(faceQuad(P,unit,.14,.3,UH-.28,UH-.14),ORANGE);ctx.globalAlpha=1;
1423    drawBlk(P,scr,t);
1424    // the dashboard: rows of work; the newest glows sage while it waits for approval
1425    {ctx.globalAlpha=la;const m=.12,rw=scr.w-2*m;for(let r=0;r<3;r++){const z1=scr.h-.16-r*.26,z0=z1-.13,len=r===0?1:r===1?.72:.86;
1426        fillQ(faceQuad(P,scr,m,m+rw*len,z0,z1),r===0&&dash>.05?mixC('#80827A','#324E48',dash):'rgba(18,29,26,.5)');}ctx.globalAlpha=1;}
1427    ctx.globalAlpha=la;person(P,per[0],per[1],glow);ctx.globalAlpha=1;
1428    inbound.forEach(([s,al])=>env(s[0],s[1],e,false,al*la));
1429    ctx.globalAlpha=la;const fs=S.fs;
1430    CH.forEach((c,k)=>{const p=pt(P,-.45,chB[k],.09);haloLabel(ctx,c,p[0]-6,p[1],INK,BONE,'right',fs);});
1431    const rc=projPt(P,unit.x+unit.w,unit.y,0);
1432    MODS.forEach((m,k)=>{const [z0,z1]=rowZ(k),p=projPt(P,unit.x+unit.w,unit.y+.12,(z0+z1)/2);haloLabel(ctx,m,rc[0]+9,p[1],hot[k]>.5?HI_L:INK,BONE,'left',fs);});
1433    let q=topOf(P,scr);haloLabel(ctx,'One dashboard',q[0],q[1]-12,INK,BONE,'center',fs);
1434    q=pt(P,per[0],per[1],0);haloLabel(ctx,'Staff approve',q[0],q[1]+15,INK,BONE,'center',fs);
1435    ctx.globalAlpha=1;
1436  };
1437
1438  /* ---------- ADROTEAM ---------- */
1439  const drawTeam=(t,la)=>{const {ctx}=S,P=S.P,u=P.u,ft=RM?30:t,e=clamp(u*.3,7,15),B=Bt;
1440    const sR=BA+BS*C30,xR=sR+.45,chkB=BA+4.3,doneL=BA+6.5-.95*C30,bb=.35;
1441    for(let k=0;k<4;k++){const z=k*FH+.1+.3;track(P,[[sR,0,z],[xR,0,z],[xR,bb,0]],[]);}
1442    track(P,[[xR,bb],[chkB,bb],[doneL,bb]],[[0,.55],[1,.55]]);
1443    const posts=(k,which)=>{const z0=k*FH+.1,z1=(k+1)*FH,x0=B.slabs[0].x,y0=B.slabs[0].y;ctx.strokeStyle=INK;ctx.lineWidth=S.mob?1.2:1.5;ctx.beginPath();
1444      which.forEach(([dx,dy])=>{const a=projPt(P,x0+dx*BS,y0+dy*BS,z0),c=projPt(P,x0+dx*BS,y0+dy*BS,z1);ctx.moveTo(a[0],a[1]);ctx.lineTo(c[0],c[1]);});ctx.stroke();};
1445    const Le=es*u*.36;
1446    for(let k=0;k<5;k++){const sl=B.slabs[k];drawBlk(P,sl,t);if(k===4)break;
1447      posts(k,[[0,0]]);
1448      const z=k*FH+.1,x0=sl.x,y0=sl.y,ag=RM?1:ss(1+k*.3,1.8+k*.3,t);ctx.globalAlpha=ag;ctx.strokeStyle=INK;ctx.lineWidth=(S.mob?1.2:1.5)+1.3;ctx.lineCap='round';ctx.beginPath();
1449      for(let n=0;n<4;n++){const ph=((RM?0:t)*.09*(k%2?1:-1)+n/4+k*.13)%1,q=((ph%1)+1)%1*4,sd=Math.floor(q),f=q-sd,m1=.28,m2=BS-.2;
1450        const c=[[m1,m1],[m2,m1],[m2,m2],[m1,m2]],p=c[sd],r=c[(sd+1)%4],lx=x0+p[0]+(r[0]-p[0])*f,ly=y0+p[1]+(r[1]-p[1])*f,an=isoAng(r[0]-p[0],r[1]-p[1],0);
1451        const h=projPt(P,lx,ly,z+.02),cc=Math.cos(an)*Le*1.5,s=Math.sin(an)*Le*1.5;ctx.moveTo(h[0]-cc,h[1]-s);ctx.lineTo(h[0]+cc,h[1]+s);}
1452      ctx.stroke();ctx.globalAlpha=1;posts(k,[[1,0],[1,1],[0,1]]);}
1453    // finished work: out of its floor, down to the lane, along to the person, a real PAUSE there while they check it
1454    // (the check appears), and only then on, sage, to Done. Items are staggered so the queue never jams the gate.
1455    const bout=[];let chk2=0;const D1=.45,D2=.8,V2=1.25,TP=1.3,Lg=chkB-xR,Lr=doneL-chkB,TBt=D1+D2+Lg/V2+TP+Lr/V2,CYC=10,OFF=[0,2.5,5.2,7.6],FLR=[3,1,0,2];
1456    const c0=Math.floor(ft/CYC);
1457    for(let c=c0;c>=c0-2;c--)OFF.forEach((o,n)=>{const age=ft-(c*CYC+o);if(age<0||age>TBt)return;const k=FLR[n],z=k*FH+.1+.3;let a,b,zz,ok=0;
1458      const tG=D1+D2+Lg/V2;
1459      if(age<D1){const q=age/D1;a=sR+(xR-sR)*q;b=0;zz=z;}
1460      else if(age<D1+D2){const q=easeIO((age-D1)/D2);a=xR;b=bb*q;zz=z*(1-q);}
1461      else if(age<tG){a=xR+(age-D1-D2)*V2;b=bb;zz=0;}
1462      else if(age<tG+TP){const w=age-tG;a=chkB;b=bb;zz=0;chk2=Math.max(chk2,ss(.3,.65,w));ok=ss(TP-.3,TP,w);}
1463      else{const w=age-tG-TP;a=chkB+w*V2;b=bb;zz=0;ok=1;chk2=Math.max(chk2,1-ss(0,.45,w));}
1464      bout.push([pt(P,a,b,zz+.3),ok,clamp(age/.25,0,1)*clamp((TBt-age)/.2,0,1)]);});
1465    drawBlk(P,B.check,t,chk2>.5);
1466    drawBlk(P,B.done,t);
1467    ctx.globalAlpha=la;person(P,chkB+.05,bb-.78,chk2);ctx.globalAlpha=1;
1468    bout.forEach(([s,ok,al])=>sheet(s[0],s[1],e*.95,ok,al*la));
1469    ctx.globalAlpha=la;
1470    S.dep.forEach((d,i)=>{const k=3-i,p=pt(P,BA-BS*C30,0,k*FH+.1+FH*.4),ls=d.split('\n');ctx.strokeStyle=INK;ctx.lineWidth=1;ctx.beginPath();ctx.moveTo(p[0]-7,p[1]);ctx.lineTo(p[0]-1,p[1]);ctx.stroke();ls.forEach((l,j)=>haloLabel(ctx,l,p[0]-11,p[1]+(j-(ls.length-1)/2)*(S.fs+4),INK,BONE,'right',S.fs));});
1471    lab(P,chkB+.05,bb-.78,0,'A person approves','center',-u*.95-12);
1472    lab(P,BA+6.5,bb-.48,.22,'Done','center',-14);
1473    ctx.globalAlpha=1;
1474  };
1475  S.draw=()=>{layout();const {ctx,W,H}=S,t=RM?30:S.t;ctx.setTransform(S.dpr,0,0,S.dpr,0,0);ctx.clearRect(0,0,W,H);
1476    const la=RM?1:ss(1.2,2,t);
1477    if(kind==='suite')drawSuite(t,la);else drawTeam(t,la);
1478  };
1479  return S;
1480}
1481
1482/* ============================================================
1483   BOOT: every page runs the same script; each part starts only if its elements are on the page.
1484   ============================================================ */
1485const $=s=>document.querySelector(s),$$=s=>[...document.querySelectorAll(s)];
1486const BOX_A=[-80,1030,-120,1060],BOX_E=[-215,1161,-365,1245],BOX_EM=[-160,1106,-250,1165];
1487// 1 Oct 2026: BOX_E grew 100 units at the top (more room between the pieces); the whole mark keeps its old size
1488const KE=(1245+365)/(1245+265),KEM=(1165+250)/(1165+180);
1489const heroFS=W=>clamp(W*.081,52,150);
1490// the world's cube size comes from the WINDOW, not the canvas (1 Oct 2026, founder: the pages' heroes are shorter than the
1491// home screen, so their cubes came out smaller); the dividers use the same size (worldU)
1492const worldU=mob=>{const vw=innerWidth,vh=innerHeight;return mob?clamp(vw/17,17,24):clamp(Math.min(vw/40,vh/25),22,38)};
1493const WORLD={edgeFrac:.46,gridStroke:'rgba(18,29,26,.1)',unit:(W,H,mob)=>worldU(mob)};
1494const STAGES=[];let S0=null,S1=null,SPC=null;
1495// concept J: the chat screen and the journey meet with no seam. Both worlds are the same lattice; the journey's is shifted
1496// so its rows continue the chat screen's, and a band of flat ground on either side of the join hides the cut.
1497const jBandB=(sy,H)=>clamp((H*.93-sy)/(H*.1),0,1),jBandT=(sy,H)=>clamp((sy-H*.05)/(H*.1),0,1);
1498// phones: room kept free at the bottom of the journey for the chat bar (set by chat-ui.js once a conversation exists)
1499const jPadB=()=>window.__jPadB||0;
1500
1501// ---- concept I landing: the world fills the screen, the swarm mark sits small above the chat box (placed on .land-mark),
1502//      and the world goes flat in the middle so the headline and the box read on a quiet plate ----
1503const landCv=$('#land-stage'),landMark=$('.land-mark');
1504// once the conversation starts (.land.talking) the swarm fades out and the crisp logo (.lm-img, CSS) fades in: a swarm
1505// that small reads blurry and jagged
1506const landEl=$('#land'),landFade={v:null,t:0};
1507// 1 Oct 2026: the landing's mark is the reference swarm (p2b-ref.js), in the band above the headline; S0 keeps the world
1508if(landCv&&landMark&&REFM)RefHero(landEl,$('.land-sub'));
1509if(landCv&&landMark){S0=new Stage(landCv,Object.assign({},WORLD,{nMin:620,nMax:1000,nMob:460,noMark:REFM,
1510  markA:()=>{const tg=landEl&&landEl.classList.contains('talking')?0:1,now=performance.now();
1511    if(landFade.v===null||RM||FREEZE)landFade.v=tg;else{landFade.v+=(tg-landFade.v)*Math.min(1,Math.min(.1,(now-landFade.t)/1000)*7);if(Math.abs(tg-landFade.v)<.01)landFade.v=tg;}
1512    landFade.t=now;return landFade.v},
1513  fits:()=>{const r=landMark.getBoundingClientRect(),c=landCv.getBoundingClientRect(),h=Math.max(8,r.height),cx=r.left+r.
1513width/2-c.left;
1514    return{A:FB(BOX_A,[cx-h,r.top-c.top,cx+h,r.top-c.top+h])}},
1515  // J, phones: the swarm assembles in place, quickly, from just around the mark (a full-screen flight crosses the headline,
1516  // the box and the notes for seconds, and the empty slot reads as a dark blot until it lands); its halo comes in with it
1517  introK:S=>S.mob?.55:1,entryD:S=>S.mob?Math.max(60,landMark.getBoundingClientRect().height*.7):0,
1518  haloK:S=>S.mob&&!RM?ss(.2,1.6,S.t):1,
1519  // J: the bottom rows stay flat too, so the grid runs on into the journey's stage below with no seam
1520  flat:(sx,sy,W,H,mob)=>{const d=Math.hypot((sx-W/2)/(mob?W*.62:Math.max(520,W*.4)),(sy-H*.52)/(H*.46));
1521    // 1 Oct 2026: and the band above the headline, where the reference swarm writes its shapes, is calm too
1522    const band=REFM?Math.max(clamp((Math.abs(sx-W/2)-W*(mob?.5:.34))/(W*.1),0,1),clamp((sy-H*.42)/(H*.08),0,1)):1;
1523    return clamp((d-.72)/.42,0,1)*band*jBandB(sy,H)}}));STAGES.push(S0);}
1524
1525
1526
1527// ---- landing: the journey, H's pinned track. f: 0 whole (J: already formed, centred), 1..5 one piece each, 6 whole and big,
1528//      7 moved aside for the last screen ----
1529const track=$('.track'),stageBox=track&&track.querySelector('.stage');
1530const steps=$$('.track .step'),cards=$$('.pcard'),fin=$('.fin'),finIn=$('.fin-in'),stageNav=stageBox&&stageBox.querySelector('.nav');
1531if(track){
1532  S1=new Stage($('#stage'),Object.assign({},WORLD,{nMob:640,cards,refFormed:1,lsMax:7.2,lwMax:3.1,chevS:1,
1533    fits:(W,H,mob,S)=>{const below=W<1024,ch=S.cardH||300;let E,A;
1534      const pb=mob?jPadB():0;
1535      if(below){const r=[10,mob?66:84,W-10,H-ch-14-20-pb];E=FB(mob?BOX_EM:BOX_E,r);A=rescale(FB(BOX_A,mob?[10,66,W-10,H-24-pb]:r),E.s*1.12*(mob?KEM:KE));}  // J, phones: whole, it sits in the middle (the labels are not up yet)  // top clear of the nav, as G
1536      else{const m=clamp(W*.24,320,480);E=FB(BOX_E,[m,52,W-m,H-24]);A=rescale(FB(BOX_A,[m,72,W-m,H-24]),E.s*1.16*KE);}
1537      const B=FB(BOX_A,[W*.1,mob?24:36,W*.9,H-(mob?24:28)]);
1538      let C;if(mob){const h=Math.min(H*.24,200);C=FB(BOX_A,[W*.2,18,W*.8,18+h]);}
1539      else{C=FB(BOX_A,[W*.53,64,W-Math.max(44,W*.045),H-56]);if(C.s>B.s*.84)C=rescale(C,B.s*.84);}
1540      return{A,E,B,C};},
1541    count:S=>S.mob?Math.round(clamp(4200*S.FT.B.s,S.o.nMob,1600)):Math.round(clamp(3400*S.FT.B.s,1800,3200)),   // round 3: phones get enough agents for the big whole mark to read as lines, not dashes
1542    // wide screens: the left half stays flat, so the labels and the last screen's text sit on a quiet plate; phones: the lower
1543    // part stays flat under the label list (as G). J: the top rows stay flat, where the chat screen's world ends
1544    flat:(sx,sy,W,H,mob)=>(mob?clamp((H*.62-sy)/(H*.12),0,1):clamp((sx-W*.46)/(W*.12),0,1))*jBandT(sy,H),
1545    // J: shift the lattice so its rows continue the chat screen's rows above it
1546    oyAdj:(S,u)=>{if(!landCv)return 0;const Hl=landCv.clientHeight||S.H,d=(Hl*.5-5*u-Hl)-(S.H*.5-5*u);return((d%u)+u)%u},
1547    onResize:S=>document.documentElement.style.setProperty('--mkh',Math.round((BOX_A[3]-BOX_A[2])*S.FT.C.s)+'px'),
1548    phase:f=>{
1549      if(RM){const r=Math.round(f),w=[0];for(let k=1;k<=NSTEP;k++)w.push(r===k?1:0);
1550        return{e:r>=1&&r<=NSTEP?1:0,w,fit:r<=0?['A','A',0]:r<=NSTEP?['E','E',0]:r===6?['B','B',0]:['C','C',0]}}
1551      const e=ss(.18,.86,f)*(1-ss(NSTEP+.1,NSTEP+.68,f));
1552      const w=[0];for(let k=1;k<=NSTEP;k++)w.push(1-ss(.2,.56,Math.abs(f-k)));
1553      const fit=f<1?['A','E',easeIO(ss(.18,.86,f))]:f<NSTEP+.05?['E','E',0]:f<6.04?['E','B',easeIO(ss(NSTEP+.1,NSTEP+.92,f))]:['B','C',easeIO(ss(6.1,6.94,f))];
1554      return{e,w,fit};}}));
1555  STAGES.push(S1);
1556  // J: the mark is already whole when the journey comes into view (the chat screen above has had the intro)
1557  S1.t=INTRO_END*1+2;
1558  // QA: the stage's transform and its agents in mark units (the swarm-fidelity check, ll/logo/swarmfid.py)
1559  window.__swarm=()=>({s:S1.s,ox:S1.ox,oy:S1.oy,e:S1.e,rise:S1.rise,ag:S1.agents.filter(a=>a.on).map(a=>[+((a.x-S1.ox)/S1.s).toFixed(1),+((a.y-S1.oy)/S1.s).toFixed(1),a.edge?1:0,a.tone])});
1560  // J: where the mark is on screen right now (chat-ui.js keeps the corner chat off it when it can)
1561  window.__jMark=()=>{const r=S1.cv.getBoundingClientRect();if(r.bottom<=0||r.top>=innerHeight)return null;let x0=1e9,y0=1e9,x1=-1e9,y1=-1e9;
1562    for(let k=0;k<PARTS.length;k++)S1.partPolys(k).forEach(q=>q.forEach(p=>{x0=Math.min(x0,p[0]);y0=Math.min(y0,p[1]);x1=Math.max(x1,p[0]);y1=Math.max(y1,p[1]);}));
1563    return{left:r.left+x0,top:r.top+y0,right:r.left+x1,bottom:r.top+y1,width:x1-x0}};
1564}
1565// scroll -> phase f, measured from where each step's centre crosses the middle of the screen (the last step: its first screen)
1566const stepC=()=>steps.map((s,i)=>{const r=s.getBoundingClientRect();return i===steps.length-1?r.top+Math.min(r.height,innerHeight)/2:r.top+r.height/2});
1567function readScroll(){if(!S1)return;
1568  let f;const mid=innerHeight/2,c=stepC();
1569  if(mid<=c[0])f=0;else if(mid>=c[c.length-1])f=c.length-1;
1570  else{for(let k=0;k<c.length-1;k++)if(mid>=c[k]&&mid<c[k+1]){f=k+(mid-c[k])/Math.max(1,c[k+1]-c[k]);break}}
1571  S1.fT=f;
1572  // J: the journey's own nav shows once the stage is pinned (the chat screen's nav has scrolled away by then)
1573  if(stageNav){const on=track.getBoundingClientRect().top<=1;if(on!==stageNav._on){stageNav._on=on;stageNav.classList.toggle('shown',on);}}
1574  // once the last screen is scrolled past its first screenful, the small mark moves with it
1575  S1.shiftY=Math.min(0,fin.getBoundingClientRect().top-stageBox.getBoundingClientRect().top);
1576  if(finIn){const o=RM?1:ss(6.2,6.8,f);finIn.style.opacity=o.toFixed(3);}
1577}
1577
1578// for screenshots: scroll to where the phase is f
1579window.__toF=f=>{const c=stepC().map(v=>v+scrollY),k=clamp(Math.floor(f),0,c.length-2),q=f-k;scrollTo({top:c[k]+(c[k+1]-c[k])*q-innerHeight/2,behavior:'instant'});readScroll();if(S1)S1.f=S1.fT;};
1580if(S1){
1581  // cursor over a piece of the broken-apart mark: a pointer, and its label lights up; a click opens its page
1582  const setHot=hp=>{if(hp!==S1.hot){S1.hot=hp;S1.cv.style.cursor=hp?'pointer':'';S1.placeCards();}};
1583  addEventListener('pointermove',ev=>{const r=S1.cv.getBoundingClientRect();if(ev.clientY<r.top||ev.clientY>r.bottom)return;
1584    if(ev.target.closest&&ev.target.closest('.pcard'))return;
1585    setHot(S1.e>.6?S1.hitPart(ev.clientX-r.left,ev.clientY-r.top):null);},{passive:true});
1586  S1.cv.addEventListener('click',ev=>{if(S1.e<.6)return;const r=S1.cv.getBoundingClientRect(),hp=S1.hitPart(ev.clientX-r.left,ev.clientY-r.top);if(hp){const el=cards.find(l=>l.dataset.part===hp);if(el)el.click();}});
1587  cards.forEach(el=>{el.addEventListener('pointerenter',()=>setHot(el.dataset.part));el.addEventListener('pointerleave',()=>setHot(null));
1588    // keyboard: tabbing onto a label scrolls the breakdown to its piece, so its card opens beside it
1589    el.addEventListener('focus',()=>{const q=+Object.keys(STEP_PART).find(n=>STEP_PART[n]===el.dataset.part);if(Math.abs(S1.f-q)>.15){const c=stepC(),y=scrollY+c[q]-innerHeight/2;scrollTo({top:y,behavior:RM?'auto':'smooth'});}});});
1590}
1591
1592// ---- the pages: the page's own piece of the official mark ----
1593const pieceCv=$('canvas.piece');
1594if(pieceCv){const id=pieceCv.dataset.part==='all'?null:pieceCv.dataset.part;
1595  // the reading pages (articles, privacy, terms, 404: .doc-hero) centre their text in a container, so the piece sits in that
1596  // container's right part (and a little into the margin), below the nav, instead of at the far right of a wide screen
1597  const docIn=pieceCv.closest('.doc-hero')&&pieceCv.parentNode.querySelector('.phero-in');
1598  let DX=null;const docX=W=>{const c=pieceCv.getBoundingClientRect(),r=docIn.getBoundingClientRect(),L=r.left-c.left,R=r.right-c.left;return [L+r.width*.66,Math.min(W-Math.max(28,W*.03),R+(W-R)*.45)]};
1599  const box=id?partBox(id,46):BOX_A,region=(W,H,mob)=>{if(mob)return [16,74,W-16,74+Math.min(W*.58,220)];
1600    if(docIn){const [x0,x1]=DX=docX(W);return [x0,104,x1,H-34]}return [W*.55,86,W-Math.max(44,W*.045),H-30]};
1601  SPC=new Stage(pieceCv,Object.assign({},WORLD,{only:id,
1602    fits:(W,H,mob)=>{const F=FB(box,region(W,H,mob));return{A:id?F:rescale(F,Math.min(F.s,.5))}},
1603    count:S=>Math.round(clamp((id?4600:3000)*S.FT.A.s*(S.mob?.62:1),S.mob?600:900,id?9000:2000)),
1604    flat:(sx,sy,W,H,mob)=>mob?clamp((74+Math.min(W*.58,220)+16-sy)/40,0,1):docIn?clamp((sx-(DX||docX(W))[0]+W*.1)/(W*.1),0,1):clamp((sx-W*.42)/(W*.14),0,1)}));
1605  STAGES.push(SPC);}
1606
1607// ---- small scenes ----
1608const D=$$('canvas.divider').map((cv,ix)=>Object.assign({cv,ix,from:cv.dataset.from,to:cv.dataset.to,pal:cv.dataset.pal},setupCanvas(cv)));
1609const G=$$('canvas[data-glyph]').map(cv=>Object.assign({cv,kind:cv.dataset.glyph,hot:false,t0:0},setupCanvas(cv)));
1610G.forEach(g=>{const li=g.cv.closest('.belief');li.addEventListener('pointerenter',()=>{g.hot=true;g.t0=performance.now()/1000});li.addEventListener('pointerleave',()=>{g.hot=false;drawGlyph(g,0,true)});});
1611const floorCv=document.getElementById('floor'),FS=floorCv?FloorScene(floorCv):null,floorEl=$('.floor');
1612const onScreen=(el,m)=>{const r=el.getBoundingClientRect();return r.bottom>-(m||0)&&r.top<innerHeight+(m||0)&&r.width>0};
1613const SC=$$('canvas[data-scene]').map(SwarmScene);
1614const OF=$$('canvas[data-office]').map(OfficeScene);
1615
1616// newsletter: POST /api/subscribe (share3/api/subscribe.js: Resend contacts, list "ADROLAB lab notes"); a real success or an honest error
1617$$('form.news').forEach(nf=>{const i=nf.querySelector('input[type=email]'),m=nf.querySelector('.news-ok'),b=nf.querySelector('button[type=submit]');let busy=false;
1618  const say=(h,cls)=>{m.innerHTML=h;m.className='news-ok'+(cls?' '+cls:'')};
1619  nf.addEventListener('submit',async ev=>{ev.preventDefault();if(busy)return;
1620    if(!i.value||!i.checkValidity()){say('Please enter a valid email.','bad');i.focus();return}
1621    busy=true;b.disabled=true;say('Subscribing…');const hp=nf.querySelector('input[name="company_website"]');let j=null;
1622    try{const r=await fetch('/api/subscribe',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({email:i.value.trim(),company_website:hp?hp.value:''})});try{j=await r.json()}catch(e){}}catch(e){j=null}
1623    busy=false;b.disabled=false;
1624    if(j&&j.ok){say(j.already?"You're already on the list. Thanks.":j.welcome?"You're on the list. Check your inbox.":"You're on the list.",'good');i.value='';}   // 1 Oct 2026: "check your inbox" only when the welcome email really went out
1625    else if(j&&j.reason==='invalid'){say('Please enter a valid email.','bad');i.focus();}
1626    else say('Something went wrong. Please email <a href="mailto:[email protected]?subject=Lab%20notes">[email protected]</a>.','bad');});});
1627
1628// the reading pages' rail: "On this page" marks the section being read (only while the rail is shown, from 1180px)
1629{const tl=$$('.rail-toc a');if(tl.length){const hs=tl.map(a=>document.getElementById(a.getAttribute('href').slice(1)));let cur=-1,raf=0;
1630  const upd=()=>{raf=0;if(!tl[0].offsetParent)return;let k=-1;hs.forEach((h,i)=>{if(h&&h.getBoundingClientRect().top<innerHeight*.35)k=i});
1631    if(k!==cur){cur=k;tl.forEach((a,i)=>i===k?a.setAttribute('aria-current','true'):a.removeAttribute('aria-current'))}};
1632  addEventListener('scroll',()=>{if(!raf)raf=requestAnimationFrame(upd)},{passive:true});addEventListener('resize',upd);upd();}}
1633
1634// the menu button (narrow screens)
1635$$('.burger').forEach(b=>{const m=document.getElementById(b.getAttribute('aria-controls'));
1636  const set=o=>{b.setAttribute('aria-expanded',String(o));b.setAttribute('aria-label',o?'Close menu':'Menu');m.hidden=!o;};
1637  b.addEventListener('click',()=>{const o=b.getAttribute('aria-expanded')!=='true';set(o);if(o){const a=m.querySelector('a');a&&a.focus({preventScroll:true});}});
1638  addEventListener('keydown',e=>{if(e.key==='Escape'&&b.getAttribute('aria-expanded')==='true'){set(false);b.focus();}});
1639  // while the menu is open, Tab and Shift+Tab stay inside it (the close button plus the page links)
1640  addEventListener('keydown',e=>{if(e.key!=='Tab'||b.getAttribute('aria-expanded')!=='true')return;const f=[b,...m.querySelectorAll('a[href]')],i=f.indexOf(document.activeElement);
1641    if(i<0){e.preventDefault();f[e.shiftKey?f.length-1:0].focus();return}
1642    if(e.shiftKey&&i===0){e.preventDefault();f[f.length-1].focus()}else if(!e.shiftKey&&i===f.length-1){e.preventDefault();f[0].focus()}});
1643  document.addEventListener('click',e=>{if(b.getAttribute('aria-expanded')==='true'&&!m.contains(e.target)&&!b.contains(e.target))set(false);});
1644  if(Q.has('menu'))set(true);});
1645
1646// cursor: a gentle nudge to the agents and a small lift in the world, on whichever stage it is over
1647addEventListener('pointermove',ev=>{for(const S of STAGES){const r=S.cv.getBoundingClientRect();
1648  if(ev.clientX>=r.left&&ev.clientX<=r.right&&ev.clientY>=r.top&&ev.clientY<=r.bottom){S.mouse=[ev.clientX-r.left,ev.clientY-r.top];S.mt=performance.now();}}},{passive:true});
1649addEventListener('scroll',()=>{readScroll();if(RM||FREEZE)drawAll(performance.now(),0)},{passive:true});
1650readScroll();if(S1)S1.f=S1.fT;
1651/* THE QUIZ: "Which one fits you?" (from G's hy/quiz.js; H adds an icon per answer and tactile tiles, and drops the company-size row)
1652   Three questions, all in the page: nothing is sent anywhere. The result is shown on the comparison table itself.
1653   ?quiz=q1|q2|q3|suite|team|team51 sets a state for screenshots. */
1654(()=>{
1655const qzBody=document.getElementById('qz-body'),qzFill=document.getElementById('qz-fill'),fitEl=document.getElementById('fit'),cmpEl=document.getElementById('cmp');
1656if(!qzBody||!cmpEl)return;
1657const ARR='<svg viewBox="0 0 14 14" aria-hidden="true"><path d="M2 7h9.5M7.5 3l4 4-4 4" fill="none" stroke="currentColor" stroke-width="1.8"/></svg>';
1658const EXT='<svg viewBox="0 0 14 14" aria-hidden="true"><path d="M3.5 10.5l7-7M5 3.5h5.5V9" fill="none" stroke="currentColor" stroke-width="1.8"/></svg>';
1659const CHK='<svg class="ck" viewBox="0 0 16 16" aria-hidden="true"><path d="M2.5 8.5l3.6 3.4 7.4-8" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="square"/></svg>';
1660// one small line icon per answer (24px grid, drawn in the site's stroke)
1661const IC={
1662 p1:'<circle cx="12" cy="8" r="3.5"/><path d="M5 20.5v-1.5a7 7 0 0 1 14 0v1.5"/>',
1663 p2:'<circle cx="9" cy="8.5" r="3"/><path d="M3 20.5v-1a6 6 0 0 1 12 0v1"/><circle cx="16.5" cy="7.5" r="2.6"/><path d="M16 13.1a5 5 0 0 1 5.5 5v1.4"/>',
1664 p3:'<circle cx="12" cy="7.5" r="2.8"/><path d="M6.5 20.5v-1a5.5 5.5 0 0 1 11 0v1"/><circle cx="5" cy="10.5" r="2.2"/><path d="M1.5 19.5a4 4 0 0 1 4.3-4.4"/><circle cx="19" cy="10.5" r="2.2"/><path d="M22.5 19.5a4 4 0 0 0-4.3-4.4"/>',
1665 b1:'<path d="M6 21V4h10v17M16 9.5h3.5V21M3 21h18M9 8h1M12.5 8h1M9 12h1M12.5 12h1M9 16h1M12.5 16h1"/>',
1666 b2:'<path d="M3.5 21V7h7v14M10.5 21V3h8v18M1.5 21h21M6 11h2M6 15h2M13.5 7h2M13.5 11h2M13.5 15h2"/>',
1667 b3:'<path d="M2 21V11h5v10M7 21V4h6.5v17M13.5 21V8H18v13M18 21v-7h4v7M1 21h22M9.5 8h1.5M9.5 12h1.5M9.5 16h1.5"/>',
1668 chat:'<path d="M4 4.5h16v11.5h-9.5L6 20v-4H4z"/><path d="M8 9h8M8 12h5"/>',
1669 phone:'<path d="M5.5 3.5h3.5l1.8 4.6-2.3 1.4a11 11 0 0 0 6 6l1.4-2.3 4.6 1.8v3.5a1.6 1.6 0 0 1-1.7 1.6A16.5 16.5 0 0 1 3.9 5.2 1.6 1.6 0 0 1 5.5 3.5z"/>',
1670 cal:'<rect x="3.5" y="5" width="17" height="15.5"/><path d="M3.5 10h17M8 3v4M16 3v4M7.5 14h2M11 14h2M14.5 14h2M7.5 17.3h2M11 17.3h2"/>',
1671 loop:'<path d="M19.5 12a7.5 7.5 0 1 1-2.3-5.4"/><path d="M18 2.8v4.4h-4.4"/><path d="M9.5 12.2l2 2 3.5-4"/>',
1672 mega:'<path d="M3 9.5v5h3.5L15 19.5v-15L6.5 9.5z"/><path d="M18.5 8.5a4.5 4.5 0 0 1 0 7M6.5 14.5l1.6 6h3.2l-1.3-4.7"/>',
1673 star:'<path d="M12 3.2l2.7 5.6 6.1.8-4.5 4.2 1.2 6.1L12 17l-5.5 2.9 1.2-6.1-4.5-4.2 6.1-.8z"/>',
1674 doc:'<path d="M6 2.8h8.8l4.2 4.2v14.2H6z"/><path d="M14.5 3v4.3h4.3M9 11.5h7M9 14.5h7M9 17.5h4"/>',
1675 head:'<path d="M4 14v-2a8 8 0 0 1 16 0v2"/><rect x="3" y="13.5" width="4" height="6"/><rect x="17" y="13.5" width="4" height="6"/><path d="M19 19.5v.5a2.3 2.3 0 0 1-2.3 2.3H13.5"/>',
1676 gear:'<circle cx="12" cy="12" r="3"/><path d="M12 2.8v2.6M12 18.6v2.6M2.8 12h2.6M18.6 12h2.6M5.5 5.5l1.9 1.9M16.6 16.6l1.9 1.9M5.5 18.5l1.9-1.9M16.6 7.4l1.9-1.9"/><circle cx="12" cy="12" r="6.6"/>',
1677 chart:'<path d="M3 20.5h18M6.5 20.5v-6M11 20.5V9.5M15.5 20.5v-4M20 20.5V5"/>',
1678 video:'<rect x="2.5" y="6" width="13" height="12"/><path d="M15.5 10.4l6-3.4v10l-6-3.4z"/>
1678',
1679 cap:'<path d="M1.8 9L12 4l10.2 5L12 14z"/><path d="M6 11v5.2c3.3 2.6 8.7 2.6 12 0V11M22.2 9v6"/>',
1680 calck:'<rect x="3.5" y="5" width="17" height="15.5"/><path d="M3.5 10h17M8 3v4M16 3v4M8.5 15l2.4 2.4 4.6-4.6"/>',
1681 shop:'<path d="M3 9l1.6-5h14.8L21 9"/><path d="M3 9h18v1.8a3 3 0 0 1-6 0 3 3 0 0 1-6 0 3 3 0 0 1-6 0z"/><path d="M4.5 13.4V21h15v-7.6M10 21v-4.8h4V21"/>',
1682 cart:'<path d="M1.8 3.5h3l2.6 12h11.2L21 7.2H6"/><circle cx="9" cy="19.6" r="1.5"/><circle cx="17" cy="19.6" r="1.5"/>',
1683 card:'<rect x="2.5" y="5" width="19" height="14"/><circle cx="8.5" cy="10.8" r="2.2"/><path d="M5 16.6a3.5 3.5 0 0 1 7 0M14.5 10h4.5M14.5 13.5h4.5"/>',
1684 house:'<path d="M2.8 11.3L12 3.6l9.2 7.7M5.5 9.2v11.3h13V9.2"/><path d="M10 20.5v-5.8h4v5.8"/>',
1685 clip:'<rect x="5" y="4.5" width="14" height="17"/><path d="M9 4.5V2.8h6v1.7M8.5 11l2.2 2.2 4.5-4.5M8.5 17h7"/>',
1686 sign:'<path d="M6 2.8h8.8l4.2 4.2v14.2H6z"/><path d="M14.5 3v4.3h4.3M9 10h7M9 13h5M8.5 18c1.4-2 2.4-2 3 0s1.5 1 2.4-.5 1.6-.5 2.6.6"/>',
1687 fact:'<path d="M2.5 21V11l5 3v-3l5 3v-3l5 3V3.5h4V21z"/><path d="M1.5 21h21M6.5 17.5h2M11.5 17.5h2"/>',
1688 truck:'<path d="M1.8 5.8h12.4V16H1.8zM14.2 9.8h4.4l3.2 3.6V16h-7.6"/><circle cx="6" cy="18.2" r="2"/><circle cx="17.6" cy="18.2" r="2"/>',
1689 brief:'<rect x="2.5" y="7" width="19" height="13.5"/><path d="M8.5 7V3.8h7V7M2.5 12.6h19M10.8 11v3.2h2.4V11"/>',
1690 net:'<circle cx="5" cy="5" r="2.2"/><circle cx="5" cy="19" r="2.2"/><circle cx="19" cy="12" r="2.4"/><path d="M5 7.2v9.6M7 6l9.8 5M7 18l9.8-5"/>',
1691 dots:'<circle cx="12" cy="12" r="9"/><circle cx="7.8" cy="12" r=".6"/><circle cx="12" cy="12" r=".6"/><circle cx="16.2" cy="12" r=".6"/>'
1692};
1693const ICON=k=>`<svg viewBox="0 0 24 24">${IC[k]||IC.dots}</svg>`;
1694const SUITE='https://clinics.adrolab.ai';
1695// each option: label, and what it points to. sv/tv: points; row/side: where its tick goes on the table; why: the tick's words
1696const {QS,sentence,list}=window.ADRO_QUIZ;const score=()=>window.ADRO_QUIZ.score(ans);
1697const NQ=QS.length;let step=0,ans=[null,[],null];
1698const esc=t=>t.replace(/&/g,'&amp;').replace(/</g,'&lt;');
1699function render(focus){
1700  qzFill.style.width=(step/NQ*100)+'%';
1701  if(step>=NQ){qzBody.innerHTML=`<p class="qz-done">Your fit is marked on the table below. <button type="button" class="qz-back" data-act="reset">Start over</button></p>`;return;}
1702  const Q=QS[step];
1703  let h=`<div class="qz-top"><span class="qz-count">Question ${step+1} of ${NQ}</span>${step>0?'<button type="button" class="qz-back" data-act="back">Back</button>':''}</div>`;
1704  h+=`<h5 class="qz-q" id="qz-q" tabindex="-1">${Q.q}</h5>`+(Q.hint?`<p class="qz-hint" id="qz-hint">${Q.hint}</p>`:'');
1705  h+=`<div class="qz-opts${Q.multi?' multi':''}${Q.wide?' wide':''}" role="group" aria-labelledby="qz-q"${Q.hint?' aria-describedby="qz-hint"':''}>`;
1706  Q.opts.forEach((o,i)=>{const on=Q.multi?ans[1].includes(i):ans[step]===i;
1707    h+=`<button type="button" class="qz-opt${on?' sel':''}" data-i="${i}"${Q.multi?` aria-pressed="${on}"`:''}><span class="qz-ic" aria-hidden="true">${ICON(o.ic)}</span><span class="qz-l">${esc(o.l)}${o.d?`<small>${esc(o.d)}</small>`:''}</span><span class="qz-end" aria-hidden="true">${Q.multi?'<i class="bx"></i>':ARR.replace('<svg','<svg class="ar"')+CHK}</span></button>`;});
1708  h+='</div>';
1709  if(Q.multi)h+=`<button type="button" class="btn btn-o qz-go" data-act="next"${ans[1].length?'':' disabled'}>Continue ${ARR}</button>`;
1710  qzBody.innerHTML=h;
1711  if(focus){const q=document.getElementById('qz-q');q&&q.focus({preventScroll:true});}
1712}
1713qzBody.addEventListener('click',ev=>{const b=ev.target.closest('button');if(!b)return;
1714  const act=b.dataset.act;
1715  if(act==='back'){step=Math.max(0,step-1);render(true);return}
1716  if(act==='reset'){reset(true);return}
1717  if(act==='next'){if(ans[1].length){step++;render(true)}return}
1718  const i=+b.dataset.i,Q=QS[step];
1719  if(Q.multi){const k=ans[1].indexOf(i);if(k<0)ans[1].push(i);else ans[1].splice(k,1);
1720    b.classList.toggle('sel',k<0);b.setAttribute('aria-pressed',String(k<0));qzBody.querySelector('.qz-go').disabled=!ans[1].length;return}
1721  ans[step]=i;b.classList.add('sel');
1722  setTimeout(()=>{step++;if(step>=NQ){render(false);show(true)}else render(true)},RM?0:170);
1723});
1724// ---- scoring ----
1725// ---- the result, on the table ----
1726const rowCell=(k,side)=>{const r=cmpEl.querySelector(`.cmp-r[data-k="${k}"]`);return r&&r.querySelector(side==='S'?'.su':'.tm')};
1727function clear(){cmpEl.classList.remove('rec-S','rec-T');
1727cmpEl.querySelectorAll('.why,.badge,.alt').forEach(e=>e.remove());fitEl.innerHTML='';
1728  const bs=document.getElementById('btn-suite'),bt=document.getElementById('btn-team');
1729  bs.className='btn';bs.innerHTML='Talk to us about ADROSUITE '+ARR;bt.className='btn btn-o';bt.href='mailto:[email protected]?subject=ADROTEAM';bt.innerHTML='Talk to us about ADROTEAM '+ARR;
1730  OF.forEach(o=>o.paused=false);}
1731function show(scroll){const r=score();clear();const side=r.rec;cmpEl.classList.add('rec-'+side);
1732  // ticks on the matching rows of the recommended column
1733  const ticks={};const add=(k,w)=>{(ticks[k]||(ticks[k]=[])).push(w)};
1734  r.picks.forEach(o=>{if(o.k===side)add(o.row,o.why);else if(side==='T'&&o.k==='S')add('ch',o.why);});
1735  if(r.q3.k===side)add('who',r.q3.why);
1736  for(const k in ticks){const c=rowCell(k,side);if(!c)continue;ticks[k].slice(0,2).forEach(w=>{const e=document.createElement('span');e.className='why';e.textContent='✓ '+w;c.appendChild(e);});}
1737  const hd=cmpEl.querySelector('.cmp-h');const hs=hd.querySelector(side==='S'?'.su':'.tm'),ho=hd.querySelector(side==='S'?'.tm':'.su');
1738  hs.insertAdjacentHTML('afterbegin','<span class="badge">Best fit for you</span>');ho.insertAdjacentHTML('afterbegin','<span class="alt">Also an option</span>');
1739  const bs=document.getElementById('btn-suite'),bt=document.getElementById('btn-team');
1740  if(side==='S'){bs.className='btn btn-o';bt.className='btn btn-ghost';}
1741  else{bt.className='btn btn-o';bs.className='btn btn-ghost';}
1742  // the recommended figure keeps moving; the other one pauses
1743  OF.forEach(o=>o.paused=(o.kind==='suite')!==(side==='S'));
1744  let h=`<div class="fit-line"><p class="fit-s" tabindex="-1" id="fit-s">${esc(sentence(r))}</p><div class="fit-notes">`;
1745  if(r.mix)h+=`<p>For something live this week, <a href="mailto:[email protected]?subject=ADROSUITE" data-chat="I'd like to talk to someone about ADROSUITE">talk to us about ADROSUITE</a>.</p>`;
1746  h+=`<p><button type="button" class="qz-back" data-act="reset">Start over</button></p></div></div>`;
1747  fitEl.innerHTML=h;
1748  if(scroll){const y=fitEl.getBoundingClientRect().top+scrollY-20;scrollTo({top:y,behavior:RM?'auto':'smooth'});const f=document.getElementById('fit-s');f&&f.focus({preventScroll:true});}
1749}
1750fitEl.addEventListener('click',ev=>{const b=ev.target.closest('[data-act="reset"]');if(b)reset(true);});
1751function reset(focus){step=0;ans=[null,[],null];clear();render(false);
1752  if(focus){const y=document.getElementById('quiz').getBoundingClientRect().top+scrollY-20;scrollTo({top:y,behavior:RM?'auto':'smooth'});const q=document.getElementById('qz-q');q&&q.focus({preventScroll:true});}}
1753render(false);
1754const qzBox=document.getElementById('qz');
1755if(!RM&&qzBox&&'IntersectionObserver' in window){const io=new IntersectionObserver(es=>{if(es.some(e=>e.isIntersecting)){io.disconnect();qzBox.classList.add('nudge');setTimeout(()=>qzBox.classList.remove('nudge'),1600);}},{threshold:.45});io.observe(qzBox);}
1756if(Q.has('nudge')&&qzBox)qzBox.classList.add('nudge');
1757// screenshot helpers
1758const QZ=Q.get('quiz');
1759// q2/q3: that question, with the earlier ones answered; suite/team: a result; team51: 51–100 people with only ADROSUITE answers (still ADROTEAM)
1760const PRE={suite:[0,[0,1,2],0],team:[3,[6,7,8],10],team51:[3,[0,1,2],0]};
1761if(PRE[QZ]){ans=[PRE[QZ][0],PRE[QZ][1].slice(),PRE[QZ][2]];step=NQ;render(false);show(false);}
1762else if(QZ==='q2'||QZ==='q3'){ans=[0,QZ==='q3'?[0,1]:[],null];step=QZ==='q3'?2:1;render(false);}
1763// concept I: the chat agent runs the same quiz in the conversation and hands its answers over, so the page shows the same result.
1764// window.__quizApply([q1,[q2...],q3]) applies them; ?fit=q1.q2a-q2b.q3 does the same from a link (e.g. ?fit=3.6-7.10)
1765const apply=a=>{if(!a||a[0]==null||a[2]==null)return false;ans=[+a[0],(a[1]||[]).map(Number),+a[2]];step=NQ;render(false);show(false);return true};
1766window.__quizApply=apply;
1767if(Q.get('fit')){const p=Q.get('fit').split('.');apply([p[0],p[1]?p[1].split('-').filter(Boolean):[],p[2]]);}
1768})();
1769/* end of the quiz */
1770
1771// agent counts, for QA
1772window.__bench=n=>{const r=[];for(const S of STAGES){const t0=performance.now();for(let i=0;i<n;i++){S.update(1/60);S.draw();}S.ctx.getImageData(0,0,1,1);r.push(+((performance.now()-t0)/n).toFixed(2))}return r};
1773window.__adro=()=>({stages:STAGES.map(S=>S.agents.length),floor:FS?FS.nAg:0,scenes:SC.map(s=>s.kind+':'+s.n).join(' ')});
1774// ?hover=<n>: show belief row n (0-based) in its hover state, for screenshots
1775if(Q.has('hover')){const li=$$('.belief')[+Q.get('hover')||0];if(li){li.classList.add('is-hover');const g=G.find(g=>g.cv.closest('.belief')===li);if(g){g.hot=true;g.t0=performance.now()/1000}}}
1776const resizeAll=()=>{STAGES.forEach(S=>S.resize());if(FS)FS.resize();SC.forEach(s=>s.resize());OF.forEach(s=>s.resize());D.forEach(d=>{Object.assign(d,setupCanvas(d.cv));d.P=null});G.forEach(g=>Object.assign(g,setupCanvas(g.cv)));readScroll();drawAll(performance.now(),0)};
1777let rt;addEventListener('resize',()=>{clearTimeout(rt);rt=setTimeout(resizeAll,140)});
1778// guard: if a canvas's box changed without a resize event (fonts, iframes, zoom), resize it
1779const sizeOK=S=>Math.abs(S.cv.clientWidth-S.W)<2&&Math.abs(S.cv.clientHeight-S.H)<2;
1780
1781function drawAll(now,dt){
1782  const t=now/1000;
1783  if(STAGES.some(S=>!sizeOK(S))){resizeAll();return}
1784  for(const S of STAGES){const on=onScreen(S.cv);if(on)S.started=true;if(on||dt===0){S.update(dt);S.draw();}}
1785  if(FS&&(onScreen(floorEl,100)||dt===0)){if(dt&&!RM)FS.step(dt);FS.draw();}
1786  for(const s of OF){const on=onScreen(s.cv,60);if(on)s.started=true;if(on||dt===0){if(!s.paused)s.step(dt);s.draw();}}
1787  for(const s of SC){const on=onScreen(s.cv,60);if(on)s.started=true;if(on||dt===0){s.step(dt);s.draw();}}
1788  for(const d of D)if(dt===0||onScreen(d.cv,100))drawDivider(d,RM?0:t);
1789  if(dt===0)G.forEach(g=>drawGlyph(g,0,true));
1790  else G.forEach(g=>{if(g.hot)drawGlyph(g,t-g.t0+.2,false)});
1791}
1792// fast-forward for screenshots
1793if(FT&&!RM){if(FS)FS.t=FT;STAGES.forEach(S=>{S.started=true;for(let i=0;i<Math.round(FT*60);i++)S.update(1/60);});SC.forEach(s=>{s.started=true;s.t=FT});OF.forEach(s=>{s.started=true;s.t=FT});}
1794drawAll(performance.now(),0);
1795if(document.fonts)document.fonts.ready.then(()=>{resizeAll()});
1796// ?scroll=<px|N%|Nvh>: jump there and run the real scroll path; ?f=<phase>: jump to that point of the landing breakdown
1797if(Q.has('scroll')||Q.has('f')){const v=Q.get('scroll')||'0',H=()=>document.documentElement.scrollHeight-innerHeight;
1798  const go=()=>{if(Q.has('f'))__toF(parseFloat(Q.get('f')));else{const y=v.endsWith('%')?parseFloat(v)/100*H():v.endsWith('vh')?parseFloat(v)*innerHeight/100:parseFloat(v)||0;scrollTo({top:y,behavior:'instant'});}readScroll();if(S1)S1.f=S1.fT;drawAll(performance.now(),0)};
1799  go();addEventListener('load',go);}
1800if(!RM&&!FREEZE){let last=performance.now();const loop=now=>{const dt=Math.min(.05,(now-last)/1000);last=now;drawAll(now,dt);requestAnimationFrame(loop)};requestAnimationFrame(loop);}
1801// QA (round 7): the swarm's coverage of the official ink. __swCov(i): stage i's agents where they are now, whole mark,
1802// rasterised at 2 px per mark unit as discs of the particle's size, against the official ink. A gap = ink farther than one
1803// particle spacing from every particle (a hole wider than two spacings). Homes by default (the ink agents' home points);
1804// tt: their live positions at time tt instead. Returns the numbers and a picture (gaps in red).
1805window.__swCov=(si,tt)=>{const S=STAGES[si||0];if(!S||!SWM)return null;const R=2,W=950*R,H=1000*R,sR=S.swsR,g=S.swg,P0=SWP[MK];
1806  for(const a of S.agents)swHome(a,tt==null?0:tt,S.G,MK,S.swFsp);const X=a=>a.mk&&tt==null?a.bx:a.hx,Y=a=>a.mk&&tt==null?a.by:a.hy;
1807  const sp=S.agents.esp||Math.sqrt(P0.eArea*g*g/(sR*sR)),rho=(P0.eL*g/sR*.5+P0.elw*g/sR*.5);   // spacing and particle radius, mark units
1808  const fp=PARTS[PI.frame].faces[0].p,list=S.agents.filter(a=>a.edge&&a.tone!=='V'&&a.tone!=='CS'&&a.tone!=='CI'&&!(a.core&&inPoly(a.hx,a.hy,fp)));
1809  const mkc=()=>{const c=document.createElement('canvas');c.width=W;c.height=H;const x=c.getContext('2d');x.setTransform(R,0,0,R,0,0);return[c,x]};
1810  const [ci,xi]=mkc();xi.fillStyle='#000';xi.fill(MARK_FULL,'evenodd');
1811  const disc=(r)=>{const [c,x]=mkc();x.fillStyle='#000';x.beginPath();for(const a of list){x.moveTo(X(a)+r,Y(a));x.arc(X(a),Y(a),r,0,TAU)}x.fill();return x.getImageData(0,0,W,H).data};
1812  const I=xi.getImageData(0,0,W,H).data,C1=disc(rho),C2=disc(rho+sp);
1813  let ink=0,cov=0,gap=0,out=0;const offBy={};const G=new Uint8Array(W*H);
1814  for(let i=0;i<W*H;i++){if(I[i*4+3]<128)continue;ink++;if(C1[i*4+3]>=128)cov++;if(C2[i*4+3]<128){G[i]=1;gap++}}
1815  for(const a of list){const i=Math.floor(X(a)*R),j=Math.floor(Y(a)*R);if(i<0||j<0||i>=W||j>=H||I[(j*W+i)*4+3]<128){out++;const q=(a.core?'core-'+a.tone:a.rim?'rim':'fill');offBy[q]=(offBy[q]||0)+1}}
1816  // gap regions (4-connected)
1817  const regs=[];const st=[];for(let i=0;i<W*H;i++){if(G[i]!==1)continue;let n=0,x0=1e9,x1=-1,y0=1e9,y1=-1;G[i]=2;st.push(i);
1818    while(st.length){const q=st.pop(),x=q%W,y=(q/W)|0;n++;x0=Math.min(x0,x);x1=Math.max(x1,x);y0=Math.min(y0,y);y1=Math.max(y1,y);
1819      for(const d of[q-1,q+1,q-W,q+W])if(d>=0&&d<W*H&&G[d]===1){G[d]=2;st.push(d)}}
1820    regs.push({x:(x0+x1)/2/R,y:(y0+y1)/2/R,w:(x1-x0+1)/R,h:(y1-y0+1)/R,px:n})}
1821  // the picture: cream, the ink pale, the particles' discs dark green, the gaps red (circled)
1822  const [co,xo]=mkc();xo.setTransform(1,0,0,1,0,0);const im=xo.createImageData(W,H),d=im.data;
1823  for(let i=0;i<W*H;i++){let c=[239,231,218];if(I[i*4+3]>=128)c=C1[i*4+3]>=128?[18,29,26]:G[i]?[220,30,30]:[196,206,199];else if(C1[i*4+3]>=128)c=[120,120,220];d[i*4]=c[0];d[i*4+1]=c[1];d[i*4+2]=c[2];d[i*4+3]=255}
1824  xo.putImageData(im,0,0);xo.setTransform(R,0,0,R,0,0);xo.strokeStyle='#dc1e1e';xo.lineWidth=2;
1824regs.forEach(r=>{xo.beginPath();xo.arc(r.x,r.y,Math.max(r.w,r.h)/2+10,0,TAU);xo.stroke()});
1825  xo.setTransform(1,0,0,1,0,0);xo.fillStyle='#121D1A';xo.font='600 34px sans-serif';
1826  xo.fillText(`mk=4 coverage (${tt==null?'home positions':'live, t='+tt+'s'}): ${list.length} stroke agents, spacing ${sp.toFixed(2)} u, particle r ${rho.toFixed(2)} u; ink covered by particles ${(100*cov/ink).toFixed(1)}%; gaps wider than 2 spacings: ${regs.length}; agents off the ink: ${out}`,24,1980);
1827  return{agents:list.length,sp:+sp.toFixed(2),rho:+rho.toFixed(2),covered:+(100*cov/ink).toFixed(2),gapPx:gap,gaps:regs.length,regs:regs.slice(0,20),off:out,offBy,png:co.toDataURL('image/png')}};
1828window.__stages=STAGES;window.__parts=PARTS;   // QA handles
1829})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.