A single HTML file. A Supabase backend. An Edge Function, two webhooks, and an email notification pipeline. Everything a real SaaS product has — built by a content designer, without an engineering team.
What this project delivered
The brief I wrote for myself had one constraint: it had to do everything a real product does. Gate sensitive content. Capture visitor intent. Notify me when someone reaches out. Measure who's reading and how far they get. Not with third-party plugins — with infrastructure I own and understand.
That meant building a backend. Not because it was technically required for a portfolio, but because a content designer who can wire up a production-grade notification pipeline is a different kind of hire than one who cannot.
The portfolio became the proof of the thing it was claiming.
If someone with engineering experience opened the browser console or inspected the network requests, would they find something worth respecting? That was the bar. Every feature was built to that standard.
Hiring managers need to see outcomes in under 10 seconds. Design leads need to see process depth. Engineers and technical peers need to see that the infrastructure is real — not simulated, not faked with a mailto link. Each section of the portfolio was written and built with one of these audiences in mind at a time.
The portfolio is a single index.html file. No build step, no framework, no server. But it connects to a real database, calls a serverless function, and triggers email notifications — the same infrastructure pattern used in production SaaS applications.
Alongside the Supabase pipeline, a session fingerprinting system captures every real visit. This uses a separate channel — Google Apps Script — so visitor analytics and form notifications don't share infrastructure. Both run silently.
The schema was designed to be queryable and readable from the Supabase dashboard without writing SQL. Each table captures a distinct visitor intent — configuration, access requests, and contact messages — and Row Level Security controls what anonymous visitors can do.
Row Level Security means the anon key (which is visible in the browser source) can only do what the policy allows. Anonymous visitors can read the password row and insert new records — they cannot read other people's submissions, delete anything, or update the password. The service role key (used only in webhooks, never in the browser) bypasses RLS entirely.
A single Edge Function handles both notification types. It reads the table field from the webhook payload to decide which email template to send. One function, two webhooks, zero duplication.
index.html and is visible in source. It can only do what RLS policies allow: read one row, insert into two tables.supabase secrets set RESEND_API_KEY=.... Accessed via Deno.env.get() at runtime. Never in source code.Supabase CLI shows a Docker warning because local development typically uses Docker to emulate the Supabase stack. For deployment only — which is all we needed — it communicates directly with the Supabase cloud API. The warning is noise. The function deployed successfully.
Four case studies are gated: Workflow Hub, Carina Components, Phoenix Onboarding, SPP Reporting, and Search AI. Visitors can unlock with a password, or request access by email. Both paths feed into the notification pipeline.
The previous version had const CORRECT_PASSWORD = 'rishi2025' directly in the HTML. Anyone who viewed source had the password. More importantly, changing it required editing code, committing, and redeploying. Now: open Supabase dashboard → Table Editor → access_config → edit the value cell. No deployment. No git commit. Done in 10 seconds.
Every word on the portfolio was written with the same process I apply to product copy: audience first, outcome second, copy last. Here are the decisions that took the most thinking.
| Decision | What was considered | What shipped and why |
|---|---|---|
| Hero headline | "Content that makes products click" · "UX content that ships, scales, sticks" | "I design content. I build tools. I measure both." Three declarative sentences. Each adds information the previous one didn't. No tagline to decode. |
| Gate copy | "This content is private" · "NDA-protected work" | "This case study is gated." Direct. Honest. Signals the work is worth protecting without sounding defensive. |
| Gate lock indicator | Red dot (danger signal) · lock icon | Blue dot ("Authorized viewers only"). Shifts the frame from restriction to exclusivity. Visitors feel qualified, not blocked. |
| Portfolio layout | Card grid (3 col) · Masonry · Tabs by category | List/table rows. Fastest to scan. Each row shows title, company, impact, and CTA in one horizontal pass. No layout metaphor — just signal. |
| Stats in hero | Bullet list of achievements · Text bio | 4-stat horizontal card row. Numbers are faster than sentences. Each stat has a source context as the label, not just the number. |
| Ticker banner | Static announcement bar · No banner | "Built this myself. The design, the code, the copy. If you have feedback, I'm all ears." Sentence case. Conversational. Signals the voice before they've read a word of the portfolio. |
| Dark mode toggle label | "Toggle theme" · sun/moon icons only | "Dark" / "Light" text + icon. Accessible. Explicit. Matches the direct tone of the rest of the site. |
| Reckless years section | Hiding pre-2017 work entirely · Standard timeline entry | Collapsible "notebook page" with hand-drawn aesthetic. The chaos before the craft — honest, human, and memorable. Hides on first load for hiring managers, visible for anyone curious enough to expand it. |
No em dashes in prose. No AI tell-tale phrases (seamlessly, transformative, ensure, leveraging). No <strong> as a label prefix. Sentence case everywhere. Declarative, direct sentences. These aren't style preferences — they're the same rules applied to the products I design for.
The portfolio was audited on Slow 4G mobile — the harshest Lighthouse condition — using an emulated Moto G Power. These are the scores without any build tooling, bundler, or CDN optimisation layer.
Lighthouse flagged Chrome extensions as affecting the run. The real performance score on a clean profile is 96–97. CLS of 0 and TBT of 0ms are the metrics that matter most for real users — both are perfect.
The audit surfaced three actionable issues. Each was diagnosed, understood, and fixed — not suppressed or deferred.
role="list" without role="listitem" on child rows. Screen readers couldn't identify the list structure. Fixed by adding role="listitem" to all 15 portfolio row elements.event APIswitchTab() function relied on the global event object — a deprecated browser API flagged by Lighthouse. Fixed by passing this as a parameter from the onclick handler instead._headers file in the project root — Netlify injects these on every response without touching index.html.A perfect SEO score covers the technical baseline: crawlable structure, valid meta description, proper heading hierarchy, descriptive link text, and mobile viewport. This site scores 100 on all of them.
SEO is the floor. AEO — Answer Engine Optimisation — is the next layer. Where SEO is about appearing in search results, AEO is about being the answer that ChatGPT, Gemini, or Perplexity cites. That requires a different writing approach: direct factual prose, unambiguous statements, clear question-and-answer structure, and content that an LLM can extract and attribute with confidence.
This portfolio is structured for both. Every section opens with a declarative statement. The meta description answers the query a recruiter would run. The About section is written as a factual profile, not a narrative. These are content design decisions that happen to align with how answer engines parse and cite content.
A content designer who can read a Lighthouse report, identify the root cause of each flag, and apply the right fix — at the HTML layer, the JS layer, and the CDN layer — is operating across a wider surface than most content roles require. These aren't developer tasks. They're content infrastructure tasks, and they're part of what it means to own a product end to end.
Shipping the measurement infrastructure before traffic. The Supabase backend and notification pipeline was built before the site was publicly deployed. Every visitor since launch has been trackable.
Writing before building. Every content decision — headline, gate copy, stat labels, nav labels — was made as a deliberate choice, not filled in at the end. The site sounds like it was designed, not assembled.
Set up a staging environment. Cursor's AI agent made changes to the gate HTML that weren't explicitly requested — a staging branch would have caught this before it affected the live file.
Write a more explicit Cursor prompt before letting the agent near the gate system. Vague instructions produced a modified UI that needed manual repair. Specificity matters in prompts the same way it matters in product copy.