/* global React, Label, useCloud */
const { useState: useBG, useEffect: useBGE, useRef: useBGR } = React;

// ============================================================
// Canvas2D 蝴蝶玻璃缸 — growth-v4 物理引擎 + Design 配色
// ============================================================

// 马卡龙配色（4色：上翅、翅尖、下翅、斑点/描边）
const PALETTES = [
  { top: '#d4869c', tip: '#f0c4d0', bottom: '#c47088', spot: '#7a3a50', dot: '#f5e0e8' },
  { top: '#c9956b', tip: '#ecd4b8', bottom: '#d4a070', spot: '#6a4830', dot: '#f5e6c8' },
  { top: '#6e94b8', tip: '#b0cce0', bottom: '#4a7498', spot: '#2a4060', dot: '#d8e4f0' },
  { top: '#9878b0', tip: '#d0b8e4', bottom: '#7a5a98', spot: '#3a2850', dot: '#e8d4f0' },
  { top: '#d4b870', tip: '#f0e4b0', bottom: '#c0a050', spot: '#6a5828', dot: '#f5eec0' },
  { top: '#6aaa8a', tip: '#b0dcc8', bottom: '#4a8a6a', spot: '#2a5040', dot: '#d0f0e0' },
  { top: '#cc7888', tip: '#f0b0b8', bottom: '#b05868', spot: '#5a2030', dot: '#f0d0d8' },
  { top: '#b87840', tip: '#e4b888', bottom: '#986030', spot: '#4a3018', dot: '#f0d4c0' },
  { top: '#5a9aaa', tip: '#a0ccd4', bottom: '#3a7a8a', spot: '#1a4048', dot: '#d0f0f0' },
  { top: '#c89898', tip: '#e8ccc8', bottom: '#a87878', spot: '#584040', dot: '#f0e0d8' },
  { top: '#8aaa68', tip: '#c4d8a8', bottom: '#6a8a48', spot: '#3a4828', dot: '#e4f0d0' },
  { top: '#a88858', tip: '#d8c098', bottom: '#886838', spot: '#483818', dot: '#f5eec0' },
  { top: '#9868a0', tip: '#cca8d4', bottom: '#784888', spot: '#382040', dot: '#e8d4f0' },
  { top: '#6888c0', tip: '#a8b8e0', bottom: '#c87898', spot: '#384060', dot: '#d8e0f0' },
  { top: '#e0d8d0', tip: '#f4f0ec', bottom: '#c8c0b8', spot: '#787068', dot: '#f8f4f0' },
  { top: '#c87888', tip: '#e8b0b8', bottom: '#78a8c0', spot: '#583848', dot: '#f0d0d8' },
  { top: '#c89848', tip: '#e8cc88', bottom: '#68a878', spot: '#584820', dot: '#f0e0c0' },
  { top: '#a84848', tip: '#d08080', bottom: '#383030', spot: '#502020', dot: '#f0c0c0' },
];

// 花朵马卡龙色
const FLORA_COLORS = [
  [210,140,160],[180,140,190],[140,170,200],[200,175,140],
  [160,190,155],[200,155,130],[170,155,195],[190,140,140],
  [210,200,160],[150,180,180],
];

function hexToRgb(h) {
  return [parseInt(h.slice(1,3),16), parseInt(h.slice(3,5),16), parseInt(h.slice(5,7),16)];
}

function ButterflyJar() {
  const canvasRef = useBGR(null);
  const stateRef = useBGR({ butterflies: [], motes: [], floraCanvas: null, W: 0, H: 0 });
  const countRef = useBGR(0);
  const [count, setCount] = useBG(0);
  const { data: bfData, save: saveBf } = useCloud('butterfly');
  const saveBfRef = useBGR(saveBf);
  saveBfRef.current = saveBf;
  const bfDataRef = useBGR(bfData);
  bfDataRef.current = bfData;
  const initializedRef = useBGR(false);
  const prevCountRef = useBGR(0);

  function addButterfly(sx, sy) {
    const pal = PALETTES[Math.floor(Math.random() * PALETTES.length)];
    const margin = 25;
    const S = stateRef.current;
    if (!S.W) return;
    S.butterflies.push({
      x: sx ?? (margin + Math.random() * (S.W - margin * 2)),
      y: sy ?? (margin + Math.random() * (S.H - margin * 2)),
      vx: (Math.random() - 0.5) * 0.3, vy: 0.2 + Math.random() * 0.3,
      targetX: margin + Math.random() * (S.W - margin * 2),
      targetY: margin + Math.random() * (S.H - margin * 2),
      lastTargetChange: Date.now() + 1000,
      angle: Math.PI * 0.5,
      flapPhase: Math.random() * Math.PI * 2,
      flapSpeed: 130 + Math.random() * 80,
      wingFlap: 0.5,
      size: 0.5 + Math.random() * 0.4,
      pal, wingType: Math.floor(Math.random() * 5),
      pattern: Math.floor(Math.random() * 5),
      spotPattern: Math.floor(Math.random() * 3),
      phase: Math.random() * Math.PI * 2,
      resting: false, restUntil: 0,
      scared: 0, scaredUntil: 0,
      spawnedAt: Date.now(), fadeIn: 0,
    });
    countRef.current = S.butterflies.length;
    setCount(S.butterflies.length);
  }

  function _saveButterfly() {
    const today = new Date().toISOString().slice(0, 10);
    const prev = bfDataRef.current || { date: today, count: 0 };
    const c = prev.date === today ? (prev.count || 0) : 0;
    const newCount = c + 1;
    prevCountRef.current = newCount;
    saveBfRef.current({ date: today, count: newCount });
  }

  // Expose global fly-butterfly for other components
  useBGE(() => {
    window._flyButterfly = () => {
      const S = stateRef.current;
      addButterfly(S.W / 2, 0);
      _saveButterfly();
    };
    return () => { window._flyButterfly = null; };
  }, []);

  // Sync butterflies from cloud (initial load + detect external writes)
  useBGE(() => {
    if (!bfData || !stateRef.current.W) return;
    const today = new Date().toISOString().slice(0, 10);
    const cloudCount = bfData.date === today ? (bfData.count || 0) : 0;
    if (!initializedRef.current) {
      // First load: populate all today's butterflies
      for (let i = 0; i < cloudCount; i++) {
        addButterfly();
      }
      prevCountRef.current = cloudCount;
      initializedRef.current = true;
    } else {
      // External change detected (溟 or 备忘录 added butterflies)
      const diff = cloudCount - prevCountRef.current;
      if (diff > 0) {
        for (let i = 0; i < diff; i++) {
          addButterfly(stateRef.current.W / 2, 0);
        }
        prevCountRef.current = cloudCount;
      }
    }
  }, [bfData]);

  useBGE(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext('2d');
    const dpr = Math.min(window.devicePixelRatio || 1, 2);
    const S = stateRef.current;

    function resize() {
      const rect = canvas.parentElement.getBoundingClientRect();
      S.W = rect.width; S.H = 240;
      canvas.width = S.W * dpr; canvas.height = S.H * dpr;
      canvas.style.width = S.W + 'px'; canvas.style.height = S.H + 'px';
      renderFlora();
      initMotes();
    }

    // ---- 花园预渲染 ----
    function renderFlora() {
      S.floraCanvas = document.createElement('canvas');
      S.floraCanvas.width = S.W * dpr; S.floraCanvas.height = S.H * dpr;
      const fc = S.floraCanvas.getContext('2d');
      fc.scale(dpr, dpr);
      const W = S.W, H = S.H;

      // 三层草丛
      for (let layer = 0; layer < 3; layer++) {
        for (let i = 0; i < 12 + layer * 6; i++) {
          const x = Math.random() * W;
          const h = 12 + Math.random() * (30 + layer * 12);
          const sway = (Math.random() - 0.5) * (10 + layer * 5);
          fc.beginPath(); fc.moveTo(x, H);
          fc.quadraticCurveTo(x + sway * 0.6, H - h * 0.5, x + sway, H - h);
          const g = 100 + Math.random() * 70;
          fc.strokeStyle = 'rgba(' + (g*0.6|0) + ',' + g + ',' + (g*0.55|0) + ',' + (0.12 + layer*0.05 + Math.random()*0.08) + ')';
          fc.lineWidth = 0.5 + Math.random() * (layer === 2 ? 1 : 0.5);
          fc.lineCap = 'round'; fc.stroke();
          if (Math.random() < 0.25) {
            const ly = H - h * (0.3 + Math.random() * 0.35), lx = x + sway * 0.4, side = Math.random() < 0.5 ? -1 : 1;
            fc.beginPath(); fc.moveTo(lx, ly);
            fc.quadraticCurveTo(lx + side * 7, ly - 3, lx + side * 4, ly - 7);
            fc.quadraticCurveTo(lx + side * 1, ly - 3, lx, ly);
            fc.fillStyle = 'rgba(85,130,65,0.12)'; fc.fill();
          }
        }
      }

      // 花朵（28朵）
      for (let i = 0; i < 28; i++) {
        const fx = 15 + Math.random() * (W - 30);
        const stemH = 28 + Math.random() * 65;
        const fy = H - stemH;
        const sway = (Math.random() - 0.5) * 12;
        const rgb = FLORA_COLORS[Math.floor(Math.random() * FLORA_COLORS.length)];

        fc.beginPath(); fc.moveTo(fx, H);
        fc.bezierCurveTo(fx + sway * 0.3, H - stemH * 0.3, fx + sway * 0.8, H - stemH * 0.6, fx + sway, fy);
        fc.strokeStyle = 'rgba(100,140,90,' + (0.18 + Math.random() * 0.1) + ')';
        fc.lineWidth = 0.7 + Math.random() * 0.3; fc.lineCap = 'round'; fc.stroke();

        if (Math.random() < 0.45) {
          const ly = H - stemH * (0.25 + Math.random() * 0.3), lx = fx + sway * 0.4, side = Math.random() < 0.5 ? -1 : 1;
          const leafL = 7 + Math.random() * 5;
          fc.beginPath(); fc.moveTo(lx, ly);
          fc.quadraticCurveTo(lx + side * leafL, ly - leafL * 0.4, lx + side * leafL * 0.6, ly - leafL);
          fc.quadraticCurveTo(lx + side * leafL * 0.15, ly - leafL * 0.5, lx, ly);
          fc.fillStyle = 'rgba(85,130,65,' + (0.12 + Math.random() * 0.06) + ')'; fc.fill();
        }

        const topX = fx + sway, type = Math.random();
        if (type < 0.35) {
          const petals = 6 + Math.floor(Math.random() * 3), len = 5 + Math.random() * 4;
          for (let p = 0; p < petals; p++) {
            const a = (p / petals) * Math.PI * 2 + Math.random() * 0.15;
            fc.save(); fc.translate(topX, fy); fc.rotate(a); fc.beginPath();
            fc.moveTo(0, 0); fc.bezierCurveTo(-len*0.4, -len*0.3, -len*0.3, -len*0.85, 0, -len);
            fc.bezierCurveTo(len*0.3, -len*0.85, len*0.4, -len*0.3, 0, 0);
            fc.fillStyle = 'rgba(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ',' + (0.25 + Math.random()*0.1) + ')'; fc.fill(); fc.restore();
          }
          fc.beginPath(); fc.arc(topX, fy, len * 0.2, 0, Math.PI * 2);
          fc.fillStyle = 'rgba(' + Math.min(255,rgb[0]+50) + ',' + Math.min(255,rgb[1]+40) + ',' + (rgb[2]*0.6|0) + ',0.35)'; fc.fill();
        } else if (type < 0.6) {
          const rSize = 4 + Math.random() * 4;
          for (let ring = 2; ring >= 0; ring--) {
            const cnt = 4 + ring * 2, pLen = rSize * (0.4 + ring * 0.12);
            for (let p = 0; p < cnt; p++) {
              const a = (p / cnt) * Math.PI * 2 + ring * 0.4;
              fc.save(); fc.translate(topX, fy); fc.rotate(a); fc.beginPath();
              fc.moveTo(0, 0); fc.bezierCurveTo(-pLen*0.3, -pLen*0.3, -pLen*0.25, -pLen*0.8, 0, -pLen);
              fc.bezierCurveTo(pLen*0.25, -pLen*0.8, pLen*0.3, -pLen*0.3, 0, 0);
              fc.fillStyle = 'rgba(' + Math.min(255,rgb[0]+ring*15) + ',' + Math.min(255,rgb[1]+ring*10) + ',' + Math.min(255,rgb[2]+ring*8) + ',' + (0.22 - ring*0.03) + ')';
              fc.fill(); fc.restore();
            }
          }
        } else if (type < 0.8) {
          for (let j = 0; j < 6; j++) {
            fc.beginPath(); fc.ellipse(topX, fy - j * 3, 2.5, 1.8, 0, 0, Math.PI * 2);
            fc.fillStyle = 'rgba(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ',' + (0.25 - j * 0.02) + ')'; fc.fill();
          }
        } else {
          const budR = 2 + Math.random() * 1.5;
          fc.beginPath(); fc.arc(topX, fy, budR, 0, Math.PI * 2);
          fc.fillStyle = 'rgba(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ',0.3)'; fc.fill();
        }
      }
    }

    // ---- 光影粒子 ----
    function initMotes() {
      S.motes = [];
      const colors = [[255,240,210],[230,210,255],[210,235,255],[255,220,230],[255,255,235]];
      for (let i = 0; i < 30; i++) {
        const c = colors[Math.floor(Math.random() * colors.length)];
        S.motes.push({
          x: Math.random() * S.W, y: Math.random() * S.H,
          r: 0.4 + Math.random() * 1.8, phase: Math.random() * Math.PI * 2,
          speed: 0.008 + Math.random() * 0.014, drift: Math.random() * Math.PI * 2,
          alpha: 0.06 + Math.random() * 0.12, cr: c[0], cg: c[1], cb: c[2],
        });
      }
    }

    function pickTarget(bf) {
      const m = 25;
      bf.targetX = m + Math.random() * (S.W - m * 2);
      bf.targetY = m + Math.random() * (S.H - m * 2);
    }

    // ---- 物理更新（溟的目标点系统）----
    function updateBf(bf, dt) {
      const now = Date.now();
      bf.fadeIn = Math.min(1, bf.fadeIn + 0.02);

      // 翅膀拍动（溟的锐化正弦）
      bf.flapPhase += dt / (bf.scared > 0 ? 80 : bf.flapSpeed);
      const rawT = Math.sin(bf.flapPhase * Math.PI * 2);
      const sharp = Math.sign(rawT) * Math.pow(Math.abs(rawT), 0.55);
      bf.wingFlap = bf.resting ? 0.28 - Math.sin(bf.flapPhase * Math.PI * 0.4) * 0.18 : 0.35 + (sharp + 1) * 0.25;

      if (bf.scared > 0) {
        if (now > bf.scaredUntil) { bf.scared = 0; bf.flapSpeed = 130 + Math.random() * 80; pickTarget(bf); }
        else { bf.vx *= 0.96; bf.vy *= 0.96; }
      } else if (bf.resting) {
        bf.vx *= 0.92; bf.vy *= 0.92;
        if (now > bf.restUntil) { bf.resting = false; pickTarget(bf); bf.lastTargetChange = now; }
      } else {
        const dx = bf.targetX - bf.x, dy = bf.targetY - bf.y, dist = Math.sqrt(dx*dx + dy*dy);
        if (dist < 15 || now - bf.lastTargetChange > 4200) {
          if (Math.random() < 0.2 && now - bf.spawnedAt > 2000 && bf.y > S.H * 0.4) {
            bf.resting = true; bf.restUntil = now + 2000 + Math.random() * 3000;
          } else { pickTarget(bf); bf.lastTargetChange = now; }
        }
        const speed = 0.003;
        if (dist > 1) { bf.vx += (dx/dist)*speed*dt; bf.vy += (dy/dist)*speed*dt; }
        bf.vx *= 0.96; bf.vy *= 0.96;
        const v = Math.sqrt(bf.vx*bf.vx + bf.vy*bf.vy);
        if (v > 2) { bf.vx = bf.vx/v*2; bf.vy = bf.vy/v*2; }
        bf.phase += dt * 0.004;
        bf.x += bf.vx + Math.sin(bf.phase) * 0.25;
        bf.y += bf.vy + Math.cos(bf.phase * 1.3) * 0.2;
      }
      if (bf.scared > 0 || bf.resting) { bf.x += bf.vx; bf.y += bf.vy; }

      const m = 15;
      if (bf.x<m){bf.x=m;bf.vx=Math.abs(bf.vx)*0.5;pickTarget(bf);}
      if (bf.x>S.W-m){bf.x=S.W-m;bf.vx=-Math.abs(bf.vx)*0.5;pickTarget(bf);}
      if (bf.y<m){bf.y=m;bf.vy=Math.abs(bf.vy)*0.5;pickTarget(bf);}
      if (bf.y>S.H-m){bf.y=S.H-m;bf.vy=-Math.abs(bf.vy)*0.5;pickTarget(bf);}

      const spd = Math.sqrt(bf.vx*bf.vx+bf.vy*bf.vy);
      if (spd > 0.05) {
        const ta = Math.atan2(bf.vy, bf.vx) + Math.PI * 0.5;
        let da = ta - bf.angle;
        while (da > Math.PI) da -= Math.PI * 2;
        while (da < -Math.PI) da += Math.PI * 2;
        bf.angle += da * 0.08;
      }
    }

    // ---- 画蝴蝶（4色渐变 + 白色花纹）----
    function drawBf(bf) {
      const { x, y, angle, wingFlap, size, pal, wingType, pattern, spotPattern } = bf;
      const rgbT = hexToRgb(pal.top), rgbTip = hexToRgb(pal.tip), rgbB = hexToRgb(pal.bottom);
      const rgbS = hexToRgb(pal.spot), rgbD = hexToRgb(pal.dot);

      ctx.save(); ctx.translate(x, y); ctx.rotate(angle); ctx.globalAlpha = bf.fadeIn;

      // 光晕
      const glow = ctx.createRadialGradient(0, 0, 0, 0, 0, 16 * size);
      glow.addColorStop(0, 'rgba(' + rgbT[0] + ',' + rgbT[1] + ',' + rgbT[2] + ',0.05)');
      glow.addColorStop(1, 'rgba(' + rgbT[0] + ',' + rgbT[1] + ',' + rgbT[2] + ',0)');
      ctx.fillStyle = glow; ctx.beginPath(); ctx.arc(0, 0, 16 * size, 0, Math.PI * 2); ctx.fill();

      const fs = wingFlap;
      function drawWing(side) {
        ctx.save(); ctx.scale(side * fs, 1);

        // 上翅
        ctx.beginPath();
        const s = size;
        switch(wingType) {
          case 0: ctx.moveTo(0,0); ctx.bezierCurveTo(-8*s,-14*s,-20*s,-8*s,-16*s,2*s); ctx.bezierCurveTo(-12*s,8*s,-4*s,5*s,0,0); break;
          case 1: ctx.moveTo(0,0); ctx.bezierCurveTo(-6*s,-16*s,-22*s,-12*s,-18*s,0); ctx.bezierCurveTo(-14*s,6*s,-4*s,4*s,0,0); break;
          case 2: ctx.moveTo(0,0); ctx.bezierCurveTo(-10*s,-12*s,-18*s,-10*s,-14*s,2*s); ctx.bezierCurveTo(-10*s,8*s,-3*s,4*s,0,0); break;
          case 3: ctx.moveTo(0,0); ctx.bezierCurveTo(-12*s,-14*s,-26*s,-6*s,-22*s,3*s); ctx.bezierCurveTo(-16*s,8*s,-6*s,6*s,0,0); break;
          default: ctx.moveTo(0,0); ctx.bezierCurveTo(-5*s,-14*s,-22*s,-16*s,-22*s,-2*s); ctx.bezierCurveTo(-18*s,4*s,-8*s,5*s,0,0);
        }
        const g1 = ctx.createRadialGradient(0,-2*s,2*s,-10*s,-4*s,18*s);
        g1.addColorStop(0,'rgba('+rgbTip[0]+','+rgbTip[1]+','+rgbTip[2]+',0.8)');
        g1.addColorStop(0.5,'rgba('+rgbT[0]+','+rgbT[1]+','+rgbT[2]+',0.75)');
        g1.addColorStop(1,'rgba('+rgbS[0]+','+rgbS[1]+','+rgbS[2]+',0.45)');
        ctx.fillStyle = g1; ctx.fill();
        ctx.strokeStyle = 'rgba('+rgbS[0]+','+rgbS[1]+','+rgbS[2]+',0.2)'; ctx.lineWidth = 0.3*s; ctx.stroke();

        // 内翅浅色
        ctx.beginPath();
        ctx.moveTo(-1*s,-1*s); ctx.bezierCurveTo(-6*s,-8*s,-13*s,-4*s,-10*s,2*s); ctx.bezierCurveTo(-7*s,4*s,-3*s,3*s,-1*s,-1*s);
        ctx.fillStyle = 'rgba('+rgbTip[0]+','+rgbTip[1]+','+rgbTip[2]+',0.4)'; ctx.fill();

        // 下翅
        ctx.beginPath(); ctx.moveTo(0,0);
        if (wingType===2||wingType===4) { ctx.bezierCurveTo(-6*s,5*s,-16*s,10*s,-14*s,20*s); ctx.bezierCurveTo(-8*s,23*s,-3*s,12*s,0,2*s); }
        else if (wingType===3) { ctx.bezierCurveTo(-8*s,6*s,-16*s,14*s,-12*s,18*s); ctx.bezierCurveTo(-6*s,18*s,-2*s,10*s,0,2*s); }
        else { ctx.bezierCurveTo(-7*s,5*s,-14*s,12*s,-10*s,16*s); ctx.bezierCurveTo(-5*s,16*s,-2*s,8*s,0,2*s); }
        const g2 = ctx.createLinearGradient(0,2*s,-12*s,18*s);
        g2.addColorStop(0,'rgba('+rgbB[0]+','+rgbB[1]+','+rgbB[2]+',0.7)');
        g2.addColorStop(1,'rgba('+rgbT[0]+','+rgbT[1]+','+rgbT[2]+',0.4)');
        ctx.fillStyle = g2; ctx.fill();

        // 白色花纹（Design 的 dot 色）
        if (pattern === 0) {
          // 散点
          ctx.fillStyle = 'rgba('+rgbD[0]+','+rgbD[1]+','+rgbD[2]+',0.6)';
          ctx.beginPath(); ctx.arc(-9*s,-4*s,1.8*s,0,Math.PI*2); ctx.fill();
          ctx.beginPath(); ctx.arc(-6*s,-8*s,1.2*s,0,Math.PI*2); ctx.fill();
          ctx.beginPath(); ctx.arc(-5*s,9*s,1.5*s,0,Math.PI*2); ctx.fill();
        } else if (pattern === 1) {
          // 条纹
          ctx.strokeStyle = 'rgba('+rgbD[0]+','+rgbD[1]+','+rgbD[2]+',0.5)'; ctx.lineWidth = 0.6;
          ctx.beginPath(); ctx.moveTo(-18*s,-4*s); ctx.quadraticCurveTo(-12*s,-5*s,-5*s,-1*s); ctx.stroke();
          ctx.beginPath(); ctx.moveTo(-12*s,9*s); ctx.quadraticCurveTo(-8*s,11*s,-4*s,7*s); ctx.stroke();
        } else if (pattern === 2) {
          // 眼斑
          ctx.beginPath(); ctx.arc(-13*s,-5*s,2.5*s,0,Math.PI*2);
          ctx.fillStyle = 'rgba('+rgbS[0]+','+rgbS[1]+','+rgbS[2]+',0.45)'; ctx.fill();
          ctx.beginPath(); ctx.arc(-13*s,-5*s,1*s,0,Math.PI*2);
          ctx.fillStyle = 'rgba('+rgbD[0]+','+rgbD[1]+','+rgbD[2]+',0.8)'; ctx.fill();
          ctx.beginPath(); ctx.arc(-8*s,10*s,1.5*s,0,Math.PI*2);
          ctx.fillStyle = 'rgba('+rgbD[0]+','+rgbD[1]+','+rgbD[2]+',0.5)'; ctx.fill();
        } else if (pattern === 3) {
          // 蕾丝
          ctx.strokeStyle = 'rgba('+rgbD[0]+','+rgbD[1]+','+rgbD[2]+',0.55)'; ctx.lineWidth = 0.4;
          ctx.beginPath(); ctx.moveTo(-20*s,-5*s); ctx.quadraticCurveTo(-15*s,-1*s,-10*s,-5*s); ctx.quadraticCurveTo(-15*s,-9*s,-20*s,-5*s); ctx.stroke();
          ctx.beginPath(); ctx.arc(-16*s,-5*s,0.7*s,0,Math.PI*2);
          ctx.fillStyle = 'rgba('+rgbD[0]+','+rgbD[1]+','+rgbD[2]+',0.7)'; ctx.fill();
        } else {
          // 翅缘白点排列（沿下翅边缘一排小圆点）
          ctx.fillStyle = 'rgba('+rgbD[0]+','+rgbD[1]+','+rgbD[2]+',0.75)';
          for (let i = 0; i < 5; i++) {
            const t = (i + 1) / 6;
            const px = -4*s - t * 8*s;
            const py = 4*s + t * 10*s;
            ctx.beginPath(); ctx.arc(px, py, (0.7 + (1-t)*0.5)*s, 0, Math.PI*2); ctx.fill();
          }
          // 上翅也加两个小点
          ctx.beginPath(); ctx.arc(-12*s, -6*s, 0.8*s, 0, Math.PI*2); ctx.fill();
          ctx.beginPath(); ctx.arc(-8*s, -9*s, 0.6*s, 0, Math.PI*2); ctx.fill();
        }

        // 翅脉
        ctx.strokeStyle = 'rgba('+rgbS[0]+','+rgbS[1]+','+rgbS[2]+',0.1)'; ctx.lineWidth = 0.3;
        ctx.beginPath(); ctx.moveTo(-1*s,0); ctx.quadraticCurveTo(-6*s,-5*s,-14*s,-2*s); ctx.stroke();
        ctx.beginPath(); ctx.moveTo(0,1*s); ctx.quadraticCurveTo(-5*s,6*s,-8*s,14*s); ctx.stroke();

        ctx.restore();
      }

      drawWing(1); drawWing(-1);

      // 身体
      ctx.beginPath(); ctx.ellipse(0,1*size,0.9*size,5*size,0,0,Math.PI*2);
      ctx.fillStyle = 'rgba('+rgbS[0]+','+rgbS[1]+','+rgbS[2]+',0.65)'; ctx.fill();
      // 分节
      ctx.strokeStyle = 'rgba('+rgbS[0]+','+rgbS[1]+','+rgbS[2]+',0.25)'; ctx.lineWidth = 0.3;
      for (let i=0;i<3;i++) { ctx.beginPath(); ctx.moveTo(-0.7*size,(-2+i*2)*size); ctx.lineTo(0.7*size,(-2+i*2)*size); ctx.stroke(); }
      // 头
      ctx.beginPath(); ctx.arc(0,-5*size,0.9*size,0,Math.PI*2);
      ctx.fillStyle = 'rgba('+rgbS[0]+','+rgbS[1]+','+rgbS[2]+',0.6)'; ctx.fill();
      // 触角
      ctx.strokeStyle = 'rgba('+rgbS[0]+','+rgbS[1]+','+rgbS[2]+',0.35)'; ctx.lineWidth = 0.5; ctx.lineCap = 'round';
      for (const side of [-1,1]) {
        ctx.beginPath(); ctx.moveTo(0,-5.5*size); ctx.quadraticCurveTo(side*4*size,-12*size,side*6*size,-14*size); ctx.stroke();
        ctx.beginPath(); ctx.arc(side*6*size,-14*size,0.6*size,0,Math.PI*2);
        ctx.fillStyle = 'rgba('+rgbS[0]+','+rgbS[1]+','+rgbS[2]+',0.35)'; ctx.fill();
      }
      ctx.restore();
    }

    // ---- 主渲染循环 ----
    let lastTime = performance.now();
    function render(now) {
      const dt = Math.min(now - lastTime, 33);
      lastTime = now;
      ctx.clearRect(0, 0, S.W * dpr, S.H * dpr);
      ctx.save(); ctx.scale(dpr, dpr);

      // 花草
      if (S.floraCanvas) ctx.drawImage(S.floraCanvas, 0, 0, S.W, S.H);

      // 光影粒子
      for (const p of S.motes) {
        p.phase += p.speed;
        p.x += Math.sin(p.phase * 0.7 + p.drift) * 0.12;
        p.y += Math.cos(p.phase * 0.5) * 0.08 - 0.015;
        if (p.y < -4) p.y = S.H + 4;
        if (p.x < -4) p.x = S.W + 4; if (p.x > S.W + 4) p.x = -4;
        const tw = 0.3 + 0.7 * Math.pow(Math.sin(p.phase) * 0.5 + 0.5, 2);
        const a = p.alpha * tw;
        const g = ctx.createRadialGradient(p.x,p.y,0,p.x,p.y,p.r*3);
        g.addColorStop(0,'rgba('+p.cr+','+p.cg+','+p.cb+','+a+')');
        g.addColorStop(0.4,'rgba('+p.cr+','+p.cg+','+p.cb+','+(a*0.4)+')');
        g.addColorStop(1,'rgba('+p.cr+','+p.cg+','+p.cb+',0)');
        ctx.fillStyle = g; ctx.beginPath(); ctx.arc(p.x,p.y,p.r*3,0,Math.PI*2); ctx.fill();
        if (p.r > 1) { ctx.beginPath(); ctx.arc(p.x,p.y,p.r*0.4,0,Math.PI*2); ctx.fillStyle='rgba(255,255,255,'+(a*0.5)+')'; ctx.fill(); }
      }

      // 蝴蝶
      for (const bf of S.butterflies) { updateBf(bf, dt); ctx.globalAlpha = bf.fadeIn; drawBf(bf); ctx.globalAlpha = 1; }

      ctx.restore();
      requestAnimationFrame(render);
    }

    resize();
    requestAnimationFrame(render);
    window.addEventListener('resize', resize);

    // 交互
    let lastClick = 0;
    const container = canvas.parentElement;

    container.addEventListener('click', (e) => {
      const now = Date.now();
      if (now - lastClick < 350) return;
      lastClick = now;
      const rect = canvas.getBoundingClientRect();
      addButterfly(e.clientX - rect.left, e.clientY - rect.top);
      _saveButterfly();
    });

    container.addEventListener('dblclick', (e) => {
      e.preventDefault();
      const cx = S.W / 2, cy = S.H / 2;
      for (const bf of S.butterflies) {
        bf.resting = false; bf.scared = 1; bf.scaredUntil = Date.now() + 1200; bf.flapSpeed = 80;
        const dx = bf.x - cx, dy = bf.y - cy, d = Math.sqrt(dx*dx+dy*dy) || 1;
        bf.vx = (dx/d)*4 + (Math.random()-0.5)*2;
        bf.vy = (dy/d)*4 + (Math.random()-0.5)*2;
      }
    });

    return () => window.removeEventListener('resize', resize);
  }, []);

  return (
    <section style={{ padding: '32px 72px 12px' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 10 }}>
        <Label color="var(--accent)" spacing="0.3em">§ JAR · 蝴蝶罐</Label>
        <Label color="var(--ink-mute)">{count} 只</Label>
      </div>

      <div style={{
        position: 'relative', width: '100%', height: 240, cursor: 'pointer',
        border: '1px solid rgba(255,255,255,0.15)', overflow: 'hidden',
        borderRadius: 28,
        background: 'linear-gradient(180deg, rgba(200,155,184,0.06) 0%, rgba(107,63,122,0.14) 100%)',
        boxShadow: '0 6px 40px rgba(10,8,18,0.4), inset 0 0 60px rgba(232,196,212,0.04), inset 0 2px 0 rgba(255,255,255,0.06)',
        backdropFilter: 'blur(16px)',
        WebkitBackdropFilter: 'blur(16px)',
      }}>
        {/* 玻璃高光 — 顶部光泽 */}
        <div style={{ position: 'absolute', top: 0, left: 0, right: 0, height: '35%', background: 'linear-gradient(180deg, rgba(255,255,255,0.07) 0%, transparent 100%)', pointerEvents: 'none', borderRadius: '28px 28px 0 0' }} />
        {/* 左上角细长高光条 */}
        <div style={{ position: 'absolute', top: 12, left: 24, width: 140, height: 4, background: 'linear-gradient(90deg, transparent, rgba(255,255,255,0.12), transparent)', pointerEvents: 'none', borderRadius: 4 }} />
        {/* 右侧竖高光 */}
        <div style={{ position: 'absolute', top: 20, right: 18, width: 3, height: 80, background: 'linear-gradient(180deg, rgba(255,255,255,0.08), transparent)', pointerEvents: 'none', borderRadius: 2 }} />

        {/* 标签 */}
        <div style={{ position: 'absolute', top: 12, left: 18, fontFamily: 'var(--serif-en)', fontStyle: 'italic', fontSize: 12, color: 'var(--her)', letterSpacing: '0.08em', pointerEvents: 'none', opacity: 0.6, zIndex: 2 }}>Butterfly Jar</div>
        <div style={{ position: 'absolute', top: 12, right: 18, fontFamily: 'var(--serif-zh)', fontSize: 12, color: 'var(--her)', letterSpacing: '0.2em', pointerEvents: 'none', opacity: 0.6, zIndex: 2 }}>壹 · 今日</div>

        {/* Canvas */}
        <canvas ref={canvasRef} style={{ width: '100%', height: '100%', display: 'block' }} />

      </div>

      {/* 底部标题 — 缸外 */}
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 12, marginTop: 14 }}>
        <span style={{ width: 60, height: 1, background: 'var(--rule-strong)' }} />
        <span style={{ fontFamily: 'var(--serif-zh)', fontSize: 12, color: 'var(--ink-soft)', letterSpacing: '0.25em' }}>今日飞舞进来的所有小事</span>
        <span className="en" style={{ fontSize: 11, fontStyle: 'italic', color: 'var(--ink-mute)', letterSpacing: '0.04em' }}>moments captured</span>
        <span style={{ width: 60, height: 1, background: 'var(--rule-strong)' }} />
      </div>
    </section>
  );
}

window.ButterflyJar = ButterflyJar;
