/* BinTat — dynamic FX layer: scroll progress, story rail, word reveals, parallax, scroll cue */
const { useEffect:useFxE, useRef:useFxR, useState:useFxS } = React;

/* thin progress bar at the very top */
function ScrollProgress(){
  const ref = useFxR(null);
  useFxE(()=>{
    const el = ref.current; if(!el) return;
    let t=false;
    const f=()=>{ t=false;
      const max = document.documentElement.scrollHeight - innerHeight;
      el.style.transform = `scaleX(${max>0 ? Math.min(1, scrollY/max) : 0})`;
    };
    const on=()=>{ if(!t){ t=true; requestAnimationFrame(f); } };
    addEventListener('scroll', on, {passive:true});
    addEventListener('resize', on, {passive:true});
    f();
    return ()=>{ removeEventListener('scroll', on); removeEventListener('resize', on); };
  },[]);
  return <div className="sprogress" aria-hidden="true"><div ref={ref} className="sprogress__bar"></div></div>;
}

/* global scroll effects: hero parallax, [data-plx] parallax, body.is-scrolled */
function ScrollFX(){
  useFxE(()=>{
    if(window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches) return;
    let els=[], heroBg=null, ticking=false, raf=0;
    const collect=()=>{
      els=[...document.querySelectorAll('[data-plx]')];
      heroBg=document.querySelector('[data-hero-bg]');
    };
    collect();
    const root=document.getElementById('root');
    const mo=new MutationObserver(()=>{ collect(); onScroll(); });
    if(root) mo.observe(root,{childList:true,subtree:true});
    const frame=()=>{
      ticking=false;
      const sc=scrollY, vh=innerHeight||800;
      document.body.classList.toggle('is-scrolled', sc>70);
      if(heroBg) heroBg.style.transform=`translate3d(0,${(sc*0.16).toFixed(1)}px,0) scale(1.08)`;
      for(const el of els){
        const r=el.getBoundingClientRect();
        if(r.bottom<-80||r.top>vh+80) continue;
        const c=(r.top+r.height/2)-vh/2;
        const sp=parseFloat(el.dataset.plx)||0.1;
        const y=Math.max(-30,Math.min(30,-(c*sp)));
        el.style.transform=`translate3d(0,${y.toFixed(1)}px,0) scale(1.13)`;
      }
    };
    const onScroll=()=>{ if(!ticking){ ticking=true; raf=requestAnimationFrame(frame); } };
    addEventListener('scroll', onScroll, {passive:true});
    addEventListener('resize', onScroll, {passive:true});
    frame();
    return ()=>{ removeEventListener('scroll', onScroll); removeEventListener('resize', onScroll); mo.disconnect(); cancelAnimationFrame(raf); };
  },[]);
  return null;
}

/* left chapter rail — the "fil conducteur" (home) */
function StoryRail({chapters}){
  const [act, setAct] = useFxS(0);
  const fillRef = useFxR(null);
  useFxE(()=>{
    let t=false;
    const f=()=>{ t=false;
      const mid=(innerHeight||800)*0.46;
      const tops=chapters.map(c=>{ const el=document.getElementById('ch-'+c.id); return el?el.getBoundingClientRect().top:1e9; });
      let a=0; tops.forEach((tp,i)=>{ if(tp<mid) a=i; });
      setAct(a);
      const first=tops[0], last=tops[tops.length-1];
      let p=(mid-first)/Math.max(1,(last-first));
      p=Math.max(0,Math.min(1,p));
      if(fillRef.current) fillRef.current.style.transform=`scaleY(${p})`;
    };
    const on=()=>{ if(!t){ t=true; requestAnimationFrame(f); } };
    addEventListener('scroll', on, {passive:true});
    addEventListener('resize', on, {passive:true});
    f();
    return ()=>{ removeEventListener('scroll', on); removeEventListener('resize', on); };
  },[chapters]);
  const goTo=(id)=>(e)=>{
    e.preventDefault();
    const el=document.getElementById('ch-'+id);
    if(el){ const y=el.getBoundingClientRect().top+scrollY-76; scrollTo({top:y, behavior:'smooth'}); }
  };
  return (
    <aside className="srail">
      <div className="srail__line"><span ref={fillRef} className="srail__fill"></span></div>
      {chapters.map((c,i)=>(
        <a key={c.id} href={'#ch-'+c.id} onClick={goTo(c.id)}
           className={'srail__dot'+(i<=act?' is-on':'')+(i===act?' is-act':'')}
           aria-label={c.label}>
          <span className="srail__n">{String(i+1).padStart(2,'0')}</span>
          <span className="srail__lb">{c.label}</span>
        </a>
      ))}
    </aside>
  );
}

/* word-by-word title reveal (transform-only, driven by parent .reveal.in) */
function WordSplit({text, base=0, step=55}){
  const words=String(text).split(' ').filter(Boolean);
  return (
    <React.Fragment>
      {words.map((w,i)=>(
        <React.Fragment key={i}>
          <span className="w"><span className="wi" style={{'--wd':(base+i*step)+'ms'}}>{w}</span></span>
          {i<words.length-1?' ':null}
        </React.Fragment>
      ))}
    </React.Fragment>
  );
}

/* bouncing scroll cue at hero bottom */
function ScrollCue({label}){
  return (
    <div className="scue" aria-hidden="true">
      <span className="scue__lb">{label}</span>
      <span className="scue__ic">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M6 9l6 6 6-6"/></svg>
      </span>
    </div>
  );
}

Object.assign(window, { ScrollProgress, ScrollFX, StoryRail, WordSplit, ScrollCue });
