/* Shared components & primitives — Republic Square (JAL Strategies) */
const { useState, useEffect, useRef, useMemo, useCallback } = React;

/* ------------------------------------------------------------------ */
/*  Deal data                                                          */
/* ------------------------------------------------------------------ */
const DEAL = {
  sponsor: 'JAL Strategies',
  principal: { name:'Justin A. Levine', title:'Founder & CEO', email:'jlevine@jalstrategies.com' },
  counterparty: { name:'George D. Vincent, Jr.', title:'Senior Managing Director', firm:'SRC' },
  property: 'Republic Square',
  address: '13501 Katy Freeway · Houston, TX · Energy Corridor Submarket',
  office: '301,851 SF',
  officeLeased: '86%',
  acres: '~10',
  srcPrice: '$54.3M',
  srcPsf: '$180 / SF',
  workersOnSite: '10,640',
  workersAdjacent: '28,000',
  workersCorridor: '91,000+',
  drive15HH: '125,000',
  retailSF2019: '65,942',
  retailSFBase: '77,700',
  retailSFFull: '103,000',
  retailSFHigh: '100,000+',
  yoc: '9.9%',
  irr: '26.5%',
  em: '3.04×',
  valueCreated: '$27.4M',
  noiStabilized: '$4.74M',
  saleY5: '$75.2M',
  totalCost: '$47.8M',
  landEquity: '$10.9M',
  cashEquity: '$3.5M',
  debt: '$33.5M',
  jalEconomicsTotal: '$13.90M',
  jalEconomicsMultiple: '13.9×',
  exitCap: '6.5%',
  lpIRR: '21%',
  lpEM: '2.6×',
};

const PALETTES = {
  institutional: {
    name: 'Midnight',
    ivory:'#F4F2ED', ivory2:'#EAE6DC', ink:'#0B163C', ink2:'#162148',
    muted:'#6E6A61', rule:'#D6D1C2', accent:'#3A243A', accentSoft:'#A0B4C3',
  },
  editorial: {
    name: 'Aubergine',
    ivory:'#F4F2ED', ivory2:'#ECE4E4', ink:'#3A243A', ink2:'#4A2F4A',
    muted:'#6E6A61', rule:'#D9CBD4', accent:'#0B163C', accentSoft:'#A0B4C3',
  },
  verdant: {
    name: 'Slate',
    ivory:'#F4F2ED', ivory2:'#E4EAEE', ink:'#0B163C', ink2:'#162148',
    muted:'#6E6A61', rule:'#C8D2DC', accent:'#A0B4C3', accentSoft:'#3A243A',
  },
};

function applyPalette(key){
  const p = PALETTES[key] || PALETTES.institutional;
  const r = document.documentElement.style;
  r.setProperty('--ivory', p.ivory);
  r.setProperty('--ivory-2', p.ivory2);
  r.setProperty('--ink', p.ink);
  r.setProperty('--ink-2', p.ink2);
  r.setProperty('--muted', p.muted);
  r.setProperty('--rule', p.rule);
  r.setProperty('--gold', p.accent);
  r.setProperty('--gold-soft', p.accentSoft);
}

/* Direct mailto — no forms, just open the user's email client */
const MAILTO = 'mailto:jlevine@jalstrategies.com?subject=Republic%20Square%20%E2%80%94%20Retail%20JV';

/* Utils */
const fmtUSD = (n, d=0) => '$' + Number(n).toLocaleString(undefined,{maximumFractionDigits:d});
const fmtUSDshort = (n) => {
  if (n >= 1_000_000) return '$' + (n/1_000_000).toFixed(n>=10_000_000?1:2).replace(/\.0+$/,'') + 'M';
  if (n >= 1_000) return '$' + Math.round(n/1_000) + 'K';
  return '$' + n;
};
const fmtPct = (n, d=1) => `${n.toFixed(d)}%`;

function useInView(options={threshold:0.15}){
  const ref = useRef(null);
  const [inView, setInView] = useState(false);
  useEffect(()=>{
    const el = ref.current; if(!el) return;
    const check = () => {
      const r = el.getBoundingClientRect();
      const vh = window.innerHeight || document.documentElement.clientHeight;
      if (r.top < vh * 0.9 && r.bottom > 0) { setInView(true); return true; }
      return false;
    };
    if (check()) return;
    const io = new IntersectionObserver(([e])=>{
      if(e.isIntersecting){ setInView(true); io.disconnect(); }
    }, options);
    io.observe(el);
    const t = setTimeout(()=>{ if(check()) io.disconnect(); }, 100);
    return ()=>{ io.disconnect(); clearTimeout(t); };
  },[]);
  return [ref, inView];
}

function useCounter(target, active, duration=1400){
  const [val, setVal] = useState(0);
  useEffect(()=>{
    if(!active) return;
    let raf, start;
    const tick = t => {
      if(!start) start = t;
      const p = Math.min(1, (t-start)/duration);
      const eased = 1 - Math.pow(1-p, 3);
      setVal(target * eased);
      if(p<1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return ()=>cancelAnimationFrame(raf);
  },[active,target,duration]);
  return val;
}

/* Atoms */
function Eyebrow({children, color}){
  return <div style={{
    fontFamily:"'JetBrains Mono',ui-monospace,monospace", fontSize:11, letterSpacing:'0.22em',
    textTransform:'uppercase', color: color || 'var(--muted)', marginBottom:18
  }}>{children}</div>;
}

function Btn({children, variant='primary', onClick, full, style, href, target, download}){
  const base = {
    display:'inline-flex', alignItems:'center', justifyContent:'center', gap:10,
    padding:'14px 22px', border:'1px solid transparent', fontSize:13,
    letterSpacing:'0.04em', fontWeight:500, transition:'all .2s ease',
    width: full?'100%':'auto', cursor:'pointer', textDecoration:'none',
  };
  const variants = {
    primary:{ background:'var(--ink)', color:'var(--ivory)', borderColor:'var(--ink)' },
    ghost:{ background:'transparent', color:'var(--ink)', borderColor:'var(--ink)' },
    gold:{ background:'var(--gold)', color:'var(--ivory)', borderColor:'var(--gold)' },
    light:{ background:'var(--ivory)', color:'var(--ink)', borderColor:'var(--ivory)' },
    lightGhost:{ background:'transparent', color:'var(--ivory)', borderColor:'rgba(244,242,237,0.4)' },
  };
  if(href){
    return <a href={href} target={target} download={download} onClick={onClick} className="btn-lift"
      style={{...base, ...variants[variant], ...style}}>{children}</a>;
  }
  return <button onClick={onClick} className="btn-lift" style={{...base, ...variants[variant], ...style}}>
    {children}
  </button>;
}

function Stat({label, value, sub, big}){
  return <div>
    <div style={{fontFamily:"'JetBrains Mono',ui-monospace,monospace", fontSize:10, letterSpacing:'0.22em',
      textTransform:'uppercase', color:'var(--muted)', marginBottom:10}}>{label}</div>
    <div style={{fontFamily:"'Jost',sans-serif", fontSize: big?64:40, lineHeight:0.95,
      fontWeight:300, letterSpacing:'-0.02em'}} className="tnum">{value}</div>
    {sub && <div style={{fontSize:12, color:'var(--muted)', marginTop:8}}>{sub}</div>}
  </div>;
}

function Monogram({size=40}){
  return <div style={{
    width:size, height:size, border:'1px solid var(--ink)',
    display:'flex', alignItems:'center', justifyContent:'center',
    fontFamily:"'Jost',sans-serif", fontSize:size*0.34, fontStyle:'italic',
    letterSpacing:'-0.02em'
  }}>RS</div>;
}

function SectionLabel({num, title}){
  return <div style={{display:'flex', alignItems:'baseline', gap:20,
    fontFamily:"'JetBrains Mono',ui-monospace,monospace", fontSize:11, letterSpacing:'0.22em',
    textTransform:'uppercase', color:'var(--muted)', marginBottom:40}}>
    <span>§ {num}</span>
    <span style={{flex:1, height:1, background:'var(--rule)'}}/>
    <span>{title}</span>
  </div>;
}

function Nav(){
  const [scrolled, setScrolled] = useState(false);
  const [active, setActive] = useState('');
  const [mobileOpen, setMobileOpen] = useState(false);
  const [dlOpen, setDlOpen] = useState(false);
  const dlRef = useRef(null);

  useEffect(()=>{
    if(!dlOpen) return;
    const onDoc = e => { if(dlRef.current && !dlRef.current.contains(e.target)) setDlOpen(false); };
    const onKey = e => { if(e.key === 'Escape') setDlOpen(false); };
    document.addEventListener('mousedown', onDoc);
    document.addEventListener('keydown', onKey);
    return ()=>{ document.removeEventListener('mousedown', onDoc); document.removeEventListener('keydown', onKey); };
  },[dlOpen]);

  const links = [
    ['Opportunity','opportunity'],
    ['Gap','gap'],
    ['Method','method'],
    ['Demand','demand'],
    ['Program','program'],
    ['Structure','structure'],
    ['Pro Forma','proforma'],
    ['Path','path'],
    ['Sponsor','sponsor'],
  ];

  useEffect(()=>{
    const onScroll = ()=> {
      setScrolled(window.scrollY > 40);
      const y = window.scrollY + 120;
      let current = '';
      for(const [,id] of links){
        const el = document.getElementById(id);
        if(el && el.offsetTop <= y) current = id;
      }
      setActive(current);
    };
    window.addEventListener('scroll', onScroll, {passive:true});
    onScroll();
    return ()=>window.removeEventListener('scroll', onScroll);
  },[]);

  useEffect(()=>{
    document.body.style.overflow = mobileOpen ? 'hidden' : '';
    return ()=>{ document.body.style.overflow = ''; };
  },[mobileOpen]);

  const scrollTo = id => {
    const el = document.getElementById(id);
    if(el){ window.scrollTo({top: el.offsetTop - 60, behavior:'smooth'}); }
    setMobileOpen(false);
  };

  return <React.Fragment>
    <nav style={{
      position:'fixed', top:0, left:0, right:0, zIndex:100,
      padding: scrolled ? '14px 40px' : '22px 40px',
      background: scrolled || mobileOpen ? 'rgba(244,242,237,0.96)' : 'transparent',
      backdropFilter: scrolled || mobileOpen ? 'blur(10px)' : 'none',
      borderBottom: scrolled ? '1px solid var(--rule)' : '1px solid transparent',
      display:'flex', alignItems:'center', gap:40, transition:'all .25s ease',
    }}>
      <div style={{display:'flex', alignItems:'center', gap:14, cursor:'pointer', minWidth:0, flex:'0 1 auto', overflow:'hidden'}}
        onClick={()=>{ window.scrollTo({top:0, behavior:'smooth'}); setMobileOpen(false); }}>
        <Monogram size={36}/>
        <div style={{minWidth:0, overflow:'hidden'}}>
          <div style={{fontFamily:"'Jost',sans-serif", fontSize:16, lineHeight:1, whiteSpace:'nowrap',
            overflow:'hidden', textOverflow:'ellipsis'}}>Republic Square</div>
          <div style={{fontFamily:"'JetBrains Mono',ui-monospace,monospace", fontSize:9, letterSpacing:'0.2em',
            textTransform:'uppercase', color:'var(--muted)', marginTop:3, whiteSpace:'nowrap',
            overflow:'hidden', textOverflow:'ellipsis'}}>JAL Strategies · Retail JV · Apr 2026</div>
        </div>
      </div>
      <div style={{flex:1, minWidth:12}}/>
      <div className="nav-desktop-links" style={{display:'flex', gap:20}}>
        {links.map(([l,id]) => <a key={id} onClick={()=>scrollTo(id)}
          className={"nav-link" + (active===id ? ' nav-link--active' : '')} style={{
          fontSize:12, letterSpacing:'0.04em', cursor:'pointer', color:'var(--ink)'
        }}>{l}</a>)}
      </div>
      <div className="nav-desktop-cta" style={{display:'flex', gap:10, alignItems:'center'}}>
        <div ref={dlRef} style={{position:'relative'}}>
          <button onClick={()=>setDlOpen(v=>!v)} aria-haspopup="menu" aria-expanded={dlOpen}
            style={{
              background:'transparent', border:'1px solid var(--ink)', color:'var(--ink)',
              padding:'10px 16px', fontSize:12, letterSpacing:'0.04em', cursor:'pointer',
              fontFamily:"inherit", display:'inline-flex', alignItems:'center', gap:8,
            }}>
            Materials <span style={{opacity:0.6, fontSize:10}}>{dlOpen ? '▴' : '▾'}</span>
          </button>
          {dlOpen && <div role="menu" style={{
            position:'absolute', top:'calc(100% + 8px)', right:0, minWidth:320,
            background:'var(--ivory)', border:'1px solid var(--ink)',
            boxShadow:'0 20px 40px rgba(11,22,60,0.18)', padding:6, zIndex:110,
          }}>
            {[
              ['Pitch deck','uploads/JAL_Republic_Square_Pitch_Deck.pptx','PPTX · 16-slide term sheet'],
              ['Pro Forma model','uploads/Republic_Square_Retail_ProForma_Model.xlsx','XLSX · full financial model'],
              ['Demand model','uploads/Republic_Square_Retail_Demand_Model.xlsx','XLSX · five-source framework'],
            ].map(([label, href, sub], i, arr) =>
              <a key={href} href={href} target="_blank" rel="noopener" download
                onClick={()=>setDlOpen(false)} role="menuitem"
                style={{display:'flex', flexDirection:'column', gap:3, padding:'12px 14px',
                        color:'var(--ink)', textDecoration:'none',
                        borderBottom: i < arr.length-1 ? '1px solid var(--rule)' : 'none'}}>
                <span style={{fontSize:13, fontWeight:500}}>{label} ↓</span>
                <span style={{fontSize:10, color:'var(--muted)', letterSpacing:'0.04em'}}>{sub}</span>
              </a>
            )}
          </div>}
        </div>
        <Btn variant="primary" href={MAILTO} style={{padding:'10px 18px', fontSize:12}}>
          Email Justin <span style={{opacity:0.6}}>→</span>
        </Btn>
      </div>
      <button className="nav-hamburger" aria-label="Menu" aria-expanded={mobileOpen}
        onClick={()=>setMobileOpen(o=>!o)} style={{
          background:'none', border:'1px solid var(--ink)', width:40, height:40, padding:0,
          display:'none', position:'relative', cursor:'pointer', flexShrink:0
        }}>
        <span style={{position:'absolute', left:'50%', width:18, height:1.5, background:'var(--ink)',
          top: mobileOpen ? '50%' : 'calc(50% - 6px)',
          transform: mobileOpen ? 'translate(-50%,-50%) rotate(45deg)' : 'translate(-50%,-50%)',
          transition:'top .2s ease, transform .2s ease'}}/>
        <span style={{position:'absolute', top:'50%', left:'50%', width:18, height:1.5, background:'var(--ink)',
          transform:'translate(-50%,-50%)',
          opacity: mobileOpen ? 0 : 1, transition:'opacity .15s ease'}}/>
        <span style={{position:'absolute', left:'50%', width:18, height:1.5, background:'var(--ink)',
          top: mobileOpen ? '50%' : 'calc(50% + 6px)',
          transform: mobileOpen ? 'translate(-50%,-50%) rotate(-45deg)' : 'translate(-50%,-50%)',
          transition:'top .2s ease, transform .2s ease'}}/>
      </button>
    </nav>

    {mobileOpen && <div onClick={()=>setMobileOpen(false)} style={{
      position:'fixed', top:0, left:0, right:0, bottom:0, zIndex:90,
      background:'rgba(244,242,237,0.98)', backdropFilter:'blur(14px)',
      display:'flex', flexDirection:'column', justifyContent:'center', padding:'80px 28px 40px',
      overflowY:'auto'
    }}>
      <div onClick={e=>e.stopPropagation()} style={{display:'flex', flexDirection:'column', gap:4}}>
        {links.map(([l,id]) => <a key={id} onClick={()=>scrollTo(id)} style={{
          fontFamily:"'Jost',sans-serif", fontSize:28, letterSpacing:'-0.01em',
          color: active===id ? 'var(--gold)' : 'var(--ink)',
          padding:'12px 0', borderBottom:'1px solid var(--rule)', cursor:'pointer',
          display:'flex', alignItems:'center', justifyContent:'space-between'
        }}>
          <span>{l}</span>
          <span style={{fontFamily:"'JetBrains Mono',ui-monospace,monospace", fontSize:11,
            letterSpacing:'0.2em', color:'var(--muted)'}}>→</span>
        </a>)}
        <div style={{marginTop:28, display:'flex', flexDirection:'column', gap:10}}>
          <Btn variant="ghost" full href="uploads/JAL_Republic_Square_Pitch_Deck.pptx" download
            onClick={()=>setMobileOpen(false)}
            style={{padding:'16px 22px', fontSize:14}}>
            Download pitch deck <span style={{opacity:0.6}}>↓</span>
          </Btn>
          <Btn variant="ghost" full href="uploads/Republic_Square_Retail_ProForma_Model.xlsx" download
            onClick={()=>setMobileOpen(false)}
            style={{padding:'16px 22px', fontSize:14}}>
            Download pro forma <span style={{opacity:0.6}}>↓</span>
          </Btn>
          <Btn variant="primary" full href={MAILTO}
            onClick={()=>setMobileOpen(false)}
            style={{padding:'16px 22px', fontSize:14}}>
            Email Justin <span style={{opacity:0.6}}>→</span>
          </Btn>
        </div>
      </div>
    </div>}
  </React.Fragment>;
}

function Placeholder({label, height='100%', dark, style}){
  return <div className={"ph-stripes" + (dark?' ph-dark':'')} data-ph={label}
    style={{width:'100%', height, ...style}}/>;
}

function ScrollProgress(){
  const [pct, setPct] = useState(0);
  useEffect(()=>{
    const onScroll = () => {
      const h = document.documentElement;
      const scrolled = h.scrollTop;
      const max = h.scrollHeight - h.clientHeight;
      setPct(max > 0 ? (scrolled/max)*100 : 0);
    };
    window.addEventListener('scroll', onScroll, {passive:true});
    onScroll();
    return ()=>window.removeEventListener('scroll', onScroll);
  },[]);
  return <div className="scroll-progress" aria-hidden="true">
    <div className="scroll-progress__bar" style={{width: pct + '%'}}/>
  </div>;
}

function AnimatedNumber({value, prefix='', suffix='', decimals=0, duration=1400, active=true}){
  const n = useCounter(value, active, duration);
  return <span className="tnum">{prefix}{n.toLocaleString(undefined, {
    maximumFractionDigits: decimals, minimumFractionDigits: decimals
  })}{suffix}</span>;
}

function TweaksPanel({open, tweaks, setTweaks, onClose}){
  if(!open) return null;
  const update = (k,v) => {
    const next = {...tweaks, [k]:v};
    setTweaks(next);
    window.parent?.postMessage({type:'__edit_mode_set_keys', edits:{[k]:v}}, '*');
  };
  return <div style={{
    position:'fixed', bottom:24, right:24, width:320, zIndex:200,
    background:'var(--ink)', color:'var(--ivory)', padding:24,
    boxShadow:'0 30px 60px rgba(0,0,0,0.25)'
  }}>
    <div style={{display:'flex', alignItems:'center', justifyContent:'space-between', marginBottom:20}}>
      <div style={{fontFamily:"'Jost',sans-serif", fontSize:20}}>Tweaks</div>
      <button onClick={onClose} style={{background:'none', border:'1px solid rgba(244,242,237,0.3)',
        color:'var(--ivory)', width:28, height:28, padding:0}}>×</button>
    </div>
    <div style={{marginBottom:20}}>
      <div style={{fontFamily:"'JetBrains Mono',ui-monospace,monospace", fontSize:10, letterSpacing:'0.2em',
        textTransform:'uppercase', opacity:0.6, marginBottom:10}}>Palette</div>
      <div style={{display:'grid', gridTemplateColumns:'1fr 1fr 1fr', gap:8}}>
        {Object.entries(PALETTES).map(([k,p])=>(
          <button key={k} onClick={()=>update('palette', k)} style={{
            padding:'10px 6px', background: tweaks.palette===k ? 'var(--gold)' : 'transparent',
            color:'var(--ivory)', border:'1px solid rgba(244,242,237,0.25)',
            fontSize:11, letterSpacing:'0.04em', cursor:'pointer',
            display:'flex', flexDirection:'column', gap:8, alignItems:'center'
          }}>
            <div style={{display:'flex', gap:3}}>
              <span style={{width:12,height:12, background:p.ivory, border:'1px solid rgba(244,242,237,0.3)'}}/>
              <span style={{width:12,height:12, background:p.ink}}/>
              <span style={{width:12,height:12, background:p.accent}}/>
            </div>
            {p.name}
          </button>
        ))}
      </div>
    </div>
    <div style={{marginBottom:20}}>
      <div style={{display:'flex', justifyContent:'space-between',
        fontFamily:"'JetBrains Mono',ui-monospace,monospace", fontSize:10, letterSpacing:'0.2em',
        textTransform:'uppercase', opacity:0.6, marginBottom:10}}>
        <span>Type scale</span><span>{tweaks.fontScale.toFixed(2)}×</span>
      </div>
      <input type="range" min="0.9" max="1.15" step="0.01" value={tweaks.fontScale}
        onChange={e=>update('fontScale', parseFloat(e.target.value))}
        style={{width:'100%', accentColor:'var(--gold)'}}/>
    </div>
  </div>;
}

Object.assign(window, {
  DEAL, MAILTO, PALETTES, applyPalette, fmtUSD, fmtUSDshort, fmtPct,
  useInView, useCounter, Eyebrow, Btn, Stat, Monogram, SectionLabel,
  Nav, Placeholder, TweaksPanel,
  ScrollProgress, AnimatedNumber,
});
