/* The signal field, seven instrument studies, and the glyph-to-outcome transition. */
(() => {
  const root = document.documentElement;
  const reduce = matchMedia('(prefers-reduced-motion: reduce)');
  const motionButton = document.querySelector('#motion-toggle');
  const hero = document.querySelector('#portrait-dots');
  const iconCanvases = [...document.querySelectorAll('.part-icon')];
  const outcomeCanvas = document.querySelector('#outcome-canvas');
  const proofs = [...document.querySelectorAll('.proof')];
  const outcomes = [...document.querySelectorAll('.outcome')];
  const outcomeSection = document.querySelector('#outcomes');
  const visible = new WeakMap();
  const observer = new IntersectionObserver(entries => { entries.forEach(e => visible.set(e.target, e.isIntersecting)); wake(); }, {rootMargin:'120px'});
  [hero, outcomeCanvas, ...iconCanvases].filter(Boolean).forEach(el => observer.observe(el));
  let paused = reduce.matches, time = 0, frame = 0, outcomeTarget = 0, outcomeValue = 0;
  let pointer = null;
  const playing = () => !paused && !reduce.matches && !document.hidden;
  function size(canvas) {
    const r = canvas.getBoundingClientRect(), dpr = Math.min(devicePixelRatio || 1, 2);
    const w = Math.max(1, Math.round(r.width*dpr)), h = Math.max(1, Math.round(r.height*dpr));
    if (canvas.width !== w || canvas.height !== h) { canvas.width=w; canvas.height=h; }
    const c = canvas.getContext('2d'); c.setTransform(dpr,0,0,dpr,0,0); c.clearRect(0,0,r.width,r.height);
    return {c,w:r.width,h:r.height};
  }
  hero?.parentElement?.addEventListener('pointermove', e => { const r=hero.getBoundingClientRect(); pointer={x:e.clientX-r.left,y:e.clientY-r.top}; wake(); });
  hero?.parentElement?.addEventListener('pointerleave', () => { pointer=null; wake(); });
  function drawHero() {
    const {c,w,h}=size(hero), cx=w*.54, cy=h*.48, rx=w*.45, ry=h*.45;
    const base='137,225,241', gap=w<460?15:17;
    for(let y=gap/2;y<h;y+=gap) for(let x=gap/2;x<w;x+=gap) {
      const nx=(x-cx)/rx, ny=(y-cy)/ry, d=Math.hypot(nx,ny);
      const annulus=Math.exp(-Math.pow((d-.78)/.28,2)), lobe=Math.exp(-Math.pow((nx+.65)/.5,2)-Math.pow((ny-.05)/.65,2));
      const wave=.5+.5*Math.sin(nx*5.2-ny*3.5+time*.35);
      const pd=pointer?Math.hypot(x-pointer.x,y-pointer.y):1e4;
      const touch=Math.exp(-(pd*pd)/12000);
      const alpha=Math.min(.86,.035+annulus*(.2+.26*wave)+lobe*.14+touch*.4);
      if(alpha<.09)continue;
      const depth=annulus*.8+lobe*.4;
      c.fillStyle=`rgba(${base},${alpha.toFixed(3)})`;
      c.beginPath();c.arc(x+(playing()?Math.sin(y*.022+time*.45)*depth:0),y,Math.max(.7,1+depth*.8+touch*.8),0,Math.PI*2);c.fill();
    }
  }
  const point = (x,y,z=0) => ({x,y,z});
  function path(points, steps=9) {
    const out=[];
    for(let j=0;j<points.length-1;j++) for(let k=0;k<steps;k++) {
      const a=points[j],b=points[j+1],f=k/steps;
      out.push(point(a.x+(b.x-a.x)*f,a.y+(b.y-a.y)*f,(a.z||0)+((b.z||0)-(a.z||0))*f));
    }
    return out.concat(points.at(-1));
  }
  function ring(cx,cy,r,z=0,n=56,ry=1){return Array.from({length:n},(_,i)=>{const a=i/n*Math.PI*2;return point(cx+Math.cos(a)*r,cy+Math.sin(a)*r*ry,z);});}
  function iconPoints(i) {
    const pts=[];
    if(i===0){
      const nodes=[[-.78,.55,-.3],[-.5,.2,-.15],[-.23,.35,.08],[.07,-.13,.22],[.37,-.02,.35],[.76,-.6,.52]];
      pts.push(...path(nodes.map(v=>point(...v)),15));
      for(const [x,y,z] of nodes)pts.push(...ring(x,y,.038,z,12));
      for(let k=0;k<3;k++)pts.push(...path([point(-.75,.7-k*.11,-.35),point(.74,.7-k*.11,-.35)],22));
    } else if(i===1){
      for(let j=0;j<4;j++){const ox=j*.11-.2,oy=-j*.1+.16,z=j*.16-.3;
        pts.push(...path([point(-.42+ox,-.55+oy,z),point(.35+ox,-.55+oy,z),point(.35+ox,.48+oy,z),point(-.42+ox,.48+oy,z),point(-.42+ox,-.55+oy,z)],20));
        for(let l=0;l<4;l++)pts.push(...path([point(-.24+ox,-.33+oy+l*.17,z),point(.2+ox,-.33+oy+l*.17,z)],9));
      }
    } else if(i===2){
      for(let lat=-2;lat<=2;lat++){const yy=lat*.25,r=.65*Math.sqrt(1-yy*yy);pts.push(...ring(0,yy,r,lat*.11,54,.32));}
      for(let lon=0;lon<8;lon++){const a=lon*Math.PI/4;pts.push(...path(Array.from({length:17},(_,j)=>{const phi=-Math.PI/2+j/16*Math.PI;return point(Math.cos(phi)*Math.cos(a)*.65,Math.sin(phi)*.72,Math.cos(phi)*Math.sin(a)*.65);}),5));}
      pts.push(...ring(0,0,.13,.8,28));
    } else if(i===3){
      for(let k=0;k<4;k++){const arr=[];for(let j=0;j<45;j++){const x=-.8+j/44*1.6;arr.push(point(x,Math.sin(x*6+k*.65)*(.13+k*.02)+k*.23-.36,k*.2-.4));}pts.push(...arr);}
      for(let j=0;j<8;j++)pts.push(...path([point(-.8+j*.23,.68,-.42),point(-.8+j*.23,-.65,-.42)],11));
    } else if(i===4){
      for(let k=0;k<5;k++)pts.push(...ring(-.16,.1,.15+k*.14,k*.08-.3,54,.78).filter(p=>p.x>-.22));
      pts.push(...ring(-.16,.1,.085,.5,26));
      for(let j=0;j<7;j++){const a=-1.1+j*.35;pts.push(...path([point(-.16,.1,.35),point(-.16+Math.cos(a)*.8,.1+Math.sin(a)*.8,-.2)],9));}
    } else if(i===5){
      for(const z of [-.42,.4])pts.push(...path([point(-.55,-.55,z),point(.55,-.55,z),point(.55,.55,z),point(-.55,.55,z),point(-.55,-.55,z)],18));
      for(const x of [-.55,.55])for(const y of [-.55,.55])pts.push(...path([point(x,y,-.42),point(x,y,.4)],17));
      for(let a=-1;a<=1;a++)for(let b=-1;b<=1;b++)pts.push(point(a*.37,b*.37,.42));
      pts.push(...ring(0,0,.19,.61,32));
    } else {
      const nodes=Array.from({length:7},(_,j)=>{const a=j/7*Math.PI*2-.6;return point(Math.cos(a)*.64,Math.sin(a)*.52,Math.sin(a*2)*.2);});
      for(let j=0;j<nodes.length;j++){pts.push(...ring(nodes[j].x,nodes[j].y,.09,nodes[j].z,22));pts.push(...path([nodes[j],nodes[(j+1)%nodes.length]],12));pts.push(...path([nodes[j],point(0,0,.43)],12));}
      pts.push(...ring(0,0,.14,.5,32));
    }
    return pts;
  }
  const iconSets=Array.from({length:7},(_,i)=>iconPoints(i));
  function renderDots(canvas,points,rotation=0,scale=.34,alpha=1) {
    const {c,w,h}=size(canvas),cos=Math.cos(rotation),sin=Math.sin(rotation),S=Math.min(w,h)*scale;
    const sorted=points.map(p=>({x:p.x*cos+p.z*sin,y:p.y,z:p.z*cos-p.x*sin})).sort((a,b)=>a.z-b.z);
    c.strokeStyle=`rgba(150,231,246,${.12*alpha})`;
    c.beginPath();c.ellipse(w/2,h/2,S*1.35,S*.42,-.22,0,Math.PI*2);c.stroke();
    for(let k=0;k<sorted.length;k++){
      const p=sorted[k],depth=(p.z+1)/2,px=w/2+p.x*S,py=h/2+p.y*S*.95;
      const a=(.30+depth*.65)*alpha;
      c.fillStyle=`rgba(${depth>.67?'188,239,249':'107,203,232'},${a})`;
      c.beginPath();c.arc(px,py,Math.max(.7,1.05+depth*1.35),0,Math.PI*2);c.fill();
    }
    c.fillStyle='#bdf3f9';
    for(let j=0;j<sorted.length;j+=Math.max(15,Math.floor(sorted.length/18))){const p=sorted[j];c.beginPath();c.arc(w/2+p.x*S,h/2+p.y*S*.95,2.8,0,Math.PI*2);c.fill();}
  }
  const glyphPath = new Path2D('M66.47,373.49H0l34.67-238.97h63.55l76.26,141.21,76.55-141.21h59.99l34.67,238.97h-68.25l-13.87-165.41-73.07,165.41h-37.09l-73.07-165.41-13.87,165.41ZM346.13,435.97H.07v59.75h346.06v-59.75Z');
  function glyphPoints(){const c=document.createElement('canvas');c.width=220;c.height=220;const q=c.getContext('2d');q.setTransform(.47,0,0,.47,28,-45);q.fill(glyphPath);const data=q.getImageData(0,0,220,220).data,pts=[];for(let y=7;y<214;y+=6)for(let x=7;x<214;x+=6)if(data[(y*220+x)*4+3]>90)pts.push(point((x-110)/112,(y-110)/112,Math.sin(x*.07+y*.04)*.14));return pts;}
  const glyph=glyphPoints();
  const outcomeIcons=[glyph,iconSets[0],iconSets[5],iconSets[2],iconSets[3],iconSets[6],iconSets[4]];
  const ordered=outcomeIcons.map(points=>points.slice().sort((a,b)=>Math.atan2(a.y,a.x)-Math.atan2(b.y,b.x)||Math.hypot(a.x,a.y)-Math.hypot(b.x,b.y)));
  function drawOutcome(){
    const {c,w,h}=size(outcomeCanvas),from=Math.floor(outcomeValue),to=Math.min(6,Math.ceil(outcomeValue));
    const f=outcomeValue-from,A=ordered[from],B=ordered[to],n=410,S=Math.min(w,h)*.42;
    for(let k=0;k<n;k++){
      const a=A[Math.floor(k*A.length/n)%A.length],b=B[Math.floor(k*B.length/n)%B.length];
      const x=a.x+(b.x-a.x)*f,y=a.y+(b.y-a.y)*f,z=a.z+(b.z-a.z)*f;
      const breathe=playing()?Math.sin(time*.8+k*.11)*.7:0;
      c.fillStyle=`rgba(175,238,248,${.58+(k%7)*.058})`;
      c.beginPath();c.arc(w/2+x*S,h/2+y*S+breathe,1.55+(z+1)*.36+(k%13===0?.7:0),0,Math.PI*2);c.fill();
    }
  }
  function reading(){
    if(proofs.length){
      const first=proofs[0].getBoundingClientRect(),last=proofs.at(-1).getBoundingClientRect();
      let active=0;
      if(first.top<innerHeight*.55)proofs.forEach((el,i)=>{if(el.getBoundingClientRect().top<innerHeight*.55)active=i;});
      if(last.bottom<innerHeight*.45)active=proofs.length-1;
      proofs.forEach((el,i)=>el.classList.toggle('active',i===active));
    }
    if(outcomes.length){
      let active=-1;
      outcomes.forEach((el,i)=>{if(el.querySelector('h3').getBoundingClientRect().top<innerHeight*.52)active=i;});
      if(outcomeSection.getBoundingClientRect().top>innerHeight*.5)active=-1;
      if(outcomeSection.getBoundingClientRect().bottom<innerHeight*.25)active=outcomes.length-1;
      outcomeTarget=active+1;
      outcomes.forEach((el,i)=>{el.classList.toggle('active',i===active);el.classList.toggle('seen',i<=active);});
      outcomeSection.classList.toggle('is-entered',outcomeSection.getBoundingClientRect().top<innerHeight*.8);
    }
    wake();
  }
  let queued=false;
  addEventListener('scroll',()=>{if(!queued){queued=true;requestAnimationFrame(()=>{queued=false;reading();});}},{passive:true});
  addEventListener('resize',reading,{passive:true});
  document.addEventListener('visibilitychange',wake);
  motionButton?.addEventListener('click',()=>{
    paused=motionButton.getAttribute('aria-pressed')!=='true';
    motionButton.setAttribute('aria-pressed',String(paused));
    motionButton.setAttribute('aria-label',paused?'Включить анимацию':'Приостановить анимацию');
    root.classList.toggle('motion-paused',paused);
    document.dispatchEvent(new Event('club:motion'));wake();
  });
  reduce.addEventListener('change',()=>{paused=reduce.matches;wake();});
  function tick(){
    frame=0;time+=.016;
    if(visible.get(hero))drawHero();
    iconCanvases.forEach((canvas,i)=>{if(visible.get(canvas))renderDots(canvas,iconSets[i],playing()?Math.sin(time*.37+i*.4)*.18:.06,.38,1);});
    if(reduce.matches)outcomeValue=outcomeTarget;
    else outcomeValue+=Math.max(-.055,Math.min(.055,(outcomeTarget-outcomeValue)*.1));
    if(visible.get(outcomeCanvas))drawOutcome();
    if(playing()&&(visible.get(hero)||iconCanvases.some(el=>visible.get(el))||visible.get(outcomeCanvas))||Math.abs(outcomeValue-outcomeTarget)>.004)wake();
  }
  function wake(){if(!frame)frame=requestAnimationFrame(tick);}
  reading();wake();
})();
