/* primitives.jsx — light theme atomic components */
const { useEffect, useRef, useState, useMemo } = React;
/* ---------- ICONS ---------- */
const ICONS = {
search: ,
scale: ,
gavel: ,
judge: ,
chart: ,
sparkles: ,
shield: ,
arrow: ,
arrowUp: ,
check: ,
x: ,
bolt: ,
doc: ,
lock: ,
globe: ,
database: ,
layers: ,
msgs: ,
pin: ,
history: ,
chevron: ,
chevronDown: ,
star: ,
cpu: ,
brain: ,
filter: ,
euro: ,
upload: ,
arrowRight: ,
warning: ,
info: ,
hash: ,
paperclip: ,
refresh: ,
};
function Icon({ name, className = "w-5 h-5", stroke = 1.6 }) {
const path = ICONS[name];
if (!path) return null;
return (
);
}
function GoogleGlyph({ className = "w-5 h-5" }) {
return (
);
}
/* ---------- Aurora (light · 4 radial blobs) ---------- */
function Aurora({ mask = "", className = "", strength = "medium" }) {
const cls =
strength === 'strong' ? 'aurora-strong'
: strength === 'soft' ? 'aurora-soft'
: 'aurora-medium';
const maskCls = mask ? `mask-${mask}` : '';
return (
);
}
/* ---------- Flourish — serif italic phrase with hand-drawn underline ---------- */
function Flourish({ children, className = "" }) {
const [ref, shown] = useReveal(0.2);
return (
{children}
);
}
/* ---------- StaggerGroup wrapper ---------- */
function StaggerGroup({ children, className = "", threshold = 0.15 }) {
const [ref, shown] = useReveal(threshold);
return (
{children}
);
}
/* ---------- Logo (ink monogram, no period) ---------- */
function Logo({ size = 28, className = "", dark = false }) {
const dot = Math.max(3, Math.round(size * 0.12));
return (
);
}
/* ---------- Badge ---------- */
function Eyebrow({ children, className = "", tone = "violet" }) {
const toneMap = {
violet: 'text-violet2-600',
indigo: 'text-indigo2-600',
ink: 'text-ink-500',
emerald: 'text-emerald-600',
};
return (
{children}
);
}
function Pill({ children, className = "", icon }) {
return (
{icon}
{children}
);
}
/* ---------- Section ---------- */
function Section({ id, eyebrow, title, kicker, screen, children, className = "", dense = false, center = false }) {
return (
{(eyebrow || title) && (
{eyebrow &&
{eyebrow}}
{title && (
{title}
)}
{kicker && (
{kicker}
)}
)}
{children}
);
}
/* ---------- useReveal ---------- */
function useReveal(threshold = 0.15) {
const ref = useRef(null);
const [shown, setShown] = useState(false);
useEffect(() => {
if (!ref.current) return;
const io = new IntersectionObserver(([e]) => {
if (e.isIntersecting) { setShown(true); io.disconnect(); }
}, { threshold });
io.observe(ref.current);
return () => io.disconnect();
}, [threshold]);
return [ref, shown];
}
/* ---------- Buttons ---------- */
function PrimaryButton({ children, onClick, icon, size = 'md', className = '' }) {
const sz = size === 'lg' ? 'px-7 py-4 text-[15px]' : size === 'sm' ? 'px-3.5 py-2 text-[13px]' : 'px-5 py-3 text-[14px]';
return (
);
}
function GhostButton({ children, onClick, icon, size = 'md', className = '' }) {
const sz = size === 'lg' ? 'px-7 py-4 text-[15px]' : size === 'sm' ? 'px-3.5 py-2 text-[13px]' : 'px-5 py-3 text-[14px]';
return (
);
}
function LightButton({ children, onClick, icon, size = 'md', className = '' }) {
const sz = size === 'lg' ? 'px-7 py-4 text-[15px]' : size === 'sm' ? 'px-3.5 py-2 text-[13px]' : 'px-5 py-3 text-[14px]';
return (
);
}
/* ---------- AnswerBody ---------- */
function inlineMd(s) {
return s
.replace(/`([^`]+)`/g, '$1')
.replace(/\*\*([^*]+)\*\*/g, '$1')
.replace(/(^|[^*])\*([^*]+)\*/g, '$1$2');
}
function parseLite(text) {
if (!text) return [];
const lines = text.split(/\n/);
const out = [];
let buf = [];
let bullets = null;
const flushPara = () => { if (buf.length) { out.push({ type: 'p', html: inlineMd(buf.join(' ')) }); buf = []; } };
const flushList = () => { if (bullets) { out.push({ type: 'ul', items: bullets.map(inlineMd) }); bullets = null; } };
for (const raw of lines) {
const line = raw.trim();
if (!line) { flushPara(); flushList(); continue; }
if (/^([•\-*])\s+/.test(line)) {
flushPara();
bullets = bullets || [];
bullets.push(line.replace(/^([•\-*])\s+/, ''));
} else {
flushList();
buf.push(line);
}
}
flushPara(); flushList();
return out;
}
function AnswerBody({ text }) {
const blocks = useMemo(() => parseLite(text), [text]);
return (
{blocks.map((b, i) => {
if (b.type === 'ul') return
;
return
;
})}
);
}
function Dots() { return ; }
Object.assign(window, {
Icon, GoogleGlyph, Aurora, Logo, Eyebrow, Pill, Section, useReveal,
PrimaryButton, GhostButton, LightButton, AnswerBody, Dots, inlineMd, parseLite,
Flourish, StaggerGroup,
});