A single-column personal site over a hand-coded illustration of an Indian royal garden — sandstone palace, domed baradari pavilion, cypress and palm, a balustrade, and a lily pond. Day and night palettes, drifting petals, a reshuffleable scene, and a live PST clock.
npm run dev # http://localhost:3000
npm run build # all routes prerender as staticEverything you'd normally want to change lives in content/site.ts — your name, the intro paragraphs, the nav items, the project and writing lists, and your links. The components read from it, so you shouldn't need to touch any JSX to fill in real copy.
Intro paragraphs support inline links via {token} placeholders, each declared as "href|label".
Links starting with / render as client-side navigations, mailto: and tel: open in place, and
everything else opens in a new tab.
lib/scene.ts generates the scene geometry and components/Scene.tsx draws it. Two constraints hold the design together, and both are easy to break by accident:
Nothing may sit behind the text. The text column reaches further down the page than you'd guess — the nav row and clock land near the middle of the viewport. So both buildings are pushed into the outer thirds, tall planting is confined to the outer zones, and the centre of the horizon is left as sky, hills, and low bushes. A soft scrim behind the column does the rest. Contrast was verified by computation rather than by eye; the tightest measured ratio is 4.78:1 for day inline links against the worst reachable background, with body text at 7.9:1 or better everywhere.
Nothing may call Math.random() during render. Geometry comes from a seeded PRNG, and
DEFAULT_SEED drives both the server render and the first client render so the markup matches.
The regenerate button changes the seed only after mount. The same concern is why
components/Clock.tsx uses useSyncExternalStore with an empty server
snapshot, and why the theme toggle picks its icon in CSS rather than from React state.
Colors are CSS custom properties in app/globals.css, so the night theme is a
recolor of identical geometry. Petals are disabled under prefers-reduced-motion.
app/ holds one route per nav item. The nav row and clock live in the root layout rather than in
each page, so no page can ship without them.
vercel deploy # preview
vercel deploy --prod # production