A dark editorial marketing site for an observability console that doesn’t exist — recreated from a reference design as a maintainable Next.js codebase rather than a static dump, and held to the constraints a real property would be held to.
Meridian is fiction. There is no Meridian company, no customers, and no incidents behind the figures the site displays — the copy, the studies and the dashboards are written content, the way a set is built. What follows describes the engineering, and every number in it was read off the repository.
The starting point was a reference landing page and a set of screenshots. The rule was that none of it could be served as-is: no iframe, no copied bundles, no shipping the original HTML. The page had to be rebuilt from its public presentation as a codebase somebody else could pick up.
| Surface | Count | Notes |
|---|---|---|
| Landing page | 1 | Hero dashboard, seismic tape, pricing, comparison |
| Customer studies | 3 | Basin, Compass, Tideline — written as part of the fiction |
| Blog | 5 | Index plus five prerendered posts |
| Legal | 2 | Terms and privacy, sharing one page shell |
| Components | 25 | All under components/, none deeper than one level |
| CSS | 1,173 lines | Two plain stylesheets, 32 custom properties |
The self-imposed constraint that shaped everything else: three runtime dependencies — Next, React and React DOM. No UI kit, no animation library, no icon package, no webfont. Each of those would have been the easy answer to something on the page, and declining them is most of what makes the build interesting.
The site opens on a short film that hands off to the dashboard when it ends. Almost none of the difficulty was in the playback — it was in the browser’s autoplay rules and in the gap between server-rendered markup and the moment React takes over.
Autoplay eligibility is decided from the markup. Binding muted to React state loses it — the attribute has to be literal in JSX, and sound gets toggled imperatively on the element afterwards.
The first stall guard waited for a playing event. Server-rendered autoPlay starts the video before React attaches the listener, so the event had already fired and the guard killed the film at 4.8 s.
The rewritten guard reads currentTime when its timeout expires. It cannot miss an event it never subscribes to, and it still bails on a genuinely stalled load.
The film dispatches meridian:intro-done and the dashboard boot sequence starts from that, not from a matching setTimeout that would drift out of sync the moment the clip changed length.
Completion writes a sessionStorage key, and a repeat visit takes a fast path that never mounts the film — the dashboard boots in 1.5 s instead of 11.
The second and third steps are the same bug seen twice. An event listener attached in useEffect is attached late — anything the browser did between HTML parse and hydration has already happened, unobserved. Guards that poll state survive that window; guards that wait for an event do not.
A 2.35 MB autoplaying video is a real cost on the first paint of a page whose whole argument is restraint. Six encodes were produced — H.264 across CRF 24 to 30, and VP9 at two quality levels — and each was scored against the source rather than eyeballed.
| Encode | Size | Verdict |
|---|---|---|
| Source | 2.35 MB | As delivered |
| H.264 · CRF 28 | 848 KB | Shipped — SSIM 0.985 against source |
| VP9 | — | Smaller at equal quality, but a second asset to serve |
SSIM was the tiebreak because the alternative — deciding by eye at 1× speed — reliably prefers the largest file. A structural similarity of 0.985 says the difference between the shipped encode and the source is compression noise, not lost detail, and a pixel-difference pass confirmed the two were still frame-aligned.
The page counts figures up, grows a bar chart on load, boots a dashboard in sequence and drives a seismograph from scroll position. All of it has a documented still state.
Five components read a shared useReducedMotion hook and return their static arrangement when it is set — the count-up figures, the growing bar chart, the scroll-driven seismic tape, the dispatch list and the intro film.
Five prefers-reduced-motion blocks across the two stylesheets cover the animation the hook can’t reach, including the scanning gradient on the footer credit.
The seismic section maps scroll progress onto a drift figure and a horizontal offset, coalescing every scroll and resize into a single requestAnimationFrame rather than writing style on each event.
Reveals, count-ups, the boot sequence and the seismograph are hand-rolled against the platform. The dependency list stayed at three because nothing here needed a fourth.
| Layer | Choice | Why |
|---|---|---|
| Framework | Next.js 16 · React 19 | App Router; every page prerenders |
| Language | TypeScript, strict | No any, and no suppressed errors |
| Styling | Plain CSS | Custom properties as tokens — no framework |
| Type | System stacks | No webfont request on any route |
| Graphics | Inline SVG | Charts and marks authored in the markup |
| Hosting | Vercel | Push to main deploys meridian.intlo.com |
Every page prerenders and the site makes no request to a third party at runtime — no font host, no analytics beacon, no embed. A visitor loading Meridian talks to exactly one origin.