Placeholders are now generated into src/assets/placeholders/ instead of
public/ so Vite's asset pipeline processes them. Since all are < 4KB,
Vite auto-inlines them as base64 data URIs via assetsInlineLimit — zero
extra HTTP requests for placeholders.
- Static imports resolve to data:image/png;base64,... at build time
- JSDoc on placeholder props documents why static imports are required
(synchronous availability, Vite inlining) and warns against dynamic
imports and public/ paths
- New LazyVideo component: same pixelated poster pattern, uses native
<video preload="none" loading="lazy"> for zero-JS lazy loading
Build-time script generates tiny 32px-wide versions of all images in
public/ using sharp. At runtime, PixelatedImage component renders these
with CSS image-rendering: pixelated (nearest-neighbor / point sampling)
for a crisp mosaic effect, then fades in the real image on load.
- Auto-discovers images in public/, skips placeholder- prefixed files
- Mtime check skips regeneration when placeholder is newer than source
- Callback ref handles cached images where onLoad fires before mount
- Explicit z-index prevents placeholder from covering the real image
- Runs automatically as part of pnpm build in website package
All CSS variables previously scoped under .liveline-page with --ll- prefix
are now at :root in globals.css with no prefix. Four names conflicting with
shadcn were renamed: --brand-primary, --brand-secondary, --link-accent,
--page-border.
Class renames: .liveline-page → .editorial-page, .liveline-article →
.editorial-article, .ll-bleed → .bleed, .ll-inline-code → .inline-code,
.ll-prose → .editorial-prose.
Filenames kept as-is for this commit to isolate the rename.