/* Shared hooks + components → window */
const { useState, useEffect, useRef, useCallback } = React;

/* ---- scroll reveal: observes .reveal nodes, adds .in ----
   Robust against throttled/hidden documents: the initial above-fold
   pass and the safety timer run WITHOUT requestAnimationFrame (which
   freezes when the tab/iframe is not visible), so content is never
   left permanently invisible. ---- */
function useReveals(route){
  useEffect(() => {
    const reveal = (el) => el.classList.add('in');
    const force  = () => document.querySelectorAll('.reveal').forEach(el => el.classList.add('shown'));
    const vh = () => window.innerHeight || 800;

    // If the document is not visible (offscreen / background tab), CSS
    // transitions freeze at their start frame — so reveal everything
    // instantly with no transition instead of leaving it invisible.
    if (document.visibilityState !== 'visible') force();
    const onVis = () => { if (document.visibilityState !== 'visible') force(); };
    document.addEventListener('visibilitychange', onVis);

    // visible users: reveal above-fold now, observe the rest on scroll
    document.querySelectorAll('.reveal:not(.in)').forEach(el => {
      if (el.getBoundingClientRect().top < vh() * 0.95) reveal(el);
    });
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => { if (e.isIntersecting){ reveal(e.target); io.unobserve(e.target); } });
    }, { threshold: 0.12, rootMargin: '0px 0px -6% 0px' });
    document.querySelectorAll('.reveal:not(.in)').forEach(el => io.observe(el));

    const onScroll = () => {
      document.querySelectorAll('.reveal:not(.in)').forEach(el => {
        if (el.getBoundingClientRect().top < vh() * 0.9) reveal(el);
      });
    };
    window.addEventListener('scroll', onScroll, { passive:true });

    // Detect a throttled/offscreen document (rAF frozen): if a frame
    // hasn't fired shortly, CSS transitions are frozen too — so reveal
    // everything instantly. Real visible tabs fire rAF in ~16ms and keep
    // full scroll-in animations.
    let rafFired = false;
    requestAnimationFrame(() => { rafFired = true; });
    const throttleCheck = setTimeout(() => { if (!rafFired) force(); }, 350);

    return () => {
      io.disconnect();
      clearTimeout(throttleCheck);
      window.removeEventListener('scroll', onScroll);
      document.removeEventListener('visibilitychange', onVis);
    };
  }, [route]);
}

/* ---- a reveal wrapper ---- */
function Reveal({ as='div', d, className='', children, ...rest }){
  const Tag = as;
  return <Tag className={`reveal ${className}`} {...(d?{'data-d':d}:{})} {...rest}>{children}</Tag>;
}

/* ---- nav ---- */
function Nav({ route, go }){
  const [scrolled, setScrolled] = useState(false);
  const [open, setOpen] = useState(false);
  // Only mount the mobile menu/burger on narrow screens, so desktop can
  // NEVER show the overlay's contents even if the stylesheet is stale.
  const mq = '(max-width:720px)';
  const [isMobile, setIsMobile] = useState(
    typeof window !== 'undefined' && window.matchMedia ? window.matchMedia(mq).matches : false
  );
  useEffect(() => {
    const m = window.matchMedia(mq);
    const onChange = () => setIsMobile(m.matches);
    onChange();
    m.addEventListener ? m.addEventListener('change', onChange) : m.addListener(onChange);
    return () => { m.removeEventListener ? m.removeEventListener('change', onChange) : m.removeListener(onChange); };
  }, []);
  useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 24);
    onScroll();
    window.addEventListener('scroll', onScroll, { passive:true });
    return () => window.removeEventListener('scroll', onScroll);
  }, []);
  // close the menu if we grow past the breakpoint while it's open
  useEffect(() => { if (!isMobile && open) setOpen(false); }, [isMobile]);
  // lock body scroll + close on Escape while the mobile menu is open
  useEffect(() => {
    document.body.style.overflow = open ? 'hidden' : '';
    const onKey = (e) => { if (e.key === 'Escape') setOpen(false); };
    window.addEventListener('keydown', onKey);
    return () => { document.body.style.overflow=''; window.removeEventListener('keydown', onKey); };
  }, [open]);
  const links = [['home','Home','01'],['about','About','02'],['contact','Contact','03']];
  const nav = (id) => { setOpen(false); go(id); };
  return (
    <nav className={`nav ${scrolled||open?'scrolled':''} ${open?'menu-open':''}`}>
      <a className="nav-mark hov" href={window.pathFor ? window.pathFor('home') : '/'} onClick={(e)=>{e.preventDefault();nav('home');}}>
        <svg className="glyph" viewBox="0 0 24 24" fill="none" aria-hidden="true">
          <circle cx="12" cy="12" r="3" fill="var(--accent)"/>
          <ellipse cx="12" cy="12" rx="10.5" ry="4.4" stroke="var(--ink)" strokeWidth="1.3" transform="rotate(28 12 12)"/>
        </svg>
        SHARAYU&nbsp;MAKNIKAR
      </a>

      {/* desktop links */}
      {!isMobile && (
        <div className="nav-links">
          {links.map(([id,label,num]) => (
            <a key={id} href={window.pathFor ? window.pathFor(id) : ('/'+(id==='home'?'':id))} onClick={(e)=>{e.preventDefault();nav(id);}}
               className={`nav-link hov ${route===id?'active':''}`}>
              <span className="num">{num}</span>{label}
            </a>
          ))}
        </div>
      )}

      {/* mobile hamburger + overlay — only mounted below the breakpoint */}
      {isMobile && (
        <button className={`nav-burger hov ${open?'on':''}`} aria-label={open?'Close menu':'Open menu'}
                aria-expanded={open} onClick={()=>setOpen(o=>!o)}>
          <span></span><span></span>
        </button>
      )}
      {isMobile && (
        <div className={`mobile-menu ${open?'open':''}`} aria-hidden={!open}>
          <div className="mm-inner">
            <div className="mm-kicker mono">Navigation</div>
            {links.map(([id,label,num]) => (
              <a key={id} href={window.pathFor ? window.pathFor(id) : ('/'+(id==='home'?'':id))} onClick={(e)=>{e.preventDefault();nav(id);}}
                 className={`mm-link ${route===id?'active':''}`}>
                <span className="mm-num mono">{num}</span>
                <span className="mm-label">{label}</span>
                <span className="mm-arr">↗</span>
              </a>
            ))}
          </div>
        </div>
      )}
    </nav>
  );
}

/* ---- footer ---- */
function Footer({ go }){
  const S = window.SITE;
  return (
    <footer className="foot">
      <div className="wrap">
        <div className="kicker" style={{color:'rgba(244,238,226,.55)'}}>Let&rsquo;s build the rulebook for what&rsquo;s next</div>
        <button className="foot-big hov" style={{background:'none',border:0,color:'var(--cream)',textAlign:'left',cursor:'pointer',padding:0,marginTop:18}}
                onClick={()=>go('contact')}>
          Get in touch&nbsp;<span className="accent">&rarr;</span>
        </button>
        <div className="foot-rule"></div>
        <div className="foot-row">
          <div className="foot-meta">
            <span>{S.name}</span>
            <a href={`https://${S.linkedin}`} target="_blank" rel="noreferrer" className="hov">LinkedIn</a>
            <a href={`mailto:${S.email}`} className="hov">Email</a>
          </div>
          <div className="foot-meta">
            <span>&copy; {new Date().getFullYear()}</span>
            <span>Lat 28.5°N · Cape Canaveral frame</span>
          </div>
        </div>
      </div>
    </footer>
  );
}

/* ---- custom cursor (home page only) ---- */
function Cursor(){
  const dot = useRef(null), ring = useRef(null);
  useEffect(() => {
    if (window.matchMedia('(hover:none)').matches) return;
    let rx=window.innerWidth/2, ry=window.innerHeight/2, dx=rx, dy=ry, raf;
    const move = (e) => { dx=e.clientX; dy=e.clientY; if(dot.current) dot.current.style.transform=`translate(${dx}px,${dy}px) translate(-50%,-50%)`; };
    const loop = () => {
      rx += (dx-rx)*0.18; ry += (dy-ry)*0.18;
      if(ring.current) ring.current.style.transform=`translate(${rx}px,${ry}px) translate(-50%,-50%)`;
      raf=requestAnimationFrame(loop);
    };
    const over = (e) => { if(e.target.closest('.hov,a,button,input,textarea')) ring.current?.classList.add('hot'); };
    const out  = (e) => { if(e.target.closest('.hov,a,button,input,textarea')) ring.current?.classList.remove('hot'); };
    window.addEventListener('mousemove', move);
    document.addEventListener('mouseover', over);
    document.addEventListener('mouseout', out);
    loop();
    return () => { cancelAnimationFrame(raf); window.removeEventListener('mousemove',move); document.removeEventListener('mouseover',over); document.removeEventListener('mouseout',out); };
  }, []);
  return (<><div className="cursor-ring" ref={ring}></div><div className="cursor-dot" ref={dot}></div></>);
}

/* ---- starfield (constellation of dark dots on cream) ---- */
function Starfield({ density=0.00012 }){
  const ref = useRef(null);
  useEffect(() => {
    const cvs = ref.current, ctx = cvs.getContext('2d');
    let w,h,stars=[],raf,mx=0.5,my=0.5;
    const dpr = Math.min(window.devicePixelRatio||1, 2);
    const build = () => {
      w = cvs.clientWidth; h = cvs.clientHeight;
      cvs.width = w*dpr; cvs.height = h*dpr; ctx.setTransform(dpr,0,0,dpr,0,0);
      const n = Math.max(40, Math.floor(w*h*density));
      stars = Array.from({length:n}, () => ({
        x:Math.random()*w, y:Math.random()*h,
        r:Math.random()*1.5+0.4, z:Math.random()*0.9+0.1,
        tw:Math.random()*Math.PI*2, sp:Math.random()*0.6+0.2,
      }));
    };
    const draw = (t) => {
      ctx.clearRect(0,0,w,h);
      const ox=(mx-0.5)*24, oy=(my-0.5)*24;
      stars.forEach(s => {
        const a = 0.18 + 0.16*Math.sin(t*0.001*s.sp + s.tw);
        ctx.beginPath();
        ctx.fillStyle = `rgba(30,26,21,${a*s.z})`;
        ctx.arc(s.x+ox*s.z, s.y+oy*s.z, s.r, 0, Math.PI*2);
        ctx.fill();
      });
      raf = requestAnimationFrame(draw);
    };
    const onMove = (e) => { mx=e.clientX/window.innerWidth; my=e.clientY/window.innerHeight; };
    build(); raf=requestAnimationFrame(draw);
    window.addEventListener('resize', build);
    window.addEventListener('mousemove', onMove);
    return () => { cancelAnimationFrame(raf); window.removeEventListener('resize',build); window.removeEventListener('mousemove',onMove); };
  }, []);
  return <canvas ref={ref} className="starfield" aria-hidden="true"></canvas>;
}

/* ---- ticker / marquee ---- */
function Ticker({ items, sep='✦' }){
  const row = [...items, ...items, ...items];
  return (
    <div className="ticker" aria-hidden="true">
      <div className="ticker-track">
        {row.map((it,i)=>(<span className="ticker-item" key={i}>{it}<span className="ticker-sep">{sep}</span></span>))}
      </div>
    </div>
  );
}

Object.assign(window, { useReveals, Reveal, Nav, Footer, Cursor, Starfield, Ticker });
