// ============================================================================
// Principia White Paper — Layout primitives (document grammar)
// Section openers, prose, pull quotes, spec tables, service cards, compares.
// ============================================================================
const WP_MAXW = 1080;
const WP_TEXTW = 720;
// ───── Section — vertical rhythm + anchor ─────
function Section({ id, dark, children, style = {}, pad = '104px 0' }) {
return (
);
}
// ───── SectionOpener — eyebrow number · double-rule · italic-tail heading ─────
function WpOpener({ num, label, title, italicTail, lede, dark }) {
const ink = dark ? '#fff' : C_INK;
const dim = dark ? 'rgba(255,255,255,0.6)' : C_MUTED;
const rule = dark ? 'rgba(255,255,255,0.2)' : C_RULE;
return (
{num}
{label}
{title}{italicTail && <> {italicTail}>}
{lede && {lede}
}
);
}
// ───── Prose paragraph ─────
function P({ children, dark, style = {} }) {
return (
{children}
);
}
// ───── Pull quote — navy left rule, Fraunces ─────
function Pull({ children, dark }) {
return (
{children}
);
}
// ───── Source caption ─────
function Source({ children, dark }) {
return (
{children}
);
}
// ───── Spec table — desktop: grid rows · mobile: stacked cards ─────
function SpecTable({ head, rows, cols = '150px 1fr' }) {
const mobile = useIsMobile(640);
const __tracks = cols.trim().split(/\s+/).length;
if (mobile) {
return (
{rows.map((r, i) => (
2 ? 12 : 6 }}>{r[0]}
{r.slice(1).map((cell, ci) => (
0 ? 10 : 0 }}>
{head && head[ci + 1] && (
{head[ci + 1]}
)}
{cell}
))}
))}
);
}
return (
= 3 ? 640 : undefined }}>
{head && (
{head.map((h, i) => (
{h}
))}
)}
{rows.map((r, i) => (
{r.map((cell, ci) => (
{cell}
))}
))}
);
}
// ───── KPI strip — hero stat cards ─────
function KpiStrip({ items, dark }) {
return (
{items.map((it, i) => (
))}
);
}
// ───── Trend cards (3-up) ─────
function TrendCards({ items }) {
return (
{items.map((it, i) => (
{it.tag &&
{it.tag}
}
{it.t}
{it.d}
))}
);
}
// ───── Service line card (A–F) ─────
function ServiceCard({ letter, title, tag, rows, fee, market }) {
return (
{rows.map((r, i) => (
))}
{(fee || market) && (
)}
);
}
// ───── Compare columns — TODAY vs OUR MODEL ─────
function CompareCols({ today, ours }) {
return (
{today.title || 'Today'}
{today.rows.map((r, i) => (
—{r}
))}
{ours.title || 'Our model'}
{ours.rows.map((r, i) => (
✓{r}
))}
);
}
// ───── Figure block with generous top margin (wraps Figure for spacing) ─────
function Fig(props) {
return
;
}
Object.assign(window, {
WP_MAXW, WP_TEXTW,
Section, WpOpener, P, Pull, Source, SpecTable, KpiStrip, TrendCards, ServiceCard, CompareCols, Fig,
});