/**
* Font loading utility for the grain design system — the brand faces.
*
* Self-hosted OFL variable fonts (Archivo, Source Serif 4), embedded as
* base64 woff2 with `font-display: block`. No ``, no Google Fonts
* network request, no `display=swap` FOUT-by-design.
*
* `document.fonts.check()` ALONE IS NOT PROOF a face is actually loaded
* and painting. The CSS font-matching algorithm it consults can be
* satisfied by any registered face, including one that has merely been
* *declared*, not successfully downloaded and parsed — so it can report
* `true` for a face that never actually renders. loadFonts() therefore
* runs a proof per face and reports it, rather than asserting it
* silently:
*
* 1. FontFace status — `await face.load()`, then `face.status === 'loaded'`.
* 2. `document.fonts.check()` for the face — required, but never
* trusted alone (see above).
* 3. Laid-out width divergence — identical text measured once with the
* face in the stack and once forced onto the bare fallback generic.
* Identical widths mean the fallback painted and steps 1/2 lied.
*
* A face is `proved` only when all three agree. See
* foundry-business-hearth, proposal 20260802T2107, foundation/design-system.md §3.1.
*/
interface FontFaceSpec {
family: string;
base64: string;
/** CSS font-weight range for the variable font. */
weightRange: string;
/** Bare generic fallback used for the width-divergence probe. */
fallbackGeneric: string;
}
declare const FONT_FACES: FontFaceSpec[];
declare const PROOF_TEXT = "The quick brown fox jumps over the lazy dog 0123456789";
declare const PROOF_SIZE_PX = 48;
declare const WIDTH_DIVERGENCE_EPSILON_PX = 1;
export interface FontLoadFaceReport {
family: string;
/** Step 1: the FontFace object itself reports 'loaded'. */
faceLoaded: boolean;
/** Step 2: document.fonts.check() for this family. Required, but see module doc — not trusted alone. */
checkPasses: boolean;
/** Step 3: a laid-out width measured with this face in the stack differs from the
* same text laid out with the bare fallback generic. */
widthDivergent: boolean;
/** faceLoaded && checkPasses && widthDivergent — all three, none alone. */
proved: boolean;
error?: string;
}
export interface FontLoadReport {
faces: FontLoadFaceReport[];
ok: boolean;
}
/**
* `display: 'block'` per `migration.md` §7's contract — mirrors
* `scripts/build-grain-css.mjs`'s `fontFace()` CSS output (`font-display: block`)
* so the JS runtime path `loadFonts()` actually calls, and the static
* `dist/grain.css` `@font-face` rule, agree. The FontFace API defaults
* `display` to `'auto'`, not `'block'`, when the descriptor omits it —
* exported standalone so it is unit-testable without a real `FontFace`
* implementation (vitest's node environment has neither, see load.test.ts).
*/
export declare function faceDescriptor(weightRange: string): FontFaceDescriptors;
/**
* Loads the self-hosted brand faces and returns a structured proof
* report. Never throws — a failed face is reported, not thrown, so a
* font regression degrades to the CSS fallback stack rather than
* crashing app startup. Failures are logged loudly via console.error;
* callers that need to gate on the result should inspect `report.ok`.
*/
declare function loadFonts(): Promise;
export { loadFonts, FONT_FACES, PROOF_TEXT, PROOF_SIZE_PX, WIDTH_DIVERGENCE_EPSILON_PX };