window.sketch = (runtime) => {
  const { c2, canvas, startFrame } = runtime;
  const renderer = new c2.Renderer(canvas);

  const pointer = { x: 0, y: 0, active: false, lastX: 0, lastY: 0, vx: 0, vy: 0, hasMoved: false };
  const particles = [];
  const bursts = [];
  const tendrils = [];
  const seeds = [];
  const MAX_P = 260;

  const W = () => canvas.width;
  const H = () => canvas.height;

  const hsl = (h, s, l) => {
    h = ((h % 360) + 360) % 360;
    s = Math.max(0, Math.min(100, s)) / 100;
    l = Math.max(0, Math.min(100, l)) / 100;
    const k = n => (n + h / 30) % 12;
    const a = s * Math.min(l, 1 - l);
    const f = n => {
      const v = l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));
      return Math.round(255 * v).toString(16).padStart(2, '0');
    };
    return `#${f(0)}${f(8)}${f(4)}`;
  };

  const seed = (x, y, hueBase) => {
    const n = 6 + Math.floor(Math.random() * 6);
    for (let i = 0; i < n; i++) {
      const a = Math.random() * Math.PI * 2;
      const s = 0.4 + Math.random() * 1.6;
      particles.push({
        x, y,
        vx: Math.cos(a) * s,
        vy: Math.sin(a) * s - 0.2,
        life: 1,
        size: 3 + Math.random() * 10,
        hue: hueBase + (Math.random() * 60 - 30),
        sat: 80 + Math.random() * 20,
        kind: Math.random() < 0.5 ? 'c' : 'r'
      });
    }
    if (particles.length > MAX_P) particles.splice(0, particles.length - MAX_P);
  };

  const burst = (x, y) => {
    bursts.push({ x, y, t: 0, hue: Math.random() * 360 });
    if (bursts.length > 6) bursts.shift();
  };

  canvas.addEventListener('pointerdown', (e) => {
    const rect = canvas.getBoundingClientRect();
    const x = (e.clientX - rect.left) * (canvas.width / rect.width);
    const y = (e.clientY - rect.top) * (canvas.height / rect.height);
    pointer.x = x; pointer.y = y;
    pointer.lastX = x; pointer.lastY = y;
    pointer.vx = 0; pointer.vy = 0;
    pointer.active = true;
    pointer.hasMoved = true;
    seed(x, y, 30 + Math.random() * 30);
    burst(x, y);
  });

  canvas.addEventListener('pointermove', (e) => {
    const rect = canvas.getBoundingClientRect();
    const x = (e.clientX - rect.left) * (canvas.width / rect.width);
    const y = (e.clientY - rect.top) * (canvas.height / rect.height);
    pointer.vx = x - pointer.lastX;
    pointer.vy = y - pointer.lastY;
    pointer.lastX = x; pointer.lastY = y;
    pointer.x = x; pointer.y = y;
    pointer.hasMoved = true;
    if (pointer.active && (Math.abs(pointer.vx) + Math.abs(pointer.vy) > 1)) {
      if (Math.random() < 0.5) seed(x, y, Math.random() * 60 + 20);
    }
  });

  const endPointer = () => { pointer.active = false; };
  canvas.addEventListener('pointerup', endPointer);
  canvas.addEventListener('pointercancel', endPointer);
  canvas.addEventListener('pointerleave', endPointer);

  // Pre-seed ambient regrowth tendrils
  for (let i = 0; i < 14; i++) {
    tendrils.push({ phase: Math.random() * Math.PI * 2, speed: 0.3 + Math.random() * 0.5, len: 80 + Math.random() * 120, hue: i * 27 });
  }

  startFrame((frameCount) => {
    const t = frameCount * 0.012;
    const w = W(), h = H();
    const cx = w * 0.5;
    const cy = h * 0.5;

    // ---------- Base: deep layered gradient background ----------
    renderer.clear('#08050f');
    for (let i = 0; i < 7; i++) {
      renderer.alpha(0.07);
      renderer.fill(hsl(t * 6 + i * 50, 70, 40 + i * 4));
      renderer.rect(0, 0, w, h);
    }
    renderer.alpha(1);

    // ---------- Radiating light spokes (structure) ----------
    const spokeCount = 48;
    for (let i = 0; i < spokeCount; i++) {
      const a = (i / spokeCount) * Math.PI * 2 + t * 0.08;
      const len = 240 + Math.sin(t * 0.7 + i * 0.3) * 90 + Math.cos(t * 0.4 + i) * 40;
      const w1 = 50;
      const w2 = len;
      const hue = (i * 8 + t * 30) % 360;
      renderer.alpha(0.18 + (i % 3) * 0.05);
      renderer.stroke(hsl(hue, 80, 65));
      renderer.lineWidth(1 + (i % 4 === 0 ? 1.2 : 0.4));
      renderer.line(cx + Math.cos(a) * w1, cy + Math.sin(a) * w1, cx + Math.cos(a) * w2, cy + Math.sin(a) * w2);
    }

    // ---------- Ambient tendrils of regeneration ----------
    for (let i = 0; i < tendrils.length; i++) {
      const tn = tendrils[i];
      tn.phase += tn.speed * 0.02;
      const baseA = (i / tendrils.length) * Math.PI * 2 + t * 0.15;
      const steps = 26;
      let px = cx + Math.cos(baseA) * 70;
      let py = cy + Math.sin(baseA) * 70;
      renderer.alpha(0.55);
      renderer.stroke(hsl(tn.hue + t * 25, 85, 60));
      renderer.lineWidth(2.2);
      for (let s = 1; s <= steps; s++) {
        const k = s / steps;
        const swirl = Math.sin(tn.phase + s * 0.4) * (10 + k * 30);
        const a2 = baseA + swirl * 0.012;
        const r = 70 + k * tn.len + Math.sin(tn.phase + k * 6) * 20;
        const nx = cx + Math.cos(a2) * r;
        const ny = cy + Math.sin(a2) * r;
        renderer.line(px, py, nx, ny);
        px = nx; py = ny;
      }
      // bloom at tip
      const tipX = px, tipY = py;
      renderer.alpha(0.7);
      renderer.fill(hsl(tn.hue + t * 25, 95, 70));
      renderer.circle(tipX, tipY, 3 + Math.sin(tn.phase * 2) * 1.5);
    }

    // ---------- Central abstract figure ----------
    // Core orb (heart of the figure / soul)
    const breathe = 0.5 + 0.5 * Math.sin(t * 1.2);
    const coreR = 14 + breathe * 6;
    renderer.alpha(0.9);
    renderer.fill(hsl(40 + t * 20, 90, 70));
    renderer.circle(cx, cy, coreR);
    renderer.alpha(0.4);
    renderer.fill(hsl(60 + t * 20, 100, 80));
    renderer.circle(cx, cy, coreR * 0.5);

    // Head (rising)
    const headY = cy - 70 + Math.sin(t * 0.9) * 3;
    renderer.alpha(0.85);
    renderer.fill(hsl((t * 50) % 360, 85, 65));
    renderer.circle(cx, headY, 22 + Math.sin(t * 1.6) * 2);

    // Halo crown of hope
    renderer.alpha(0.6);
    renderer.stroke(hsl(50 + t * 30, 95, 70));
    renderer.lineWidth(2.2);
    renderer.circle(cx, headY, 32 + Math.sin(t * 2) * 3);
    renderer.alpha(0.3);
    renderer.lineWidth(1);
    renderer.circle(cx, headY, 42 + Math.sin(t * 2 + 1) * 3);

    // Torso: stacked translucent organic forms (mixed media layering)
    for (let i = 0; i < 8; i++) {
      const k = i / 8;
      const ey = cy - 35 + i * 16;
      const rx = 30 - i * 1.8 + Math.sin(t * 1.4 + i) * 5;
      const ry = 11 + Math.cos(t + i * 0.7) * 2;
      renderer.alpha(0.55 - k * 0.35);
      renderer.fill(hsl((t * 55 + i * 42) % 360, 88, 60));
      renderer.ellipse(cx, ey, rx, ry);
    }

    // Outstretched arms (regeneration gesture)
    const armWave = Math.sin(t * 1.3) * 14;
    const armWave2 = Math.sin(t * 1.3 + Math.PI) * 14;
    renderer.alpha(0.8);
    renderer.stroke(hsl((t * 60) % 360, 92, 68));
    renderer.lineWidth(9);
    renderer.line(cx - 28, cy - 28, cx - 70, cy - 10 + armWave);
    renderer.line(cx + 28, cy - 28, cx + 70, cy - 10 + armWave2);

    // Hands (sparks of hope)
    renderer.alpha(0.95);
    renderer.fill(hsl(50 + t * 40, 100, 75));
    renderer.circle(cx - 70, cy - 10 + armWave, 6);
    renderer.circle(cx + 70, cy - 10 + armWave2, 6);

    // Legs / root
    renderer.lineWidth(7);
    renderer.stroke(hsl((t * 70) % 360, 85, 60));
    renderer.line(cx - 12, cy + 65, cx - 22, cy + 120 + Math.sin(t) * 5);
    renderer.line(cx + 12, cy + 65, cx + 22, cy + 120 + Math.cos(t) * 5);

    // ---------- Orbiting regrowth shapes ----------
    for (let i = 0; i < 22; i++) {
      const a = (i / 22) * Math.PI * 2 + t * 0.28;
      const dist = 150 + Math.sin(t + i * 0.5) * 60;
      const x = cx + Math.cos(a) * dist;
      const y = cy + Math.sin(a) * dist;
      const size = 5 + Math.sin(t * 1.7 + i) * 3;
      const hue = (i * 18 + t * 45) % 360;
      renderer.alpha(0.75);
      if (i % 3 === 0) {
        renderer.fill(hsl(hue, 88, 62));
        renderer.circle(x, y, size);
      } else if (i % 3 === 1) {
        renderer.fill(hsl(hue, 88, 62));
        renderer.rect(x - size, y - size, size * 2, size * 2);
      } else {
        renderer.fill(hsl(hue + 180, 88, 62));
        renderer.triangle(x, y - size, x - size, y + size, x + size, y + size);
      }
    }

    // ---------- Inner aura rings ----------
    for (let r = 70; r < 200; r += 22) {
      const a = (r / 200);
      renderer.alpha(0.12 * (1 - a * 0.7));
      renderer.stroke(hsl((t * 70 + r * 2) % 360, 92, 72));
      renderer.lineWidth(1.5);
      renderer.circle(cx, cy, r + Math.sin(t * 1.6 + r * 0.12) * 6);
    }

    // ---------- Burst rings (interactive ripples) ----------
    for (let i = bursts.length - 1; i >= 0; i--) {
      const b = bursts[i];
      b.t += 0.018;
      if (b.t > 1) { bursts.splice(i, 1); continue; }
      renderer.alpha((1 - b.t) * 0.6);
      renderer.stroke(hsl((b.t * 360 + t * 60) % 360, 95, 72));
      renderer.lineWidth(3);
      renderer.circle(b.x, b.y, b.t * 180);
      renderer.alpha((1 - b.t) * 0.3);
      renderer.lineWidth(1.2);
      renderer.circle(b.x, b.y, b.t * 240);
    }

    // ---------- Particles ----------
    for (let i = particles.length - 1; i >= 0; i--) {
      const p = particles[i];
      p.x += p.vx;
      p.y += p.vy;
      p.vy += 0.015;
      p.vx *= 0.985;
      p.life -= 0.012;
      if (p.life <= 0 || p.y > h + 50) { particles.splice(i, 1); continue; }
      renderer.alpha(Math.max(0, p.life * 0.85));
      renderer.fill(hsl(p.hue + t * 20, p.sat, 60));
      if (p.kind === 'c') {
        renderer.circle(p.x, p.y, p.size * Math.max(0.2, p.life));
      } else {
        renderer.rect(p.x - p.size * p.life, p.y - p.size * p.life, p.size * 2 * p.life, p.size * 2 * p.life);
      }
    }

    // ---------- Chaotic texture: scattered micro-pixels (process/mark-making) ----------
    renderer.alpha(0.12);
    for (let i = 0; i < 70; i++) {
      const x = (i * 73.7 + t * 47) % w;
      const y = (i * 97.3 + t * 31) % h;
      renderer.fill(hsl((i * 11 + t * 25) % 360, 70, 55));
      renderer.circle(x, y, 0.8 + (i % 3) * 0.4);
    }
    renderer.alpha(1);

    // ---------- Pointer indicator (discoverable affordance) ----------
    if (pointer.hasMoved) {
      renderer.alpha(0.5);
      renderer.stroke('#ffffff');
      renderer.lineWidth(1.5);
      renderer.circle(pointer.x, pointer.y, 16 + Math.sin(t * 4) * 4);
      renderer.alpha(0.85);
      renderer.fill('#ffffff');
      renderer.circle(pointer.x, pointer.y, 2.5);
    }
  });
};
