// Chart primitives + UI atoms — refined editorial palette. // Theme: cream paper background, deep ink text, terracotta accent for PM, // indigo accent for Researcher. Health gradient stays sequential rust→amber→sage. function healthColor(score, alpha = 1) { // 1 (poor) → muted rust; 5 (excellent) → muted sage const t = Math.max(0, Math.min(1, (score - 1) / 4)); const hue = 25 + t * 115; const chroma = 0.10 + t * 0.04; const light = 0.58 + t * 0.04; return `oklch(${light} ${chroma} ${hue} / ${alpha})`; } function riskColor(risk) { if (risk === "critical") return "oklch(0.58 0.15 28)"; if (risk === "watch") return "oklch(0.72 0.13 75)"; return "oklch(0.62 0.10 145)"; } function riskLabel(risk) { if (risk === "critical") return "Critical"; if (risk === "watch") return "Watch"; return "Healthy"; } function signedN(n, d=1){ const s = n.toFixed(d); return n>=0?`+${s}`:s; } function avg(a){ return a.length ? a.reduce((s,v)=>s+v,0)/a.length : 0; } function stddev(arr) { const m = avg(arr); return Math.sqrt(arr.reduce((s,v)=>s+(v-m)*(v-m),0)/arr.length); } function fmtDate(iso) { const d = new Date(iso + "T00:00:00"); return d.toLocaleDateString("en-US", { month: "short", day: "numeric" }); } function fmtDateLong(iso) { const d = new Date(iso + "T00:00:00"); return d.toLocaleDateString("en-US", { weekday: "short", month: "long", day: "numeric", year: "numeric" }); } const INK = "oklch(0.22 0.02 250)"; const SUBINK = "oklch(0.45 0.015 250)"; const FAINT = "oklch(0.72 0.01 250)"; const RULE = "oklch(0.88 0.005 250)"; const PAPER = "oklch(0.985 0.005 80)"; const PAPER_2 = "oklch(0.97 0.006 80)"; function Sparkline({ data, width = 120, height = 32, min = 1, max = 5, stroke, fill }) { if (!data || !data.length) return null; const pad = 2; const w = width - pad * 2, h = height - pad * 2; const dx = w / (data.length - 1 || 1); const pts = data.map((v, i) => [pad + i * dx, pad + h - ((v - min) / (max - min)) * h]); const path = pts.map(([x, y], i) => (i === 0 ? `M${x},${y}` : `L${x},${y}`)).join(" "); const last = data[data.length - 1]; const c = stroke || healthColor(last); const f = fill || healthColor(last, 0.16); const area = `${path} L${pts[pts.length-1][0]},${pad+h} L${pts[0][0]},${pad+h} Z`; return ( ); } function TrendLine({ data, width = 720, height = 220, min = 1, max = 5, compare, compareLabel, label }) { const padL = 38, padR = 14, padT = 18, padB = 30; const w = width - padL - padR, h = height - padT - padB; const xs = (i) => padL + (i * w) / (data.length - 1 || 1); const ys = (v) => padT + h - ((v - min) / (max - min)) * h; const path = data.map((d, i) => `${i ? "L" : "M"}${xs(i)},${ys(d.score)}`).join(" "); const area = `${path} L${xs(data.length-1)},${padT+h} L${xs(0)},${padT+h} Z`; const cmpPath = compare && compare.length === data.length ? compare.map((d, i) => `${i ? "L" : "M"}${xs(i)},${ys(d.score)}`).join(" ") : null; const last = data[data.length - 1]; return ( {[1,2,3,4,5].map(v => ( {v} ))} {data.map((d, i) => ( {d.label || `W${d.week}`} ))} {cmpPath && } {data.map((d, i) => )} {compareLabel && ( {compareLabel} )} ); } function Radar({ dimensions, size = 280, max = 5, compare }) { const cx = size/2, cy = size/2; const radius = size/2 - 42; const n = dimensions.length; const ang = (i) => -Math.PI/2 + (i*2*Math.PI)/n; const pt = (i, v) => { const r = (v/max)*radius; return [cx+Math.cos(ang(i))*r, cy+Math.sin(ang(i))*r]; }; const polyD = (vals) => vals.map((v,i)=>pt(i,v)).map(([x,y],i)=>`${i?"L":"M"}${x},${y}`).join(" ") + " Z"; const teamVals = dimensions.map(d=>d.current); const teamAvg = avg(teamVals); return ( {[1,2,3,4,5].map(r=>{ const pts = Array.from({length:n},(_,i)=>pt(i,r)); const d = pts.map(([x,y],i)=>`${i?"L":"M"}${x},${y}`).join(" ")+" Z"; return ; })} {dimensions.map((d,i)=>{ const [x,y]=pt(i,max); return ; })} {compare && } {teamVals.map((v,i)=>{ const [x,y]=pt(i,v); return ; })} {dimensions.map((d,i)=>{ const [x,y]=pt(i,max+0.5); return ( {d.short} ); })} ); } function Heatmap({ teams, dimensions, onTeamClick }) { const cellH = 30, headerH = 32, labelW = 220, cellW = 64; const width = labelW + dimensions.length * cellW + 80; const height = headerH + teams.length * cellH; return ( {dimensions.map((d, di) => ( {d.short} ))} AVG {teams.map((t, ti) => { const y = headerH + ti*cellH; const tAvg = avg(t.dimensions.map(d=>d.current)); return ( onTeamClick && onTeamClick(t.id)}> {t.short.length > 26 ? t.short.slice(0,26)+"…" : t.short} {dimensions.map((d, di) => { const v = t.dimensions[di].current; return ( {v.toFixed(1)} ); })} {tAvg.toFixed(1)} ); })} ); } function DistBar({ buckets, width = 140, height = 36 }) { const segW = width/5; const max = Math.max(...buckets, 1); return ( {buckets.map((b, i) => { const score = i+1; const bh = (b/max) * (height-12); return ( {score} ); })} ); } function ResponseBar({ weekly, width = 720, height = 90, teamSize, onPick, selectedWeek }) { const padL = 36, padR = 12, padT = 10, padB = 28; const w = width-padL-padR, h = height-padT-padB; const max = teamSize || Math.max(...weekly.map(d=>d.responses), 1); const slot = w/weekly.length; const barW = slot - 8; return ( {weekly.map((d, i) => { const x = padL + i*slot + 4; const bh = (d.responses/max)*h; const y = padT + h - bh; const sel = selectedWeek === d.week; return ( onPick&&onPick(d.week)}> W{d.week} {d.responses} ); })} {max} 0 ); } function Bar({ value, max=5, width=120, height=8, color }) { return (
); } // Refined UI atoms function Card({ className = "", children, ...rest }) { return
{children}
; } function CardHeader({ title, subtitle, action, eyebrow }) { return (
{eyebrow &&
{eyebrow}
} {title &&

{title}

} {subtitle &&

{subtitle}

}
{action}
); } function CardBody({ className = "", children }) { return
{children}
; } function Pill({ children, color = "neutral", className = "" }) { const palette = { neutral: { bg: "oklch(0.95 0.005 250)", fg: INK, border: RULE }, healthy: { bg: "oklch(0.96 0.04 145)", fg: "oklch(0.4 0.10 145)", border: "oklch(0.86 0.06 145)" }, watch: { bg: "oklch(0.97 0.05 75)", fg: "oklch(0.45 0.10 75)", border: "oklch(0.88 0.08 75)" }, critical:{ bg: "oklch(0.96 0.04 28)", fg: "oklch(0.45 0.13 28)", border: "oklch(0.85 0.08 28)" }, accent: { bg: "oklch(0.96 0.04 270)", fg: "oklch(0.42 0.13 270)", border: "oklch(0.86 0.08 270)" }, pm: { bg: "oklch(0.96 0.04 38)", fg: "oklch(0.45 0.13 38)", border: "oklch(0.85 0.09 38)" }, }; const p = palette[color]; return {children}; } function Segmented({ value, onChange, options }) { return (
{options.map(o => ( ))}
); } function Select({ value, onChange, options, className = "" }) { return ( ); } // Calendar dropdown for week selection function WeekPicker({ weeks, value, onChange, startDate = "2026-04-27" }) { const [open, setOpen] = React.useState(false); const ref = React.useRef(null); React.useEffect(() => { const onClick = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); }; document.addEventListener("mousedown", onClick); return () => document.removeEventListener("mousedown", onClick); }, []); const weekDates = React.useMemo(() => { const start = new Date(startDate + "T00:00:00"); return weeks.map((w) => { const d = new Date(start); d.setDate(start.getDate() + (w.week - 1) * 7); return { ...w, date: d, label: d.toLocaleDateString("en-US", { month: "short", day: "numeric" }) }; }); }, [weeks, startDate]); const sel = weekDates.find(w => w.week === value) || weekDates[weekDates.length - 1]; return (
{open && (
Pulse week
{weekDates[0].label} – {weekDates[weekDates.length-1].label}, 2026
{weekDates.map((w) => ( ))}
Latest · W{weekDates[weekDates.length-1].week}
)}
); } Object.assign(window, { healthColor, riskColor, riskLabel, signedN, avg, stddev, fmtDate, fmtDateLong, INK, SUBINK, FAINT, RULE, PAPER, PAPER_2, Sparkline, TrendLine, Radar, Heatmap, DistBar, ResponseBar, Bar, Card, CardHeader, CardBody, Pill, Segmented, Select, WeekPicker, });