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

  const ease = (t) => t * t * (3 - 2 * t);

  const stars = Array.from({ length: 220 }, () => ({
    x: Math.random(),
    y: Math.random(),
    r: Math.random() * 1.4 + 0.2,
    phase: Math.random() * Math.PI * 2,
    speed: 0.4 + Math.random() * 1.2
  }));

  const motes = Array.from({ length: 80 }, () => ({
    x: Math.random(),
    y: Math.random(),
    r: 2 + Math.random() * 4,
    hue: 180 + Math.random() * 140,
    phase: Math.random() * Math.PI * 2
  }));

  startFrame((frameCount) => {
    const t = frameCount * 0.005;
    const w = canvas.width;
    const h = canvas.height;
    const cx = w / 2;
    const cy = h / 2;

    renderer.clear('#0a0a14');

    // Subtle radial vignette via concentric soft rings
    for (let i = 0; i < 6; i++) {
      const radius = Math.min(w, h) * (0.18 + i * 0.08) + Math.sin(t * 0.6 + i) * 8;
      renderer.stroke('rgba(80, 90, 140, 0.06)');
      renderer.lineWidth(1);
      renderer.circle(cx, cy, radius);
    }

    // Starfield — perception layer
    for (let s of stars) {
      const flicker = 0.5 + 0.5 * Math.sin(frameCount * 0.04 * s.speed + s.phase);
      renderer.fill(`rgba(200, 210, 240, ${0.2 + flicker * 0.6})`);
      renderer.circle(s.x * w, s.y * h, s.r * (0.6 + flicker * 0.6));
    }

    // Drifting motes — emotional depth
    for (let m of motes) {
      const px = (m.x + Math.sin(t * 0.4 + m.phase) * 0.05) * w;
      const py = (m.y + Math.cos(t * 0.5 + m.phase * 1.3) * 0.05) * h;
      const alpha = 0.15 + 0.25 * (0.5 + 0.5 * Math.sin(t * 1.2 + m.phase));
      renderer.fill(`hsla(${m.hue}, 70%, 70%, ${alpha})`);
      renderer.circle(px, py, m.r);
    }

    // Structured geometry — the "seen" world
    const gridSize = 7;
    const cellW = w / gridSize;
    const cellH = h / gridSize;
    for (let i = 0; i < gridSize; i++) {
      for (let j = 0; j < gridSize; j++) {
        const x = i * cellW + cellW / 2;
        const y = j * cellH + cellH / 2;
        const dist = Math.hypot(x - cx, y - cy);
        const maxDist = Math.hypot(cx, cy);
        const norm = dist / maxDist;
        const pulse = 0.5 + 0.5 * Math.sin(t * 1.5 - norm * 6);
        const sz = (1 - norm * 0.7) * 14 * (0.4 + pulse * 0.6);
        renderer.stroke(`rgba(120, 160, 220, ${0.08 + pulse * 0.12})`);
        renderer.lineWidth(0.8);
        renderer.rect(x - sz / 2, y - sz / 2, sz, sz);
      }
    }

    // Outer rotating structured frame — identity boundary
    const frameCount2 = 3;
    for (let f = 0; f < frameCount2; f++) {
      const angle = t * (0.3 + f * 0.15) + (f * Math.PI * 2) / frameCount2;
      const r = Math.min(w, h) * 0.34;
      const fx = cx + Math.cos(angle) * r;
      const fy = cy + Math.sin(angle) * r;
      const sz = 40 + Math.sin(t + f) * 10;
      renderer.stroke('rgba(180, 140, 220, 0.5)');
      renderer.lineWidth(1.2);
      renderer.rect(fx - sz / 2, fy - sz / 2, sz, sz);
      renderer.fill('rgba(220, 180, 255, 0.7)');
      renderer.circle(fx, fy, 3);
    }

    // Connecting lines between frames — tension between realities
    renderer.stroke('rgba(140, 120, 200, 0.15)');
    renderer.lineWidth(0.6);
    const fr = Math.min(w, h) * 0.34;
    for (let a = 0; a < 3; a++) {
      const ang1 = t * (0.3 + a * 0.15) + (a * Math.PI * 2) / 3;
      const ang2 = t * (0.3 + ((a + 1) % 3) * 0.15) + (((a + 1) % 3) * Math.PI * 2) / 3;
      const x1 = cx + Math.cos(ang1) * fr;
      const y1 = cy + Math.sin(ang1) * fr;
      const x2 = cx + Math.cos(ang2) * fr;
      const y2 = cy + Math.sin(ang2) * fr;
      renderer.line(x1, y1, x2, y2);
    }

    // Central chaotic orb — deeper truth, blending self
    const coreR = Math.min(w, h) * 0.12;
    const corePulse = 0.85 + 0.15 * Math.sin(t * 1.8);
    const layers = 5;
    for (let l = layers; l >= 0; l--) {
      const lr = coreR * corePulse * (0.4 + (l / layers) * 0.6);
      const hue = 260 + l * 18 + Math.sin(t + l) * 12;
      const alpha = 0.05 + (l / layers) * 0.15;
      renderer.fill(`hsla(${hue}, 70%, 55%, ${alpha})`);
      renderer.circle(cx, cy, lr);
    }

    // Inner chaos — fragmented polygons
    const polyCount = 9;
    for (let p = 0; p < polyCount; p++) {
      const ang = (p / polyCount) * Math.PI * 2 + t * 0.7;
      const orbit = coreR * 0.7 * (0.6 + 0.4 * Math.sin(t * 1.2 + p));
      const px = cx + Math.cos(ang) * orbit;
      const py = cy + Math.sin(ang) * orbit;
      const sz = 6 + Math.sin(t * 2 + p * 1.3) * 3;
      const hue = (p * 35 + frameCount * 0.5) % 360;
      renderer.fill(`hsla(${hue}, 80%, 65%, 0.55)`);
      renderer.circle(px, py, sz);
    }

    // Filaments — emotional currents
    for (let k = 0; k < 5; k++) {
      const baseAngle = (k / 5) * Math.PI * 2 + t * 0.4;
      const len = coreR * 2.2 + Math.sin(t * 0.9 + k) * 20;
      const x1 = cx + Math.cos(baseAngle) * coreR * 0.4;
      const y1 = cy + Math.sin(baseAngle) * coreR * 0.4;
      const x2 = cx + Math.cos(baseAngle) * len;
      const y2 = cy + Math.sin(baseAngle) * len;
      const cp1x = cx + Math.cos(baseAngle + 0.4) * len * 0.6;
      const cp1y = cy + Math.sin(baseAngle + 0.4) * len * 0.6;
      const cp2x = cx + Math.cos(baseAngle - 0.4) * len * 0.6;
      const cp2y = cy + Math.sin(baseAngle - 0.4) * len * 0.6;
      // Approximate curved filament with multiple line segments
      for (let seg = 0; seg < 16; seg++) {
        const u1 = seg / 16;
        const u2 = (seg + 1) / 16;
        const bx = (1 - u1) * (1 - u1) * (1 - u1) * x1 +
                   3 * (1 - u1) * (1 - u1) * u1 * cp1x +
                   3 * (1 - u1) * u1 * u1 * cp2x +
                   u1 * u1 * u1 * x2;
        const by = (1 - u1) * (1 - u1) * (1 - u1) * y1 +
                   3 * (1 - u1) * (1 - u1) * u1 * cp1y +
                   3 * (1 - u1) * u1 * u1 * cp2y +
                   u1 * u1 * u1 * y2;
        const bx2 = (1 - u2) * (1 - u2) * (1 - u2) * x1 +
                    3 * (1 - u2) * (1 - u2) * u2 * cp1x +
                    3 * (1 - u2) * u2 * u2 * cp2x +
                    u2 * u2 * u2 * x2;
        const by2 = (1 - u2) * (1 - u2) * (1 - u2) * y1 +
                    3 * (1 - u2) * (1 - u2) * u2 * cp1y +
                    3 * (1 - u2) * u2 * u2 * cp2y +
                    u2 * u2 * u2 * y2;
        const alpha = 0.15 * Math.sin(u1 * Math.PI);
        renderer.stroke(`hsla(${(k * 50 + 240) % 360}, 70%, 70%, ${alpha})`);
        renderer.lineWidth(1.2);
        renderer.line(bx, by, bx2, by2);
      }
    }

    // Eye-like focal — perception
    const eyeW = Math.min(w, h) * 0.28;
    const eyeH = eyeW * 0.35;
    const blink = 0.7 + 0.3 * Math.abs(Math.sin(t * 0.6));
    renderer.stroke('rgba(220, 200, 240, 0.35)');
    renderer.lineWidth(1.5);
    renderer.ellipse(cx, cy, eyeW, eyeH * blink);
    if (blink > 0.2) {
      renderer.fill('rgba(255, 240, 255, 0.85)');
      renderer.circle(cx, cy, 5 * blink);
    }

    // Title-like whisper
    renderer.fill('rgba(180, 160, 220, 0.25)');
    renderer.fontSize(Math.max(10, Math.min(w, h) * 0.012));
    renderer.textAlign('center');
    renderer.text('I am the space between', cx, h - 30);
  });
};
