// SVG chart primitives — no external libs. // Uses CSS vars for colors so light/dark works automatically. const Sparkline = ({ data, w = 120, h = 36, stroke = "var(--primary)", fill = "none", area = false, strokeWidth = 1.5 }) => { if (!data || data.length < 2) return null; const min = Math.min(...data), max = Math.max(...data), range = max - min || 1; const stepX = w / (data.length - 1); const pts = data.map((v, i) => [i * stepX, h - ((v - min) / range) * (h - 4) - 2]); const d = pts.map((p, i) => `${i === 0 ? 'M' : 'L'} ${p[0].toFixed(1)} ${p[1].toFixed(1)}`).join(" "); const areaD = area ? `${d} L ${w} ${h} L 0 ${h} Z` : null; return ( ); }; const LineChart = ({ series, w = 600, h = 220, labels, colors = ["var(--primary)", "var(--accent)"], showGrid = true, showAxes = true, yFormat = (v) => v }) => { // series: [{name, data:[..]}, ...] const pad = { t: 12, r: 12, b: 24, l: 40 }; const iw = w - pad.l - pad.r, ih = h - pad.t - pad.b; const allVals = series.flatMap(s => s.data); const min = Math.min(...allVals, 0), max = Math.max(...allVals); const range = max - min || 1; const len = series[0].data.length; const xStep = iw / (len - 1); const yTicks = 4; return ( ); }; const BarChart = ({ data, w = 600, h = 220, labels, color = "var(--primary)", accent, horizontal = false, showValue = false, format = (v) => v }) => { const pad = horizontal ? { t: 8, r: 40, b: 8, l: 120 } : { t: 12, r: 12, b: 26, l: 40 }; const iw = w - pad.l - pad.r, ih = h - pad.t - pad.b; const max = Math.max(...data); if (horizontal) { const barH = Math.min(22, (ih / data.length) - 6); return ( ); } const barW = iw / data.length * 0.7; const gap = iw / data.length * 0.3; return ( ); }; const Donut = ({ data, w = 160, colors = ["var(--primary)", "var(--accent)", "var(--info)", "var(--ok)", "oklch(0.55 0.14 300)"], thickness = 22, centerLabel, centerSub }) => { const r = w / 2 - 4; const inner = r - thickness; const cx = w / 2, cy = w / 2; const total = data.reduce((s, d) => s + d.value, 0); let angle = -Math.PI / 2; const arcs = data.map((d, i) => { const frac = d.value / total; const nextAngle = angle + frac * Math.PI * 2; const large = frac > 0.5 ? 1 : 0; const x1 = cx + r * Math.cos(angle), y1 = cy + r * Math.sin(angle); const x2 = cx + r * Math.cos(nextAngle), y2 = cy + r * Math.sin(nextAngle); const x3 = cx + inner * Math.cos(nextAngle), y3 = cy + inner * Math.sin(nextAngle); const x4 = cx + inner * Math.cos(angle), y4 = cy + inner * Math.sin(angle); const path = `M ${x1} ${y1} A ${r} ${r} 0 ${large} 1 ${x2} ${y2} L ${x3} ${y3} A ${inner} ${inner} 0 ${large} 0 ${x4} ${y4} Z`; angle = nextAngle; return { path, color: colors[i % colors.length], label: d.label, value: d.value }; }); return (