/* ── v4 Charts: NodeNetwork, SkillRadar, Dashboard Charts ── */

/* ── Node Network Canvas ── */
function NodeNetwork({ style }) {
  const canvasRef = React.useRef(null);
  const animRef = React.useRef(null);
  const nodesRef = React.useRef([]);
  const mouseRef = React.useRef({ x: -1000, y: -1000 });
  const isMobile = typeof window !== 'undefined' && window.innerWidth < 768;

  React.useEffect(() => {
    if (isMobile) return; // skip on mobile
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext('2d');
    const dpr = window.devicePixelRatio || 1;
    let cw, ch;

    const resize = () => {
      cw = canvas.offsetWidth; ch = canvas.offsetHeight;
      canvas.width = cw * dpr; canvas.height = ch * dpr;
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    };

    const initNodes = () => {
      const count = Math.min(70, Math.floor((cw * ch) / 14000));
      nodesRef.current = Array.from({ length: count }, () => ({
        x: Math.random() * cw, y: Math.random() * ch,
        vx: (Math.random() - 0.5) * 0.35, vy: (Math.random() - 0.5) * 0.35,
        r: Math.random() * 2 + 1, pulse: Math.random() * Math.PI * 2,
      }));
    };

    const draw = () => {
      ctx.clearRect(0, 0, cw, ch);
      const nodes = nodesRef.current;
      const mx = mouseRef.current.x, my = mouseRef.current.y;

      nodes.forEach(n => {
        n.x += n.vx; n.y += n.vy; n.pulse += 0.02;
        if (n.x < 0 || n.x > cw) n.vx *= -1;
        if (n.y < 0 || n.y > ch) n.vy *= -1;
        const dxm = n.x - mx, dym = n.y - my, dm = Math.sqrt(dxm*dxm + dym*dym);
        if (dm < 150) { const f = (150-dm)/150*0.8; n.x += (dxm/dm)*f; n.y += (dym/dm)*f; }
      });

      for (let i = 0; i < nodes.length; i++) {
        for (let j = i+1; j < nodes.length; j++) {
          const dx = nodes[i].x-nodes[j].x, dy = nodes[i].y-nodes[j].y, d = Math.sqrt(dx*dx+dy*dy);
          if (d < 130) { ctx.beginPath(); ctx.moveTo(nodes[i].x, nodes[i].y); ctx.lineTo(nodes[j].x, nodes[j].y); ctx.strokeStyle = `rgba(124,58,237,${(1-d/130)*0.14})`; ctx.lineWidth = 0.5; ctx.stroke(); }
        }
        const dxm = nodes[i].x-mx, dym = nodes[i].y-my, dm = Math.sqrt(dxm*dxm+dym*dym);
        if (dm < 200) { ctx.beginPath(); ctx.moveTo(nodes[i].x, nodes[i].y); ctx.lineTo(mx, my); ctx.strokeStyle = `rgba(45,212,191,${(1-dm/200)*0.3})`; ctx.lineWidth = 0.8; ctx.stroke(); }
      }

      nodes.forEach(n => {
        const g = Math.sin(n.pulse)*0.3+0.7;
        ctx.beginPath(); ctx.arc(n.x, n.y, n.r*g, 0, Math.PI*2);
        ctx.fillStyle = `rgba(124,58,237,${0.4+g*0.3})`; ctx.fill();
      });

      animRef.current = requestAnimationFrame(draw);
    };

    resize(); initNodes(); draw();
    const onMove = e => { const r = canvas.getBoundingClientRect(); mouseRef.current = { x: e.clientX-r.left, y: e.clientY-r.top }; };
    const onLeave = () => { mouseRef.current = { x: -1000, y: -1000 }; };
    canvas.addEventListener('mousemove', onMove);
    canvas.addEventListener('mouseleave', onLeave);

    const onVis = () => { if (document.hidden) cancelAnimationFrame(animRef.current); else draw(); };
    document.addEventListener('visibilitychange', onVis);
    const onResize = () => { resize(); initNodes(); };
    window.addEventListener('resize', onResize);

    return () => { cancelAnimationFrame(animRef.current); canvas.removeEventListener('mousemove', onMove); canvas.removeEventListener('mouseleave', onLeave); document.removeEventListener('visibilitychange', onVis); window.removeEventListener('resize', onResize); };
  }, [isMobile]);

  if (isMobile) return null;
  return <canvas ref={canvasRef} aria-hidden="true" role="presentation" style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', ...style }} />;
}
window.NodeNetwork = NodeNetwork;

/* ── Typing Code ── */
function TypingCode() {
  const lines = [
    { text: 'import pandas as pd', color: '#c084fc' },
    { text: 'from sklearn.ensemble import GradientBoostingClassifier', color: '#c084fc' },
    { text: '', color: '' },
    { text: 'df = pd.read_sql("SELECT * FROM customers", conn)', color: '#2dd4bf' },
    { text: 'X_train, X_test = train_test_split(df, test_size=0.2)', color: '#2dd4bf' },
    { text: '', color: '' },
    { text: 'model = GradientBoostingClassifier(n_estimators=200)', color: '#f9a8d4' },
    { text: 'model.fit(X_train, y_train)', color: '#f9a8d4' },
    { text: 'accuracy = model.score(X_test, y_test)  # 0.94', color: '#fbbf24' },
  ];
  const [displayLines, setDisplayLines] = React.useState([]);
  const [curLine, setCurLine] = React.useState(0);
  const [curChar, setCurChar] = React.useState(0);
  const [blink, setBlink] = React.useState(true);

  React.useEffect(() => { const iv = setInterval(() => setBlink(b => !b), 530); return () => clearInterval(iv); }, []);

  React.useEffect(() => {
    if (curLine >= lines.length) { const t = setTimeout(() => { setDisplayLines([]); setCurLine(0); setCurChar(0); }, 3000); return () => clearTimeout(t); }
    const line = lines[curLine];
    if (line.text === '') { setDisplayLines(p => [...p, { text: '', color: '' }]); setCurLine(l => l+1); setCurChar(0); return; }
    if (curChar <= line.text.length) {
      const t = setTimeout(() => {
        setDisplayLines(p => { const n = [...p]; if (n.length <= curLine) n.push({ text: '', color: line.color }); n[curLine] = { text: line.text.substring(0, curChar), color: line.color }; return n; });
        setCurChar(c => c+1);
      }, 25 + Math.random()*20);
      return () => clearTimeout(t);
    } else { const t = setTimeout(() => { setCurLine(l => l+1); setCurChar(0); }, 400); return () => clearTimeout(t); }
  }, [curLine, curChar]);

  return (
    <div aria-label="Live Python ML code demonstration" role="img" style={{
      background: 'rgba(15,23,42,0.85)', border: `1px solid ${C.border}`, borderRadius: 14,
      padding: '16px 20px', fontFamily: "'DM Mono', monospace", fontSize: 13, lineHeight: 1.8,
      maxWidth: 520, width: '100%', backdropFilter: 'blur(12px)',
    }}>
      <div style={{ display: 'flex', gap: 6, marginBottom: 12, alignItems: 'center' }}>
        <div style={{ width: 10, height: 10, borderRadius: '50%', background: '#ef4444' }}></div>
        <div style={{ width: 10, height: 10, borderRadius: '50%', background: '#f59e0b' }}></div>
        <div style={{ width: 10, height: 10, borderRadius: '50%', background: '#22c55e' }}></div>
        <span style={{ fontSize: 11, color: C.textDim, marginLeft: 8 }}>model.py</span>
      </div>
      {displayLines.map((l, i) => (
        <div key={i} style={{ color: l.color || C.textDim, minHeight: '1.8em', whiteSpace: 'pre' }}>
          <span style={{ color: '#1e293b', marginRight: 12, userSelect: 'none' }}>{String(i+1).padStart(2,' ')}</span>
          {l.text}
          {i === displayLines.length-1 && curLine < lines.length && <span style={{ opacity: blink ? 1 : 0, color: C.purple }}>▊</span>}
        </div>
      ))}
      {curLine >= lines.length && displayLines.length > 0 && (
        <div style={{ marginTop: 8, padding: '6px 12px', background: 'rgba(45,212,191,0.1)', borderRadius: 6, color: C.teal, fontSize: 12 }}>✓ Model trained — Accuracy: 94.2%</div>
      )}
    </div>
  );
}
window.TypingCode = TypingCode;

/* ── Skill Radar ── */
function SkillRadar({ skills, size = 280 }) {
  const canvasRef = React.useRef(null);
  const [ref, visible] = useReveal(0.2);
  const [hovered, setHovered] = React.useState(-1);
  const animProgress = React.useRef(0);

  React.useEffect(() => {
    if (!visible) return;
    const canvas = canvasRef.current; if (!canvas) return;
    const ctx = canvas.getContext('2d');
    const dpr = window.devicePixelRatio || 1;
    canvas.width = size*dpr; canvas.height = size*dpr; ctx.scale(dpr, dpr);
    const cx = size/2, cy = size/2, maxR = size/2-40, n = skills.length, step = (Math.PI*2)/n;
    let startTime = null;

    const animate = (ts) => {
      if (!startTime) startTime = ts;
      const p = Math.min((ts-startTime)/1200, 1), eased = 1-Math.pow(1-p, 3);
      animProgress.current = eased;
      ctx.clearRect(0, 0, size, size);

      // Grid + percentage labels
      for (let ring = 1; ring <= 5; ring++) {
        const r = (maxR/5)*ring;
        ctx.beginPath();
        for (let i = 0; i <= n; i++) { const a = step*i - Math.PI/2, x = cx+Math.cos(a)*r, y = cy+Math.sin(a)*r; i===0 ? ctx.moveTo(x,y) : ctx.lineTo(x,y); }
        ctx.closePath(); ctx.strokeStyle = 'rgba(255,255,255,0.05)'; ctx.lineWidth = 1; ctx.stroke();
        // % labels on right axis
        ctx.font = '500 9px "DM Sans", sans-serif'; ctx.fillStyle = 'rgba(255,255,255,0.15)'; ctx.textAlign = 'left'; ctx.textBaseline = 'middle';
        ctx.fillText(`${ring*20}%`, cx + r + 4, cy);
      }

      skills.forEach((_, i) => { const a = step*i - Math.PI/2; ctx.beginPath(); ctx.moveTo(cx, cy); ctx.lineTo(cx+Math.cos(a)*maxR, cy+Math.sin(a)*maxR); ctx.strokeStyle = 'rgba(255,255,255,0.04)'; ctx.stroke(); });

      ctx.beginPath();
      skills.forEach((s, i) => { const a = step*i - Math.PI/2, r = (s.value/100)*maxR*eased; const x = cx+Math.cos(a)*r, y = cy+Math.sin(a)*r; i===0 ? ctx.moveTo(x,y) : ctx.lineTo(x,y); });
      ctx.closePath();
      const grad = ctx.createLinearGradient(0, 0, size, size);
      grad.addColorStop(0, 'rgba(124,58,237,0.25)'); grad.addColorStop(1, 'rgba(45,212,191,0.15)');
      ctx.fillStyle = grad; ctx.fill(); ctx.strokeStyle = 'rgba(124,58,237,0.6)'; ctx.lineWidth = 2; ctx.stroke();

      skills.forEach((s, i) => {
        const a = step*i - Math.PI/2, r = (s.value/100)*maxR*eased;
        const px = cx+Math.cos(a)*r, py = cy+Math.sin(a)*r;
        ctx.beginPath(); ctx.arc(px, py, hovered===i ? 5 : 3.5, 0, Math.PI*2);
        ctx.fillStyle = hovered===i ? C.teal : C.purple; ctx.fill();
        if (hovered===i) { ctx.beginPath(); ctx.arc(px, py, 10, 0, Math.PI*2); ctx.fillStyle = 'rgba(45,212,191,0.15)'; ctx.fill(); }
        const lr = maxR+24, lx = cx+Math.cos(a)*lr, ly = cy+Math.sin(a)*lr;
        ctx.font = `${hovered===i ? '600' : '500'} 11px "DM Sans", sans-serif`;
        ctx.fillStyle = hovered===i ? C.text : C.textMuted; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(s.label, lx, ly);
        if (hovered===i) { ctx.font = '700 12px "DM Sans"'; ctx.fillStyle = C.teal; ctx.fillText(`${s.value}%`, lx, ly+14); }
      });

      if (p < 1) requestAnimationFrame(animate);
    };
    requestAnimationFrame(animate);
  }, [visible, skills, size, hovered]);

  const handleMove = (e) => {
    const canvas = canvasRef.current; if (!canvas) return;
    const rect = canvas.getBoundingClientRect();
    const mx = e.clientX-rect.left, my = e.clientY-rect.top;
    const cx = size/2, cy = size/2, maxR = size/2-40, n = skills.length, step = (Math.PI*2)/n;
    let closest = -1, closestDist = 30;
    skills.forEach((s, i) => { const a = step*i-Math.PI/2, r = (s.value/100)*maxR*animProgress.current; const d = Math.sqrt((mx-cx-Math.cos(a)*r)**2 + (my-cy-Math.sin(a)*r)**2); if (d < closestDist) { closestDist = d; closest = i; } });
    setHovered(closest);
  };

  return (
    <div ref={ref} style={{ display: 'flex', justifyContent: 'center' }}>
      <canvas ref={canvasRef} aria-label="Skill proficiency radar chart" role="img" onMouseMove={handleMove} onMouseLeave={() => setHovered(-1)} style={{ width: size, height: size, cursor: 'crosshair' }} />
    </div>
  );
}
window.SkillRadar = SkillRadar;

/* ── Sales Forecast Line Chart ── */
function ForecastChart({ width = 360, height = 200 }) {
  const canvasRef = React.useRef(null);
  const [ref, visible] = useReveal(0.2);
  const actual = [42, 48, 55, 52, 61, 68, 65, 72, 78, 74, 82, 88];
  const forecast = [null,null,null,null,null,null,null,null,null,null,null,null, 92, 97, 103];
  const months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec','Jan','Feb','Mar'];
  const allVals = [...actual, 92, 97, 103];
  const maxV = Math.max(...allVals), minV = Math.min(...allVals);

  React.useEffect(() => {
    if (!visible) return;
    const canvas = canvasRef.current; if (!canvas) return;
    const ctx = canvas.getContext('2d');
    const dpr = window.devicePixelRatio || 1;
    canvas.width = width*dpr; canvas.height = height*dpr; ctx.scale(dpr, dpr);
    const pad = { t: 20, r: 20, b: 30, l: 40 };
    const cw = width-pad.l-pad.r, ch = height-pad.t-pad.b;
    const toX = i => pad.l + (i/(months.length-1))*cw;
    const toY = v => pad.t + (1-(v-minV+5)/(maxV-minV+10))*ch;
    let startTime = null;

    const draw = (ts) => {
      if (!startTime) startTime = ts;
      const p = Math.min((ts-startTime)/1500, 1), e = 1-Math.pow(1-p, 3);
      ctx.clearRect(0, 0, width, height);

      // Grid
      ctx.strokeStyle = 'rgba(255,255,255,0.04)'; ctx.lineWidth = 1;
      for (let v = Math.ceil(minV/10)*10; v <= maxV+5; v += 20) {
        const y = toY(v); ctx.beginPath(); ctx.moveTo(pad.l, y); ctx.lineTo(width-pad.r, y); ctx.stroke();
        ctx.font = '500 10px "DM Sans"'; ctx.fillStyle = C.textDim; ctx.textAlign = 'right'; ctx.fillText(`$${v}K`, pad.l-6, y+3);
      }
      months.forEach((m, i) => { if (i % 2 === 0) { ctx.font = '500 9px "DM Sans"'; ctx.fillStyle = C.textDim; ctx.textAlign = 'center'; ctx.fillText(m, toX(i), height-8); } });

      // Actual line
      const drawCount = Math.floor(e * actual.length);
      ctx.beginPath(); ctx.strokeStyle = C.purple; ctx.lineWidth = 2.5; ctx.lineJoin = 'round';
      actual.forEach((v, i) => { if (i > drawCount) return; const x = toX(i), y = toY(v); i===0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y); });
      ctx.stroke();

      // Forecast line (dashed)
      if (e > 0.7) {
        const fp = (e-0.7)/0.3;
        ctx.beginPath(); ctx.strokeStyle = C.teal; ctx.lineWidth = 2; ctx.setLineDash([6, 4]);
        const forecastData = [actual[actual.length-1], 92, 97, 103];
        const drawF = Math.floor(fp * (forecastData.length));
        forecastData.forEach((v, i) => { if (i > drawF) return; const x = toX(11+i), y = toY(v); i===0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y); });
        ctx.stroke(); ctx.setLineDash([]);
      }

      // Dots
      actual.forEach((v, i) => { if (i > drawCount) return; ctx.beginPath(); ctx.arc(toX(i), toY(v), 3, 0, Math.PI*2); ctx.fillStyle = C.purple; ctx.fill(); });

      if (p < 1) requestAnimationFrame(draw);
    };
    requestAnimationFrame(draw);
  }, [visible]);

  return (
    <div ref={ref} style={{ background: C.bgCard, borderRadius: 14, border: `1px solid ${C.border}`, padding: '20px 16px 12px', overflow: 'hidden' }}>
      <div style={{ fontSize: 13, fontWeight: 700, color: C.white, marginBottom: 4 }}>Revenue Forecast</div>
      <div style={{ fontSize: 11, color: C.textDim, marginBottom: 12 }}>12-month actual + 3-month forecast</div>
      <div style={{ display: 'flex', gap: 16, marginBottom: 8, fontSize: 11 }}>
        <span style={{ display: 'flex', alignItems: 'center', gap: 4 }}><span style={{ width: 16, height: 2, background: C.purple, borderRadius: 1 }}></span><span style={{ color: C.textMuted }}>Actual</span></span>
        <span style={{ display: 'flex', alignItems: 'center', gap: 4 }}><span style={{ width: 16, height: 2, background: C.teal, borderRadius: 1, borderTop: '2px dashed' }}></span><span style={{ color: C.textMuted }}>Forecast</span></span>
      </div>
      <canvas ref={canvasRef} style={{ width, height, maxWidth: '100%' }} />
    </div>
  );
}
window.ForecastChart = ForecastChart;

/* ── Customer Segment Donut ── */
function DonutChart({ size = 220 }) {
  const canvasRef = React.useRef(null);
  const [ref, visible] = useReveal(0.2);
  const [hovered, setHovered] = React.useState(-1);
  const segments = [
    { label: 'Champions', value: 35, color: '#7c3aed', count: '4,200' },
    { label: 'Loyal', value: 28, color: '#2dd4bf', count: '3,360' },
    { label: 'At Risk', value: 22, color: '#f59e0b', count: '2,640' },
    { label: 'Lost', value: 15, color: '#ef4444', count: '1,800' },
  ];

  React.useEffect(() => {
    if (!visible) return;
    const canvas = canvasRef.current; if (!canvas) return;
    const ctx = canvas.getContext('2d');
    const dpr = window.devicePixelRatio || 1;
    canvas.width = size*dpr; canvas.height = size*dpr; ctx.scale(dpr, dpr);
    const cx = size/2, cy = size/2, outerR = size/2-10, innerR = outerR*0.6;
    let startTime = null;

    const draw = (ts) => {
      if (!startTime) startTime = ts;
      const p = Math.min((ts-startTime)/1200, 1), e = 1-Math.pow(1-p, 3);
      ctx.clearRect(0, 0, size, size);
      let angle = -Math.PI/2;
      const totalAngle = Math.PI*2*e;
      segments.forEach((s, i) => {
        const sweep = (s.value/100)*totalAngle;
        const r = hovered === i ? outerR + 4 : outerR;
        ctx.beginPath(); ctx.arc(cx, cy, r, angle, angle+sweep); ctx.arc(cx, cy, innerR, angle+sweep, angle, true); ctx.closePath();
        ctx.fillStyle = s.color; ctx.globalAlpha = hovered === i ? 1 : 0.85; ctx.fill(); ctx.globalAlpha = 1;
        angle += sweep;
      });
      // Center text
      ctx.font = '700 22px "DM Sans"'; ctx.fillStyle = C.white; ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
      if (hovered >= 0) {
        ctx.fillText(`${segments[hovered].value}%`, cx, cy-6);
        ctx.font = '500 11px "DM Sans"'; ctx.fillStyle = C.textMuted; ctx.fillText(segments[hovered].label, cx, cy+12);
      } else {
        ctx.fillText('12K', cx, cy-6);
        ctx.font = '500 11px "DM Sans"'; ctx.fillStyle = C.textMuted; ctx.fillText('Customers', cx, cy+12);
      }
      if (p < 1) requestAnimationFrame(draw);
    };
    requestAnimationFrame(draw);
  }, [visible, hovered]);

  const handleMove = (e) => {
    const canvas = canvasRef.current; if (!canvas) return;
    const rect = canvas.getBoundingClientRect();
    const mx = e.clientX-rect.left-size/2, my = e.clientY-rect.top-size/2;
    const d = Math.sqrt(mx*mx+my*my), outerR = size/2-10, innerR = outerR*0.6;
    if (d < innerR || d > outerR+5) { setHovered(-1); return; }
    let a = Math.atan2(my, mx) + Math.PI/2; if (a < 0) a += Math.PI*2;
    let cum = 0;
    for (let i = 0; i < segments.length; i++) { cum += segments[i].value/100; if (a/(Math.PI*2) < cum) { setHovered(i); return; } }
    setHovered(-1);
  };

  return (
    <div ref={ref} style={{ background: C.bgCard, borderRadius: 14, border: `1px solid ${C.border}`, padding: '20px 16px 12px', display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
      <div style={{ fontSize: 13, fontWeight: 700, color: C.white, marginBottom: 4, alignSelf: 'flex-start' }}>Customer Segments</div>
      <div style={{ fontSize: 11, color: C.textDim, marginBottom: 12, alignSelf: 'flex-start' }}>RFM segmentation analysis</div>
      <canvas ref={canvasRef} onMouseMove={handleMove} onMouseLeave={() => setHovered(-1)} style={{ width: size, height: size, cursor: 'crosshair', maxWidth: '100%' }} />
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 12, marginTop: 12, justifyContent: 'center' }}>
        {segments.map((s, i) => (
          <span key={i} style={{ display: 'flex', alignItems: 'center', gap: 4, fontSize: 11, color: C.textMuted }}>
            <span style={{ width: 8, height: 8, borderRadius: 2, background: s.color }}></span>{s.label}
          </span>
        ))}
      </div>
    </div>
  );
}
window.DonutChart = DonutChart;

/* ── NLP Sentiment Bars ── */
function SentimentChart() {
  const [ref, visible] = useReveal(0.2);
  const data = [
    { label: 'Positive', value: 64, color: '#22c55e' },
    { label: 'Neutral', value: 24, color: '#94a3b8' },
    { label: 'Negative', value: 12, color: '#ef4444' },
  ];
  return (
    <div ref={ref} style={{ background: C.bgCard, borderRadius: 14, border: `1px solid ${C.border}`, padding: '20px 20px 16px' }}>
      <div style={{ fontSize: 13, fontWeight: 700, color: C.white, marginBottom: 4 }}>Sentiment Analysis</div>
      <div style={{ fontSize: 11, color: C.textDim, marginBottom: 16 }}>NLP classification on 100K reviews</div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
        {data.map((d, i) => (
          <div key={i}>
            <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6, fontSize: 12 }}>
              <span style={{ color: C.text, fontWeight: 600 }}>{d.label}</span>
              <span style={{ color: d.color, fontWeight: 700 }}>{d.value}%</span>
            </div>
            <div style={{ height: 8, borderRadius: 4, background: 'rgba(255,255,255,0.04)', overflow: 'hidden' }}>
              <div style={{
                height: '100%', borderRadius: 4, background: d.color,
                width: visible ? `${d.value}%` : '0%',
                transition: `width 1.2s cubic-bezier(0.16,1,0.3,1) ${i*0.15}s`,
              }}></div>
            </div>
          </div>
        ))}
      </div>
      <div style={{ marginTop: 16, padding: '8px 12px', background: 'rgba(34,197,94,0.08)', borderRadius: 8, fontSize: 12, color: '#22c55e' }}>
        Overall sentiment score: <strong>+0.72</strong> (bullish)
      </div>
    </div>
  );
}
window.SentimentChart = SentimentChart;
