import { buildVoronoiMesh, type VoronoiCell } from "@/lib/voronoi-mesh"; import { buildFooterMidlineCurve, midlineCurveToClipPath, midlineCurveToClipPathPercent, } from "@/lib/footer-curve-edge"; import { cn } from "@/lib/utils"; const VIEW_W = 160; const VIEW_H = 100; const MIDLINE_Y = VIEW_H * 0.5; const CURVE_SEED = 0xa3f21c; const MIDLINE_CURVE = buildFooterMidlineCurve(VIEW_W, 20, CURVE_SEED, MIDLINE_Y, 11); const FACET_CLIP_PATH = midlineCurveToClipPath(MIDLINE_CURVE, VIEW_W, VIEW_H); const FACET_CLIP_CSS = midlineCurveToClipPathPercent(MIDLINE_CURVE, VIEW_W, VIEW_H); const FACET_TONES = [ "#f5fbf7", "#ecf8f0", "#dff3e6", "#ccebd8", "#b5e4c8", "#9ed9b4", "#84d09f", "#6bc98a", "#52c878", "#4aad6e", ] as const; const LOW_POLY_MESH = buildVoronoiMesh(64, VIEW_W, VIEW_H, CURVE_SEED, { siteGenerator: "poisson", shape: "sharp", minDist: 6.5, }); function hexToRgb(hex: string) { const n = parseInt(hex.slice(1), 16); return { r: (n >> 16) & 255, g: (n >> 8) & 255, b: n & 255 }; } function mixHex(a: string, b: string, t: number) { const c1 = hexToRgb(a); const c2 = hexToRgb(b); const u = (v: number) => Math.round(v).toString(16).padStart(2, "0"); return `#${u(c1.r + (c2.r - c1.r) * t)}${u(c1.g + (c2.g - c1.g) * t)}${u(c1.b + (c2.b - c1.b) * t)}`; } function facetFill(cell: VoronoiCell, index: number) { const cx = cell.points.reduce((s, p) => s + p[0], 0) / cell.points.length; const cy = cell.points.reduce((s, p) => s + p[1], 0) / cell.points.length; const xNorm = cx / VIEW_W; const yNorm = Math.max(0, (cy - MIDLINE_Y) / (VIEW_H - MIDLINE_Y)); const tone = FACET_TONES[(index * 9 + Math.floor(cx * 0.55)) % FACET_TONES.length]; const wash = mixHex("#ffffff", "#e8f5ec", 0.35); const mix = 0.42 + (1 - xNorm) * 0.2 + yNorm * 0.12; return mixHex(wash, tone, mix); } const FACET_CELLS = LOW_POLY_MESH.map((cell, i) => ({ d: cell.d, fill: facetFill(cell, i), })); type Props = { className?: string; }; export function GeometricMessBackground({ className }: Props) { return (