diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css index b71bc59..f4b32a1 100644 --- a/apps/web/src/app/globals.css +++ b/apps/web/src/app/globals.css @@ -327,3 +327,40 @@ html[data-splash="skip"] #splash-overlay { display: none !important; } + +/* Splash handover burst: the four pillar cells disperse as the mark hands over to the page. + The blobs sit where the monogram's four cells were (a 2x2 grid about the centre), so this + reads as the logo coming apart rather than as a generic flash. + + ONLY transform and opacity animate, both compositor properties. This deliberately avoids + animating filter/background: the matrix animation pegged a core doing exactly that, because + a filter change re-renders the whole filter graph every frame. Soft edges come from a static + radial-gradient that is painted once and then only transformed. */ +@keyframes splash-burst { + from { transform: translate(-50%, -50%) scale(0.35); opacity: var(--burst-peak); } + to { transform: translate(-50%, -50%) scale(3.1); opacity: 0; } +} +.splash-burst-blob { + position: absolute; + width: 34vmin; + height: 34vmin; + border-radius: 9999px; + opacity: 0; + will-change: transform, opacity; + animation: splash-burst 420ms cubic-bezier(0.16, 0.84, 0.44, 1) forwards; + /* Half-transparent by construction: the gradient's own alpha never reaches 1, and + --burst-peak holds the whole layer under half opacity. */ + --burst-peak: 0.42; +} +.dark .splash-burst-blob { --burst-peak: 0.5; } +.splash-burst-s { background: radial-gradient(circle, rgb(26 92 56 / 0.55), transparent 70%); } +.splash-burst-u { background: radial-gradient(circle, rgb(184 54 112 / 0.55), transparent 70%); } +.splash-burst-c { background: radial-gradient(circle, rgb(23 106 146 / 0.55), transparent 70%); } +.splash-burst-h { background: radial-gradient(circle, rgb(18 133 126 / 0.55), transparent 70%); } +.dark .splash-burst-s { background: radial-gradient(circle, rgb(53 146 92 / 0.55), transparent 70%); } +.dark .splash-burst-u { background: radial-gradient(circle, rgb(232 104 158 / 0.55), transparent 70%); } +.dark .splash-burst-c { background: radial-gradient(circle, rgb(74 173 224 / 0.55), transparent 70%); } +.dark .splash-burst-h { background: radial-gradient(circle, rgb(58 212 198 / 0.55), transparent 70%); } +@media (prefers-reduced-motion: reduce) { + .splash-burst-blob { display: none; } +} diff --git a/apps/web/src/components/landing/hero-splash.tsx b/apps/web/src/components/landing/hero-splash.tsx index 1bf68bf..afa99e7 100644 --- a/apps/web/src/components/landing/hero-splash.tsx +++ b/apps/web/src/components/landing/hero-splash.tsx @@ -12,6 +12,16 @@ const SPLASH_MS = 1337; // gate stacked on the first. const FADE_MS = 260; +// The four monogram cells, in the 2x2 grid the mark ends on: S and C in the left column, +// U and H in the right. The burst throws one blob out of each, in that letter's own colour, +// so the handover reads as the logo dispersing rather than as an unrelated flash. +const BURST_CELLS = [ + { key: "s", left: "44%", top: "44%" }, + { key: "u", left: "56%", top: "44%" }, + { key: "c", left: "44%", top: "56%" }, + { key: "h", left: "56%", top: "56%" }, +] as const; + /** * First-load splash: the pillar signature. * @@ -77,30 +87,55 @@ export function HeroSplash({ onEnter, leaving }: { onEnter: () => void; leaving? }; }, [leaving]); + const fade = { transitionDuration: `${FADE_MS}ms`, opacity: fadeOut ? 0 : 1 }; + return (