ashik.co

This site. Specified before it was built, audited after it shipped, and carrying two rules it broke on purpose. Everything below is checkable against the repository - or against the page you are standing in.

01 - THE BRIEF

The brief

the job
CREATIVE-BRIEF §8.1 lists four objectives in priority order. The third is the reason this page exists: the site’s own craft has to demonstrate product taste, because the site is exhibit A until the products ship.
the risk it names
§0.1, written before anything was designed: with projects and writing deferred, the site launches as claims and no evidence - “the exact style without substance you said you fear.” The structural answer was to make /now load-bearing: a dated ledger rather than a status page, and an information architecture that could absorb work and writing later without a redesign.
the test
Every design and copy decision had to survive one question (§1.5): does this make a stranger feel they’ve met a warm, sharp person who is obviously going somewhere?
the anti-goals
§1.4 rules out being a portfolio, a resume, or a hire-me billboard, and bans the vocabulary - no “revolutionizing”, no “passionate about leveraging”. The confidence is supposed to come from specificity. This page is held to that too.

02 - BEFORE CODE

Before code

what existed first
Seven specification documents - creative brief, inspiration analysis, design language, design spec, design system, content, experience - totalling 1,889 lines. Each one states its own boundary on its second line. The brief’s reads “no code until this is agreed”; the experience document’s reads, simply, “no code.”
what the repo can’t prove
The order. Every document and the entire first implementation landed in the same initial commit, so git cannot testify that one preceded the other. What it can show is that the code keeps citing back: 66 § references across 28 of the 38 files in src/. Section.astro carries DESIGN-SYSTEM §11.3, astro.config.mjs carries CREATIVE-BRIEF §8.6, Mark.astro carries DESIGN-SPEC §2. A comment that names a section is a claim someone can go and check.
what it bought
A decision procedure instead of a mood board. DESIGN-LANGUAGE ends with a five-part coherence test every screen has to pass - one-sentence, verb, light, truth, lineage. The truth test (“is every mono string real data someone could check?”) is why /work has fields missing rather than fields padded, and why this site has no social links: CONTENT.md marks them unconfirmed, so they are absent.
and where the spec was wrong
The README lists five deviations from the design documents. Three are accessibility corrections to values the spec itself supplied - Night’s muted text was specced at #857A6E and shipped at #8D8276, because measuring it against the spec’s own 4.5:1 floor showed the spec had missed it. Writing the rules down first is what made them checkable enough to fail.

03 - BUILDING WITH AI

Building with AI

the record
Seven of the eleven commits in this repository carry a Co-Authored-By trailer naming a Claude model. The commit bodies are the working log: what was reworked, what was added, what was deleted, and what was verified.
what the agents did
Bounded, specified work. The motion pass replaced transition-per-pointermove magnetism and card tilt with rAF velocity springs, added the hero arrival choreography, and deleted drift-breathe. The production-audit pass fixed a dead card tilt, gave the mobile menu real dialog semantics, and hid controls that could not act without JS. The Apex renderer - about 3,100 lines of hand-rolled WebGL across ten modules, zero runtime dependencies - was written this way.
what stayed human
The scope. The audit fix briefs are explicit about what an agent may not touch: “Do not: remove Apex, remove the road, remove the car, flatten the scene, alter typography, change page copy.” The same brief forbids the cheap fix - “do not simply reduce the entire scene opacity globally” - and the code records that the cheap fix was tried first and rejected for a measured one.
what it does not prove
That any of this is faster. There is no before-and-after in the repo, so no speed claim is made here - and the specification documents never mention AI at all. The only AI they plan for is image generation for a hero illustration, and the README records that that path was dropped in favour of the spec’s own fallback.

04 - THE VISUAL SYSTEM

The visual system

the mark
A·D·C drawn as one continuous solid unit: the A’s right stem doubles as the D’s spine, and the D’s bowl flows into the C through a shared cusp. One evenodd path, filled with currentColor and never a gradient, so it inherits both themes’ ink. The A’s open lower counter is the spark - an invisible cutout at rest, ember when lit.
two hours, not two skins
Night and Daylight are two palettes designed independently - same person, different hour - switched by a data-theme attribute over roughly 200 lines of tokens. Daylight is not a filter over Night: it re-derives its own ember for label contrast, and the WebGL scene ships a second palette so the same road and the same car are re-lit rather than hidden.
typography
Two self-hosted variable families, Latin-subset - General Sans for display and text, JetBrains Mono for the logbook voice - and a third family is a defect, not a preference. Fallbacks are metrics-tuned with size-adjust and ascent/descent overrides so font-display: swap causes zero layout shift. Display sits at 620–680 weight rather than 800 on an explicit argument: at 800 the site collapses into the template look half the reference sites share, so the drama has to come from light contrast instead of mass.
the mono only tells the truth
The monospace face is reserved for real data - dates, ledger rows, chapter markers, footer telemetry. The rule, written down before any of it existed: if a mono string could be replaced by lorem ipsum without anyone noticing, it shouldn’t exist. This site has no social links for the same reason - the content document marked them unconfirmed, so they are absent rather than placeheld.
motion has verbs
Five named verbs - Ignite, Reveal, Settle, Drift, Dawn - and a screen only ships if every moving thing is one of them. Easings and durations are tokens, not per-component guesses. Light has a rule too: a cinematographer has to be able to point at the source, and the source is always off-frame - never depicted, no rings, no suns, no lens flares.
the icons
Three glyphs, stroke-only, 1.5px on a 24px grid. The spark triangle is the one filled shape permitted, because it echoes the A’s counter in the mark. There are no photographs and no illustrations: a grep for an image tag across the built site returns zero matches, and the entire pictorial inventory is the mark, those three glyphs, and the scene.

05 - THE CONSTRAINT THAT BROKE

The constraint that broke

what was written
Four documents say it. DESIGN-LANGUAGE §10: “v1 ships zero WebGL, zero 3D runtime - a conviction, not a cutback.” The brief’s performance budget, under a heading that reads non-negotiable, lists “no WebGL/heavy animation runtime in v1.” The argument was that the effect people call 3D is really light and depth, and light costs about 0KB. The same sections list what is never allowed, and “camera-scrubbed scenes” is on that list.
the escape hatch
One WebGL moment was permitted, under four conditions locked in advance: it expresses ignition better than CSS can, it lazy-loads behind an interaction, desktop-only, after LCP, it has a full non-WebGL fallback with meaning-parity, and it survives the question of whether anyone would remember ours or just Bruno Simon’s. “If in doubt - and it is in doubt - the answer stays no.”
what shipped instead
Apex: a full-page, always-on WebGL scene behind the home page. A car drives a road generated from the page’s own six beats, so the starting grid, the gantries, the tunnel, the corner, the climb and the summit land under their matching sections. It is a camera-scrubbed scene, which §10 lists under Never.
the honest count
One of the four holds completely: the non-WebGL fallback is real, meaning-parity included. A second holds in one third - initialisation is deferred to idle, so it never competes with the hero text for first paint - and fails the other two thirds, because it is neither gated behind an interaction nor desktop-only; it runs on phones. The remaining two are not arguable in its favour, and the scene is the exact shape the same section forbids by name.
the reasoning on record
The renderer’s own header calls it “a deliberate departure from DESIGN-LANGUAGE §10” and says it “spends that one earned-moment exception”, then lists what it keeps. That is the whole justification in the repository. What changed between the brief and the build is not written down anywhere, so it is not reconstructed here. The decision is documented; the deliberation is not.
it was not the only one
The motion doctrine reads “spring behavior: none - warm physics replaces spring physics, elements ignite quickly and settle gently, like heat, not like a spring factory.” The motion pass then added an --ease-spring token whose curve overshoots past 1, and rAF velocity springs with soft overshoot on release. Both rules were broken in the same direction: something felt better in the hand than it read on the page.
the README still says otherwise
One line of the README reads “no animation library, no WebGL, no tracker, no cookie.” Half of that is now false - the scene is WebGL and the site loads Vercel’s analytics script - and the line above it still claims the homepage ships about 6 KB of JavaScript, which was true before Apex. Stale documentation is worth naming on a page whose whole argument is that claims should be checkable.

06 - WHAT SHIPPED

What shipped

the build
Static Astro, seven routes, no UI framework. All CSS is inlined at build time so first paint needs zero render-blocking stylesheet requests, and trailing-slash URLs are the single canonical form, enforced in the config, the sitemap, the internal links and a redirect at the edge.
weight, measured
A cold homepage visit is 104 kB over nine requests: 20 kB of JavaScript gzipped across four chunks, 4.8–6.5 kB of inlined CSS, and two subset variable fonts at 26 kB and 44 kB. No images at all. The budget written before the build said JavaScript under 100 kB compressed on any page - the clause about WebGL in the same paragraph was broken, the number was not.
it degrades three ways, and this was tested
With WebGL forced to return a null context before any page script ran: the CSS night stands in permanently, zero uncaught exceptions, and 1,579 characters of body text still readable. With script execution disabled: content renders at full opacity rather than waiting on JavaScript, the hamburger gives way to a plain visible nav, and the theme toggle hides rather than offering a dead control. Under prefers-reduced-motion the entrance choreography does not run at all and the scene renders real still frames that track scroll instead of switching off.
keyboard and contrast
Twenty tab stops on the home page in DOM order, every one showing a 2px ember focus ring; the mobile menu moves focus to its close button, wraps at both ends, marks the background inert, and returns focus to the trigger on Escape. Fifteen text-on-background pairs sampled from computed style in both themes all clear their threshold - including two the code’s own comments had flagged as previously failing.
frames are rationed
The renderer draws at full rate only while something is still converging, then drops to 12 fps ambient. The number is argued, not picked: the fastest motion in the shaders is a tunnel fixture pulse at 2.2 rad/s, which 12 fps still samples about 34 times a cycle. Any input returns to full rate on the next animation frame, so the cadence never adds latency to scroll.
what it refuses to claim
When the Now page could not honestly say it had been updated that month, the fix was to delete the words “updated monthly” rather than invent an entry. Cumulative layout shift measures 0.0000 on every page. The Lighthouse row in the README - four hundreds - was measured on the build before the scene existed and has not been re-run since; it is not evidence for the site as it stands today.
the boring parts
HSTS with preload, nosniff, a strict referrer policy, camera, microphone and geolocation disabled, immutable year-long caching on fonts, OG images and hashed assets, and one canonical URL form enforced in four places at once - the config, the sitemap, every internal link, and a redirect at the edge. No forms, no external links, no third-party embeds.

07 - WHAT WAS CUT

What was cut

the ledger of removals
The experience document ends with a complexity ledger: fifteen rows of things considered and dropped, each with its reason, kept - in its own words - “so nobody improves these back in later.” Deleting an idea is cheap; recording why, so it stays deleted, is the part that takes discipline.
sound
Cut in the brief and still absent. The motion pass notes the omission was deliberate: unsolicited audio would undercut the register the rest of the site is working to hold.
the illustrated hero
The design spec called for a stylised figure defined by light rather than outline, produced by art-directed image generation, curated hard - generate twenty to forty, shortlist five, ship one. It was not shipped. The README records the deviation and the reason: the abstract mark-based hero was the spec’s own fallback, and it was the better of the two.
a loop nobody could see
drift-breathe was a nine-second infinite opacity animation on a full-viewport layer. It was imperceptible and it cost the compositor permanently. It was deleted.

08 - WHAT BROKE

What broke

the drive crossed the copy
The scene’s bright line passes rendered straight through readable text. The first attempt dimmed the whole scene to 60% opacity, which made the picture murky without ever getting the road paint below body-text contrast. The repair measures the real DOM instead: each beat reports how far right its own copy actually reaches, and the road paint and point clouds are held back inside that column, eased between beats. Opaque geometry is untouched, so the road still runs behind the words.
the car came apart
Shards floated around the nose and the flank showed through. The wheels sat at y=0.3 with radius 0.33, so their crowns reached 0.63, while the side panel’s arch line was a flat 0.7 - above the wedge itself at the front axle, 0.52. Every front panel quad had its bottom above its top: self-intersecting bowties. The repair was proportional rather than cosmetic - the wheel sized to fit an arch, the arch an arc that can never rise above the fender, and a fender crest added over the front axle so an arch exists there at all.
the headlights raked the frame
Hard yellow lines cut across the whole picture at the summit. The headlight cone threw 24 units ahead of the nose, and at that beat the camera stands about 8 units in front of the car - inside the beam, with 16 units of it behind the lens, where near-plane clipping drew the cone’s edges as straight lines. The throw is now 14 units and the shader fades the beam as it approaches the lens, so standing in front of the car is silent instead of catastrophic.
the tilt was dead
The fuel-card tilt did nothing for a while. The Reveal verb set transform: none as a global rule, which fought the card’s own scoped tilt transform. Reveal now animates translate instead, so a component can keep owning its transform. The detail that makes it more than a CSS bug: that tilt was the only 3D object the design system permitted, so a global rule had silently deleted the one thing the rules allowed.
how the scene bugs were caught
Not by looking. A harness drives real headless Chrome through every viewport, theme and beat, records the bounding boxes of the readable copy, hides the copy so only the scene remains, screenshots, and measures how bright the picture is exactly where the words were. Judging “is this legible” by eye is how the first version passed.

09 - WHAT IS STILL WRONG

What is still wrong

reduced motion is honoured, then isn’t
The scene draws still frames as promised, but a frame counter over a 1.2-second idle window returns 73 frames whether the reduce preference is set or not: the setting changes what gets drawn, not how often the engine wakes. The page is also still ten viewport-heights long either way. The documents promised “a calmer site, not a lesser one”, and what a reduced-motion visitor currently gets is the same ten screens with the transitions taken out.
a contrast claim that was true of the wrong thing
A code comment states that the ember accent passes 3:1 in both themes. Sampled against the flat background token, it does. Sampled against the actual rendered composite - the headline over the live scene in Daylight - four points measure between 2.90:1 and 3.47:1, averaging 3.24, with one below the floor. Night measures 6.58 to 6.90. Because the backdrop moves, the honest statement is “sometimes below”, not “always below” - which is still a failure, and still not what the comment says. The claim was true of a colour, not of the picture.
the reveals cost the metric they were meant to serve
Every revealed element starts at zero opacity, and a browser will not nominate a zero-opacity element as the largest contentful paint. The choreography therefore inflates the number it is measured by, by 300 to 700 milliseconds. It is the only performance budget line the site fails, and it fails for an animation-timing reason rather than a weight one.
the ratio problem
Nine audits were run against the shipped build, several of them written from the rendered site before any planning document was opened. The scores they returned: 83 on performance, 85 on accessibility, 91 on reliability - and 4.8 out of 10 on visual design, 5.1 on copy, 3.1 on conversion. The sharpest number is not a score. The site carries roughly 1,000 words of prose against some 24,000 words of design documentation - about 23 to 1, specification to artefact - and asks for 9,019 pixels of scrolling to deliver around 350 of them on the home page. The craft is real. There is not yet enough on the other side of it.

back to the ledger

take it from the top