// living-lab-flywheel.jsx — the closing beat. After the main diagram erases, a
// chalk racetrack "flywheel" of the GrytLabs cycle draws out, the GrytLabs logo
// strikes (stings) in its centre, and FROM INQUIRY TO INNOVATION writes across
// the bottom where the subtitles sit.
const { LAB, rng, frac, seg, sampleLine, Line, HandText, boxPath, dashDraw, clamp } = window;

const FW_C = { x: 960, y: 452, w: 1372, h: 520, r: 148 };

// nodes seated on the racetrack, ordered around the loop (t = 0..1 position)
const FW_NODES = [
  { id: 'AI R+D',            sub: 'AI Research + Development',  x: 960,  y: 192, w: 258, h: 66, t: 0.00, o: 0 },
  { id: 'World Model Studio', sub: 'Founder',                   x: 1622, y: 300, w: 250, h: 66, t: 0.13, o: 4 },
  { id: 'ProtoLex',          sub: 'Enterprise',                x: 1648, y: 452, w: 226, h: 62, t: 0.24, o: 5 },
  { id: 'Loom',              sub: 'Developer',                 x: 1622, y: 604, w: 226, h: 62, t: 0.35, o: 6 },
  { id: 'WMGF',              sub: 'World Model Governance',    x: 1150, y: 712, w: 258, h: 66, t: 0.50, o: 3 },
  { id: 'DLP',               sub: 'Decision Lineage Protocol', x: 770,  y: 712, w: 258, h: 66, t: 0.60, o: 2 },
  { id: 'GEM',               sub: 'Generated Enterprise Manifest', x: 300, y: 604, w: 270, h: 66, t: 0.74, o: 7 },
  { id: 'WMI',               sub: 'World Model Initiative',    x: 300,  y: 300, w: 258, h: 66, t: 0.87, o: 1 },
];

// rounded-rect racetrack as a jittered closed polyline (for chalk draw + orbs)
function racetrack(c, seed = 1) {
  const x0 = c.x - c.w / 2, x1 = c.x + c.w / 2, y0 = c.y - c.h / 2, y1 = c.y + c.h / 2, r = c.r;
  const pts = [];
  const seg2 = (ax, ay, bx, by, n) => { for (let i = 0; i < n; i++) pts.push({ x: ax + (bx - ax) * i / n, y: ay + (by - ay) * i / n }); };
  const arc = (cx, cy, a0, a1, n) => { for (let i = 0; i < n; i++) { const a = a0 + (a1 - a0) * i / n; pts.push({ x: cx + Math.cos(a) * r, y: cy + Math.sin(a) * r }); } };
  seg2(x0 + r, y0, x1 - r, y0, 22);          // top
  arc(x1 - r, y0 + r, -Math.PI / 2, 0, 12);  // TR
  seg2(x1, y0 + r, x1, y1 - r, 14);          // right
  arc(x1 - r, y1 - r, 0, Math.PI / 2, 12);   // BR
  seg2(x1 - r, y1, x0 + r, y1, 22);          // bottom
  arc(x0 + r, y1 - r, Math.PI / 2, Math.PI, 12); // BL
  seg2(x0, y1 - r, x0, y0 + r, 14);          // left
  arc(x0 + r, y0 + r, Math.PI, Math.PI * 1.5, 12); // TL
  pts.push(pts[0]);
  return pts.map((p, i) => ({ x: p.x + (rng(seed + i) - 0.5) * 2.4, y: p.y + (rng(seed + i * 1.7) - 0.5) * 2.4 }));
}

// the GrytLabs mark, chalk — chrome strata pierced by the gold spine + apex node
function GrytSting({ draw }) {
  const bars = [-40, -20, 0, 20, 40];
  const spine = seg(draw, 0.32, 0.72);
  const apex = seg(draw, 0.66, 1);
  return (
    <g>
      <g filter="url(#ll-chalk)">
        {bars.map((y, i) => {
          const bd = seg(draw, i * 0.07, i * 0.07 + 0.4);
          const w = 132 - Math.abs(i - 2) * 8;
          return <line key={i} x1={-w / 2} y1={y} x2={w / 2} y2={y} stroke={LAB.chalk} strokeWidth="4.5"
                       strokeOpacity="0.72" strokeLinecap="round" {...dashDraw(bd)} />;
        })}
      </g>
      <line x1="0" y1="-72" x2="0" y2="52" stroke={LAB.gold} strokeWidth="3.2" strokeLinecap="round"
            filter="url(#ll-chalk)" {...dashDraw(spine)} />
      {bars.map((y, i) => <circle key={i} cx="0" cy={y} r="3.4" fill={LAB.gold} opacity={seg(draw, 0.38 + i * 0.05, 0.72)} />)}
      <circle cx="0" cy="-72" r="16" fill="url(#ll-goldcore)" opacity={apex} />
      <circle cx="0" cy="-72" r="4.4" fill="#fff3cf" opacity={apex} />
      <HandText text="GRYTLABS" x={0} y={92} size={30} color={LAB.chalk} anchor="middle"
                filter="url(#ll-chalk)" seed={5} jr={2} reveal={seg(draw, 0.74, 1)} />
      <HandText text="RESEARCH INSTITUTE" x={0} y={119} size={14} color={LAB.chalk} anchor="middle"
                filter="url(#ll-chalk)" seed={6} jr={1} spacing={1.34} opacity={0.72} reveal={seg(draw, 0.82, 1)} />
    </g>
  );
}

function FwNode({ n, draw }) {
  if (draw <= 0.01) return null;
  const inner = seg(draw, 0.45, 1);
  return (
    <g transform={`translate(${n.x} ${n.y})`} opacity={seg(draw, 0, 0.28)}>
      <g filter="url(#ll-chalk)">
        <path d={boxPath(n.w, n.h, 10, n.x, 2)} fill={LAB.panel} fillOpacity={0.86 * inner}
              stroke={LAB.chalk} strokeWidth="1.6" strokeOpacity="0.6" {...dashDraw(seg(draw, 0, 0.6))} />
      </g>
      <HandText text={n.id} x={0} y={n.sub ? -6 : 4} size={16} color={LAB.chalk} anchor="middle"
                filter="url(#ll-chalk)" seed={n.x} jr={2} reveal={seg(draw, 0.5, 0.9)} />
      {n.sub ? <HandText text={n.sub} x={0} y={18} size={10.5} color={LAB.chalk} anchor="middle"
                filter="url(#ll-chalk)" seed={n.x + 1} jr={1} opacity={0.62} reveal={seg(draw, 0.62, 0.95)} /> : null}
    </g>
  );
}

// draw: 0..1 for the whole flywheel beat. `mt` drives the spinning orbs.
function Flywheel({ draw = 1, mt = 0, labels = true }) {
  if (draw <= 0.001) return null;
  const loop = React.useMemo(() => racetrack(FW_C, 7), []);
  const loopDraw = seg(draw, 0.0, 0.42);
  const nodesAt = (t) => seg(draw, 0.30 + t * 0.30, 0.30 + t * 0.30 + 0.26);
  const stingDraw = seg(draw, 0.60, 0.98);
  const textReveal = seg(draw, 0.78, 1);
  const flywheelSpin = seg(draw, 0.4, 0.55);

  const orbs = [];
  if (flywheelSpin > 0.05) {
    const KINDS = [
      { g: 'll-goldcore', c: '#fff3cf' },
      { g: 'll-pb', c: LAB.blueHi },
      { g: 'll-pg', c: LAB.greenHi },
      { g: 'll-pp', c: LAB.purpleHi },
      { g: 'll-whitecore', c: '#ffffff' },
    ];
    for (let k = 0; k < 10; k++) {
      const u = frac(k / 10 - mt * 0.045);
      const p = sampleLine(loop, u);
      const kd = KINDS[k % KINDS.length];
      orbs.push(
        <g key={k} opacity={flywheelSpin}>
          <circle cx={p.x} cy={p.y} r="7" fill={`url(#${kd.g})`} />
          <circle cx={p.x} cy={p.y} r="2.6" fill={kd.c} />
        </g>);
    }
  }

  return (
    <g>
      <Line pts={loop} w={2} color={LAB.chalk} opacity={0.5} draw={loopDraw} />
      <Line pts={loop} w={5} color={LAB.chalk} opacity={0.08 * seg(loopDraw, 0.6, 1)} draw={loopDraw} />
      <g>{orbs}</g>
      {FW_NODES.map((n) => <FwNode key={n.id} n={n} draw={labels ? nodesAt(n.o / 7) : 0} />)}
      <g transform={`translate(${FW_C.x} ${FW_C.y - 6})`}><GrytSting draw={stingDraw} /></g>
      <HandText text="FROM  INQUIRY  TO  INNOVATION" x={960} y={1006} size={34} color={LAB.chalk}
                anchor="middle" filter="url(#ll-chalk)" seed={12} jr={1.6} spacing={1.16} reveal={textReveal} />
    </g>
  );
}

Object.assign(window, { Flywheel });
