Every entry is a cbv: commit, GitHub push, vercel --prod on
anirudhux.vercel.app. The "cb" is historical from Codeberg; the ritual name
stayed on the migration to GitHub, 2026-07-30. Newest first. Each entry is a
checkpoint: the commit is the rollback target in git, the deploy in Vercel's
history.
The contract, from 2026-07-17: the entry gets written during the cbv itself.
Gates pass, then deploy, then the entry gets the deploy URL, then the commit
takes the entry with it. Nothing ships without a line here. The seed below
(2026-07-09 to 17) was distilled from the commit history; pre-17 deploy URLs
are not recorded (Vercel's dashboard maps them by commit metadata).
An external agent-readiness report brought five findings; two were real.
The homepage now ships a JSON-LD Person block (name, role, employer,
sameAs to LinkedIn, GitHub and X; facts mirror content.ts), and a real
404 page with the site chrome points agents at /sitemap.xml and
/llms.txt. The status code was already a true 404.
llms.txt gains a "When to use this" section between Best fit for and
Summary, generated with the rest of the file.
Dismissed with evidence: the no-JavaScript check (the export already
serves the H1 and ~3,460 characters of text in raw HTML) and the Vary
header advice (the site does no Accept negotiation).
2026-08-18 · Dialog focus seats on the panel, not the close button
showModal() spec-focuses a dialog's first focusable, the close button,
and Safari paints its focus ring on that: every dialog opened with a
highlighted ✕ (my catch, on Hire Me). All four dialogs (Hire Me, Contact,
Tools, Video Overview) now seat the opening focus on their panel
(tabIndex −1, focused after showModal, no outline for it). Keyboard flow
is unchanged: focus starts inside the dialog, the first Tab reaches the
close button, Escape still closes.
A GitHub contributions section in the Hire Me dialog, between
Testimonials and Tools: the pulse-embed heatmap (one iframe per theme,
swapped by data-theme like the hero stills), a credit line under it
("Visualization in Pulse by Sooraj Nair.", both names linked), and a
GitHub entry in the dialog's index. /hire-me#github lands on it.
The smoke gate blocks third-party requests by design, so the gate sees
the section without the pixels; the heatmap comes from
pulse-embed.vercel.app at view time.
2026-08-11 · The video doors close while the clips get replaced
The homepage "Overview" CTA and every case page's "Summary" CTA (index
door and floating pill) are hidden behind one flag,
VIDEO_OVERVIEW_DOORS_ENABLED in lib/curation.ts. The Sarvam-voiced clips
are being replaced with my own narration; the dialog, reel-data, and the
VIDEOS curation stay intact, so re-enabling is one line.
2026-08-11 · UPI International's core journey, re-cut and cache-proof
I re-cut all four Core User Journey videos (light/dark × desktop/mobile,
~37–39s from 45s). They ship on new -v2 Blob pathnames: the store
serves with a 30-day max-age, so an in-place overwrite never reaches a
browser that cached the old file. The lesson is a comment in
project-pages.ts now: a re-cut takes a versioned pathname, always.
Origin copies updated in private-refs/sources/blob-archive/.
I had every entry in this file rewritten, masthead to the 2026-07-09
seed: em-dashes out, bold fragment labels out, the pivots rephrased,
the ledger speaking in first person. Every fact, deploy URL and
measurement kept. /changelog serves the rewrite now.
2026-08-11 · Same overlay on the hero as on every tile
The lead card had its own mesh and scrim rules, tuned for Constella's dark
graph still; deleted them. The hero now uses the same mesh, scrim, and
hover as every other tile. UPI International is the reference.
2026-08-11 · The hero shows UPI Lite's own screens, uncut
HERO_STILL still pointed at Constella's images after the hero change; it
now points at UPI Lite's. The coupling with HIGHLIGHT_HERO is documented
in lib/curation.ts.
New hero image: ftux-hero.webp, four screens cut from the ftux strip and
padded to 1880×1226 to match the hero box's aspect. Nothing gets cropped.
Deleted the scale(1.45) zoom on .showcase-card-poster; it existed to crop
Constella's app chrome and clipped every other hero's headroom.
Highlights order from the bench: UPI Lite · UPI International · Constella ·
Contraire · GitLab Talk · Transform Design · Shape of portfolios.
I re-curated Highlights from the bench: UPI Lite leads the feed and takes
the hero banner; Constella moves to second.
The Video Overview opens on UPI Lite now. The rail order: UPI Lite ·
UPI International · Constella · ExpressBuy · Contraire · PhonePe Cart ·
Shape of portfolios · Design System.
2026-08-10 · The case-page summary keeps to its own clip
The Summary door on a case page opens a single-clip dialog now: no clip
rail, titled "Summary". A reader on UPI Lite gets UPI Lite instead of
seven doors out of the page. The player spans the full width in this mode.
The homepage door is unchanged: "Video Overview", the full rail, browsing
across all eight clips. One shared dialog, one flag
(openVideoOverview(slug, { single: true })) set by the case doors.
2026-08-07 · The dark door dims, the index draws its arrow
In dark mode the Video Overview door drops the white fill for the petrol
wash (24% mix); the secondary pill no longer outshines the primary Contact
below it. Light mode keeps the white ground as specced.
Index headings render text arrows as the drawn ArrowRight glyph (Cart's
"User stories → structure"): one arrow grammar with the Read More cards.
2026-08-07 · Summary doors on every clip page, eight indexes cleaned
Every case page with a clip has a Summary door ("▶ Summary · m:ss"). On
desktop it goes under the sticky section index: the rail stops at the last
section, the door follows a 56 step below at index-size type. On tablet
and phone it is a centered compact floating pill that hides past the last
row of text. Family wash from --tile via the NDA-chip recipe; it opens the
Video Overview on that page's own clip. Clip-less pages get no door;
membership is decided at build from VIDEOS plus the reel index.
Six indexes restructured to my shapes (Constella, Cart, ExpressBuy, Shape
of Portfolios, Contraire, plus renames): folded topics as sub-heads and
every figure key following its section. Design System, UPI Lite and UPI
International signed off unchanged.
The reel timelines now hold the combined audio file per combo;
Constella's retired four-voice files are gone from reel-data.
The store is back (the Pro upgrade lifted the suspension) and the 96
combined audio files are uploaded. The case-page walkthroughs healed with
no deploy since their URLs never changed.
The kill switch is deleted. Sound is a client decision, per clip: the
viewer probes the combo audio before mounting and plays captioned silent
video when the store fails to answer. An outage degrades by itself and
recovery is automatic; no flag, no deploy in the loop.
Verified on production: Constella plays with voice from the store, and a
clip switch stays paused per the no-auto-play rule.
2026-08-07 · Reel sound off while the Blob store is suspended
The audio store hit the free tier's operations cap (2,952 uploaded files
against 2,000 included operations) and Vercel suspended it. Uncached reads
403 ("Your store is blocked"), so clips went silent unevenly.
Kill switch until the account gets sorted: REEL_SOUND_ENABLED = false in
lib/reels.ts removes every audio element from the composition. The reels
play as captioned silent videos and the site sends the store nothing.
Flip the constant and cbv to restore sound once the store resumes.
2026-08-06 · The Video Overview: eight narrated reels in six languages
A new door on the Highlights foot ("▶ Video Overview · 23 min", secondary
pill stacked above Contact, both column-wide) opens a two-panel dialog:
the live player on the left with the project name and its positioning
copy, the eight clips as chapters on the right. The structure mirrors the
Hire Me dialog; desktop first, the mobile ingress arrives with the mobile
design.
Each clip speaks six languages in two voices (Sunny default), with speed
control. Timing is measured from the real audio per sentence, so a
language or voice switch stays on the same sentence. Audio streams from
Vercel Blob (2,952 files, 177 MB); the repo holds only timelines and
manifests, and the player loads lazily so the homepage bundle stayed
139 kB.
This dialog is the one exception to the site-wide force-mute: narrated
clips play with sound at the visitor's own play press, never
automatically. Closing the dialog stops the voice on the synchronous
close path.
Curation joins the bench: a VIDEOS set and order in lib/curation.ts
drives the rail (first = opener), editable on /bench like HIGHLIGHTS.
2026-08-05 · llms.txt names the page as well as the product
Agents were 404ing on guessed URLs. Each heading had a title and the
external product link only, never the page on this site, so an agent that
wanted the case study slugified the title: "Anatomy of a Lie" became
/ai/anatomy-of-a-lie and 404'd, because that project's slug is
null-detector. I watched it happen live. No redirect was involved; the
URL was invented because nothing published the real one.
Every heading now leads with the canonical page, then the live product
where one exists. The sitemap had the right paths all along but shares no
key with llms.txt, so it could never close the gap.
The same trap covered two other title/slug mismatches: UCI: Counterfeit
Study (uci-certification) and Nutella 400g (nutella-400g).
Verified after the build: all 14 canonical URLs in the generated file
return 200.
2026-08-05 · Fluid sizes become tokens, and a gate that keeps them that way
The 30 hand-tuned clamp() sizes are now 21 named tokens declared in one
block with their px range at 390 and 1440 written beside each. They had
accumulated across 19 commits from 2 to 26 July, each authored for one
element and never compared with the rest. Below roughly 900px nearly every
curve rests on its floor, so phone and tablet were already fixed values
and the fluid behaviour is a desktop concern; that is visible in one place
now.
Removed 21 dead rules. Nine classes no component emits any more:
sib-name, sib-pill, sib-cta, sib-availability, mosaic-vnav-item,
mosaic-card--feature, route-heading, listing-title,
bento-loader-count. Checked against the markup rather than against
whether they happened to render in a sample.
The smoke suite gains a type gate. The build now fails on a raw
font-size or font-weight in globals.css, and on any inline
fontSize/fontWeight in a component. The second case is the one that
mattered: the tile titles had their size inline, which outranks every
stylesheet rule, so the type pass could never reach them and nothing said
so.
Verified against an 84-point baseline across six routes and six widths,
before and after each step: zero differences, twice.
The Nutella thumbnail is a real asset now, recaptured from the report at
1.8:1 rather than held together by a cover-crop rule. It renders the same
height as its neighbours at every width, on every surface.
2026-08-05 · UCI thumbnail, two renames, and the broken research images
New UCI thumbnail from my render, cropped and zoomed 1.33x so the white
above and below the frame tube balances on the left edge (350 against 378,
from 812 against 436). One file feeds every surface, verified at 390, 768
and 1440.
Two rows renamed on every band: UCI: Counterfeit Study and PhonePe:
Consumer Study '22. Both had a separate phoneTitle fork; one title works
at every width now, so the forks are gone and the UCI kernel title in
content.ts agrees with the config again.
research.anirudhux.com/uci images were failing to load. The files were
fine; the path was wrong. /uci serves the page without redirecting to
/uci/, so the browser resolved the relative assets/… against the root
and asked for /assets/trek-stickers.png, which 404s. The five UCI images
and Nutella's jar illustration are root-absolute now and resolve with or
without the trailing slash. Deployed separately to the research project.
2026-08-05 · The switcher moves to the top of the phone
The tab switcher docks under the wordmark bar. Readers called the old
floating pill jittery, and the measurements agreed: it dodged the footer,
so its position was a function of page length. At the end of scroll it
measured 16px from the bottom on Highlights, 86 on Work, 281 on Community
and 288 on AI, and on AI it started moving before the first screen was
read. It now holds one position on every view at every scroll offset,
gets out of the way while you scroll down, and returns the moment you
scroll up.
Swipe changes tabs. Left goes forward, right goes back, through the same
path a tap takes. Gestures starting at the left edge are ignored (that is
Safari's back swipe), as are diagonals and anything under 60px.
One blanket 24px gap governs the top of the phone page: under the
wordmark bar, under the switcher, and under the last card. The 84px the
stage reserved for the pill that used to float there is gone.
One card type across all four tabs: 18/800 titles, 14/400 descriptions,
which is what tablet already ran. The tile titles could never be restyled
before this: their size was an inline style, and inline outranks every
rule, so the type pass could never reach them. They read from CSS now.
AI joins the phone card grammar: Anatomy of a Lie skips the band, and the
three that remain stack and centre with the whole device shown, the same
numbers the Highlights feed uses for the same cards.
The hover tint is gone on phone. It fired on tap and stayed, so the last
row you touched kept looking selected.
Centred copy balances now: the Highlights ledes were -webkit-box for a
clamp, which blocks balancing outright; the clamp came off since every
lede fits in three lines.
Cleanup: 13 duplicate declarations resolved at source, three override
rules deleted, and the dead side-by-side row flex props removed.
2026-08-05 · A type scale in rem, and the dead wrapper it uncovered
Type is tokens now. Eleven size tokens and five weight tokens in :root,
baseline 16px = 1rem, and all 194 declarations read from them. Sizes scale
with the reader's own browser setting instead of staying pinned to px.
The fixed tier went from 27 loose values to 11. Six sizes crowded inside
15 to 17 became one: prose, hire copy, the lightbox and the blockquote
all read 16. The p strong bump is gone, so emphasis comes from weight
rather than a 1.1px size change that looked like a font-loading glitch.
Eight sizes inside 10.5 to 14.5 became five steps. The sealed-row label,
which spanned 6, 7.5 and 9px across three rules, is one token at 10.
Weights 650 and 750 are retired: five declarations in the whole file,
used nowhere else in the system. The case section title reads 700 now.
The case-study rhythm is one scale: 8 for a sub-head to its own prose, 12
for the body run, 14 for a title to what it heads, 28 for the block break
around figures and quotes, 56 between sections. Figures were 26 above and
34 below, which put every image off-centre in its own gap.
Removed 15 dead rules. .mosaic-static stopped existing when Highlights2
renamed the wrapper to hl2-static, so those rules had been sizing and
positioning an element that is on no page. The company pills read a flat
13px at every width because of it, instead of scaling from 11.5 to 19 as
the rule intended. Deleting changed nothing on screen; verified identical
at 390, 760 and 1440 across all four tabs.
The deck embed is gone from the GitLab Talk study. A visitor was waiting
on pitch.com for the page's load event, measured around 80s. The page now
loads in under a second.
2026-08-05 · The phone footer is cut to a sign-off
Two sitemap columns leave the phone band. Explore repeated the switch
pill, which is fixed on screen at all times, and Highlights listed
projects the reader had just scrolled past. Testimonials and Tools are
dialogs the Hire me surface already holds, and Hire me itself is in the
nav, so nothing became unreachable.
What remains: the brand stack, Resume, Contact and LinkedIn on one
horizontal row, the (c) line and the theme switch. The three blocks now
step by an even 30px; the old 22px brand margin was tuned for a
two-column sitemap and left a 50px hole above the links. Tablet and
desktop keep the full footer.
The smoke gate is hermetic now. It had started failing on
/community/gitlab-talk: the page has a pitch.com deck iframe and the
parent's load event waits on it, measured around 80s in a real browser
against a 30s gate. Third-party requests are blocked at the context, so
the gate tests our export rather than a vendor's uptime. The static
server also answers Range requests properly now.
Rows stack and centre on the phone. The side-by-side plate read dated, so
every Work row now leads with its thumbnail on one 132px plate, cover
cropped, with the title and the line under it centred beneath. One height
for every thumb: the source aspect no longer leaks into the layout, which
is what made a short Nutella beside a tall consumer study look ragged.
Radii are a system thing: no sharp media edges anywhere on the four
homepage addresses, Highlights rows included.
Sealed rows skip the band. The phone gets most of the incoming traffic
and a locked plate spends that attention on nothing; they still render on
tablet and desktop.
Phone titles include the company (PhonePe UPI Lite, Ennoventure Design
System, and so on), so the group headers retire on this band; the
research stack leads with the consumer study. Wider bands keep their
headers, names and order.
Community takes the same grammar: Transform Design, GitLab Talk and
Various Meetups stack and centre; PRSOS and DARD skip the band. The AI
tab keeps its left-bleed device crop, deliberately: flat artefacts take a
cover crop, device shots do not.
Separators and the fused card's closing edge now follow visual order.
Hidden and reordered siblings still count in the DOM, so :first-child
and :last-child kept matching rows that render second or not at all.
2026-08-04 · Work tab recomposed around the Design System
Columns re-sorted by family (my call, on desktop-lab): Design System is
an Ennoventure thing, so it moved right under the Ennoventure umbrella
and the cross-company research stack moved left under PhonePe. The config
comment had claimed this arrangement since July; the grid matches it now.
Design System is the flagship tile: top-right at full height with its
diagram visible at rest (the Work tab shows evidence without a hover, so
the AI-tab reveal grammar does not apply here), centered at 360px, corner
icon retired. Its blurb absorbed the Ennoventure credential line.
Research Reports renamed from Design Research Reports (it names the shelf
it links to), title-to-thumbnail gap halved, and the consumer study
returns at tablet; every band now shows all three reports.
The NDA label retires everywhere, chip and plate stamp both: the lock
says it, and the surrounding context now reads sealed-on-purpose.
Tablet and phone follow: slug tiles learned tabletLast so the flagship
takes the big slot in the zigzag, and the phone row shows the diagram
plate.
The NDA comes off: /work/en-design-system is live, reworked section by
section with me holding the pen (seven sections, h3 sub-heads for the
governance split and the conclusion's Takeaway/Performance/Impact). The
standalone tile and the phone row both link in; the NDA-door machinery
emptied.
Hero motion: my animated governance diagram as the walkthrough banner
(2.4x, 11.8s loop, 353KB in-repo, canvas lifted clear of the player
controls), poster still cut from frame one.
Five exhibits under their sections (typography, palette, spacing, radii
via the new h3 figure key, interaction states), captionless by my rule;
caption became optional through the figure pipeline.
The Tools card returns on a Work page as my named exception: Claude,
Claude Code, GitHub. The positioning line seats the under-title lede,
consistent with Constella. og-still cut; llms.txt picks the entry up
automatically. PARKED entry closed.
2026-08-03 · Design System study drafted, then benched
The Design System case study got its first full drafting session (my
dictation, a deep KB, three cuts, a dev page rendered through the real
case system for review) and was then parked on my call: the narration is
not my voice yet, and the NDA line is undrawn. Drafts stay whole in
for cowork/; the dev wiring is torn down; the tile keeps its NDA;
production shows no trace (verified 404).
PARKED gains the entry (lib/curation.ts) with the working-set map and
the open questions for the revisit.
The register skill gains the duality-header ban (my call-out, encoded
with the fix pattern).
2026-08-03 · Research link previews actually deliver
Deploy: research-bg4u2rfha (research.anirudhux.com; folio untouched. All
fix files are in gitignored private-refs; this entry logs the satellite ship)
The PhonePe report never had working previews: its og tags were inside
the bundler template JSON that only client JS renders, invisible to every
scraper. The transform now injects the meta block into the wrapper head
(with an assert), verified with a scraper-agent fetch on all three pages.
Cards re-rendered at native 1200×630 (they were 2×, 0.8–1.1MB, over
WhatsApp's ~600KB cap; now 280–375KB) and all three pages declare
og:image width and height for first-paste rendering.
www.research.anirudhux.com attached (my new CNAME plus a production
domain on the project; a raw alias went behind Vercel SSO, and domain
registration is the fix). Verified the unfurl in WhatsApp.
2026-08-03 · Docs and ground-truth sync; the dump pen
llms.txt gains the research satellite: the agent site map now names
research.anirudhux.com with all three reports (verified live, 3 refs).
research.anirudhux.com also serves its own robots.txt now (deploy
research-aaoys3aur).
Docs synced to today's truth: ARCHITECTURE.md documents the satellite
beside the museum wings; FOLIO-KNOWLEDGE-BASE.md gets a dateline note so
its July netlify URLs can't mislead; CLAUDE.md lists all five docs and
the asset map's private-refs rows.
private-refs/dump/ created: the review pen holding everything
chuckable (zips, mined study sources, extracted report originals, the
ten generated_pages sidecars; 15M). Nothing references it; I empty it at
will.
Memory updated alongside (the flagship pipeline reflects the shipped
reports; the OG-preview-unprompted rule encoded).
2026-08-03 · Filesystem reorg: one home per asset type
public/ consolidated: every glyph and logo is now in public/icons/
(the work_assets / projects_assets / community icon sets merged in);
portrait/ and tools/ folded into public/assets/; community_assets
renamed community. Reference sweep across bento-config, BentoTiles
(path helper), ShowcaseFold, HireMeContent, tools.ts; all moved paths
verified 200 in production.
Strays deleted: root ak.jpeg (its processed webp is the served avatar),
four orphan portrait-anim*.webm, the og-cards review PNGs, every
.DS_Store. public/_preview/ is gitignored now.
private-refs/ bucketed: sources/ (capture sources, zips, the ten
generated_pages sidecars), ops/ (uxr-transform.py, og-cards.mjs),
research/ frozen in place; script paths repointed.
The asset map joins CLAUDE.md: one home per type, including the
walkthrough-in-repo vs Figure-clips-on-Blob distinction.
2026-08-03 · Research chrome goes folio-native; phonepe22 becomes the study's face
Research pages use the folio's real chrome now (deploy research-rkmzo2cfr):
a fixed frosted 56px bar at the browser top on all four pages, the footer
as a full-width band flush with the page bottom, columns centered at
desktop. The old in-container header (with its stale tagline) is gone;
the transform script lints it dead.
All three reports get OG cards in the folio anatomy: PhonePe (phonepe22
inset), Nutella and UCI (Work-tab thumbs as insets, my titles "Nutella
counterfeit study" / "UCI counterfeit study" on the cards); og/twitter
meta wired on both new pages.
phonepe22 sweep on the folio: the consumer-study row thumbnail
regenerated from my capture, with scoped left top crop rules in both
row bands (desktop and phone); anchor top-left, crop bottom-right.
The CLAUDE.md satellite section updated to the new chrome contract.
2026-08-03 · Nutella + UCI reports join the research shelf
Both Ennoventure-era studies are now native at
research.anirudhux.com/nutella and /uci: standardized chrome (wordmark
plus Hire me header, four-line brand-stack footer), each in its
artifact's own family colour (Nutella amber, UCI navy), responsive to
390, body copy byte-identical to the source artifacts. Built by two
parallel agents.
The UCI bike diagram fits every desktop width down to 1160 (the whole
SVG scales to its section); on phones it pans horizontally at natural
size, my shortcut. Fixed the source's own 34px clip at 1440 as a side
effect.
Shelf cards wired: all three reports now route inside the subdomain with
real thumbnails (Nutella and UCI reuse the Work-tab thumbs); netlify
links retired from the shelf, the Work tile rows, and content.tslive: fields (llms.txt follows).
Research chrome standardization ships with it: report and shelf footers
are the folio brand stack; the transform script now lints dead chrome
strings; satellite ops documented in CLAUDE.md.
2026-08-03 · Consumer study goes live; case pages join the family worlds
PhonePe Consumer Study 2022 is published at research.anirudhux.com/phonepe
(new research subdomain, its own Vercel project; the shelf at the root
with Nutella and UCI placeholder cards). The Work tile's DRR row sheds
its NDA seal: href out to the report, real thumbnail from the report's
opening view.
Case pages ground in their family colour in both themes via data-group
joining the homepage's world system; the ghost-white fallback survives
only on hire-me.
New skill: .claude/skills/writing-like-anirudh, the register as an
invocable tool (hard rules, caught failure modes, my line patterns).
2026-08-02 · Contact modernized: dots out, form visible, labels retired
Dot grids out: the phone top-of-page fields (Highlights T1 plus tab meta
zones, ≤600 only; desktop and tablet keep theirs) and the Contact
dialogs' dot ground on every platform. Element, spotlight JS, and ~70
lines of CSS deleted at the source.
The Contact section joins the section rhythm in the Hire Me surface:
48px cadence and 24px title gap like every sibling (the dialog variant
had a padding:0 that pulled the title against the divider).
Form fields read as live inputs: white fill (dark: 7% ink lift), 26% ink
border, labels retired; the placeholder is the label proxy (my copy
verbatim: "Enter your work email" · "Where do you work?" · "Purpose of
contact?" · "Give me some context - a line or two about what you have in
your mind."). Screen readers keep sr-only labels.
2026-08-02 · Declutter pass: identity line and tile arrows retired
The lens-bar identity element is killed whole (my call): wave, "Designing
& building since 2014", "Previously at" plus logos, pills, the hidden
Hire Me click, the arrow. It existed as visual anchoring; ≤820 proved the
anchor unnecessary. Switch pill pinned right (flex-end); ~65 lines of
dead CSS and the rotation machinery removed.
Tile and row ↗ arrows retired sitewide (clutter, plus mid-title wrap at
in-between widths): tile heads, bento rows (the href state now renders
nothing), Highlights phone rows. Locks, NDA chips and play stubs stay.
One deliberate survivor: the case hero's live-product ↗. A proper
leaves-the-site language comes later; the row table updated in CLAUDE.md.
Team and Tools cards go transparent on case pages: the family wash
clashed with the page ground; the hairline draws the card now.
Constella: the site's only blockquote folded into the preceding
paragraph as a bold close. ExpressBuy: Ashna Jain reads Product Lead.
2026-08-01 · OG card polish: centered link line, smoke gate, ops docs
Card bottom row: the URL now centers on the title's midline (it was
baseline-hung and read as off in real share sheets; my catch).
Smoke gains an OG-card gate: every leaf case route must ship its PNG
card, its html must point og:image at it, and its still must exist in
public/og-stills/. Negative-tested (a hidden still fails with the exact
filename).
CLAUDE.md: the full link-preview contract (template anatomy, the
one-file step for a new leaf, vercel.json do-not-delete, the UTM pasting
convention); the new-case-study checklist gains the og-still step.
2026-08-01 · Per-page link previews: 13 OG cards from one template
Every leaf case page now shares with its own preview card; the homepage
and the four tab pages keep the default face card. 13 cards: 4 Work
(forest), 4 AI (moss), 5 Community (copper).
One template in code (lib/og-card.tsx, next/og at build time): the
project's still uncropped in the body, the name bottom-left in the
family colour, the spelled-out URL bottom-right, the site's tile wash as
the ground, Manrope throughout. Long names scale down instead of
wrapping (my catch).
Stills are per-slug PNGs in public/og-stills/ (the OG engine reads
png/jpeg, never webp), sourced from each project's existing preview
asset. UPI International also got its phone-row thumbnail fixed to show
both Pay cards whole (contain, this asset only).
vercel.json is new: one header rule forcing image/png on the
extensionless card routes; scrapers reject octet-stream.
Team avatars: my portrait crops tuned; the disc fill grounds the
transparent cutout on both team cards.
2026-08-01 · ExpressBuy dressed: figures, my VNAV, figure-spacing grammar
ExpressBuy gets its three figures (my exports, native res, bare, pan on
phone): the core user journey under "In the PhonePe ecosystem", coupons
under "Whose coupon discounts are they, anyway?", the payment leg
(updated export, opens on Payment Summary) under "Checkout vs payment
UX". Sources in private-refs/expressbuy-source/.
VNAV rewritten to my thirteen titles; the address-refill section cut,
return-to-merchant folded into Respectful data boundaries as a note, the
pilot blurb deleted, the conclusion rewritten Cart-style (and
de-grandiosed on review).
Sitewide figure-spacing grammar (a usability fix, my observation): the
header pulled tight to its figure (8px when adjacent), the caption
fenced to its image with a faint hairline beneath, 34px before body
prose. One .case-figure rule; every study inherits.
Team cards tuned: I lead both rosters as "SPD (Project Lead)" linked to
/hire-me; Amit off ExpressBuy (Roopa and Ashna only); UPI International
adds Atul Kumar and Animesh Bagora; the avatar disc fill grounds my
transparent portrait; face crops recentred; 18px phone row gap.
The ExpressBuy hero positioning rewritten question-first.
2026-08-01 · Team cards, ExpressBuy rewrite, tools bars go AI-only
The Team card joins the case-study pipeline: team: in projectPages
renders a second plate under the tools bar with avatar, linked name,
designation. ExpressBuy (me · Roopa Nadgiri · Ashna Jain) and UPI
International (me · Amit Vernekar · Atul Kumar · Animesh Bagora). I lead
both as Senior Product Designer (Project Lead); my name links to
/hire-me in-tab, the others open LinkedIn in a new tab. Real avatar
webps in assets/profiles; sources in private-refs/.
Tools bars belong to the AI tab exclusively (my call): Cart and
ExpressBuy drop their Figma chips; all four PhonePe studies are
consistent now. Rule encoded in CLAUDE.md.
The ExpressBuy study rewritten end to end: 15 plain VNAV titles, the
theatrical coinages cut, written for a stranger. The hero positioning
rewritten to the question-first construction. "The team" prose section
retired; the card is the single home for credits.
The intro-shape rule enforced: study bodies never open with headerless
paragraphs. ExpressBuy and Cart fixed, all 13 studies audited, the rule
in CLAUDE.md.
Phone polish: team rows breathe (18px gap ≤820); the card stands alone
when no tools bar precedes it.
2026-08-01 · Cart study visual pass: figures, retitles, dark-marker fix
PhonePe Cart gets its figures: 7 figures (10 renders with theme pairs)
across 5 sections. The research grid, IA stickies, core flow, the
user-flow swimlane (light/dark), the RCBP scope map, the coin item
(light/dark), the remove-vs-clear confirmations (light/dark). All render
bare; my exports have their own grounds. Sources in private-refs/.
All 12 VNAV titles rewritten (my dictation): The invisible cart · The
omnipresent cart · The partial payments trap · The cart breaks the
funnel, and eight more. Figure keys renamed in lockstep.
"Three reasons why this did not ship": collective framing; the
braggadocious opener cut.
Marker density 6 → 13 across the study, and a sitewide dark fix: the
.marker wash was light-tuned (16% family, invisible on near-black); dark
now lifts family toward ink at 30%. Every case study's highlights become
visible in dark for the first time.
CLAUDE.md rewritten in two spots: the 'Known open work' bullet reflects
post-ExpressBuy, post-Cart reality (Ennteract 2.0, Printer Toolkit, Vyu
= NDA; PhonePe Consumer Study = note-only). A new 'Week-to-week
additions' section documents the repeat rituals (new case study, copy
update, thumb swap, park, cbv) so a cold-start session can execute
without archaeology.
README.md and docs/ARCHITECTURE.md: origin URL Codeberg → GitHub
(matches post-migration reality). CHANGELOG masthead: the cbv definition
Codeberg → GitHub (the ritual name stays 'cbv').
lib/project-pages.ts: two orphan entries removed (en-platform,
en-printer-toolkit). They had no generated_pages/<slug>/ folder,
were absent from LIVE_CASE_SLUGS, and were never reachable (both NDA
tiles).
Local dev: .claude/settings.local.json (gitignored) had four stale
codeberg curl probes in the Bash allowlist; pruned.
Branches: merged local and remote branches deleted (case-study-lab,
desktop-lab). video-lab kept (parked per lib/curation.ts).
2026-07-31 · Repo hygiene: file aggregation, dead-file removal, disk cleanup
public/ organized by kind. Loose files at public/ and public/assets/
moved into semantic subfolders: public/portrait/ (6 files: 2 stills
plus 4 anims), public/tools/ (capso, cleanshot, rotato),
public/assets/profiles/ (5 team headshots), public/assets/ work_assets/ (adori, ennoventure, phonepe brand pngs), public/icons/
(arrow-right-bold svg). All <img src> refs updated in HireMeContent,
HireTestimonials, ShowcaseFold, lib/tools.ts.
Root de-cluttered. FOLIO-KNOWLEDGE-BASE.md moved to docs/;
folio-tree.html and readiness-board.html (Jul 9 dead scaffolds, no
code references) deleted.
Local disk trim (dev-only, no ship impact): .next/ and
.vercel/output/ nuked (they regenerate on the next build and deploy),
git gc --aggressive ran. Folder size 980 MB → 241 MB.
2026-07-30 · Phone card grout; Community photo crops
The phone card separator between rows now uses the nested-lift formula
(family mixed with ink first, then transparent), theme-adaptive: 8% in
light, 22% in dark. Visible on both grounds; it matches the same family
the outer tile stroke uses at a lower prominence.
Community photos recropped to 16:9 (transformdesign 1600×900,
various-meetups 1200×675; trimmed from the top where the ceiling lights
were). Both now fill the tile-reveal container edge to edge, same as the
GitLab illustration; no more letterboxed padding on 4:3 sources.
2026-07-30 · Migration to GitHub; fused-shelf grout redraw; UCI thumb
The repo migrated Codeberg → GitHub (private, github.com/anirudhux/folio).
Full history rewritten with git filter-repo to swap the commit author
email to the GitHub noreply, so the contribution graph attributes
retroactively across all 322 commits. Backup bundle in ~/Downloads.
The cbv ritual name stays despite the C standing for Codeberg; the
mechanics are unchanged. CLAUDE.md remote URL, pinned identity, and the
cbv definition updated to GitHub.
The footer's Built-with tooltip and the Hire Me colophon: Codeberg →
GitHub. Case-study "Tools used" listings keep Codeberg (accurate to when
those projects shipped; my explicit call).
Fused-shelf grout redrawn: the slab top hairline gone; per-card inset
pseudo-element dividers replace border-based grout. Every divider insets
24px from its own card's edges; no line touches another. PhonePe's 2×2
now has a deliberate 48×48 void at the crossings; the visible plus is
out.
The UCI thumbnail replaced with a carbon-frame anatomy illustration
(960×576, the source stays in private-refs/).
Bench cleanup on 2026-07-30 (dev-only, no deploy): four stale entries
cut; the research-pipeline-as-AI-project park entry added.
Notes forked by surface on the six Ennoventure and DRR rows where the
constraints genuinely differ: the canonical note holds the longer
desktop-hover copy (my originals), phoneNote the compressed phone-card
version. The four PhonePe rows stay single-string; a reader already
knows what UPI Lite, International, Cart and ExpressBuy are.
Phone titles disambiguate the way the desktop umbrella tile does: seven
verbose phoneTitles (Ennteract 2.0: Brand Protection Platform ·
Package Printer's Asset Management System · Vyu: Consumer Web + Native
Apps · The Agentic First Design System · Nutella 400g: Counterfeit Study
· UCI: Cycle Equipment Certification · PhonePe: Consumer Usage Study
2022). Desktop titles unchanged.
The UPI International note breaks after the colon on hover
(white-space: pre-line).
Card body flex: the 60% cap becomes auto; the media plate 40% → 34% so
UCI's phone title fits two lines instead of three.
Design Research Reports gets 'Consumer Study' as its third section; the
PhonePe Consumer Study card wires up its first note.
Desktop Work tiles rebuilt as fused slabs (≥821): rows became card
sections flush inside each tile with a transparent ground, grout plus a
top hairline, no inner boxes. PhonePe runs a 2×2 (UPI Lite ·
International on top, Cart · ExpressBuy below); Ennoventure and the
research tile run equal thirds. A day of iterations (piano keys, Kit
Kat, zigzag) distilled to the simplest form.
Hover swap replaces text-on-card: the strip and title exit, a two-line
factual note centers in the cell; 0.22s intent delay entering, instant
exit. The community tile's reveal mechanism, at card scale.
Dark pass: slab strokes 22 → 45%, hover wash 24%, NDA plate frames 26%;
the light-tuned mixes were invisible on near-black.
"Product & Design Research" renamed "Design Research Reports"; the
consumer study returned as its third card. PhonePe's sub drops the 400M+
repeat (T1 owns the stat). Printer Toolkit's image retired: an image
means openable, everywhere, no exceptions left.
The desktop-lab branch retained for future passes.
2026-07-29 · ExpressBuy case study ships, out of NDA
ExpressBuy is live (/work/expressbuy): PhonePe's checkout for
storefronts it does not own; a 0→1 MVP told in 14 sections. The spine:
the three-rung device ladder, instrument vs mode (the Payment Summary
split, my call), the ownership data boundary, and the address flywheel.
It ends on the honest de-prioritized verdict: the pilot ran, the numbers
stayed opaque, "I don't know" said plainly.
The bento row came out of NDA (lock plus chip → href ↗); the
dummy-merchant CUJ strip stays as its face on the row and the Read-more
rail.
A teammates callout closes the lede: Amit Vernekar, Roopa Nadgiri, Ashna
Jain, linked to LinkedIn.
The knowledge base (generated_pages/expressbuy/) built from the full
flow-by-flow walkthrough; figures deferred to their own sprint.
2026-07-28 · PhonePe Cart case study ships; Work tab recomposed
PhonePe Cart is live (/work/phonepe-cart): the self-initiated
bulk-payment concept, 12 sections ending in the argued no (behaviour,
conversion, licensing). It replaced the B2B Payment Gateway NDA row; a
clickable story beats a locked title. The row and the Read-more rail
show the Cart Components sheet (960w webp).
Work tab recomposed (merged from case-study-lab): PhonePe leads the left
column at the tall ⅘ share with Design System under it; Ennoventure
right at ⅗ with Product & Design Research under it. The tablet zigzag is
intact (TABLET_BIG is slug-keyed; tabletLast moves to Ennoventure).
Quick wins: the PhonePe consumer study row removed on all devices (not
available right now); section dividers dropped from the PhonePe and P&DR
tiles; the Design System NDA chip trails to the card edge on desktop,
the same rule as rows.
P&DR gets its sub back: "Intelligence briefs from a five-agent pipeline:
every claim checked at its source." It names the system behind the two
reports.
Reference material (agent definitions, the pipeline flowchart, the Cart
Components source PNG) moved out of public/ into gitignored
private-refs/; verified 404 on production.
llms.txt leads with hiring (my call): availability plus a "Best fit for"
section (the four target roles: Founding Designer, Principal/Staff, Head
of Design, AI-Native Design Lead) directly under the title, before
Summary, Roles, and the projects. A recruiter or screening LLM hits the
hiring signal first. Gated on OPEN_TO_ROLES, so the lead drops if
availability flips off.
One source, no drift. The availability line and Best-fit roles moved to
lib/content.ts (hiring), read by both /hire-me and llms.txt. This
also fixed the drift where /hire-me's meta said "Product designer, 11
years" against the site's Design Lead / 12+ years.
Corrected llms.txt's stale "human/agent switcher" line (no such toggle
ships) to point at the human site.
Docs cleanup: CLAUDE.md trimmed (195 → ~120 lines, every invariant
kept), the README rewritten to the four-tab architecture,
FOLIO-KNOWLEDGE-BASE's "multiple versions generated" premise corrected,
stale HANDOFF.md and dummydata.md removed.
2026-07-28 · Revert inlineCss; the LCP chase moves to field data
Deploy: anirudhux-lbbhijqi1-anirudhux.vercel.app → anirudhux.com
Reverts the inlineCss entry below. It could never be shown to help, and it
had real downsides, so prod goes back to the clean render-blocking <link>.
Why revert: the "81 vs 62" that justified inlineCss was a bad comparison
(warm prod against cold preview infra). Held constant, inlineCss showed
no LCP win, it tanked the text-to-code ratio (149KB CSS in the HTML),
and the client re-fetched the CSS after hydration anyway.
The real finding: synthetic mobile LCP for this one page measured 3.2 /
5.1 / 4.7 / 9.0 / 7.5 / 7.6s across tools and environments. The lab
number is noise-dominated; chasing it further is chasing noise.
What stands: the LCP element (the Constella row image) is already
optimal (priority-hinted, discoverable, eager, ~48ms load); PostHog is
trimmed (~94KB JS off); the wave is 60KB.
The decision: stop tuning against synthetic LCP. PostHog web-vitals is
live, so real-user field LCP accumulates over the coming days, and that
is the number Google ranks on. Revisit LCP from field data.
2026-07-28 · Render-blocking CSS: inline it (the mobile lever)
Deploy: anirudhux-c14qgm4fy-anirudhux.vercel.app → anirudhux.com
The third approved lever, and the one that matters for mobile: the global
CSS shipped as a render-blocking <link>, so first paint waited on that
request (~610ms, amplified by mobile's slow-4G throttle; this is why
desktop scored 97 and mobile lagged).
experimental.inlineCss: true (Next 15.2+, framework-aware). The whole
sheet now inlines into a <style> in <head>; zero render-blocking
stylesheet links in the served HTML. Verified live and in both themes.
Flash-free by construction. The entire sheet inlines, both themes' rules
included, so a dark-mode visitor has every rule at first paint. This is
why I skipped a critical-CSS extractor (critters/beasties): those
analyse a themeless exported DOM and defer the dark rules, flashing
light to dark. That path was tried (optimizeCss) and is a no-op for
static export anyway.
Known trade-off: Next's client runtime still re-fetches the CSS files
after hydration (for client navigation), so the CSS bytes ride first
load twice. That fetch is post-paint and non-blocking, so it does not
gate FCP or LCP; the render-blocking penalty is what got removed.
Deploy: anirudhux-33ci2zhgi-anirudhux.vercel.app → anirudhux.com
Driven by a Sitechecker/PSI report (mobile LCP 3.2s). Two of three approved
levers shipped; the render-blocking CSS lever was deferred (last bullet).
PostHog trimmed to core analytics. The defaults preset silently loaded
session replay (posthog-recorder.js, ~54 KB), surveys (~33 KB),
dead-clicks and heatmaps as separate scripts; none were in use. All four
disabled, so only array.js plus web-vitals load now. About 94 KB of
third-party JavaScript off the page, and less main-thread work (helps
TBT and Speed Index).
The waving hand (512.webp) re-encoded. 23 frames down to 12 at the same
1.4s timing: 121 KB down to 60 KB. Transparency and the wave motion
intact; frame count was the lever, quality alone barely moved it.
The mobile LCP image now loads first. The lead Highlights row (the
Constella still) ships fetchPriority="high" plus loading="eager", so
the scanner pulls it ahead of default-priority resources. This answers
the report's "LCP request discovery" failure. It cannot fix LCP alone
while the render-blocking CSS still gates the render.
Three oversized "previously at" logos resized (447–554px stored, shown
at ~32px) down to 128px. Small, clean win.
Deferred: render-blocking CSS (610ms). Next's optimizeCss is a no-op
for a static export (verified, reverted). The workable path is a
postbuild critical-CSS step, but it risks a dark-mode first-paint flash
on this theme-heavy site, so it needs its own careful pass.
The Highlights hero is a still now; every other feed card keeps its
autoplay video (the gitlab loop, shape, upi). A large autoplay video
cannot hold a stable LCP because its late frame-paint keeps becoming the
LCP, so the lead card is a server-rendered image (the desktop LCP,
painted at FCP). HERO_STILL (curation) is the one place to swap it.
The chosen image: Constella's own product views, sunburst in light,
graph in dark, zoomed (scale 1.45) so only the graph is in frame and the
app chrome (Reset / Share & embed / the Graph·Sunburst·Tree toolbar) is
cropped off. The mesh dropped and the scrim pulled back on the lead so a
dark still reads.
The still is an <img>, swapped by data-theme. It has to be a real
image element to be the LCP candidate; a CSS background-image renders
identically but ranks out of LCP contention, so the metric fell to a
smaller, later feed card (a detour tried and reverted the same day).
Both themes ship; CSS shows the one matching the site toggle (a
<picture> keys off prefers-color-scheme and desyncs from the footer
switch). Hidden ≤600 so the phone keeps its device-mockup row thumbnail.
Also: below-fold feed videos lazy-load (200px), and only the visible
theme's capture loads up front.
2026-07-27 · Feed video: first-frame posters, one capture, idle-defer
Every walkthrough now ships a first-frame poster (base light included,
walkthrough-poster.webp, extracted per project); the card paints its
still instantly instead of staying blank until the video decodes.
Only the visible theme's capture loads up front. The paired path used to
preload both light and dark; in light mode the dark walkthrough
(~900 KiB) downloaded and decoded while invisible. The hidden one now
warms after idle, so a theme flip stays instant.
The <source> waits for load plus browser idle; the poster covers the
visual through the critical window, so the video download and decode are
off the first-paint path.
Honest note: these trim real payload but did not lift the desktop score
(73 → still ~73–76). Root cause diagnosed: desktop LCP is the
client-mounted hero (~2.7s) plus the loader on /, and desktop is
graded on a stricter Lighthouse curve than mobile (which holds 93 on the
same metrics). The remaining lever is a server-rendered hero poster;
parked, my call.
2026-07-27 · Lazy-load the feed walkthroughs (desktop Speed Index)
The Highlights feed used to preload="auto" every card's walkthrough MP4
(both theme captures each) at first paint: ~8.6 MB up front, the desktop
Speed-Index drag. Each card now mounts its <source> only when within
200px of the viewport (IntersectionObserver on the card). The hero and
first visible row stay eager (LCP untouched); the below-fold cards defer
and load as they scroll near, roughly halving the initial video payload.
The observer runs in a post-paint effect rather than the ref callback; a
ref callback fires during render before the mosaic lays out, so every
card read as top-of-page and the gate loaded everything. Mobile is
unaffected (the feed videos already return null ≤600).
2026-07-27 · Per-tab colour worlds: each view grounds in its family hue
Each homepage tab now grounds in its own family colour, replacing the
single global tint (which rang against the green content). The page
--bg keys off the [data-tab] already in the DOM: 4% family on white
in light, 12% family on pure black in dark (srgb, muted;
oklch-on-near-black made it pop). Petrol / moss / forest / copper for
Highlights / AI / Work / Community. Case pages and hire-me keep the
neutral base (#F8F8FF).
The bare lock thumbnails take 24% of their family (on white light, on
black dark) instead of the old warm grey; the empty plates read as
intentional family tiles, and that finally kills the temperature clash
at the source. Imaged plates keep their neutral letterbox.
The homepage hue cycle is retired. --hero-accent pins to the current
tab's family hue (dark uses a lifted family so it reads on near-black),
so the navbar, fold wash, blurb and dot fields all commit to one colour
that matches the ground instead of drifting petrol→forest→copper.
The light --bg moves #F8F8FF → #F7F9FF (a touch lighter and warmer).
The 54 color-mix consumers retint from the token; the loader ground and
the two opaque phone NDA-chip mixes track it too. Dark unchanged.
2026-07-27 · Sealed-work plates: full-bleed stills, one composite NDA chip
The seal is one component everywhere now. A composite chip (lock inside
the NDA pill, single element) goes beside the title on desktop and
tablet (absolute to the row edge, uniform 7px/6px), and as a 2px-inset
corner stamp on the phone plate. The Design System tile uses the same
chip; the old two-part lock-plus-tag pair is gone. Radii matched at 2px
across plates and chips on all bands.
Sealed rows can hold their own still. Printer Toolkit (the all-states
board) and ExpressBuy (the checkout CUJ strip) now fill their plates
edge to edge; my assets, as-is. The remaining sealed rows show a
centered lock on a bare plate (interim, until each still arrives) so an
empty plate never reads as broken.
The phone plate chip is an opaque family tint (transparent went muddy
over a still); desktop's title chip keeps its transparent base on the
light card.
Phone plates locked to true 4:3; the PhonePe consumer study skips the
tablet band (the ⅓-width tile could not seat title plus chip).
2026-07-27 · The NDA door; row plates on every band
Sealed work is askable now. The six NDA rows and the Design System tile
open the Contact dialog with a new purpose preselected, "Requesting
access to work under NDA". One listener reuses the footer's existing
dialog, so no second form mounts. A hover label was built and cut the
same day: the click is the affordance, the label was noise.
Every list row gets a plate, the phone's card language at row scale: a
real still where one exists, otherwise the family lock with NDA set
beneath it. Desktop 52px, tablet 38px (rows are title-only there, so a
desktop plate towered over one line); phone keeps its 40% card. Lock and
chip left the title line entirely.
Nutella and UCI show real captures of their live reports, clipped to the
content box rather than the page: the Nutella cover block with its stat
strip, the UCI annotated bicycle. 21 KiB and 33 KiB. No placeholder
plates remain on Work.
The card form is gated on having a plate rather than a description,
which pulled the note-less PhonePe consumer study row into line with its
neighbours.
Shipped on the evidence: mobile is 59% of visitors, so the phone Work
screen is the primary surface.
2026-07-26 · Pill clearance unified at 84px; B2B PG on phone; swap-time scroll pin
The pill-over-card overlap (my catch): the mobile system's rule is 84px
of bottom padding under the last card (pill 46 + gap 16 + air), but two
later rewrites re-derived their own constants: 40px in the lens grid
plus Highlights feed (2026-07-11), 64px in the tablet block
(2026-07-18). Latent until the Work tab grew tall enough to scroll; at
scroll end the pill overlapped the card by 22px. All three read the same
84px now.
B2B PG (phone only): new optional phoneTitle/phoneNote on ListRow,
CSS-swapped at ≤600. The B2B row reads "B2B PG · Payment gateway rails
for online businesses." on phone; full strings everywhere else.
Tab switch scroll pin: a second scrollTo(0,0) fires when the incoming
view mounts (≤820); iOS momentum scrolling could stomp the tap-time
reset and land the new tab mid-document.
2026-07-26 · Row thumbnails at row size: the mobile LCP pass
Loader v2's earlier reveal exposed the next bottleneck: the phone rows
render ~240 CSS px wide but downloaded the full case stills (~1.2 MB
across the Highlights feed; ftux.webp alone 331 KiB at 2720px). Mobile
LCP rose to ~5.5s and the score dipped to 76.
New TILE_PREVIEW_ROW map plus -row.webp variants (720w, sharp q80,
alpha preserved, ~240 KB total): the Highlights phone rows and the Work
card rows prefer them; hover reveals, the Read-more rail, and case
figures keep the full files. Same images, same framing, three times the
slot density.
Lighthouse on production after: 89 mobile / 85 desktop (from 63/63 at
the start of the day); mobile LCP 3.4s, desktop 1.4s.
2026-07-26 · Loader v2: quiet veil, family dot shimmer, 1.55s
The cold open sheds the orange mesh and grain: a plain ghost white or
near-black ground, the wordmark centered and following the live hero
cycle (one clock, inherited; never a local instance).
The corner counter is gone. In its place a 4×4 dot field under the
wordmark (the CodePen DKzBmR recipe): each dot pulses opacity on its own
offset duration; each row holds one fixed family colour in pill order:
petrol, moss, forest, copper. Deliberately disconnected from the cycle;
colour is identity, the same law as the T1 dot fields. Dark lifts the
rows in oklch.
Duration 2.6s → 1.55s; the count still runs underneath and drives the
exit.
#loader on the homepage is the bench replay flag: it forces the cold
open on every refresh for iteration. Normal skip rules untouched.
The bench branch loader-bench holds the lineage; merged fast-forward.
Baseline measured first (local Lighthouse on production): 63 mobile / 63
desktop; mobile LCP 11.9 s. After this deploy: 87 mobile / 71 desktop,
mobile LCP 3.9 s.
app/icon.png 573 KiB → 60 KiB (the 900px source resized to 512, palette
PNG). public/512.webp 280 KiB → 121 KiB (the footer wave: a 512px
46-frame animated webp downscaled to 144px, 23 frames; it renders at
44–72px).
New components/ThemeShot.tsx behind all three thumbnail renderers
(Highlights phone rows, tile hover reveals, Work card rows): the old
useIsDark remount put the light src in the server HTML, so every
dark-system visitor downloaded both theme variants. Pre-mount it is a
<picture> with a prefers-color-scheme source; post-mount it collapses
to the data-theme'd img (a cache hit unless the visitor manually
overrides the system theme).
The duplicate-thumbnail smoke gate (built after the Work-rows RCA) ships
in this commit; the suite stayed green through it.
2026-07-26 · Work rows: own faces, family locks, no false play
The repeated-thumbnail bug (my catch, with an RCA on the record): the
phone Work rows had four borrowed placeholder faces (Constella,
Contraire, Anatomy, Shape) across eight rows since 07-18. Every image
appeared twice on one screen and none was its own project. UPI Lite and
UPI International now show their case-study stills as-is (ftux,
forex-anxiety; a single-panel-crop detour was built and killed the same
day: it broke the row hierarchy).
NDA rows render a family-coloured lock in the media slot on the same
plate the thumbnails use: the footprint stays uniform, the state reads
at a glance, no borrowed faces. Research title-only rows stay plain.
The play disc on thumbnails is dead: it promised an inline player, but
rows navigate to the case study where the video is. The title's ↗
returns on card rows as the navigation cue.
CLAUDE.md now maps the two independent thumbnail sources (TILE_PREVIEW
plus bento-config item.preview): any thumbnail ask sweeps both, at every
band.
Also in this commit: the repo-privacy doc correction and the
backup-bundle script (owner tooling, unused until called).
2026-07-25 · Colour hover, a softer ground, and the footer goes bare
Deploy: anirudhux-ko9uo3z5q-anirudhux.vercel.app → anirudhux.com
(One entry, two deploys: the hover interaction went out mid-session as
anirudhux-krlvujclw and its cbv was interrupted before the ledger; this
entry covers both.)
Feed cards respond in colour; position stays fixed. Cursor on a card
saturates the footage (1.25) and deepens the petrol veil (0.22 → 0.42
light; dark re-pins its own 0.82 → 0.86 multiply). The −3px hover lift
is dead at my call ("not the right interaction pattern for this"). Gated
to @media (hover: hover) so touch never sticks a state.
The footer dot ground is gone (my call: "not adding any value"; plain is
the accepted trade). The footer is transparent on the page's own ground;
the wash died 2026-07-24, the dots died today. The CSS records both so
neither ambience layer gets re-invented.
The page ground softened: light-mode --bg is #F8F8FF (ghost white, cool
tinge, on the "never warm tan" law) instead of #ffffff. One token; 54
colour-mix consumers retint with it. Deliberate whites stay: card
plates, logo chips, frost mixes, the resume-print sheet. Seam-scanned at
1440, 1014 and 390 across the homepage, a case page, and the Hire me
dialog: zero stray pure-white surfaces. Dark is untouched.
2026-07-25 · The card treatment speaks petrol; llms.txt drops its ghosts
The light-mode Highlights card treatment, rebuilt in one language over
an afternoon of my eye. Four moves, each from a screenshot I brought:
the mesh dropped from 0.6 to a lighter cast; the charcoal scrim re-cut
to hug the text zone; then the deeper question ("why is there no petrol
blue treatment to this overlay?") turned both layers petrol. The scrim
is petrol glass (0.94 at the foot, transparent at 78%, raised at my call
when band titles lost their bed), and the mesh blends normal at 0.22 in
light, since multiply manufactured brown murk over any grey frame
(Constella dimming behind its own modals). The 1998 text-shadow (hard
edge plus heavy glow) became one soft petrol halo. Dark mode is
untouched throughout: multiply at 0.82 over near-black is the look
there.
llms.txt stops listing ghosts: the five content-less placeholder
projects (Ennteract 2.0, Design System, Printer Toolkit, Adori
interactive, B2B ExpressBuy) emitted bare "Positioning." lines;
generation now skips a project with no positioning, no live URL, no
proof. They rejoin when they earn content. CLAUDE.md gains the cbv
fact-sync line for llms.txt.
The resume-print dev surface points at draft-v3 (the ATS pass;
owner-private file, never ships).
2026-07-24 · Footer pass: aligned, transparent, a dot ground
Alignment: the brand lines (anirudhux / Crafted / Shipped) now line up
with the blurb text above, at both widths. Phone: the footer rail
matches the header's stage-plus-tile inset (34px). Desktop: only the
brand column insets by the meta-tile's 48px content gutter, so it aligns
under the blurb while the three lists stay on the bento grid.
The petrol wash is gone: the footer background is transparent in both
themes, so the page's own light or dark ground shows through. It was
reading wrong and causing styling issues.
New dot ground: a dot field behind the footer, coloured by the active
root (petrol / moss / forest / copper via --footer-accent) and masked
bottom to top so the dots gather at the foot and fade up into the body.
No wave.
Mobile fix: the "Shipped with AI in the loop" line was hidden by two
legacy rules that dropped the whole note wrapper. Now only the hover
tooltip is dropped on touch; the line shows under "Crafted in
Bangalore", with more air before the columns.
Sentence case: the column headers (Highlights / Explore / Connect) are
Manrope sentence case now instead of the tracked all-caps eyebrow.
Case-study figures were plain static images, and the gallery lightbox
had no zoom, so a visitor could only read dense screenshots by zooming
the whole page. Any content image now opens a zoomable lightbox.
New Lightbox (shared): wheel and double-click zoom toward the cursor
on desktop, pinch and double-tap on touch, drag-to-pan when zoomed, a
−/⤢/+ cluster, close on ✕ / backdrop / Esc. Hand-rolled on pointer
events, no library. touch-action: none keeps the gesture on the image
rather than the page.
New CaseFigureImage: inline case figures are click-to-zoom, single
image per my rule (each stands with its caption). The hidden theme and
viewport variants are display:none, so a click can only land on the
on-screen variant and opens exactly that one. GalleryLightbox now
routes through the same Lightbox, so gallery photos gain zoom and keep
their prev/next.
A robustness fix found in testing: setPointerCapture ran before the
pointer was recorded, and it can throw on an inactive pointer id in real
browsers too; a throw would have silently killed pinch and pan.
Tracking happens first now, with the capture call guarded.
2026-07-24 · Mobile tab switches: the crawl becomes a snap
The ≤820 tab transition was fade, dead hang, blank, then a 1.25s crawl,
where desktop is one continuous View Transition morph. Three fixes,
mobile only:
The dead hang. Platform is read up front so the exit hold matches the
exit animation: EXIT_MS_MOBILE = 180ms (the p-page-out fade) instead
of the shared 320ms, which had held ~140ms of faded-out dead time.
Desktop keeps 320.
The blank frame. ≤820 now swaps straight from exit to cards in one
synchronous step, never through the empty morph stage; the incoming view
mounts already rising over the faded ground. Morph is desktop-VT-only
now.
The sluggish rise. p-page-rise 1.25s → 0.55s (phone), t-page-rise
0.95s → 0.6s (tablet). Mobile now reads as fade (180ms) then rise
(550ms), continuous.
Desktop's View Transition morph is byte-for-byte unchanged (gated on
!document.hidden && !mobile). Verified on dev: the swap happens at
~180ms with no blank frame, the rise releases its transform to none,
the dot ground stays viewport-fixed, and a rapid three-tab burst settles
cleanly on the last.
2026-07-23 · Analytics: the six named conversion events
Wired the conversion funnel into PostHog on top of the autocapture
defaults: contact_form_submitted (the lead, with the purpose select),
the diagnostic contact_form_started, hire_dialog_opened and
contact_dialog_opened (with section and trigger), resume_viewed
(with the surface it came from), and lens_switched (tab plus origin).
One helper, lib/analytics.ts: a typed track() over
window.posthog.capture with an SSR guard. The snippet queues calls
before array.js loads, so early fires are never lost.
Verified on dev that all six fire with the right properties, and that
contact_form_started fires once only. contact_form_submitted was
left untriggered in testing on purpose, since a real submit sends a live
email; it uses the same success branch that flips the form to its sent
state.
Removed the Google Analytics gtag block from app/layout.tsx (the only
GA reference in the repo) and replaced it with the PostHog snippet, US
cloud.
The snippet ships as a script constant in <head>, after the theme and
loader scripts so it stays off the first-paint path; it loads array.js
async.
The project API key is public-safe and committed in the clear on
purpose, the same call as the Web3Forms key: it is a client-side
ingestion key, and a static export inlines client config into the
bundle regardless.
Verified on dev: PostHog initialises against us.i.posthog.com, the key
is live (remote config loads, no 401), and events reach the ingestion
endpoint; GA is fully absent (no gtag, no dataLayer, no
googletagmanager).
2026-07-23 · Highlights breathes at 821–900; UPI thumbnails; a sitemap
Highlights responsiveness (821–900px): the two-column shell (bio beside
feed) turned on at 821, but the feed is a single fused column that takes
its full-width form at ≤900, so in that gap the feed was crammed into
66% and the bio starved to a word per line. It now stacks (bio header
over the full-width feed) through the whole window and splits into two
columns at 901, where the wide mosaic has room. Scoped to
html:has(.hl2-zone), so AI, Work, and Community keep their 821 desktop
layout.
UPI Lite and UPI International thumbnails: two causes. On the phone rows
the cards had no TILE_PREVIEW, so they rendered copy-only; added both,
pointing at the real case-study stills. On tablet and desktop
object-fit: cover cropped the wide 3:1 UPI Lite montage to a blank
centre slice; a scoped object-fit: contain (the PhonePe device
montages only) shows the whole image. The photo heroes keep cover.
SEO surfaces: added app/sitemap.ts, emitting out/sitemap.xml at
build. It is driven by isCaseLive, the same gate the route prune uses,
so the sitemap and the real routes cannot drift; 17 live URLs, zero
pruned placeholders. robots.txt gained a Sitemap: line, and llms.txt
now points agents at both.
2026-07-23 · The prune: dead routes out, case pages close clean
The route prune (my call, from the site-tree parley): the eight "page to
be rebuilt" placeholder routes are deleted: en-platform,
en-design-system, en-printer-toolkit, nutella-400g, uci-certification,
adori-interactive, phonepe-b2b, vyu-hul-3-roses. generateStaticParams
filters by isCaseLive; a missing study is a 404 in dev too. NDA and
locked bento rows stay; the prune touches routes only. The withheld
Vyu × HUL project no longer has a URL naming the client relationship.
The Contact band is off all 11 case studies (option B of the audit): it
duplicated the sectional footer's Connect column and its collapsed
teaser read as a half-painted form. Case pages close on the footer; the
form stays in the two dialogs.
Case vnav travel ends at the prose. The toc was a bare grid child, so
its sticky track ran the whole .case-body and it kept scrolling beside
gallery, embed and Read more. It now goes in a .case-toc-col stretched
to row 1 only.
Design System tile: lock and NDA aligned on the row mechanism. The pair
was dropped inline into the h2 with no flex context and rendered at two
heights. .tile-nda-pair gives it the rows' flex centring, the seat
measured against the row chip; structural for any future NDA_TILES
member.
2026-07-23 · The register purge: the site stops sounding machine-written
A site-wide text audit (my call: "em dashes, LLM-isms, X-not-Y... all
the stuff has crept back in"): roughly 150 fixes across all ten case
studies, every blurb, positioning and proof line in content.ts, the UPI
figure captions, card ledes, the hire timeline plus its pill-tooltip
twin, best-fit cards, NDA tooltips, and llms.txt. My register
throughout: semicolons, colons, full stops; en-dash year ranges. The
stale "Narrated: play with sound" caption died with the mute.
The law is structural now: CLAUDE.md holds the register law, and smoke
fails any build with an em-dash or ", not " pivot in rendered copy.
Exemptions are allowlisted by my ruling: Hareesh's verbatim testimonial,
the authored hero closer, and this ledger's history.
The root cause is on the record (memory: voice-register): the rule was
written all along; the failure was generation without application. The
gate exists because intention decayed twice.
2026-07-23 · ONE footer sitewide; Constella's dark head trimmed
Constella's light walkthroughs trimmed (my catch, mis-filed as a
switching bug; it was the file): both light captures opened on ~2.7s of
the app's dark loading screen, visible at every loop restart and instant
theme swap. Heads cut past the fade (3.05s / 2.95s desktop and mobile),
re-encoded near-lossless; frame-one luminance measured light on both.
2026-07-23 · Instant theme-swap footage; every video force-muted
Theme pairs stay mounted (my catch: Constella stuttered for seconds on a
mode flip, then popped). The old key={src} remount refetched and
redecoded the other capture on every flip; now both captures mount and
CSS picks by data-theme, the case-fig still technique promoted to videos
(CardVideo plus CaseFigureVideo). A playback-sync effect pauses the
hidden one (display:none fails to stop playback), and the pair rules use
doubled classes (.case-figure video at (0,1,1) would out-rank a lone
class). Measured: the incoming capture is on screen at readyState 4 in
the same tick as the switch click.
Site-wide mute (my call): all three renderers set muted plus a
volumechange guard that snaps any unmute straight back; WebKit's volume
controls are CSS-hidden. UPI Lite's narration track stays in the file
(player policy, reversible), but no player ever sounds.
2026-07-23 · anirudhux.com: the site moves to its own name
The custom domain is live: apex plus www attached to the project
(Namecheap A @ → 76.76.21.21, www CNAME → cname.vercel-dns.com; my
DNS, minus a leftover Namecheap URL-redirect record that was
round-robining the apex to their parking IP). siteUrl →
https://anirudhux.com, so canonicals and OG URLs use the real name;
anirudhux.vercel.app stays as a working alias.
Google Analytics (gtag.js, G-KJYG4332SF) added in the root layout head:
one tag, every page, verified in every exported HTML file. The
paint-gating theme and loader scripts stay ahead of it.
2026-07-23 · The résumé ships everywhere; the blurb finds its arc; Design Lead, since 2014
Deploy: anirudhux-biwl3bpcd-anirudhux.vercel.app
The résumé is live: typeset from my cut, exported to PDF, hosted on
Drive (deliberately off-Vercel), and present at every contact point: the
footer's Connect column, a "View résumé" pill leading every contact
drawer (résumé → X → LinkedIn), a secondary CTA beside "Get in touch" in
the Hire me hero, and a line in llms.txt for the machines. One constant
(socials.resume) feeds them all. robots.txt added.
The intro blurb, final cut (my words): "Observing, connecting &
designing since 2014. Shaped 400M+ touchpoint experiences across digital
domains; now building for humans & agents." Four lines on phone and
desktop, three on tablet; wrap discipline held per band.
The bracket and year sweep: title tag, OG, description and share card
re-filed from "Product Designer" to Design Lead (the resume's lesson
applied to the site), every "2015" → 2014, the hire stat → 12+ years.
og.png re-baked from its generator with the site's middot, no em-dash.
Resume working drafts moved out of public/ into gitignored
resume-drafts/ (the repo is public and public/ ships; neither may ever
hold them). The draft commit was dropped before it reached the remote.
Verified on prod: /resume/draft-v1.md → 404. This also re-bakes the
ledger, so the previous entry now renders on /changelog.
2026-07-23 · Community re-composed: the marquee column
Deploy: anirudhux-oy92kddsw-anirudhux.vercel.app
The big column holds the two marquee credentials, Transform Design
(founded) over GitLab Talk, and the small column stacks Various Meetups /
PR;SOS / DARD at 40:30:30 (it settles at 39:31:31 on content minimums).
Founder beats attendee, talk beats circuit. The phone card flattens in
the same order.
2026-07-23 · The clamp LAW + an honest bench; Transform Design goes big
Deploy: anirudhux-8b72yrryj-anirudhux.vercel.app
The rule, made structural (after the Contraire truncation call-out):
every Highlights card lede fits its clamp; 3 lines max, an ellipsis is a
defect. The root cause was systemic: CARD_COPY held ledes "sized for the
big slot" while the bench reorders slots freely, plus a duplicate
contraire key silently shadowing fixes. All ledes now fit the smallest
slot (my copy only: Contraire's tagline line, Constella's opening
clause, UPI Lite's blurb, Shape's closing line; the two original
mobile-lab clamp debts paid). Smoke now asserts clamp integrity on every
route × viewport × theme: a truncated lede fails the build gate. It
caught three offenders across bands on its first runs.
The bench shows slots: each row's number states its feed slot (1 HERO ·
2 BIG · 3-4 SMALL · 5-6 BIG · 7-8 SMALL…), computed from feed position
(hero-first) so it never lies. Slot size was position-derived CSS the
bench kept hidden, so reorders were blind; now they see.
I re-curated the feed via the bench: Transform Design → slot 5 (BIG),
Shape → 7 (small); Shape's lede refitted to the small card. Card title
"TransformDesign.co" → "Transform Design" (the unbreakable 18-char word
clipped h1s); the lede is the tagline; case-prose links now styled
sitewide; gallery crops bias the upper third.
2026-07-23 · TransformDesign.co: the founding credential joins Community
Deploy: anirudhux-i2ly8qi6y-anirudhux.vercel.app
/community/transformdesign follows the meetups recipe: positioning, "The
movement" (the org's own tagline; the 2019–2020/COVID ending goes in the
body, since leading with it read as a eulogy), "The people" (Shami Raj,
co-founder and mentor; 12 speakers linked by name only, no titles, no
employers anywhere, "seasoned designers and design leaders in the
industry" as the blanket), a 13-photo wall (people and ambiance, latest
conference first, TDConf3-Mix packed-room hero), and all 11 promo
artifacts in one period-piece bundle (TDConf4's in-artwork employer
names stay).
Community tab: TransformDesign leads Practice (founder beats attendee);
family copper fixed (a missing tiles.ts entry had it falling to the
forest-green default). Curated into Highlights via the bench; its feed
card uses the hero still (the walkthrough-404 trap, caught by smoke).
Class-level fixes found on the way: .case-prose a did not exist, so
every case-page link rendered as plain ink; now family-coloured and
underlined, sitewide. Gallery cover crops bias to the upper third
(centered crops decapitated standing subjects).
2026-07-22 · Highlights stills + /changelog goes public (unlisted)
Deploy: anirudhux-ji1venzgj-anirudhux.vercel.app · re-baked as
anirudhux-1zwwo1jpw so this very entry renders on /changelog (the page
bakes at build time, so the ledger runs one deploy ahead of it)
Every Highlights card has a face again. Cards without a walkthrough
recording fell through to /projects//walkthrough.mp4, a silent 404
(browsers refuse to error a missing ), so the
post-recording-era highlights (UPI Lite · UPI International · Various
Meetups, curated in via the bench today) ran bare petrol scrim. New
CARD_STILL map: a declared still wins the media seat in the same fill
recipe (ftux / forex-anxiety / hero per the first-big-image rule);
one-line swap per card; delete the entry when real footage arrives.
Verified on prod: 4 videos plus 3 stills, zero blanks.
/changelog: this ledger as an unlisted page. CHANGELOG.md rendered at
build time, URL-only access (no surface links to it), site rails.
Proof-of-build, in my own checkpoints.
2026-07-22 · Work tab: NDA chips across the gated stack; notes align
Deploy: anirudhux-afz0skbch-anirudhux.vercel.app
Lock plus NDA on the whole gated stack: Ennteract 2.0 · Printer Toolkit ·
Vyu · Design System (tile and phone card) · PhonePe consumer study join
B2B Gateway and ExpressBuy; play stubs gone (no implied walkthroughs).
The nda contract re-written to my definition: the reader can't access
this because of an NDA, and it says nothing about the future. Tiles
gained the state via NDA_TILES (chip in the title line, corner icon off
while inert).
Row note alignment: every note rendered 6px left of its title; the 9.5px
indent forgot the head's flex gap (tick 3.5 + margin 6 + gap 6 = 15.5).
Fixed class-level; title and note left edges are identical now.
The Design System hover reveal parked: the Shape tablet placeholder
leaked another project's face onto the tile; no preview until the real
hero exists (re-add instructions in tile-previews.ts).
2026-07-22 · Hire me becomes a TRUE dialog; the page-hybrid is dead
Deploy: anirudhux-94h5yh8ne-anirudhux.vercel.app
The philosophy, made structural (mine): clicking Hire me must never take
a visitor off the core messaging; once people leave into a page, they
don't come back. Hire me (which is also the About surface) is now one
native dialog, callable from every door: nav pill, footer link, identity
line. Content extracted to HireMeContent, the dialog global in layout,
lazy-loaded on first open. Deleted: the iframe modal, its Escape relay,
HireDialogClose, the standalone page composition.
The address contract: an in-site open writes /hire-me, close restores
the origin path; a shared /hire-me link renders the homepage with the
dialog auto-open (the mount race solved with a pending-open queue), and
close goes to /. /hire-me joined HOME_PATHS (the load-bearing template
exemption).
Inside the dialog: CaseToc survived with a scrollRootSelector (the panel
is the scroller; it is the layout as much as the nav, and removing it
let prose sprawl past its 720px measure). The colophon became "Tools I
work with" (the full roster; the built-with subset stays in the footer
tooltip). Contact joined the index and the column; the full-width band
treatment stays case-pages-only, where a real footer follows it.
Page-era forehead and chin trimmed (~123px head, ~127px tail → ~33px
each).
Found in passing: the old "round N → date" comment scrub had corrupted
20 background 0.*s transitions (backg2026-07-22.2s), silently
invalid since that pass, hovers snapping sitewide. All repaired.
2026-07-22 · Footer v2: brand stack, family wash, and the Tools dialog
Deploy: anirudhux-3c9hi7njh-anirudhux.vercel.app
The tool roster gets one source, lib/tools.ts (19 tools, builtWith
booleans). Three surfaces render from it and can't drift: the /hire-me
Colophon (BUILT_WITH: what this site was built with; Claude Code ·
Codeberg · Figma · Vercel · Phosphor Icons · Coffee), the footer's new
Tools dialog (the full working roster, three-column chip grid, the
Contact dialog's exact header technique, no eyebrow variants), and the
slim footer's Built-with tooltip. Rotato and Supabase joined; Clideo cut
(no mark). All marks normalised to 18px across svg, raster and emoji,
class-level.
The wash takes the active tab's family colour, fixed: petrol / moss /
forest / copper via --footer-accent from TAB_COLORS. The cycling
hero-accent read out of sync with the page ("this altering thing is not
working"). Family colours are identity.
2026-07-22 · Category tabs adopt the Highlights close: one ground to the footer
Deploy: anirudhux-1qv535tw6-anirudhux.vercel.app
The footer gap collapses: the category tabs' clamp(80px,12vh,134px)
white band above the footer becomes 28px, the same rhythm as the
Highlights slab's own pad. Last tile to footer is 36px now.
The dot ground is unwalled (my recon call): the T1's dot field was an
inset:0 layer clipped by .intro-tile's overflow:hidden, sectioned in,
so it stopped 36px short of the footer while the Highlights column ran
straight down. Desktop-only: the tile goes overflow:visible and the
field extends past the tile by the exact seam (8px stage edge plus 28px
footer pad), so the bottom-weighted mask paints dots down to the footer
hairline. The gap measured 0 on prod. Highlights' T1 excluded (its
sticky column already owns its ground); tablet and phone bands
untouched.
2026-07-22 · The Highlights feed staggers like the tiles
Deploy: anirudhux-blzase0dg-anirudhux.vercel.app
Per-card enter for the feed: the enter and exit classes moved off the
feed's wrapper (which faded the slab as one block, the last lens-era
tell) and onto each card. tile-enter at the tiles' own 70ms-per-index
cadence (verified 0/70/140/210/280ms on prod), a uniform quick sink on
exit. The same keyframes and curve as the category tiles; all four tabs
now enter with one voice. ≤820 the page-plane owns all motion, so both
band blocks null the card stagger exactly as they null the tile stagger.
Standing decisions logged with it: the Highlights T1 stays the
introduction surface and the identity line stays on the category tabs;
both retire only when the T1 talking-head videos arrive (the "social
anchoring" seat), nothing before.
2026-07-22 · Succession: Highlights2 IS the homepage; the lens is dead
Deploy: anirudhux-1noh9qmi0-anirudhux.vercel.app
The Highlights ⇄ All lens split is gone. The Highlights lens rebuilt as
a tab (born "Highlights2" on animation-lab, renamed to Highlights the
same day) is the front door at / and the first of four peer pills.
Every view move is one machinery: exit, meta-card VT morph, staggered
enter, with a fade-through variant when Highlights is either side of the
switch (the stock cross-fade double-exposed its display T1 onto the
category statements). Deleted outright: the Lens state, the
flip/flipToAll choreography, the dual-layer CSS (lens-exit/enter-*,
.lens-flip-in), ShowcaseFold's fold component, and TestimonialsFeed
(fold-only). The feed pipeline (buildFeedRows → FeedCards) and
CompanyPills survive as the single shared copy in ShowcaseFold.tsx.
Bands: phone keeps its own language (centered header: wave · 28px name ·
segment-wrapped bio · pills, over the fixed dot ground, fused feed
card); tablet keeps its form (34px name, the authored t1-break
two-liner, full-width fused slab). One real bug fixed on the way: the
hl2 grid items auto-placed into a fractional track on the 2-col phone
grid (the .case-body implicit-column RCA); they now span 1 / -1.
Also in the animation-lab merge: the sectional footer (portrait plus
Highlights/Explore/Connect columns, joins the scroll on every tab), hl2
page-flow scroll (sticky static column, glass under the nav), the T1
top-anchor (copy hangs 13% below the tile's top on all tabs), Contact
seated bottom-left of the Highlights column until the talking-head video
chip claims that slot, tooltip surface 97%.
Addresses hold: //ai/work/community all server-render their
view; legacy #all-* still self-heals; the loader stays a
front-door-only ritual. Smoke: 15 routes crawled, no dead links,
overflow or console errors.
2026-07-22 · Every case study loses its orphan lede; highlighter + real sub-headings
Deploy: anirudhux-4te9ybg9c-anirudhux.vercel.app
The unnamed opening paragraph is gone from all nine case studies. Three
surfaces were answering "what is this" before the reader learned
anything: the positioning line (from content.ts, also powering the bento
tile), a free-floating lede in the md, and, on UPI International, a
section literally called Overview. The lede fell outside the TOC (which
indexes ## only), so it belonged to nothing. Now: title, positioning,
a named first section. "Overview" is gone from the codebase; DARD and
PR;SOS had substantive ledes, so they were promoted to sections titled
with my own opening sentences ("The gap I went after", "I picked the
mess on purpose") rather than deleted. Four were pure restatement and
were cut only after checking each against its positioning line.
Editorial pass: 32 highlighted phrases plus ~25 bolds across all nine,
on the load-bearing lines ("design success is not product success", "it
scaled with the vulnerability of the person in front of it"). Reuses
.marker, the half-height highlighter from /hire-me, so it inherits
each page's family colour. box-decoration-break: clone so a phrase
that wraps paints on both lines; half a marker stroke reads as a
rendering fault.
.case-prose h3 did not exist. Tailwind's preflight strips heading
defaults, so every ### rendered at body size and weight with no air,
reading as a stray sentence. Now 17px/700/family colour with 34px above,
one step down from h2 (22/750). Dark added to the same rule instead of
a light-mode-only fix. Manual "1. / 2. / 3." prefixes dropped; the
styling holds the hierarchy.
Both PhonePe heroes deleted. The banner slot was always meant for a
walkthrough video (study.walkthrough outranks heroImage, so the clip
drops in with no config change). A still was tried and rejected: it
showed the conversion screen, which Price transparency already argues,
so the page opened on its own punchline. Hero-less pages get
case-hero--solo: 24px → 80px under the positioning line, which was
otherwise equidistant between the title and the first H2 and read as a
subtitle of the section.
The Read-more rail gains a thumbnail fallback (firstFigureImage): a
project with a case study but no TILE_PREVIEW shot now borrows the
first still its own study renders, so no card stands blank beside
siblings that have one. Video figures skipped; a clip has no still to
lend.
Transaction History re-exported: the old file was 789px wide rendering
into a 720px column, 1.1× density, visibly blurry on any retina display.
Now 1440×2373, an exact 2× match, alpha intact.
2026-07-21 · UPI International gets its UX story; figures learn viewport pairs and transparency
Deploy: anirudhux-gvuvn23ug-anirudhux.vercel.app
UPI International rewritten around the actual UX decisions. Overview
merged with the friction framing (cash, forex card, hidden markup, and
the mental math underneath all three). A new Price transparency section
on the conversion screen: dual currency, live rate, the guaranteed INR
figure before the PIN. Activation absorbed the discoverability material:
contextual prompting, one-tap, a validity that expires by itself.
"Where it works" was deleted; its NRE/NRO material belongs to a
different product, and leaving it conflated two projects on one page
(my call).
Three new figures, all transparent cut-outs: forex-anxiety (Price
transparency), activation (Activation), and UPI Lite's ftux (UX
Implementation). Plus pan on the 2720×900 FTUX composite: fitted to
the column at 390 it rendered ~129px tall with unreadable screens; it
now runs 70svh in a horizontal scroll strip, the same treatment
Constella's views trio uses.
Figure.transparent, the olive-plate fix. .case-figure img paints a
family-tinted card (--tile 7% over the warm #fffdf7, measured
rgb(239,241,233) against a white page). Correct for an opaque
screenshot; wrong for cut-out artwork, where it fills every clear pixel
and reads as a dirty green slab. transparent: true drops the plate and
the hairline. The specificity trap: the dark plate is a separate
[data-theme="dark"] .case-figure img rule at (0,2,1); the override
must come after it, or you fix light mode and ship dark still tinted.
Figure.mobileSrc, viewport pairs for stills. A <name>-mobile beside
a figure now pairs on width exactly as <name>-dark pairs on theme:
both render, CSS shows one, wide is the SSR default. Built because
forex-anxiety is 1080×560 landscape and 420×750 portrait: re-composed,
so no amount of CSS gets from one to the other.
The .case-more-cta label is "Read More"; a docs pass corrected
CLAUDE.md's routing section (it still described /ai · /work · /community
as redirect shells), and ARCHITECTURE/DESIGN gained the route map and
the affordance table.
Two enforcement hooks in (.claude/settings.json plus
.claude/hooks/): a Stop hook that blocks responses ending on
unasked-for git status, and a PostToolUse hook on preview_start that
requires two fixed-viewport tabs (desktop 1440×900 plus phone 390×844)
on every server boot. Both exist because the rules they enforce had
failed repeatedly as memory alone.
2026-07-21 · Transaction History goes transparent; the Read-more CTA stops breaking
Deploy: anirudhux-8x4dlvgry-anirudhux.vercel.app
The Transaction History asset is genuinely one file for both themes now.
I re-exported it with a transparent background (789×1300, replacing the
opaque 1150×2302); re-encoded with cwebp -alpha_q 100 -exact, which
stores the alpha plane lossless. Verified per-pixel rather than by
corner sampling: source and output both report 45.1% fully-clear and 5%
anti-aliased pixels, i.e. the channel survived byte for byte.
.case-figure has no background in dark, so the page ground reads
straight through.
A method note worth keeping: the first check sampled four corners and
one edge row and concluded "no transparency" on an image whose clear
area is its body. Corner sampling is no alpha test; scan the whole
buffer.
.case-more-cta became an inline-flex unit. The arrow kept dropping to
its own line under "Read case study" even though the rule already had
white-space: nowrap; nowrap can't help when the SVG is a separate
inline box, because the break opportunity is between the text node and
the glyph. display: inline-flex plus gap: 4px removes the
opportunity entirely, so a future longer label can't reintroduce it.
Label shortened to "Read More" (my call). Verified single-rect at 1440
and 700.
The Blob store tidied earlier in the day stays at 8 objects; no media
changed here.
2026-07-21 · UPI Lite case study; NDA becomes a row state; redirect rows get the arrow
Deploy: anirudhux-i3dbs45mk-anirudhux.vercel.app
The second PhonePe case study is live at /work/phonepe-upi-lite, wired
through the real system (projectPages + generated_pages +
LIVE_CASE_SLUGS) rather than hand-rolled: arc, corner icon, TOC and
Contact band all inherited. Sections: The Problem Space · Core User
Journey · PhonePe's UX Implementation · Transaction History · Key
Outcomes.
The walkthrough keeps its audio. Four cuts (light/dark × desktop/mobile)
re-encoded 67 MB → 6.0 MB and self-hosted on Blob. Unlike every other
clip on the site these have an AAC track (the narrated "audio trial"),
so the audio was preserved at 128k instead of stripped, and
CaseFigureVideo's controls (never autoplay, never muted) does real
work. The trap: vercel blob put appends a random suffix regardless of
--add-random-suffix=false, so these urls are pinned in UPI_LITE_CUJ
and a re-upload mints new ones that must be pasted back. (The
International set predates this and keeps bare names.)
NDA is its own row state, deliberately separate from locked. "Not
written yet" is a promise that eventually arrives; work under NDA never
will, and collapsing them would make the grid promise something it
can't keep. B2B Payment Gateway and ExpressBuy now render a lock plus
an NDA chip and dropped their play stub, which had been advertising a
walkthrough that is never coming. The chip is family-tinted (--tile)
so it needs no new token; dark takes the stronger mix, the same
correction the testimonial zebra rows needed.
Row glyphs are decided by rule: any row with an href gets the redirect
arrow. "This takes you somewhere" is equally true of Nutella, UCI and
both UPI case studies; play now means only "footage, no page yet". This
intentionally changed UPI International too: the same case, aligned
rather than patched one row at a time.
Blob store tidied: 12 → 8 objects (four orphans from a re-upload deleted
after matching byte sizes, then all four live variants re-verified 206).
Known debt: the case hero is a placeholder (Contraire's shot, picked so
the two PhonePe pages avoid opening identically). The Transaction
History asset is one file for both themes at my call; its source PNG is
opaque white, so it reads as a light screenshot on the dark page. A
transaction-history-dark dropped beside it would pair automatically
via assetPath.
2026-07-21 · Every lens view gets a real address: /ai, /work, /community
Deploy: anirudhux-lqibiurnz-anirudhux.vercel.app
The redirect shells are gone. /ai, /work and /community used to be
three-line documents that bounced to /#all-*; they now serve the
bento. app/page.tsx's client body moved to components/HomeView.tsx
behind a view prop, and four thin route files render it, so the
exported HTML at /work already holds the All lens on the Work tab. No
redirect hop, no post-hydration correction, no flash. The T1 /work
plate finally names a real address instead of labelling a parked one,
and the three routes drop robots: noindex for their own titles and
canonicals.
syncHash → syncPath. Flipping a lens or tab rewrites the path with
replaceState: same-document, no reload, no history growth (verified:
history.length unchanged across flips), and a refresh reopens the same
view because the static export has a real document at each path.
Legacy /#all-* addresses still resolve, and self-heal. The boot effect
reads the hash, applies the view, then rewrites to the canonical path,
so the hash form dies out on use instead of lingering in bookmarks
forever. Bare #all resolves to /ai: its canonical form is the tab
the All lens opens on rather than whatever tab React happened to hold.
(One cosmetic tell: a legacy load keeps /'s <title>, since it IS
/'s document.)
The trap this change walks straight into: template.tsx exempts the
homepage from the .page-enter wrapper because that wrapper's transform
would become the containing block for the homepage's fixed folds,
collapsing the bento grid and un-fixing the phone's dot ground. Three
new routes render that same homepage, so the exemption now covers all
four addresses. Verified at 375px: position: fixed held across a 600px
scroll with zero transform ancestors.
The smoke gate tightened: the three tab routes must show
.lens-bento .bento-stage, so a regression back to a redirect shell (or
a Highlights-seeded route) fails loudly instead of passing on nav
elements alone. 24 routes × 3 viewports × light/dark green;
/#all-work kept in the route list as the legacy-address regression
guard.
Noted, unfixed: the Tab union still holds a vestigial "About"
(absent from NAV_TABS, meta-only template, unreachable from the
switch). PATH_FOR is Partial on purpose rather than inventing an
address for it.
2026-07-21 · UPI International case study; walkthroughs self-hosted on Vercel Blob
Deploy: anirudhux-hl8tlw4hr-anirudhux.vercel.app
The first PhonePe case study is live at
/work/phonepe-upi-international, rendered by the real CaseStudyPage:
registered in projectPages plus generated_pages/, positioning filled
in content.ts, the slug added to LIVE_CASE_SLUGS. A hand-rolled
standalone route was built first and deleted: it silently dropped
PageAmbience (family arc plus project icon), the Read-more rail and
the tools/live gates. Replicating a case study means wiring it into the
system, never rebuilding the markup by eye. The Work tab's UPI
International row now links to it (internal hrefs navigate in place;
external product and report links keep target=_blank).
Section video. Figure gained video: one url, or the light/dark ×
desktop/phone set. A new client component CaseFigureVideo picks the
variant by theme plus viewport (CardVideo's pattern: mount gate for
hydration, key={src} for a clean remount). This lets a walkthrough go
under its own heading instead of being forced into the top banner slot.
Video hosting moved to Vercel Blob (store anirudhux-media, public).
The four UPI International cuts came in as 88 MB of HEVC, Safari-only,
and ship as 10 MB of H.264. Raw screen-capture sources are gitignored;
only the Blob URLs are in code, so the repo stays lean. Blob's 5 GB free
tier makes storage a non-issue; the constraint that actually matters is
the 100 MB Hobby CLI-deploy cap, which is exactly why videos can't go in
public/.
All site videos drop their download affordances:
controlsList="nodownload noplaybackrate", disablePictureInPicture,
right-click suppressed. Friction rather than protection: the file is
still reachable via DevTools or the direct URL.
Known debt: the case hero is a placeholder (Constella's shot);
transparent-background video is off the table (mp4/H.264 can't hold
alpha), and the sustainable path is the light/dark × desktop/mobile
variant model now in place.
Perf: the ShowcaseFold feed pipeline memoized (it was rebuilding every
render, including the ~156 loader ticks). Cross-browser: paired
-webkit-backdrop-filter on the showcase CTA. Stale CLAUDE.md notes
corrected (BackLink, the CardVideo debt).
Verified live: every case page's images and videos decode and serve 200
(webp/mp4), the homepage loader plus 5 walkthroughs load, no console
errors. No visible change: the same site, about half the bytes.
2026-07-19 · Work tab rebuilt around umbrellas; interfaces become phone sub-tiles
Deploy: anirudhux-aw9eifdr0-anirudhux.vercel.app
Work is four tiles now: Ennoventure and PhonePe as umbrellas, Design
System as an offshoot, Product & Design Research as the cross-company
tool. The three-sides-one-system story is told by structure instead of
a case study.
On phone each umbrella folds its interfaces into an inset composite:
AI-style cards (40% thumbnail plus play stub plus squeezed copy),
full-bleed rows inside one container with inset accent separators;
white fill, family colour in strokes only. Text hierarchy: section 20 ›
interface 16 › note 14. Design System folds into Ennoventure as a
phone-only locked card; it stays its own tile ≥601.
Design System joins the AI reveal mechanism (tablet shot); the Shape and
Design-System tablet shots nudged (object-position) to line up with
the laptop screens above and below.
Tablet fixes: a slimmer footer, the switch pill clears the last rows,
the top gap closed.
Known debt, shipped intentionally: every Work thumbnail is a placeholder
(AI product mockups standing in) and the video walkthroughs are stubs;
both swap for real assets when they arrive. I shipped knowingly on the
low-traffic staging URL ahead of the anirudhux.com move.
2026-07-17 · The OG card goes light; the pill tooltips stop clashing
Deploy: anirudhux-ja0wxcho4-anirudhux.vercel.app
The share card is light now (my call): the same composition and IA, the
site's own light standards. A white ground with the faint cool petrol
tinge at the edge (.showcase-fold's recipe), --ink on every primary
line, tangerine #c9530c on SEEKING, copper #7A4420 dots at 26%.
Contrast measured on the real composites: 17.32 / 6.86 / 4.44 / 5.22 /
4.18, all passing. Still legible at a 160px thumbnail.
The avatar's monotone glow becomes a halo sweeping all four family
hues. Two lessons in the file: the raw tokens (L≈0.30) go grey when
blurred on white, so the hues are lifted in oklch (same L, same C,
family hue); and the sweep mirrors (petrol→forest→moss→tangerine and
back) because the only pair a one-way conic leaves adjacent is
tangerine/petrol, which no interpolation space crosses cleanly. Chroma
is the taste dial: 0.115 made every hue measurably land and read as a
cartoon rainbow; it ships at 0.055. Verify that one by eye; the
saturation numbers all passed.
The tooltip clash fixed (a regression from 089a9da, same day):
.is-open was added to the ≥821 reveal, so a desktop click pinned a
tooltip that survived mouse-leave; hovering the next pill painted a
second 288px card 150px into the first, and only an outside click could
clear it. The two reveal mechanisms are now partitioned by the same
820px line the CSS uses and never coexist: ≥821 hover/focus, ≤820 tap.
Button semantics follow the same gate (role/aria-expanded were
lying on desktop).
2026-07-17 · Hareesh's testimonial arrives
Deploy: anirudhux-60frjtqs9-anirudhux.vercel.app
A new testimonial from Hareesh Nakkerthi (PM, Ennoventure), placed
third, after Veena, in both the /hire-me list and the mosaic feed.
Paraphrased from his LinkedIn recommendation; marker phrase "thinks
sideways".
His closer was cut deliberately: it collided with Chetan's opener ("Any
team Anirudh joins simply gets better"). No LinkedIn link on the name;
the URL was not to hand, and the card renders fine without one
(Chetan's does too).
The avatar cropped from a full-body reference to head-and-shoulders,
matching the other avatars' framing: 800×800/172KB → 200×200/12KB.
A side effect of INITIAL = 4: Srinivasan now goes behind "Load more".
2026-07-17 · The switch pill tracks the footer instead of dodging it
Deploy: anirudhux-9kcczazn5-anirudhux.vercel.app
The phone and tablet switch pill now rests 16px off the screen's edge.
It had been parked at bottom: 84px: never a rest position, just a
constant sized to the footer's height, which bought one scroll position
by floating the pill high over the feed at every other one. It shipped,
and it read as broken on a real phone.
The collision moves to useSwitchLift() (app/page.tsx): it watches the
footer's top edge and lifts the pill only as the footer arrives,
holding the same 16px gap, then following it up 1:1 with the scroll.
Verified on production at 390: 16px to the edge throughout, 16px to the
footer at the end, continuous through the hand-off.
iOS collapses its URL bar without firing a window resize, so
visualViewport.resize re-measures too; a ResizeObserver on body
covers lens flips, tab switches, and late-loading images.
2026-07-17 · The blurb learns its column's ruler; pills go tappable
Deploy: anirudhux-flv97cxma-anirudhux.vercel.app
The homepage layout re-railed: an absolute 35/65 column split (the gap
carved out before the ratio), 32/64/32 rhythm (--lens-gutter: 32px,
--lens-gap: 64px), and the whole container capped at
--lens-max: 1800px via --rail-pad; ultrawide gets margin, no drift.
The blurb's type now scales on the column's ruler (container queries,
cqi) instead of the viewport's: 1280's proportions hold to the cap, and
the 252px of dead air at 1920 is gone. Absolute px bounds converted to
rem (root was always the browser-default 16px; user font-size prefs now
respected).
The bio wraps at most twice and only before "since 2015": the hardcoded
<br> after "designing &" is gone; two nowrap segments own the break.
Pills scaled ~8% down so the row always ends inside the bio's first
line.
Company pills are tappable on phone and tablet: the same tooltip,
tap-to-toggle, outside-tap or Escape closes; the card anchors centered
above the pills row (a pill-anchored 288px card would cross the intro
column's clip at 390).
2026-07-17 · The slab takes the switcher's hairline
Deploy: anirudhux-qubpdxyve-anirudhux.vercel.app
The fused Highlights slab's border and painted grout now use the nav
switcher's stroke recipe verbatim (hue 30%, 0.5px); my call: the
switcher treatment is the law. Supersedes the 55% grout A/B from 07-12.
The real fix was compositing: the grout paint ran under the border and
the perimeter stacked to ~51%. background-clip: padding-box keeps the
paint in the seams only; the edge is the bare hairline. Both bands
(≥901, 601–900).
2026-07-17 · Constella case study rewritten; the register becomes law across every case page
Deploy: anirudhux-pg99smc51-anirudhux.vercel.app
Constella's three source MDs rewritten from a full hands-on pass of the
live app and a real exported HTML file: the builder-at-altitude
structure (why / what / how / decisions / outputs / takeaway), the three
refusals, the keyhole framing, "Comparing visualizations" with a spliced
sunburst-graph-tree composite (horizontally pannable at 70svh on phone
and tablet), and a four-way outputs section. "It's built to leave" is
banned and gone.
Contraire fully rewritten in the same register: context-free exhibit
quotes deleted, jargon out, six plain-speech stage labels,
walls-as-design takeaway. Anatomy and Shape de-flourished after a
cold-read audit killed their self-quotes; PR;SOS meta deduped; generic
section headers replaced with idea-carrying ones across all three.
Section-hero figures now lead their sections (a placement bug:
heading-keyed figures appended after prose); fresh Constella stills
including sunburst and tree, the outline retired; the stills capturer
gains --dark and theme forcing.
2026-07-17 · abaf732 · One clock for the homepage hue; colour census
The homepage hue cycle runs once on body:has(.lens-topbar). It had
been four independent per-element clocks (two at 9s, two at 18s), which
is why the wordmark and the page visibly drifted apart.
Every dark petrol stroke (slab border, grout, card hairlines, switcher
border and divider, phone pill) now follows the live hue instead of
fixed petrol.
The switcher's muddy-brown dark surfaces became hue-derived: the same
lightness, breathing with the page.
docs/COLOR.md: the full colour census with measured WCAG ratios;
/bench gains the read-only "Parked" card (PARKED in lib/curation.ts).
All four Constella takes re-recorded at 1920×1080 off the real host
(constella.anirudhux.com), walking the new app (Sunburst · Tree ·
Details). Light-mobile recorded for the first time.
The recorder itself fixed: a dead host, wrong frame height, silent
dark-theme takes, and a stale-temp-dir bug that once shipped a portrait
take as the desktop file.
Case pages: the Visit-live CTA retired; the title's ↗ is the one live
link.
2026-07-16 · 3187a91 · Contact becomes a door; the petrol slab deleted
The Highlights Contact band is gone; a small Contact CTA (bottom-left
desktop, under the pills on tablet, none on phone) opens the same form
in a native dialog. Two doors, two intents: Hire-me is the pitch,
Contact is the direct line.
.is-petrol and the whole per-host colour machinery deleted; the band
is always the host page's colour now.
Two layout bugs fixed: gitlab-talk's implicit second grid column (prose
starved to 110px on phones) and the .case-arc 100vw scrollbar spill
that side-scrolled every case page by 7px.
2026-07-15 · c466c2d → 1dab8d8 · the OG share card, twice
First: a designed card replaces the stale bento screenshot.
Then the phone-first rework: name, role, Seeking and avatar at sizes
that survive a WhatsApp thumbnail; the anirudhux.com · Ex: footer
line.
The hire-me contact form: Web3Forms delivery (public-safe key, honeypot
plus time-trap plus server filtering), no name field, forest-green
focus.
Contact becomes the site's closing band: a collapsed teaser, measured
heights, its own dot field. The petrol slab scoped to Highlights only
after it wrongly went sitewide.
Testimonials: zebra tint plus "Load more" past 4. Previously-at pills
with company tooltips. The smoke suite gains the overflow gate.
2026-07-13 · tablet joins the desktop system (3 commits, 24ffcff → e61e47c)
Tablet is the phone's header on the desktop's body: a bare centered T1
over a dot ground, desktop tiles with icons, curated <br> breaks.
Nav pill and wordmark polish: 46px switch, an eased label wipe ≤600, a
grown wordmark tap target. Two orphan case pages removed.
The phone rethink: centered header bands, one fused frosted card, a
viewport-fixed dot ground, page-load nav rises. Highlights joins the
same language.
The hire-me refit (avatar hero, authored testimonials), the Work tab
dressed, the footer flows at scroll end.
2026-07-10 · the navigation contract (26 commits, f294a0a → cc1fa8c)
View state became navigation state: lens and tab addressable in the URL,
Back restores the exact view, the loader skips history returns
pre-paint.
Flat nav plus hover-reveal tiles; the unified switcher pill with tab
icons; Constella leads AI in both lenses (the cross-lens consistency
RCA).
Tablet choreography rounds (meta card, arc, packing rule), the DARD case
study, listing routes parked as redirects, hire-me dialog hardening.
2026-07-09 · the spin-out: lens homepage to production (29 commits, 7e9b7e0 → 179bd5d)
final-folio spun out of mosaic's lens-switch branch and promoted to
production the same day.
Lens v1 to v4 in one day: Highlights ⇄ All under a fixed pivot,
per-letter identity type-in, the sequenced switch unfold, the meta-card
morph.
The identity state machine, the Hire Me modal, frost-on-nav, the 28px
rails, the slim one-line footer.
lib/curation.ts plus /bench born (the owner control surface), the
dark-mode a11y overhaul, theme and viewport-aware walkthroughs, one
chrome system sitewide plus the legacy purge.