// OncoVolt — Hero · Concept 01 "Transition Wave"
// A near-white histology field. A diagonal wave of EMT sweeps the tissue —
// epithelial navy → transitional amber → mesenchymal brown — then heals back.
// Ported from the OncoVolt Hero Concepts canvas (buildA / drawA).

function HeroWave({ tweaks = {} }) {
  const canvasRef = React.useRef(null);

  React.useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;

    const TAU = Math.PI * 2;
    const reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;

    let raf = 0, W = 0, H = 0, ctx = null, acells = null, running = true;
    const t0 = performance.now();

    const clamp = (v, a, b) => (v < a ? a : v > b ? b : v);
    const lerp = (a, b, t) => a + (b - a) * t;
    const smooth = (t) => { t = clamp(t, 0, 1); return t * t * (3 - 2 * t); };
    const mix = (c, d, t) => [lerp(c[0], d[0], t), lerp(c[1], d[1], t), lerp(c[2], d[2], t)];
    // EMT signature ramp: epithelial navy → amber → mesenchymal brown
    const emt = (t) => { t = clamp(t, 0, 1); const N = [27, 42, 74], A = [201, 138, 60], B = [94, 58, 38]; return t < 0.5 ? mix(N, A, t / 0.5) : mix(A, B, (t - 0.5) / 0.5); };
    const rgb = (c) => `rgb(${c[0] | 0},${c[1] | 0},${c[2] | 0})`;
    const makeNoise = (n) => { const a = []; for (let i = 0; i < n; i++) a.push(Math.random() - 0.5); return a; };

    function sizeCanvas() {
      const dpr = Math.min(2, window.devicePixelRatio || 1);
      W = canvas.clientWidth; H = canvas.clientHeight;
      canvas.width = Math.max(1, W * dpr); canvas.height = Math.max(1, H * dpr);
      ctx = canvas.getContext('2d'); ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    }

    function build() {
      const sp = Math.max(34, Math.min(50, W / 30));
      const cells = [];
      for (let y = -sp * 0.5; y < H + sp; y += sp) for (let x = -sp * 0.5; x < W + sp; x += sp) {
        const nv = 14 + (Math.random() * 4 | 0);
        cells.push({
          x: x + sp * 0.5 + (Math.random() - 0.5) * sp * 0.4,
          y: y + sp * 0.5 + (Math.random() - 0.5) * sp * 0.4,
          r: sp * 0.45 * (0.84 + Math.random() * 0.3),
          nv, noise: makeNoise(nv + 1), dir: Math.random() * TAU, phase: Math.random() * TAU, p: 0,
          // nucleus: offset slightly from centre so it reads as organic
          nr: 0.34 + Math.random() * 0.1, nox: (Math.random() - 0.5) * 0.22, noy: (Math.random() - 0.5) * 0.22,
        });
      }
      const ax = Math.cos(-0.18), ay = Math.sin(-0.18);
      let mn = 1e9, mx = -1e9;
      cells.forEach(c => { c.proj = c.x * ax + c.y * ay; if (c.proj < mn) mn = c.proj; if (c.proj > mx) mx = c.proj; });
      cells.forEach(c => c.p = (c.proj - mn) / (mx - mn));
      acells = cells;
    }

    // map a unit-circle point through the cell's elongation/rotation into canvas space
    function cellPoint(c, a, rr, cs, sn, elong) {
      const px = Math.cos(a) * rr, py = Math.sin(a) * rr;
      const xr = px * cs + py * sn, yr = -px * sn + py * cs;
      const xs = xr * elong, ys = yr / Math.sqrt(elong);
      return [xs * cs - ys * sn + c.x, xs * sn + ys * cs + c.y];
    }

    function cellPath(c, prog, time) {
      const n = c.nv, elong = 1 + prog * 0.32, dir = c.dir;
      const cs = Math.cos(dir), sn = Math.sin(dir);
      // gentler wobble → rounder, softer cell outline
      const wob = 0.03 + prog * 0.08;
      // collect vertices, then trace them with quadratic curves through the
      // midpoints so the outline is smooth (C1-continuous) — no straight facets
      const pts = [];
      for (let i = 0; i < n; i++) {
        const a = i / n * TAU;
        const breathe = 1 + Math.sin(time * 1.0 + c.phase + i * 0.7) * 0.035;
        const rr = c.r * (1 + c.noise[i % n] * wob) * breathe;
        pts.push(cellPoint(c, a, rr, cs, sn, elong));
      }
      ctx.beginPath();
      const mid = (a, b) => [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
      let prev = pts[n - 1];
      let start = mid(prev, pts[0]);
      ctx.moveTo(start[0], start[1]);
      for (let i = 0; i < n; i++) {
        const cur = pts[i], nxt = pts[(i + 1) % n];
        const m = mid(cur, nxt);
        ctx.quadraticCurveTo(cur[0], cur[1], m[0], m[1]);
      }
      ctx.closePath();
    }

    function nucleusPath(c, prog, time) {
      const elong = 1 + prog * 0.32, dir = c.dir;
      const cs = Math.cos(dir), sn = Math.sin(dir);
      const breathe = 1 + Math.sin(time * 1.0 + c.phase) * 0.04;
      // nucleus centre rides the same elongation as the cell body
      const [ncx, ncy] = cellPoint(c, 0, 0, cs, sn, elong);
      const ox = c.nox * c.r, oy = c.noy * c.r;
      const cx = ncx + ox * cs - oy * sn, cy = ncy + ox * sn + oy * cs;
      const nr = c.r * c.nr * breathe;
      ctx.beginPath();
      ctx.ellipse(cx, cy, nr * elong, nr / Math.sqrt(elong), dir, 0, TAU);
    }

    function draw(time) {
      if (!acells || !ctx) return;
      ctx.fillStyle = '#faf9f6'; ctx.fillRect(0, 0, W, H);
      const T = 16, band = 0.22, ph = (time % T) / T;
      acells.forEach(c => {
        let prog;
        if (ph < 0.5) { const F = lerp(-band, 1 + band, ph / 0.5); prog = smooth((F - c.p) / (2 * band) + 0.5); }
        else { const Hf = lerp(-band, 1 + band, (ph - 0.5) / 0.5); prog = 1 - smooth((Hf - c.p) / (2 * band) + 0.5); }
        const body = emt(prog);
        cellPath(c, prog, time);
        ctx.fillStyle = rgb(body); ctx.fill();
        // nucleus: a darker, rounder core of the same EMT colour
        nucleusPath(c, prog, time);
        ctx.fillStyle = rgb(mix(body, [0, 0, 0], 0.38));
        ctx.fill();
      });
    }

    function frame(now) {
      draw((now - t0) / 1000);
      if (running && !reduce) raf = requestAnimationFrame(frame);
    }

    function init() {
      sizeCanvas(); build();
      if (reduce) draw(6); else raf = requestAnimationFrame(frame);
    }

    // wait for the section to have a measurable width before sizing
    let tries = 0;
    (function kick() {
      if (canvas.clientWidth <= 0 && tries++ < 40) { setTimeout(kick, 16); return; }
      init();
    })();

    const onResize = () => { sizeCanvas(); build(); if (reduce) draw(6); };
    window.addEventListener('resize', onResize);

    return () => { running = false; cancelAnimationFrame(raf); window.removeEventListener('resize', onResize); };
  }, []);

  return (
    <section id="hero" style={{
      position: 'relative', width: '100%', height: '88vh', minHeight: 600, maxHeight: 860,
      overflow: 'hidden', background: OV_BRAND.bg,
    }}>
      <canvas ref={canvasRef} style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', display: 'block' }} />

      {/* legibility wash from the left */}
      <div style={{
        position: 'absolute', inset: 0, zIndex: 2,
        background: 'linear-gradient(100deg, rgba(250,249,246,0.97) 0%, rgba(250,249,246,0.9) 30%, rgba(250,249,246,0.52) 48%, rgba(250,249,246,0) 66%)',
      }} />

      {/* hero content */}
      <div style={{
        position: 'absolute', zIndex: 3, left: 44, top: 0, bottom: 0, maxWidth: 600,
        display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: 24, paddingRight: 24,
      }}>
        <Reveal>
          <OVEyebrow>Epithelial–Mesenchymal Transition</OVEyebrow>
        </Reveal>
        <Reveal delay={120}>
          <h1 style={{
            margin: 0, fontFamily: OV_BRAND.serif, fontWeight: 400,
            fontSize: 'clamp(40px, 5.4vw, 60px)', lineHeight: 1.05, letterSpacing: -0.6, color: OV_BRAND.navy,
            textWrap: 'balance',
          }}>
            The switch that teaches cancer to <em style={{ fontStyle: 'italic', color: OV_BRAND.brown }}>move</em>.
          </h1>
        </Reveal>
        <Reveal delay={200}>
          <p style={{
            margin: 0, fontFamily: OV_BRAND.sans, fontSize: 17, lineHeight: 1.62, color: OV_BRAND.textDim, maxWidth: 448,
          }}>
            OncoVolt builds patient-specific digital twins of the tumor, simulating the EMT switch to
            forecast how cancer will respond, when it will progress, and where it will spread, before the first dose.
          </p>
        </Reveal>
        <Reveal delay={280}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 22, marginTop: 4 }}>
            <a href="#approach" style={{
              fontFamily: OV_BRAND.sans, fontSize: 15, fontWeight: 500,
              color: OV_BRAND.bg, background: OV_BRAND.navy,
              padding: '14px 26px', borderRadius: 999, textDecoration: 'none',
            }}>
              Explore the approach
            </a>
            <a href="#about" style={{
              fontFamily: OV_BRAND.sans, fontSize: 15, fontWeight: 500,
              color: OV_BRAND.navy, textDecoration: 'none', display: 'flex', alignItems: 'center', gap: 8,
            }}>
              Read the thesis <span style={{ fontSize: 18, lineHeight: 1 }}>→</span>
            </a>
          </div>
        </Reveal>
      </div>

      {/* caption */}
      <div style={{
        position: 'absolute', zIndex: 4, left: 44, bottom: 22,
        fontFamily: OV_BRAND.sans, fontSize: 12.5, color: OV_BRAND.textMute,
        display: 'flex', gap: 9, alignItems: 'center',
      }}>
        <span style={{ color: OV_BRAND.amber, fontWeight: 600 }}>01</span>
        A diagonal wave of EMT sweeps the tissue, then heals back to epithelial.
      </div>
    </section>
  );
}

Object.assign(window, { HeroWave });
