/* ============================================================
components.jsx — UI primitives for the instrument.
Status is always shape + text + color, never color alone.
Exports to window.
============================================================ */
(function () {
// ---- layout ---------------------------------------------
function Card({ tone = "surface", pad = 16, children, style, className = "", ...rest }) {
const bg = tone === "sunken" ? "var(--bg-sunken)" : tone === "raised" ? "var(--surface-raised)" : "var(--surface)";
return (
{children}
);
}
function Row({ children, gap = 8, align = "center", justify = "flex-start", wrap = false, style }) {
return {children}
;
}
function Label({ children, style }) {
return {children};
}
// ---- status mark (shape-coded, a11y safe) ----------------
// good ▸ ring · attn ▸ caret-up · acute ▸ bang · within ▸ dash · low ▸ hollow
function StatusMark({ status = "within", size = 14 }) {
const c = { good: "var(--good)", attn: "var(--attn)", acute: "var(--acute)", within: "var(--ink-3)", lowconf: "var(--ink-4)" }[status];
const s = size;
if (status === "acute") return (
);
if (status === "attn") return (
);
if (status === "good") return (
);
if (status === "lowconf") return (
);
return ;
}
// ---- confidence meter (signal bars) ----------------------
function ConfidenceMeter({ level = "high", pct, showText = true, size = 1 }) {
const fill = { high: 3, medium: 2, low: 1 }[level];
const col = level === "low" ? "var(--ink-4)" : level === "medium" ? "var(--ink-2)" : "var(--good)";
const bw = 3.5 * size, gap = 2 * size;
return (
{[0, 1, 2].map((i) => (
))}
{showText && }
);
}
// ---- chip ------------------------------------------------
function Chip({ tone = "neutral", children, style, mono = true }) {
const map = {
neutral: ["var(--surface-2)", "var(--ink-2)", "var(--line)"],
good: ["var(--good-soft)", "var(--good-ink)", "var(--good-line)"],
attn: ["var(--attn-soft)", "var(--attn-ink)", "var(--attn-line)"],
acute: ["var(--acute-soft)", "var(--acute-ink)", "var(--acute-line)"],
};
const [bg, fg, bd] = map[tone] || map.neutral;
return (
{children}
);
}
// ---- delta tag (vs baseline) -----------------------------
function Delta({ children, tone = "neutral" }) {
const col = tone === "good" ? "var(--good-ink)" : tone === "attn" ? "var(--attn-ink)" : "var(--ink-3)";
return {children};
}
// ---- big numeric readout ---------------------------------
function Readout({ value, unit, band, size = 64, status, color }) {
const col = color || (status ? { good: "var(--good)", attn: "var(--attn)", acute: "var(--acute)" }[status] : "var(--ink)") || "var(--ink)";
return (
{value}
{band != null && ±{band}}
{unit && {unit}}
);
}
// ---- tier header -----------------------------------------
function TierHeader({ tier, title, count, note }) {
const accent = { acute: "var(--acute)", subacute: "var(--attn)", chronic: "var(--ink-3)", wellness: "var(--good)" }[tier] || "var(--ink-3)";
return (
{count != null && }
{note && }
);
}
// ---- caveat block (clinical humility) --------------------
function Caveat({ children, action, tone = "neutral" }) {
const bd = tone === "acute" ? "var(--acute-line)" : tone === "attn" ? "var(--attn-line)" : "var(--line)";
return (
{children}
{action && (
)}
);
}
// ---- section divider label -------------------------------
function SubLabel({ children, right }) {
return (
{right && }
);
}
// ---- icon (minimal line glyphs) --------------------------
function Icon({ name, size = 20, color = "currentColor", stroke = 1.6 }) {
const p = { fill: "none", stroke: color, strokeWidth: stroke, strokeLinecap: "round", strokeLinejoin: "round" };
const paths = {
home: <>>,
heart: <>>,
moon: <>>,
trend: <>>,
clinical: <>>,
sun: <>{[0, 45, 90, 135, 180, 225, 270, 315].map((a) => { const r = a * Math.PI / 180; return ; })}>,
back: <>>,
gap: <>>,
};
return ;
}
Object.assign(window, { Card, Row, Label, StatusMark, ConfidenceMeter, Chip, Delta, Readout, TierHeader, Caveat, SubLabel, Icon });
})();