/* engine.jsx — Wh4 sell-side process visualisation.
   All section copy is sourced verbatim from https://wh4.co.uk.
   Panel data (stages, tiles, buyer rows) is clearly labelled "Illustrative"
   and uses generic buyer descriptors and Wh4's own published numbers — no
   invented deal names, financial figures, or specific counterparties. */

const { useState: useStateE, useEffect: useEffectE, useRef: useRefE } = React;

function useInView(margin = '-60px') {
  const ref = useRefE(null);
  const [inView, setInView] = useStateE(false);
  useEffectE(() => {
    if (!ref.current) return;
    const io = new IntersectionObserver(
      (es) => es.forEach((e) => { if (e.isIntersecting) { setInView(true); io.disconnect(); } }),
      { rootMargin: margin }
    );
    io.observe(ref.current);
    return () => io.disconnect();
  }, []);
  return [ref, inView];
}

function animateCount(el) {
  const end = parseFloat(el.dataset.count), dec = +(el.dataset.dec || 0);
  const pre = el.dataset.prefix || '', suf = el.dataset.suffix || '';
  const start = performance.now(), dur = 1400;
  (function step(now) {
    const t = Math.min(1, (now - start) / dur), e = 1 - Math.pow(1 - t, 3);
    el.textContent = pre + (end * e).toFixed(dec) + suf;
    if (t < 1) requestAnimationFrame(step);
  })(performance.now());
}

function EngIntro({ kicker, children, sub }) {
  const [ref, inView] = useInView('-70px');
  return (
    <div className="eng-intro"><div className={'eng-wrap' + (inView ? ' in' : '')} ref={ref}>
      <div className="eng-kicker eng-reveal">{kicker}</div>
      <h2 className="eng-reveal">{children}</h2>
      {sub && <p className="eng-reveal">{fmtWh4(sub)}</p>}
    </div></div>
  );
}

/* ---------- Sell-Side Process — split into 3 phases (4 steps each), scroll-linked line ---------- */
function PipelinePanel() {
  const [ref, inView] = useInView();
  const [chargedUpTo, setChargedUpTo] = useStateE(0);
  const [openKey, setOpenKey] = useStateE(null);  // "p-s" e.g. "1-2" for phase 1 step 2
  const stackRef = useRefE(null);
  const lineFillRef = useRefE(null);
  const lineDotRef = useRefE(null);

  useEffectE(() => {
    if (!inView) return;
    const timers = [];
    for (let i = 1; i <= 12; i++) {
      timers.push(setTimeout(() => setChargedUpTo((p) => Math.max(p, i)), i * 180));
    }
    return () => timers.forEach(clearTimeout);
  }, [inView]);

  // Scroll-linked progress line — tracks reading position through the 3 phases
  useEffectE(() => {
    const stack = stackRef.current;
    const fill = lineFillRef.current;
    const dot = lineDotRef.current;
    if (!stack || !fill || !dot) return;

    let raf = null;
    function update() {
      const rect = stack.getBoundingClientRect();
      const vh = window.innerHeight;
      const focal = vh * 0.45;
      let progress = (focal - rect.top) / rect.height;
      progress = Math.max(0, Math.min(1, progress));
      const pct = (progress * 100) + '%';
      fill.style.height = pct;
      dot.style.top = pct;
    }
    function onScroll() {
      if (raf) return;
      raf = requestAnimationFrame(() => { update(); raf = null; });
    }
    update();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', update);
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', update);
      if (raf) cancelAnimationFrame(raf);
    };
  }, []);

  // Phase names verbatim from wh4.co.uk/sell-side-m&a stage headings.
  // Step descriptions + expanded detail grounded in verbatim wh4.co.uk text.
  const PHASES = [
    {
      n: '01', title: 'Strategic Preparation',
      steps: [
        { n: '01', name: 'Initial Discovery',        desc: "Understand the company's strategy, financial performance, growth drivers and market positioning.",
          detail: "The process begins with careful preparation of the company's story and positioning. This stage involves working closely with the management team to understand the company's strategy, financial performance, growth drivers and market positioning." },
        { n: '02', name: 'Strategic Preparation',    desc: 'Refine the investment narrative and ensure the business is presented in the most compelling way.',
          detail: 'We help refine the investment narrative and ensure that the business is presented in the most compelling way possible. Strong preparation at this stage significantly increases the probability of achieving a successful outcome.' },
        { n: '03', name: 'Financial Materials',      desc: 'Prepare strategic materials and financial information that support the valuation.',
          detail: 'Preparing strategic materials and financial information is a critical part of a well-run transaction — the documents and data that underpin the valuation and let buyers form a serious view of the business.' },
        { n: '04', name: 'Buyer Universe',           desc: 'Identify strategic buyers, private equity firms and industry participants globally.',
          detail: 'Wh4 conducts targeted global research to identify strategic buyers, private equity firms and industry participants that may see strong strategic value in the business.' },
      ],
    },
    {
      n: '02', title: 'Controlled Market Approach',
      steps: [
        { n: '05', name: 'Curated Targeting',        desc: 'Develop a carefully curated buyer universe before initiating outreach.',
          detail: 'This stage focuses not simply on the largest buyers, but on the most strategically motivated buyers, where the combination of technologies, markets or capabilities can create a compelling acquisition rationale. A carefully curated buyer universe is then developed prior to initiating outreach.' },
        { n: '06', name: 'Confidential Outreach',    desc: 'Approach potential acquirers discreetly under strict confidentiality.',
          detail: 'Once the strategy and buyer list are finalised, Wh4 initiates a confidential and carefully controlled outreach process. Potential acquirers are approached discreetly and invited to review a high-level opportunity overview under strict confidentiality.' },
        { n: '07', name: 'Structured Discussions',   desc: 'Coordinate meetings and manage information flows while protecting confidentiality.',
          detail: 'Interested parties are then provided with access to further information and invited to engage in structured discussions. Throughout this stage, Wh4 manages all communications, coordinates meetings and ensures that the process maintains momentum while protecting confidentiality.' },
        { n: '08', name: 'Indications of Interest',  desc: 'Invite buyers to submit initial indications of interest.',
          detail: 'Where multiple buyers express interest, we structure the process to create competitive tension between potential acquirers. Buyers are typically invited to submit initial indications of interest, followed by deeper engagement with selected parties.' },
      ],
    },
    {
      n: '03', title: 'Negotiation and Transaction',
      steps: [
        { n: '09', name: 'Management Presentations', desc: 'Lead presentations and strategic discussions with potential acquirers.',
          detail: 'Selected parties move into deeper engagement — management presentations and strategic discussions with potential acquirers, designed to ensure the company\'s strategic value is fully understood.' },
        { n: '10', name: 'Diligence Coordination',   desc: 'Manage further diligence requests and clarifications of financial and operational information.',
          detail: "Clarification of financial and operational information, and further diligence requests. Our objective throughout this phase is to ensure that the company's strategic value is fully understood by potential buyers." },
        { n: '11', name: 'Negotiation',              desc: 'Negotiate offer structure, valuation and key commercial terms.',
          detail: 'Once a preferred buyer or group of buyers has emerged, Wh4 supports the negotiation of key commercial terms and assists in structuring the transaction — including negotiation of the offer structure and valuation.' },
        { n: '12', name: 'Transaction Close',        desc: 'Coordinate with legal and financial advisers and structure closing mechanics.',
          detail: "Coordination with legal and financial advisers, management of due diligence processes, and structuring of transaction terms and closing mechanics. Wh4 remains actively involved throughout the process to ensure the transaction progresses efficiently and that the founder's interests remain protected." },
      ],
    },
  ];

  // Track overall step index across all phases for the charging bars wave
  let overallIdx = 0;

  return (
    <div className="eng-stage" ref={ref}>
      <div className="phase-stack" ref={stackRef}>
        <div className="phase-line-track" aria-hidden="true">
          <div className="phase-line-fill" ref={lineFillRef} />
          <div className="phase-line-dot" ref={lineDotRef} />
        </div>
        {PHASES.map((phase, pIdx) => {
          // Find which step (if any) in this phase is currently expanded
          const openInPhase = phase.steps.find((s, sIdx) => openKey === pIdx + '-' + sIdx);
          return (
            <div
              key={phase.n}
              className={'eng-panel eng-panel-reveal eng-phase-panel' + (inView ? ' in' : '')}
              style={{ transitionDelay: (pIdx * 0.12) + 's' }}
            >
              <div className="eng-bar">
                <i className="eng-dot" /><i className="eng-dot" /><i className="eng-dot" />
                <span className="eng-ttl">Phase {phase.n} · {phase.title}</span>
                <span className="eng-deal">{phase.steps.length} steps</span>
              </div>
              <div className="eng-body">
                <div className="eng-phase-grid">
                  {phase.steps.map((s, sIdx) => {
                    const idx = overallIdx++;
                    const key = pIdx + '-' + sIdx;
                    const isOpen = openKey === key;
                    return (
                      <button
                        type="button"
                        className={'eng-step-12 eng-step-btn' + (isOpen ? ' is-open' : '')}
                        key={s.n}
                        onClick={() => setOpenKey(isOpen ? null : key)}
                        aria-expanded={isOpen}
                      >
                        <span className="eng-scan" aria-hidden="true" />
                        <div className="eng-n">Step {s.n}</div>
                        <div className="eng-s">{s.name}</div>
                        <p className="eng-step-desc">{s.desc}</p>
                        <span className="eng-charge" aria-hidden="true">
                          <span className={'eng-charge-fill' + (idx < chargedUpTo ? ' is-charged' : '')} />
                        </span>
                      </button>
                    );
                  })}
                </div>
                {openInPhase && (
                  <div className="eng-step-detail" key={openInPhase.n}>
                    <button
                      type="button"
                      className="eng-step-detail-close"
                      onClick={() => setOpenKey(null)}
                      aria-label="Close"
                    >
                      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round">
                        <path d="M18 6L6 18M6 6l12 12" />
                      </svg>
                    </button>
                    <div className="eng-step-detail-num">Step {openInPhase.n}</div>
                    <h3>{openInPhase.name}</h3>
                    <p>{openInPhase.detail}</p>
                  </div>
                )}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

/* ---------- Valuation focus (Wh4's published numbers) ---------- */
function MetricsPanel() {
  const [ref, inView] = useInView('-80px');
  useEffectE(() => { if (inView && ref.current) ref.current.querySelectorAll('[data-count]').forEach(animateCount); }, [inView]);
  // Wh4 Growth Partnerships terms — monthly fee + equity participation
  const tiles = [
    ['Initial Equity',   '5',  { 'data-suffix': '%', 'data-dec': '0' }, '0%', 'Vests over agreed period'],
    ['Maximum Equity',   '25', { 'data-suffix': '%', 'data-dec': '0' }, '0%', 'On growth milestones'],
    ['Group Divisions',  '4',  { 'data-dec': '0' }, '0', 'M&A · Growth Partnerships · Land · Capital'],
  ];
  const heights = [42, 55, 61, 74, 88, 100];
  return (
    <div className="eng-stage"><div ref={ref} className={'eng-panel eng-panel-reveal' + (inView ? ' in' : '')}>
      <div className="eng-bar"><i className="eng-dot" /><i className="eng-dot" /><i className="eng-dot" />
        <span className="eng-ttl">Partnership Terms</span><span className="eng-deal">Source · <b>wh4.co.uk</b></span></div>
      <div className="eng-body">
        <div className="eng-metrics">
          {tiles.map(([lab, count, attrs, init, sub]) => (
            <div className="eng-tile" key={lab}>
              <div className="eng-lab">{lab}</div>
              <div className="eng-val" data-count={count} {...attrs}>{init}</div>
              <div className="eng-sub">{fmtWh4(sub)}</div>
            </div>))}
        </div>
        <div className="eng-chart"><div className="eng-clab">Illustrative · how competitive interest typically builds over the outreach window (week 1 → week 6)</div>
          <div className="eng-bars">{heights.map((h, i) => <div className="eng-bar2" style={{ height: h + '%' }} key={i} />)}</div>
          <div className="eng-xrow">{['W1', 'W2', 'W3', 'W4', 'W5', 'W6'].map((w) => <span key={w}>{w}</span>)}</div>
        </div>
      </div>
    </div></div>
  );
}

/* ---------- Buyer outreach (generic buyer types from wh4.co.uk) ---------- */
function OutreachPanel() {
  const [ref, inView] = useInView();
  // Funnel proportions are typical sell-side industry shapes (not from a real deal)
  const funnel = [['Contacted', '100%', ''], ['NDA', '25%', ''], ['IOI', '10%', ''], ['LOI', '5%', '']];
  // Buyer types only — taken verbatim from wh4.co.uk's "strategic buyers, private equity firms and industry participants"
  const rows = [
    ['Strategic Buyer', 'Global', 'IOI Received', 'ioi'],
    ['Private Equity Firm', 'Mid-market', 'Mgmt Meeting', 'mtg'],
    ['Strategic Buyer', 'Regional', 'NDA Signed', 'nda'],
    ['Private Equity Firm', 'Sector-focused', 'In Diligence', 'dd'],
    ['Industry Participant', 'Specialist', 'Passed', 'pass'],
  ];
  return (
    <div className="eng-stage"><div ref={ref} className={'eng-panel eng-panel-reveal' + (inView ? ' in' : '')}>
      <div className="eng-bar"><i className="eng-dot" /><i className="eng-dot" /><i className="eng-dot" />
        <span className="eng-ttl">Buyer Outreach</span><span className="eng-deal">Illustrative</span></div>
      <div className="eng-body"><div className="eng-outreach">
        <div className="eng-funnel">{funnel.map(([name, w, n]) => (
          <div className="eng-frow" key={name}><div className="eng-name">{name}</div>
            <div className="eng-track"><div className="eng-fill" style={{ '--w': w }}>{n || w}</div></div></div>))}
        </div>
        <table className="eng-tbl"><thead><tr><th>Buyer type</th><th>Status</th></tr></thead>
          <tbody>{rows.map(([name, type, stat, cls], i) => (
            <tr key={name + i}><td><div>{name}</div><div className="eng-type">{type}</div></td>
              <td><span className={'eng-stat ' + cls}>{stat}</span></td></tr>))}
          </tbody>
        </table>
      </div></div>
    </div></div>
  );
}

/* ---------- Divisions (the 4 operating arms of Wh4 Ltd) ---------- */
const CHAPTERS = [
  {
    n: 'I', roman: 'I', title: 'Wh4 M&A',
    teaser: 'Sell-side and buy-side M&A for technology companies.',
    paragraphs: [
      'We advise founders, shareholders and acquirers across AI, fintech and the wider technology sector.',
      'For owners ready to exit, we run disciplined, competitive sale processes that maximise value. For buyers, we source, evaluate and execute acquisitions.',
      'Senior-led on every mandate. Wh4 M&A focuses on founder-led companies typically valued up to $150M.',
    ],
    img: 'assets/wh4office3.webp', href: 'advisory.html',
  },
  {
    n: 'II', roman: 'II', title: 'Wh4 Growth Partnerships',
    teaser: 'Hands-on growth support — a monthly fee plus equity participation.',
    paragraphs: [
      "Some businesses aren't ready for a transaction — yet. We partner with founders at an earlier stage, working alongside them to scale operations, strengthen the team and build value.",
      'We work for a monthly fee plus equity participation, so our interests stay aligned with long-term value creation: we win most when you do.',
      "We're not a VC — we invest our expertise, not a fund, and there's no fixed exit clock.",
    ],
    img: 'assets/hero-growth.png', href: 'partners.html',
  },
  {
    n: 'III', roman: 'III', title: 'Wh4 Land',
    teaser: 'Long-term investment in land and development.',
    paragraphs: [
      'We acquire land and undertake selective development projects, building tangible, income-generating assets for the group.',
      'As patient capital with no external investors, we can take the long view on every site.',
    ],
    img: 'assets/london-skyline.jpg', href: 'property.html',
  },
  {
    n: 'IV', roman: 'IV', title: 'Wh4 Capital',
    teaser: 'Buy. Build. Exit.',
    paragraphs: [
      'We acquire established businesses with clear potential, then put our operational and M&A expertise to work — strengthening management, driving growth and preparing each business for a successful exit.',
      'Our M&A advisory heritage means we know exactly what acquirers pay for, so we build it.',
      'We invest our own capital and selectively partner with external investors on larger opportunities.',
    ],
    img: 'assets/hero-collective.png', href: 'capital.html',
  },
];

/* Render any string containing "Wh4" with the 4 as a superscript */
function fmtWh4(str) {
  const parts = String(str).split(/Wh4/);
  if (parts.length === 1) return str;
  return parts.flatMap((p, i) => i === 0 ? [p] : [<React.Fragment key={i}>Wh<sup>4</sup></React.Fragment>, p]);
}

function ChaptersPanel() {
  const [ref, inView] = useInView('-80px');
  return (
    <div className="eng-stage eng-chapters-stage">
      <div ref={ref} className={'eng-wrap eng-chapters' + (inView ? ' in' : '')}>
        {CHAPTERS.map((c, i) => (
          <a
            href={c.href}
            key={c.n}
            className="eng-chap"
            style={{ transitionDelay: (i * 0.1) + 's' }}
          >
            <div className="eng-chap-card">
              <div className="eng-chap-head">
                <div className="eng-chap-pre">Division {c.roman}</div>
                <h3 className="eng-chap-ttl">{fmtWh4(c.title)}</h3>
              </div>
              <div className="eng-chap-img-frame">
                <div className="eng-chap-img"><img src={c.img} alt={c.title} /></div>
              </div>
              <div className="eng-chap-by"><em>by Wh<sup>4</sup></em></div>
            </div>
            <span className="eng-chap-cta"><em>Visit {fmtWh4(c.title)}</em></span>
          </a>
        ))}
      </div>
    </div>
  );
}

/* ---------- Group Story panel — "One group. Four ways to build value." ---------- */
function GroupStorySection() {
  const [ref, inView] = useInView('-80px');
  return (
    <section className="group-story" ref={ref}>
      <div className={'group-story-inner' + (inView ? ' in' : '')}>
        <div className="group-story-image">
          <img src="assets/wh4office2.webp" alt="The Wh4 boardroom — Tower Bridge view" />
        </div>
        <div className="group-story-text">
          <p className="group-story-eyebrow">About the group</p>
          <h2 className="group-story-title">One group.<br/>Four ways to build value.</h2>
          <p>Wh<sup>4</sup> began in technology M&amp;A, advising founders on the most important transaction of their lives. That work taught us what makes businesses valuable — and we now apply it as principals, not just advisers: partnering with earlier-stage companies for equity, investing in property, and acquiring businesses to build and exit.</p>
          <p>We're family-owned and intend to stay that way. The group isn't for sale — which means every division is run for the long term, with our own capital committed alongside every partner, founder and investor we work with.</p>
          <div className="group-story-strap">
            <span>Wh<sup>4</sup> Ltd</span>
            <span className="gs-dot">·</span>
            <a href="advisory.html">M&A</a>
            <span className="gs-dot">·</span>
            <a href="partners.html">Growth Partnerships</a>
            <span className="gs-dot">·</span>
            <a href="property.html">Land</a>
            <span className="gs-dot">·</span>
            <a href="capital.html">Capital</a>
          </div>
        </div>
      </div>
    </section>
  );
}

/* ---------- Glyph field — water-flow hover reveal over the engine zone ---------- */
function GlyphField() {
  const layerRef = useRefE(null);
  useEffectE(() => {
    const layer = layerRef.current;
    if (!layer) return;

    const BASE_GLYPHS = ['—','|','/','\\','+','×','·','•','○','◯','●','◉','□','▢','▣','▥','▦','←','→','↑','↓','↗','↖','↘','↙','⌐','¬','T','L','X','Y','[',']','{','}','÷','=','≡','⊥','⊤','§','¶','†','‡','*'];
    const WH4_FLAVOR = ['W','h','4','I','II','III','IV','M','&','A','$','%','€','£','π','Wh'];
    const palette = [].concat(BASE_GLYPHS, WH4_FLAVOR, WH4_FLAVOR, WH4_FLAVOR);

    let glyphs = [];
    let mouseX = -9999, mouseY = -9999;
    let lastClientX = -9999, lastClientY = -9999;
    const HISTORY_LEN = 12;
    const HISTORY_LIFETIME = 0.55;
    const history = [];
    let raf = null;

    const REVEAL_RADIUS = 280;
    const PUSH_RADIUS = 200;
    const PUSH_STRENGTH = 40;

    const rand = (a, b) => a + Math.random() * (b - a);
    const pick = (arr) => arr[Math.floor(Math.random() * arr.length)];

    function build() {
      layer.innerHTML = '';
      glyphs = [];
      const w = layer.clientWidth;
      const h = layer.clientHeight;
      if (!w || !h) return;
      // Scale density to area — roughly 1 glyph per 1700 px²
      const density = Math.min(2400, Math.max(800, Math.floor((w * h) / 1700)));
      for (let i = 0; i < density; i++) {
        const el = document.createElement('span');
        el.className = 'gz-glyph';
        const roll = Math.random();
        if (roll < 0.08) el.classList.add('cyan');
        else if (roll < 0.22) el.classList.add('serif');
        else if (roll < 0.42) el.classList.add('blue');
        else if (roll < 0.55) el.classList.add('light');
        el.textContent = pick(palette);
        el.style.fontSize = rand(11, 24) + 'px';
        const x = rand(0, w);
        const y = rand(0, h);
        el.style.left = x + 'px';
        el.style.top = y + 'px';
        layer.appendChild(el);
        glyphs.push({ el, baseX: x, baseY: y, curTx: 0, curTy: 0, curOpacity: 0, curScale: 0.85 });
      }
    }

    function updateMouseFromClient() {
      const rect = layer.getBoundingClientRect();
      mouseX = lastClientX - rect.left;
      mouseY = lastClientY - rect.top;
    }

    const onMove = (e) => {
      lastClientX = e.clientX;
      lastClientY = e.clientY;
      updateMouseFromClient();
      history.push({ x: mouseX, y: mouseY, t: performance.now() });
      if (history.length > HISTORY_LEN) history.shift();
    };
    const onLeave = () => { mouseX = mouseY = -9999; };
    const onScroll = () => { updateMouseFromClient(); };
    const onResize = () => build();

    document.addEventListener('mousemove', onMove);
    document.addEventListener('mouseleave', onLeave);
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onResize);

    function animate() {
      const now = performance.now();
      while (history.length && (now - history[0].t) / 1000 > HISTORY_LIFETIME) history.shift();

      for (let i = 0; i < glyphs.length; i++) {
        const g = glyphs[i];
        let influence = 0;
        let pushTx = 0, pushTy = 0;
        for (let h2 = 0; h2 < history.length; h2++) {
          const p = history[h2];
          const age = (now - p.t) / 1000;
          if (age > HISTORY_LIFETIME) continue;
          const dx = g.baseX - p.x;
          const dy = g.baseY - p.y;
          const dist = Math.sqrt(dx*dx + dy*dy);
          if (dist < REVEAL_RADIUS) {
            const distF = 1 - dist / REVEAL_RADIUS;
            const ageF = 1 - age / HISTORY_LIFETIME;
            const inf = distF * ageF;
            if (inf > influence) influence = inf;
          }
          if (dist < PUSH_RADIUS) {
            const distF = 1 - dist / PUSH_RADIUS;
            const e2 = distF * distF;
            const nx = dist > 0 ? dx / dist : 0;
            const ny = dist > 0 ? dy / dist : 0;
            const weight = 1 - (h2 / Math.max(1, history.length));
            pushTx += nx * e2 * PUSH_STRENGTH * weight * 0.3;
            pushTy += ny * e2 * PUSH_STRENGTH * weight * 0.3;
          }
        }
        const opacityTarget = Math.pow(influence, 1.4);
        const scaleTarget = 0.85 + influence * 0.5;
        g.curTx += (pushTx - g.curTx) * 0.42;
        g.curTy += (pushTy - g.curTy) * 0.42;
        g.curOpacity += (opacityTarget - g.curOpacity) * 0.45;
        g.curScale += (scaleTarget - g.curScale) * 0.40;
        g.el.style.opacity = g.curOpacity.toFixed(3);
        g.el.style.transform = 'translate(-50%,-50%) translate(' + g.curTx + 'px,' + g.curTy + 'px) scale(' + g.curScale + ')';
      }
      raf = requestAnimationFrame(animate);
    }

    build();
    raf = requestAnimationFrame(animate);

    return () => {
      document.removeEventListener('mousemove', onMove);
      document.removeEventListener('mouseleave', onLeave);
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onResize);
      if (raf) cancelAnimationFrame(raf);
    };
  }, []);
  return <div ref={layerRef} className="glyph-zone-layer" aria-hidden="true" />;
}

/* ---------- Word scramble (cofounder.co inspired, dynamic word-finding) ---------- */
const WH4_WORDS = [
  'ADVISORY',
  'PARTNERS',
  'PROPERTY',
  'CAPITAL',
  'FAMILY-OWNED',
  'BUY. BUILD. EXIT.',
  'SELL-SIDE M&A',
  'PRIVATE FOUNDERS COLLECTIVE',
  'EXITS',
  'CAPITAL',
  'PRIVATE',
  'STRATEGIC',
  'CONFIDENTIAL',
  'FOUNDER-LED',
  'ADVISORY',
  'DISCREET',
  'VALUATION',
  '$150M',
  'GLOBAL',
  'ENTREPRENEURS',
];
const SCRAMBLE_ROWS = 11;
const SCRAMBLE_COLS = 26;

function WordScramble() {
  const [sectionRef, inView] = useInView('-100px');
  const gridRef = useRefE(null);
  const allCellsRef = useRefE([]);
  const lockedRef = useRefE(new Set());

  // Build the grid DOM once on mount — all cells start as random letters
  useEffectE(() => {
    if (!gridRef.current) return;
    const ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    const randCh = () => ALPHA[Math.floor(Math.random() * 26)];
    const grid = gridRef.current;
    grid.innerHTML = '';
    const cells = [];
    for (let r = 0; r < SCRAMBLE_ROWS; r++) {
      const rowEl = document.createElement('div');
      rowEl.className = 'scramble-row';
      for (let c = 0; c < SCRAMBLE_COLS; c++) {
        const cell = document.createElement('span');
        cell.className = 'scramble-cell';
        cell.textContent = randCh();
        rowEl.appendChild(cell);
        cells.push(cell);
      }
      grid.appendChild(rowEl);
    }
    allCellsRef.current = cells;
  }, []);

  // Continuous scramble (skips locked cells where a word is currently displayed)
  useEffectE(() => {
    const ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    const interval = setInterval(() => {
      const cells = allCellsRef.current;
      const locked = lockedRef.current;
      if (!cells.length) return;
      for (let i = 0; i < 8; i++) {
        const idx = Math.floor(Math.random() * cells.length);
        if (!locked.has(idx)) {
          cells[idx].textContent = ALPHA[Math.floor(Math.random() * 26)];
        }
      }
    }, 80);
    return () => clearInterval(interval);
  }, []);

  // When in view: cycle through finding Wh4 words at random positions
  useEffectE(() => {
    if (!inView) return;
    let timer = null;
    let activeHl = null;
    let activeLocked = [];
    let lastRow = -1;
    let lastWord = '';

    function findNext() {
      const cells = allCellsRef.current;
      const grid = gridRef.current;
      if (!cells.length || !grid) {
        timer = setTimeout(findNext, 500);
        return;
      }

      // Pick a word that isn't the same as last
      let word;
      let attempts = 0;
      do {
        word = WH4_WORDS[Math.floor(Math.random() * WH4_WORDS.length)];
        attempts++;
      } while (word === lastWord && attempts < 5);
      lastWord = word;

      const maxCol = SCRAMBLE_COLS - word.length;
      if (maxCol < 0) { timer = setTimeout(findNext, 100); return; }

      // Pick a row different from the last one for visual jump
      let row;
      attempts = 0;
      do {
        row = Math.floor(Math.random() * SCRAMBLE_ROWS);
        attempts++;
      } while (row === lastRow && attempts < 5);
      lastRow = row;

      const col = Math.floor(Math.random() * (maxCol + 1));

      // Lock cells and write the word
      const wordCells = [];
      for (let i = 0; i < word.length; i++) {
        const idx = row * SCRAMBLE_COLS + (col + i);
        const cell = cells[idx];
        if (cell) {
          cell.textContent = word[i];
          cell.classList.add('is-found');
          lockedRef.current.add(idx);
          activeLocked.push(idx);
          wordCells.push(cell);
        }
      }

      // Position the highlight overlay over the locked cells
      if (wordCells.length > 0) {
        const first = wordCells[0];
        const last = wordCells[wordCells.length - 1];
        const fRect = first.getBoundingClientRect();
        const lRect = last.getBoundingClientRect();
        const gRect = grid.getBoundingClientRect();
        const PAD_X = 8;
        const PAD_Y = 4;
        const left = fRect.left - gRect.left - PAD_X;
        const top = fRect.top - gRect.top - PAD_Y;
        const fullWidth = (lRect.right - fRect.left) + PAD_X * 2;
        const height = fRect.height + PAD_Y * 2;

        const hl = document.createElement('div');
        hl.className = 'scramble-found';
        hl.style.left = left + 'px';
        hl.style.top = top + 'px';
        hl.style.height = height + 'px';
        hl.style.width = '0px';
        grid.appendChild(hl);
        // force reflow then animate width
        hl.offsetWidth;
        hl.style.width = fullWidth + 'px';
        activeHl = hl;
      }

      // Hold ~2s, then fade out and pick the next
      timer = setTimeout(() => {
        // Fade highlight
        if (activeHl) {
          activeHl.style.opacity = '0';
          const h = activeHl;
          setTimeout(() => { try { h.remove(); } catch (e) {} }, 450);
          activeHl = null;
        }
        // Unlock cells (visual "found" treatment fades quickly too)
        activeLocked.forEach((i) => {
          lockedRef.current.delete(i);
          if (cells[i]) cells[i].classList.remove('is-found');
        });
        activeLocked = [];
        // Brief pause then next word
        timer = setTimeout(findNext, 550);
      }, 2100);
    }

    // Initial small delay so user has a beat to see the scrambling
    timer = setTimeout(findNext, 700);

    return () => {
      if (timer) clearTimeout(timer);
      if (activeHl) { try { activeHl.remove(); } catch (e) {} }
      activeLocked.forEach((i) => {
        lockedRef.current.delete(i);
        const cell = allCellsRef.current[i];
        if (cell) cell.classList.remove('is-found');
      });
    };
  }, [inView]);

  return (
    <section className="scramble-section" ref={sectionRef}>
      <div className="container">
        <h2 className="scramble-h2">Four Businesses. One Discipline: Building Value.</h2>
        <p className="scramble-sub">Wh<sup>4</sup> is a diversified group built around one idea: creating value.</p>
        <div className="scramble-grid" ref={gridRef} aria-hidden="true"></div>
      </div>
    </section>
  );
}

/* ---------- London brand section (Rob: "make it look like we're in London") ---------- */
function LondonSection() {
  const [ref, inView] = useInView('-80px');
  return (
    <section className="london-section" ref={ref}>
      <div className="london-bg" />
      <div className="london-overlay" />
      <div className={'london-content' + (inView ? ' in' : '')}>
        <p className="london-eyebrow">From London</p>
        <h2 className="london-title">Headquartered in London. Operating globally.</h2>
        <p className="london-lede">Lower Thames Street, London EC3R — overlooking the river, in the world's centre of M&amp;A and private capital.</p>
      </div>
    </section>
  );
}

/* ---------- Pop-up CTA — appears after 50% scroll, once per session ---------- */
function PopupCTA() {
  const [open, setOpen] = useStateE(false);
  useEffectE(() => {
    if (typeof window === 'undefined') return;
    // Manual trigger via ?popup=1 in the URL — fires immediately, useful for QA
    if (window.location.search.indexOf('popup=1') >= 0) {
      setOpen(true);
      return;
    }
    // Show once per browser session (matches the Cookie Policy disclosure)
    try { if (sessionStorage.getItem('wh4-popup-shown')) return; } catch (e) {}
    let shown = false;
    const onScroll = () => {
      if (shown) return;
      const scrolled = window.scrollY;
      const total = document.documentElement.scrollHeight - window.innerHeight;
      if (total <= 0) return;
      const pct = scrolled / total;
      if (pct >= 0.25) {
        shown = true;
        setOpen(true);
        try { sessionStorage.setItem('wh4-popup-shown', '1'); } catch (e) {}
        window.removeEventListener('scroll', onScroll);
      }
    };
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, []);

  useEffectE(() => {
    if (!open) return;
    const prev = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    const onKey = (e) => { if (e.key === 'Escape') setOpen(false); };
    document.addEventListener('keydown', onKey);
    return () => {
      document.body.style.overflow = prev;
      document.removeEventListener('keydown', onKey);
    };
  }, [open]);

  if (!open) return null;
  const options = [
    { n: '01', title: 'Discuss a transaction', desc: 'Sell-side or buy-side M&A with Wh4 M&A.', href: 'advisory.html' },
    { n: '02', title: 'Partner with us', desc: 'Hands-on growth support for equity with Wh4 Growth Partnerships.', href: 'partners.html' },
    { n: '03', title: 'Sell your business to us', desc: 'A long-term home through Wh4 Capital. Buy. Build. Exit.', href: 'capital.html' },
    { n: '04', title: 'Invest alongside us', desc: 'Deal-by-deal co-investment with Wh4 Capital.', href: 'capital.html#for-investors' },
  ];
  return (
    <div className="popup-overlay" onClick={() => setOpen(false)} role="dialog" aria-modal="true">
      <div className="popup-card" onClick={(e) => e.stopPropagation()}>
        <button className="popup-close" onClick={() => setOpen(false)} aria-label="Close">
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round"><path d="M18 6L6 18M6 6l12 12" /></svg>
        </button>
        <p className="popup-eyebrow">Get in touch</p>
        <h2 className="popup-title">Take the next step.</h2>
        <p className="popup-lede">Tell us where you are in the journey — a Wh<sup>4</sup> partner will follow up confidentially.</p>
        <div className="popup-options">
          {options.map((o) => (
            <a key={o.n} href={o.href} className="popup-option">
              <span className="popup-option-num">{o.n}</span>
              <div className="popup-option-text">
                <h3>{o.title}</h3>
                <p>{fmtWh4(o.desc)}</p>
              </div>
              <span className="popup-option-arrow" aria-hidden="true">
                <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M5 12h14M13 5l7 7-7 7" /></svg>
              </span>
            </a>
          ))}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { EngIntro, PipelinePanel, MetricsPanel, OutreachPanel, ChaptersPanel, GlyphField, WordScramble, LondonSection, PopupCTA, GroupStorySection });
