diff --git a/content/writing/README.md b/content/writing/README.md new file mode 100644 index 0000000..b09356e --- /dev/null +++ b/content/writing/README.md @@ -0,0 +1,11 @@ +# Writing content + +MDX posts will live here as the writing archive grows. + +## Convention + +1. Add a post entry in `src/data/writing.ts` +2. Create `content/writing/.mdx` +3. Wire an MDX loader / route under `src/app/writing/[slug]` when the first post ships + +Keep essays decision-maker useful — product judgment, delivery, and shipping systems. diff --git a/src/app/about/page.tsx b/src/app/about/page.tsx index 46b6541..98e37f3 100644 --- a/src/app/about/page.tsx +++ b/src/app/about/page.tsx @@ -6,7 +6,7 @@ import { AboutPage } from "@/features/about"; export const metadata: Metadata = createMetadata({ title: "About", description: - "Product engineering partner for international founders — clarity, ownership, and production-ready delivery.", + "Kailash Gayari — Product Engineering Partner in Vapi, Gujarat. Nearly seven years shipping web, mobile, and IoT products for founders and teams.", alternates: { canonical: "/about", }, diff --git a/src/app/experience/page.tsx b/src/app/experience/page.tsx new file mode 100644 index 0000000..62a3629 --- /dev/null +++ b/src/app/experience/page.tsx @@ -0,0 +1,17 @@ +import type { Metadata } from "next"; + +import { createMetadata } from "@/config/site"; +import { ExperiencePage } from "@/features/experience"; + +export const metadata: Metadata = createMetadata({ + title: "Experience", + description: + "Product engineering timeline — senior delivery across web, mobile, IoT, and founder-facing platforms since 2019.", + alternates: { + canonical: "/experience", + }, +}); + +export default function Page() { + return ; +} diff --git a/src/app/page.tsx b/src/app/page.tsx index 0d98c7c..177db84 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,11 +1,13 @@ import { + CapabilitiesSection, ClientQuotesSection, + ExperiencePreviewSection, HeroSection, HomeCtaSection, + ProofMetricsSection, ProcessSnapshotSection, SelectedWorkSection, ServicesPreviewSection, - TrustBarSection, WhoIHelpSection, } from "@/features/home"; @@ -13,9 +15,11 @@ export default function HomePage() { return ( <> - + + + diff --git a/src/app/sitemap.ts b/src/app/sitemap.ts index 71b7cd6..30c63d8 100644 --- a/src/app/sitemap.ts +++ b/src/app/sitemap.ts @@ -33,12 +33,24 @@ export default function sitemap(): MetadataRoute.Sitemap { changeFrequency: "monthly", priority: 0.8, }, + { + url: `${baseUrl}/experience`, + lastModified: new Date(), + changeFrequency: "monthly", + priority: 0.8, + }, { url: `${baseUrl}/about`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.7, }, + { + url: `${baseUrl}/writing`, + lastModified: new Date(), + changeFrequency: "monthly", + priority: 0.5, + }, { url: `${baseUrl}/contact`, lastModified: new Date(), diff --git a/src/app/writing/page.tsx b/src/app/writing/page.tsx new file mode 100644 index 0000000..32c3f85 --- /dev/null +++ b/src/app/writing/page.tsx @@ -0,0 +1,17 @@ +import type { Metadata } from "next"; + +import { createMetadata } from "@/config/site"; +import { WritingPage } from "@/features/writing"; + +export const metadata: Metadata = createMetadata({ + title: "Writing", + description: + "Product engineering notes from Kailash — shipping software, delivery judgment, and building systems that last.", + alternates: { + canonical: "/writing", + }, +}); + +export default function Page() { + return ; +} diff --git a/src/components/common/case-study-badge.tsx b/src/components/common/case-study-badge.tsx index ee5beef..4ff65e6 100644 --- a/src/components/common/case-study-badge.tsx +++ b/src/components/common/case-study-badge.tsx @@ -6,5 +6,9 @@ export function CaseStudyBadge({ study }: { study: CaseStudy }) { return Founder product; } + if (study.engagement === "employment") { + return Product delivery; + } + return {study.category}; } diff --git a/src/components/common/section.tsx b/src/components/common/section.tsx index 3129c36..3058942 100644 --- a/src/components/common/section.tsx +++ b/src/components/common/section.tsx @@ -1,3 +1,5 @@ +import { forwardRef } from "react"; + import { cn } from "@/lib/utils"; import { layout } from "@/constants"; @@ -7,16 +9,20 @@ type SectionProps = React.HTMLAttributes & { muted?: boolean; }; -export function Section({ - as: Component = "section", - bordered = false, - muted = false, - className, - children, - ...props -}: SectionProps) { +export const Section = forwardRef(function Section( + { + as: Component = "section", + bordered = false, + muted = false, + className, + children, + ...props + }, + ref, +) { return ( ); -} +}); diff --git a/src/components/layout/mobile-nav.tsx b/src/components/layout/mobile-nav.tsx index 3a2b41f..bcd98c9 100644 --- a/src/components/layout/mobile-nav.tsx +++ b/src/components/layout/mobile-nav.tsx @@ -47,7 +47,7 @@ export function MobileNav() { }, [open]); return ( -
+
diff --git a/src/components/layout/site-header.tsx b/src/components/layout/site-header.tsx index 34f51b5..d2feed0 100644 --- a/src/components/layout/site-header.tsx +++ b/src/components/layout/site-header.tsx @@ -9,11 +9,14 @@ export function SiteHeader() { return (
- + {siteConfig.name} - + {" "} — {siteConfig.title} diff --git a/src/config/site.ts b/src/config/site.ts index 8059ff4..0e55a9f 100644 --- a/src/config/site.ts +++ b/src/config/site.ts @@ -6,11 +6,13 @@ export const siteConfig = { name: "Kailash", fullName: "Kailash Gayari", title: "Product Engineering Partner", + tagline: "Building production web, mobile & IoT products since 2019.", description: "I help founders and growing teams design, build, and ship production-ready web and mobile software — from architecture through deployment.", url: process.env.NEXT_PUBLIC_SITE_URL ?? "https://kailash-dev.github.io", ogImage: withBasePath("/opengraph-image"), locale: "en_US", + location: "Vapi, Gujarat, India", author: { name: "Kailash Gayari", email: "kailash514910@gmail.com", @@ -22,6 +24,8 @@ export const siteConfig = { github: "https://github.com/Kailash-dev", }, calendlyUrl: process.env.NEXT_PUBLIC_CALENDLY_URL ?? "", + /** Set when a public resume PDF is available under /public */ + resumeUrl: "", keywords: [ "product engineering partner", "software engineering consultant", @@ -30,6 +34,7 @@ export const siteConfig = { "SaaS MVP development", "international software consultant", "full stack product development", + "IoT product engineering", ], trustSignals: [ { @@ -63,6 +68,9 @@ export function createMetadata(overrides?: Partial): Metadata { keywords: [...keywords], authors: [{ name: author.name, url }], creator: author.name, + alternates: { + canonical: url, + }, openGraph: { type: "website", locale: siteConfig.locale, diff --git a/src/constants/index.ts b/src/constants/index.ts index 7405fe0..5f52c20 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -1,8 +1,9 @@ export const navigation = [ { label: "Work", href: "/work" }, - { label: "Process", href: "/process" }, + { label: "Experience", href: "/experience" }, { label: "Services", href: "/services" }, { label: "About", href: "/about" }, + { label: "Writing", href: "/writing" }, { label: "Contact", href: "/contact" }, ] as const; diff --git a/src/data/about.ts b/src/data/about.ts index abf9f0e..d1d9e31 100644 --- a/src/data/about.ts +++ b/src/data/about.ts @@ -1,11 +1,17 @@ export const aboutContent = { - headline: "Engineering partner for founders who need clarity and delivery", + headline: "Senior product engineer. Partner for founders who need ownership.", intro: - "I help international founders and growing teams turn product ideas into production-ready software. My work spans architecture, full-stack development, and deployment — with a focus on outcomes, not output.", + "I'm Kailash Gayari — a Product Engineering Partner based in Vapi, Gujarat, with nearly seven years building production web, mobile, backend, and IoT systems. I help founders and growing teams take software from idea to production with clarity and accountability.", story: [ - "Over years of building products across web, mobile, and IoT, I've learned that the hardest part isn't writing code — it's making the right decisions under uncertainty.", - "I work with decision makers who need a senior partner they can trust: someone who takes ownership, communicates clearly, and ships software that holds up in production.", - "Whether you're launching an MVP, scaling a SaaS product, or need reliable execution on a complex feature — I bring structured delivery without the overhead of a large agency.", + "Since 2019 I've shipped entertainment devices, IoT dashboards, design tools, invoice platforms, community products, and founder-facing SaaS — always with the same standard: production software that decision makers can trust.", + "I work best when ownership is clear. Architecture, delivery, and deployment are not separate conversations — they're one engagement. That is how complex products stay coherent.", + "Whether you need an MVP, a technical partner on an existing product, or senior judgment on architecture — I bring structured delivery without agency overhead, and without treating your product like a ticket queue.", + ], + availability: [ + "Full-time product engineering roles", + "Freelance and contract engagements", + "Technical consulting", + "Startup CTO / founding engineer conversations", ], workingStyle: [ { @@ -31,12 +37,12 @@ export const aboutContent = { ], capabilities: [ "Product architecture & technical strategy", - "Web applications (Next.js, React)", + "Web applications (React, Next.js, Angular)", "Mobile applications (React Native)", "API design & third-party integrations", "Authentication, payments & billing flows", + "IoT dashboards & device-facing software", "Cloud deployment (AWS, Docker, CI/CD)", - "Database design & data modeling", "Technical advisory & code review", ], } as const; diff --git a/src/data/capabilities.ts b/src/data/capabilities.ts new file mode 100644 index 0000000..67bb3ab --- /dev/null +++ b/src/data/capabilities.ts @@ -0,0 +1,76 @@ +import type { CapabilityDomain, ProofSignal } from "@/types"; + +/** Real presence signals — never invent vanity metrics. */ +export const proofSignals: ProofSignal[] = [ + { + value: "7+", + label: "Years shipping", + detail: "Production software since 2019", + }, + { + value: "Full stack", + label: "Ownership", + detail: "Web, mobile, APIs & IoT", + }, + { + value: "Idea → live", + label: "Engagement model", + detail: "Architecture through deployment", + }, + { + value: "Global", + label: "Collaboration", + detail: "India, US, UK, EU & AU", + }, +]; + +export const capabilityDomains: CapabilityDomain[] = [ + { + title: "Product interfaces", + description: + "Interfaces founders can ship with — designed for clarity, speed, and long-term maintainability.", + items: [ + "React & Next.js applications", + "Angular product platforms", + "React Native mobile apps", + "Design systems & component libraries", + "Real-time dashboards & data visualization", + ], + }, + { + title: "Systems & APIs", + description: + "Backend foundations that stay reliable under real usage — auth, data, and integrations included.", + items: [ + "Node.js & Express APIs", + "PostgreSQL & MySQL data modeling", + "Redis session & cache layers", + "JWT authentication & authorization", + "Third-party integrations", + ], + }, + { + title: "Delivery & operations", + description: + "Getting software into production — and keeping it healthy once users depend on it.", + items: [ + "AWS EC2 & RDS", + "Docker containerization", + "GitHub Actions CI/CD", + "Nginx & Linux operations", + "Playwright & Jest testing", + ], + }, + { + title: "Connected products", + description: + "Software that meets hardware — IoT dashboards, device UIs, and embedded-adjacent delivery.", + items: [ + "IoT monitoring platforms", + "Device-facing entertainment UIs", + "Raspberry Pi & camera systems", + "Multi-client telemetry architecture", + "Hardware–software collaboration", + ], + }, +]; diff --git a/src/data/case-studies.ts b/src/data/case-studies.ts index e3cd091..07b7da1 100644 --- a/src/data/case-studies.ts +++ b/src/data/case-studies.ts @@ -10,12 +10,15 @@ export const caseStudies: CaseStudy[] = [ image: withBasePath("/images/projects/safework-global.jpg"), client: "SafeWork Global", liveUrl: "https://safeworkglobal.com", + year: "2025", summary: "A compliance-first platform replacing unsafe overseas job agents with verified employers, digital contracts, and salary protection for Indian workers.", problem: "Millions of Indian workers lose ₹50,000–₹2,00,000 to fake overseas job agents every year — paying upfront for jobs that never exist, with no written contracts and no wage protection.", approach: "Built the full product platform: verified job listings, employer onboarding, worker applications, digital contracts, and escrow-style salary protection — designed as compliance infrastructure, not a job board.", + solution: + "A multi-role marketplace where employers are verified before jobs go public, workers apply through structured flows, and salaries are held in protected accounts before work begins — with ECR/ECNR checks, PDOT training, and visa guidance built into the product.", outcomes: [ "Live hiring, applications, and interview flows across 50+ countries", "Employer verification before any job goes public", @@ -30,6 +33,23 @@ export const caseStudies: CaseStudy[] = [ "Multi-role portals", "Payments & escrow", ], + tech: ["React", "TypeScript", "Node.js", "PostgreSQL", "Cloud deployment"], + architecture: + "Multi-role product surface (workers, employers, operators) with verification gates, application workflows, and protected payment rails — structured so compliance rules sit in the product, not in side processes.", + challenges: [ + "Modeling compliance as product logic rather than documentation afterthoughts", + "Supporting multi-country hiring flows without fragmenting the experience", + "Building trust UX for workers who have been burned by fake agents", + ], + contributions: [ + "Owned product engineering from workflows through production launch", + "Designed multi-role portals and application/interview flows", + "Integrated verification, training, and salary-protection concepts into the product", + ], + lessons: [ + "When the business risk is trust, the product architecture must encode trust — not bolt it on later.", + "Founders move faster when engineering owns outcomes, not just tickets.", + ], engagement: "client", featured: true, }, @@ -40,12 +60,15 @@ export const caseStudies: CaseStudy[] = [ visual: "design", image: withBasePath("/images/projects/vyaparpost.jpg"), liveUrl: "https://vyaparpost.in", + year: "2025", summary: "My own SaaS product — festival marketing posts for Indian shop owners, ready in 5 minutes, sized for WhatsApp, with shop details auto-filled.", problem: "Small shop owners need festival posts for WhatsApp marketing but cannot afford ₹200–500 per poster from local designers — and generic design apps are not built for Indian festivals or WhatsApp Status formats.", approach: "Founded and built a purpose-specific SaaS: festival calendar, template library, one-time shop setup, Hindi and English greetings, 600×600 WhatsApp-ready exports, and one-tap sharing.", + solution: + "A freemium product positioned as ₹1/day that turns festival marketing into a five-minute workflow — with templates, greetings, and exports shaped for how Indian shops actually sell on WhatsApp.", outcomes: [ "Shop owners create festival posts in under 5 minutes", "40+ Indian festivals with built-in Hindi greetings", @@ -60,6 +83,23 @@ export const caseStudies: CaseStudy[] = [ "Subscription billing", "WhatsApp integration", ], + tech: ["React", "TypeScript", "Node.js", "PostgreSQL"], + architecture: + "Template-driven generation pipeline with shop profile injection, festival calendar content, and export formats optimized for WhatsApp Status and sharing.", + challenges: [ + "Scoping a design product tightly enough for non-designers", + "Pricing for small merchants without enterprise complexity", + "Building festival content that feels local, not generic", + ], + contributions: [ + "Conceived, designed, and shipped the product end-to-end", + "Built template and export workflows for WhatsApp-first marketing", + "Defined freemium positioning and festival content model", + ], + lessons: [ + "Narrow problems convert better than broad design tools.", + "Owning the product teaches judgment that client work alone cannot.", + ], engagement: "founder", featured: true, }, @@ -71,17 +111,20 @@ export const caseStudies: CaseStudy[] = [ image: withBasePath("/images/projects/vendor-infra.jpg"), client: "Vendor Infra", liveUrl: "https://vendorinfra.com", + year: "2024–2025", summary: "AI-powered operating system for infrastructure, construction, and manufacturing — with separate admin and customer portals.", problem: "Infrastructure and construction businesses manage vendors, projects, and operations across fragmented tools — with no single system built for how the industry actually works.", approach: "Delivered a dual-portal platform: an admin operations hub for internal teams and a customer-facing portal for vendor and project stakeholders — with industry-specific workflows and AI-assisted operations.", + solution: + "A dual-portal B2B system where internal ops and external stakeholders share one operating layer — plus performance, security, and CI/CD hardening so the platform can grow without becoming fragile.", outcomes: [ "Admin portal for internal operations and vendor management", "Customer portal for external stakeholder access", "Unified platform replacing fragmented industry tooling", - "AI-assisted workflows for construction and manufacturing ops", + "CI/CD and deployment automation for safer releases", ], context: "Built for infrastructure, construction, and manufacturing — not adapted from a generic CRM.", @@ -89,11 +132,337 @@ export const caseStudies: CaseStudy[] = [ "Dual-portal architecture", "B2B platform", "Industry workflows", - "AI integration", + "CI/CD & security", + ], + tech: [ + "React", + "TypeScript", + "Node.js", + "PostgreSQL", + "GitHub Actions", + "Docker", + ], + architecture: + "Separated admin and customer portals on shared domain services, with automation for builds and deployments so operational changes ship with less risk.", + challenges: [ + "Encoding industry workflows without drowning users in CRM complexity", + "Hardening security and delivery pipelines on a live product", + "Balancing AI-assisted features with predictable core operations", + ], + contributions: [ + "Delivered dual-portal product surfaces for ops and customers", + "Optimized frontend and backend performance under real usage", + "Improved security posture and GitHub Actions CI/CD automation", + ], + lessons: [ + "Industry software wins when it mirrors how teams already work.", + "Delivery automation is part of product quality — not an afterthought.", ], engagement: "client", featured: true, }, + { + slug: "solar-monitoring", + title: "Solar Monitoring Platform", + category: "IoT · Multi-client Dashboard", + visual: "iot", + year: "2020–Present", + summary: + "Multi-client IoT dashboard for solar monitoring — real-time graphs, reusable chart systems, and performance-tuned visualization for operators who need clarity at a glance.", + problem: + "Solar operators needed a reliable way to monitor sites across clients — with live telemetry, clear graphs, and an architecture that would not collapse as more devices and tenants came online.", + approach: + "Built an Angular IoT dashboard with multi-client architecture, real-time graphing, and reusable chart components — optimizing rendering so operators could trust the data under load.", + solution: + "A production monitoring platform where singleton services and dependency injection keep shared state coherent, while reusable visualization components keep dashboards consistent across clients.", + outcomes: [ + "Multi-client solar monitoring in production", + "Real-time graph rendering for operator decision-making", + "Reusable chart system reducing UI drift across views", + "Performance work that kept dashboards usable as data grew", + ], + context: + "Long-running product engineering at Vcraft — iterating on architecture while the platform stayed live.", + capabilities: [ + "IoT dashboards", + "Real-time visualization", + "Multi-tenant UI", + "Performance optimization", + ], + tech: ["Angular", "TypeScript", "RxJS", "Charting", "IoT telemetry"], + architecture: + "Angular application with shared singleton services for telemetry state, multi-client routing/context, and a reusable chart layer so new views compose without rewriting visualization logic.", + challenges: [ + "Understanding Angular DI deeply enough to avoid subtle singleton bugs", + "Keeping graph rendering fast with continuous telemetry", + "Supporting multiple clients without forking the UI codebase", + ], + contributions: [ + "Owned dashboard architecture and reusable graph components", + "Optimized rendering paths for real-time monitoring views", + "Shaped multi-client product structure for long-term maintainability", + ], + lessons: [ + "Reusable visualization systems pay for themselves the moment a second client appears.", + "IoT products fail quietly when frontend architecture cannot keep up with telemetry.", + ], + engagement: "employment", + featured: false, + }, + { + slug: "graphic-design-platform", + title: "Graphic Design Platform", + category: "Creative Tooling", + visual: "design", + year: "2021–Present", + summary: + "A Canva-like design product with SVG editing, brand kits, templates, zoomable canvas, and separate portals for admins and designers.", + problem: + "Teams needed branded design tooling inside their own product — not another consumer design app — with templates, fonts, brand kits, and role-based access for designers and operators.", + approach: + "Built an Angular + Fabric.js canvas editor with SVG editing, third-party fonts, zoom, template management, JSON persistence, and admin/designer portals with authentication and authorization.", + solution: + "A full creative workspace: canvas editing, brand kit controls, category and template management, and persisted designs as JSON — with clear portals for the people who create versus the people who administer.", + outcomes: [ + "Production SVG/canvas editing for brand assets", + "Brand kit and third-party font support", + "Template and category management for operators", + "Separate admin and designer experiences with authz", + ], + context: + "Complex frontend systems work — state, canvas performance, and role boundaries all matter.", + capabilities: [ + "Canvas editing", + "Brand systems", + "Role-based portals", + "Template platforms", + ], + tech: [ + "Angular", + "Angular Material", + "Fabric.js", + "TypeScript", + "SVG", + "JSON persistence", + ], + architecture: + "Editor core on Fabric.js with Angular application shells for designer and admin portals; designs serialized to JSON for persistence and template reuse.", + challenges: [ + "Keeping canvas interactions smooth while supporting zoom and complex SVG", + "Modeling brand kits and fonts without breaking templates", + "Separating admin and designer concerns cleanly", + ], + contributions: [ + "Implemented canvas editing, zoom, and SVG workflows", + "Built brand kit, template, and category management", + "Delivered authentication, authorization, and dual portals", + ], + lessons: [ + "Creative tools succeed when persistence and permissions are first-class — not bolted onto a demo canvas.", + "Role-separated portals prevent product complexity from leaking into the editor itself.", + ], + engagement: "employment", + featured: false, + }, + { + slug: "digital-invoice", + title: "Digital Invoice Solution", + category: "B2B · Web & Mobile", + visual: "commerce", + year: "2021–Present", + summary: + "End-to-end invoicing and catalog product across web and mobile — with JWT sessions, Redis-backed state, and scalable frontend architecture.", + problem: + "Businesses needed a reliable way to manage catalogs and invoice workflows across web and mobile — with secure sessions and APIs that would not fall apart as usage grew.", + approach: + "Delivered Angular web, React Native mobile, and Node.js APIs with Redis integration, JWT session management, catalog flows, and authorization across the product surface.", + solution: + "A cross-platform invoice system where catalog, workflow, and auth share one backend model — so web and mobile stay aligned while Redis and JWT keep sessions and performance under control.", + outcomes: [ + "Production web and mobile invoice experiences", + "Catalog management tied to invoice workflows", + "JWT session management with Redis integration", + "Shared API layer supporting scalable frontend architecture", + ], + context: + "Full-stack ownership — mobile, web, and backend delivered as one product system.", + capabilities: [ + "Cross-platform delivery", + "Invoice workflows", + "Auth & sessions", + "API architecture", + ], + tech: [ + "Angular", + "React Native", + "Node.js", + "Redis", + "JWT", + "REST APIs", + ], + architecture: + "Shared Node.js API with JWT auth and Redis-backed session/cache concerns; Angular web and React Native mobile clients consuming the same domain workflows for catalogs and invoices.", + challenges: [ + "Keeping web and mobile workflows consistent without duplicating business logic", + "Session reliability across devices", + "Structuring frontend architecture to scale as features grew", + ], + contributions: [ + "Built mobile and web application surfaces", + "Implemented catalog and invoice workflows", + "Delivered auth, JWT sessions, Redis integration, and backend APIs", + ], + lessons: [ + "Cross-platform products stay coherent when the API owns the domain, not each client.", + "Session and cache design is product reliability work — users feel it immediately.", + ], + engagement: "employment", + featured: false, + }, + { + slug: "vipra-community", + title: "Vipra Community App", + category: "Community Platform", + visual: "mobile", + year: "2022–Present", + summary: + "Community platform with matrimony, business directory, and jobs — used by real people for real life outcomes.", + problem: + "A community needed one place for matrimony, local business discovery, and jobs — without stitching together unrelated consumer apps that ignore community context.", + approach: + "Built a community product spanning matrimony, business directory, and job listings as connected experiences inside one platform.", + solution: + "A multi-module community application where people can find partners, businesses, and opportunities in a trusted shared space.", + outcomes: [ + "Matrimony, directory, and jobs live in one community product", + "Production usage by community members", + "Two successful marriages attributed to the platform by users", + ], + context: + "A reminder that software quality is measured in human outcomes — not only dashboards.", + capabilities: [ + "Community platforms", + "Directory products", + "Matrimony workflows", + "Jobs listings", + ], + tech: ["React Native", "Node.js", "REST APIs"], + architecture: + "Modular community product with shared identity and connected modules for matrimony, business discovery, and employment.", + challenges: [ + "Balancing sensitive matrimony flows with broader community features", + "Keeping directory and jobs useful without becoming spam surfaces", + ], + contributions: [ + "Delivered community modules for matrimony, business, and jobs", + "Shipped production experiences used by real community members", + ], + lessons: [ + "When users trust a product with their personal life, reliability and care are non-negotiable.", + "Community products succeed when modules feel connected — not like three apps sharing a login.", + ], + engagement: "employment", + featured: false, + }, + { + slug: "nstream-connect", + title: "nStreamConnect", + category: "IoT · Device Experience", + visual: "iot", + year: "2019–2020", + summary: + "IoT entertainment device experience for travelers — production frontend, media playback, and tight collaboration with embedded hardware.", + problem: + "Travelers needed a polished entertainment experience on a dedicated IoT device — with media playback that felt intentional, not like a bolted-on browser demo.", + approach: + "Developed the complete frontend UI from Figma, integrated media/video playback, and worked directly with embedded hardware teams to ship a device-ready experience.", + solution: + "A device-facing entertainment interface that translates design into production UI and stays coordinated with the hardware constraints of the product.", + outcomes: [ + "Complete production frontend for the traveler entertainment device", + "Figma designs implemented as shippable UI", + "Media/video player integration on device", + "Collaboration loop with embedded hardware engineers", + ], + context: + "Early career product work at NEON TSS — where software quality shows up on physical devices.", + capabilities: [ + "Device UI", + "Media playback", + "Design-to-production", + "Hardware collaboration", + ], + tech: ["Frontend UI", "Media player integration", "Embedded collaboration"], + architecture: + "Device-facing frontend layered over embedded capabilities, with media playback integrated into the traveler entertainment flow.", + challenges: [ + "Shipping polished UI under device and hardware constraints", + "Coordinating frontend delivery with embedded timelines", + "Making media playback feel native on the device", + ], + contributions: [ + "Owned the complete frontend UI delivery", + "Converted Figma into production interfaces", + "Integrated media/video playback and partnered with hardware teams", + ], + lessons: [ + "IoT frontends succeed when designers, frontend, and embedded teams share one definition of done.", + "On devices, performance and clarity matter more than feature count.", + ], + engagement: "employment", + featured: false, + }, + { + slug: "raspberry-pi-monitoring", + title: "Raspberry Pi Monitoring Device", + category: "Embedded · Monitoring", + visual: "iot", + year: "2019–2020", + summary: + "Camera-based monitoring on Raspberry Pi Zero — MotionEye integration, camera modules, and Raspberry Pi OS customization for a reliable field device.", + problem: + "A monitoring product needed a dependable camera device stack — not a fragile hobby setup — with OS-level customization and MotionEye integration that field teams could trust.", + approach: + "Configured Raspberry Pi Zero devices with camera modules, MotionEye, and Raspberry Pi OS customizations, including embedded Linux modifications for the product environment.", + solution: + "A field-ready monitoring device image: camera capture, MotionEye workflows, and OS-level changes tailored to how the product actually runs.", + outcomes: [ + "Raspberry Pi Zero monitoring devices brought to a usable product state", + "Camera module and MotionEye integration", + "Customized Raspberry Pi OS for the deployment context", + ], + context: + "Embedded-adjacent engineering that complements application work — useful when products leave the browser.", + capabilities: [ + "Embedded Linux", + "Camera systems", + "Device imaging", + "Field monitoring", + ], + tech: [ + "Raspberry Pi Zero", + "MotionEye", + "Camera module", + "Raspberry Pi OS", + "Embedded Linux", + ], + architecture: + "Device image centered on Raspberry Pi Zero + camera module, with MotionEye for capture/monitoring workflows and OS customizations for product constraints.", + challenges: [ + "Stabilizing hobbyist hardware into something product-shaped", + "Balancing OS customization with maintainability", + ], + contributions: [ + "Integrated camera modules and MotionEye", + "Customized Raspberry Pi OS and embedded Linux configuration", + ], + lessons: [ + "Device products need reproducible images — manual setup does not scale.", + "Embedded constraints force clarity about what the software must not do.", + ], + engagement: "employment", + featured: false, + }, ]; export function getCaseStudyBySlug(slug: string): CaseStudy | undefined { diff --git a/src/data/experience.ts b/src/data/experience.ts new file mode 100644 index 0000000..e92aec1 --- /dev/null +++ b/src/data/experience.ts @@ -0,0 +1,67 @@ +import type { ExperienceRole } from "@/types"; + +export const experience: ExperienceRole[] = [ + { + id: "vcraft", + company: "Vcraft Innovations", + role: "Senior Software Developer", + location: "India", + start: "Sep 2020", + end: "Present", + summary: + "Owning product engineering across web, mobile, and IoT platforms — from architecture and delivery through production operations for multi-client systems.", + highlights: [ + "Led frontend architecture for multi-tenant IoT dashboards with real-time visualization", + "Shipped a Canva-style design platform with SVG editing, brand kits, and role-based portals", + "Built end-to-end invoice and catalog systems spanning React Native, Angular, Node.js, and Redis", + "Delivered community and matrimony products used by real users in production", + ], + projects: [ + "solar-monitoring", + "graphic-design-platform", + "digital-invoice", + "vipra-community", + ], + }, + { + id: "neon-tss", + company: "NEON TSS", + role: "Associate Software Engineer", + location: "India", + start: "Jul 2019", + end: "Sep 2020", + summary: + "Built production frontend and embedded-facing software for IoT entertainment and monitoring devices — translating designs into reliable interfaces that talk to hardware.", + highlights: [ + "Developed the complete frontend for an IoT entertainment device for travelers", + "Converted Figma designs into production UI and integrated media/video playback", + "Worked closely with embedded hardware teams on device-facing experiences", + "Customized Raspberry Pi OS and MotionEye for camera-based monitoring devices", + ], + projects: ["nstream-connect", "raspberry-pi-monitoring"], + }, +]; + +export const freelanceEngagements = [ + { + id: "vendor-infra", + name: "Vendor Infra", + summary: + "Frontend and backend optimization, security hardening, GitHub Actions CI/CD, and deployment automation for a B2B infrastructure platform.", + href: "/work/vendor-infra", + }, + { + id: "safework-global", + name: "SafeWork Global", + summary: + "Product platform engineering for a compliance-first overseas hiring marketplace — from workflows to production launch.", + href: "/work/safework-global", + }, + { + id: "ishvax", + name: "Ishvax", + summary: + "Production website development with a focus on clarity, performance, and a polished brand presence.", + href: "/work", + }, +] as const; diff --git a/src/data/index.ts b/src/data/index.ts index eec7a8a..ad82a2a 100644 --- a/src/data/index.ts +++ b/src/data/index.ts @@ -1,10 +1,13 @@ export { aboutContent } from "./about"; +export { capabilityDomains, proofSignals } from "./capabilities"; export { caseStudies, getCaseStudyBySlug, getFeaturedCaseStudies, } from "./case-studies"; +export { experience, freelanceEngagements } from "./experience"; export { faqItems } from "./faq"; export { engagementPrinciples, processSteps } from "./process"; export { getServiceBySlug, services } from "./services"; export { testimonials } from "./testimonials"; +export { getWritingPostBySlug, writingPosts } from "./writing"; \ No newline at end of file diff --git a/src/data/writing.ts b/src/data/writing.ts new file mode 100644 index 0000000..6bb0773 --- /dev/null +++ b/src/data/writing.ts @@ -0,0 +1,12 @@ +import type { WritingPost } from "@/types"; + +/** + * MDX-ready writing archive. + * Add posts here (and corresponding MDX files under content/writing/) as they ship. + * Intentionally empty — no placeholder articles. + */ +export const writingPosts: WritingPost[] = []; + +export function getWritingPostBySlug(slug: string): WritingPost | undefined { + return writingPosts.find((post) => post.slug === slug); +} diff --git a/src/features/about/about-page.tsx b/src/features/about/about-page.tsx index 7b99730..8d49fd3 100644 --- a/src/features/about/about-page.tsx +++ b/src/features/about/about-page.tsx @@ -9,7 +9,7 @@ import { Heading, Text } from "@/components/common/typography"; import { Button } from "@/components/ui/button"; import { PageHeader } from "@/components/layout/page-header"; import { cta } from "@/constants"; -import { aboutContent } from "@/data/about"; +import { aboutContent, experience } from "@/data"; import { siteConfig } from "@/config/site"; export function AboutPage() { @@ -22,6 +22,9 @@ export function AboutPage() {
+

+ {siteConfig.location} +

@@ -44,6 +47,65 @@ export function AboutPage() { +
+ + +
+ Available for + + Ways founders and teams usually engage. + +
+
+
    + {aboutContent.availability.map((item) => ( +
  • + {item} +
  • + ))} +
+
+
+ +
+ + +
+
+ Career path + + A short view of where the work has lived. + +
+ +
+
+ +
    + {experience.map((role, index) => ( +
  • + +
    +

    + {role.start} — {role.end} +

    + + {role.role} + +

    {role.company}

    + + {role.summary} + +
    +
    +
  • + ))} +
+
+
+
@@ -94,10 +156,13 @@ export function AboutPage() { ))} -
+
+
diff --git a/src/features/experience/experience-page.tsx b/src/features/experience/experience-page.tsx new file mode 100644 index 0000000..b1540f7 --- /dev/null +++ b/src/features/experience/experience-page.tsx @@ -0,0 +1,146 @@ +import Link from "next/link"; + +import { Container } from "@/components/common/container"; +import { FadeIn } from "@/components/common/fade-in"; +import { Section } from "@/components/common/section"; +import { Heading, Text } from "@/components/common/typography"; +import { Button } from "@/components/ui/button"; +import { PageHeader } from "@/components/layout/page-header"; +import { cta } from "@/constants"; +import { experience, freelanceEngagements, getCaseStudyBySlug } from "@/data"; + +export function ExperiencePage() { + return ( + <> + + +
+ +
    +
    + {experience.map((role, index) => ( +
  1. + +
    +
    +
    +

    + {role.start} — {role.end} +

    +

    + {role.location} +

    +
    + + {role.role} + +

    {role.company}

    + + {role.summary} + + +
      + {role.highlights.map((item) => ( +
    • + + {item} +
    • + ))} +
    + + {role.projects.length > 0 ? ( +
    +

    Related work

    +
      + {role.projects.map((slug) => { + const study = getCaseStudyBySlug(slug); + if (!study) return null; + return ( +
    • + + {study.title} + +
    • + ); + })} +
    +
    + ) : null} +
    + +
  2. + ))} +
+
+
+ +
+ + +
+ Independent engagements + + Selected partnerships as a product engineering partner — not a + staffing vendor. + +
+
+ +
    + {freelanceEngagements.map((item, index) => ( +
  • + +
    + + + {item.name} + + + + {item.summary} + +
    +
    +
  • + ))} +
+
+
+ +
+ +
+ + Looking for a senior engineering partner? + + + Available for product builds, contract engagements, consulting, and + founding-engineer conversations. + + +
+
+
+ + ); +} diff --git a/src/features/experience/index.ts b/src/features/experience/index.ts new file mode 100644 index 0000000..ef904a7 --- /dev/null +++ b/src/features/experience/index.ts @@ -0,0 +1 @@ +export { ExperiencePage } from "./experience-page"; diff --git a/src/features/home/capabilities-section.tsx b/src/features/home/capabilities-section.tsx new file mode 100644 index 0000000..3057528 --- /dev/null +++ b/src/features/home/capabilities-section.tsx @@ -0,0 +1,52 @@ +import { Container } from "@/components/common/container"; +import { FadeIn } from "@/components/common/fade-in"; +import { Section } from "@/components/common/section"; +import { Heading, Text } from "@/components/common/typography"; +import { capabilityDomains } from "@/data"; + +export function CapabilitiesSection() { + return ( +
+ + +
+ + Capabilities + + + Not a logo wall — domains I use to take products from idea to + production. + +
+
+ +
    + {capabilityDomains.map((domain, index) => ( +
  • + +
    + + {domain.title} + + + {domain.description} + +
      + {domain.items.map((item) => ( +
    • + {item} +
    • + ))} +
    +
    +
    +
  • + ))} +
+
+
+ ); +} diff --git a/src/features/home/experience-preview-section.tsx b/src/features/home/experience-preview-section.tsx new file mode 100644 index 0000000..7013eb7 --- /dev/null +++ b/src/features/home/experience-preview-section.tsx @@ -0,0 +1,73 @@ +import Link from "next/link"; +import { ArrowRight } from "lucide-react"; + +import { Container } from "@/components/common/container"; +import { FadeIn } from "@/components/common/fade-in"; +import { Section } from "@/components/common/section"; +import { Heading, Text } from "@/components/common/typography"; +import { Button } from "@/components/ui/button"; +import { experience } from "@/data"; + +export function ExperiencePreviewSection() { + return ( +
+ + +
+
+ + Experience + + + Nearly seven years owning product engineering — from device UIs + and IoT platforms to SaaS and B2B systems in production. + +
+ +
+
+ +
    +
    + {experience.map((role, index) => ( +
  1. + +
    +
    +

    + {role.start} — {role.end} +

    + + {role.role} + +

    {role.company}

    + + {role.summary} + +
    + +
  2. + ))} +
+ + + + See roles, projects & freelance work + + + +
+
+ ); +} diff --git a/src/features/home/hero-section.tsx b/src/features/home/hero-section.tsx index 6b969b5..d139be9 100644 --- a/src/features/home/hero-section.tsx +++ b/src/features/home/hero-section.tsx @@ -1,53 +1,123 @@ +"use client"; + import Link from "next/link"; +import { Github, Linkedin } from "lucide-react"; +import { motion, useReducedMotion } from "framer-motion"; import { AuthorPortrait } from "@/components/common/author-portrait"; import { Button } from "@/components/ui/button"; import { cta, layout } from "@/constants"; import { siteConfig } from "@/config/site"; +import { fadeInUp, getMotionVariants } from "@/lib/animations"; export function HeroSection() { + const prefersReducedMotion = useReducedMotion(); + const variants = getMotionVariants(fadeInUp, prefersReducedMotion ?? false); + const social = [ + { label: "GitHub", href: siteConfig.author.github, icon: Github }, + { label: "LinkedIn", href: siteConfig.author.linkedin, icon: Linkedin }, + ].filter((item) => item.href.length > 0); + return ( -
+
+
-
-
-

+

+
+ + {siteConfig.name} + · {siteConfig.title} -

-

- Own your product from idea to production -

-

- {siteConfig.description} -

-

- For founders and teams who need a senior partner — not another - vendor — to design, build, and ship software with clarity and - accountability. -

+
-
+ + Own the product. + + Ship what lasts. + + + + + {siteConfig.tagline} I partner with founders and teams as a senior + engineering owner — architecture, build, and production delivery. + + + -
+ {siteConfig.resumeUrl ? ( + + ) : null} +
+ {social.map((item) => { + const Icon = item.icon; + return ( + + + + ); + })} +
+
-
+ -
+

+ {siteConfig.location} +

+
diff --git a/src/features/home/home-cta-section.tsx b/src/features/home/home-cta-section.tsx index fa8fb78..16415d1 100644 --- a/src/features/home/home-cta-section.tsx +++ b/src/features/home/home-cta-section.tsx @@ -2,22 +2,40 @@ import Link from "next/link"; import { Button } from "@/components/ui/button"; import { cta, layout } from "@/constants"; +import { siteConfig } from "@/config/site"; export function HomeCtaSection() { return ( -
-
-
-

- Ready to talk about your product? +
+
+
+
+

+ Next step +

+

+ Building something that needs senior ownership?

-

- Book a discovery call to discuss goals, constraints, and whether we are a good fit. - No pressure — just a clear conversation. +

+ Tell me about the product, the constraints, and the outcome you need. + If we're a fit, we'll define a clear path to production.

- +
+ + +
diff --git a/src/features/home/index.ts b/src/features/home/index.ts index 1216c05..93dd171 100644 --- a/src/features/home/index.ts +++ b/src/features/home/index.ts @@ -1,8 +1,11 @@ export { HeroSection } from "./hero-section"; -export { TrustBarSection } from "./trust-bar-section"; +export { ProofMetricsSection } from "./proof-metrics-section"; export { WhoIHelpSection } from "./who-i-help-section"; export { ServicesPreviewSection } from "./services-preview-section"; export { SelectedWorkSection } from "./selected-work-section"; +export { ExperiencePreviewSection } from "./experience-preview-section"; +export { CapabilitiesSection } from "./capabilities-section"; export { ProcessSnapshotSection } from "./process-snapshot-section"; export { ClientQuotesSection } from "./client-quotes-section"; export { HomeCtaSection } from "./home-cta-section"; +export { TrustBarSection } from "./trust-bar-section"; diff --git a/src/features/home/proof-metrics-section.tsx b/src/features/home/proof-metrics-section.tsx new file mode 100644 index 0000000..4b255b4 --- /dev/null +++ b/src/features/home/proof-metrics-section.tsx @@ -0,0 +1,90 @@ +"use client"; + +import { useEffect, useRef, useState } from "react"; +import { motion, useInView, useReducedMotion } from "framer-motion"; + +import { Container } from "@/components/common/container"; +import { Section } from "@/components/common/section"; +import { proofSignals } from "@/data"; +import { fadeInUp, getMotionVariants } from "@/lib/animations"; + +function AnimatedValue({ + value, + active, +}: { + value: string; + active: boolean; +}) { + const [display, setDisplay] = useState(value); + const prefersReducedMotion = useReducedMotion(); + + useEffect(() => { + const match = value.match(/^(\d+)(\+?)$/); + if (!active || !match || prefersReducedMotion) { + setDisplay(value); + return; + } + + const target = Number(match[1]); + const suffix = match[2] ?? ""; + const durationMs = 900; + const start = performance.now(); + let frame = 0; + + const tick = (now: number) => { + const progress = Math.min(1, (now - start) / durationMs); + const eased = 1 - Math.pow(1 - progress, 3); + setDisplay(`${Math.round(target * eased)}${suffix}`); + if (progress < 1) { + frame = requestAnimationFrame(tick); + } + }; + + frame = requestAnimationFrame(tick); + return () => cancelAnimationFrame(frame); + }, [active, prefersReducedMotion, value]); + + return {display}; +} + +export function ProofMetricsSection() { + const ref = useRef(null); + const inView = useInView(ref, { once: true, margin: "-80px" }); + const prefersReducedMotion = useReducedMotion(); + const variants = getMotionVariants(fadeInUp, prefersReducedMotion ?? false); + + return ( +
+ +

+ Experience snapshot +

+
    + {proofSignals.map((signal, index) => ( +
  • + +

    + +

    +

    {signal.label}

    +

    + {signal.detail} +

    +
    +
  • + ))} +
+
+
+ ); +} diff --git a/src/features/home/selected-work-section.tsx b/src/features/home/selected-work-section.tsx index 408237f..8c335e5 100644 --- a/src/features/home/selected-work-section.tsx +++ b/src/features/home/selected-work-section.tsx @@ -1,5 +1,5 @@ import Link from "next/link"; -import { ArrowRight } from "lucide-react"; +import { ArrowRight, ArrowUpRight } from "lucide-react"; import { CaseStudyBadge } from "@/components/common/case-study-badge"; import { CaseStudyVisual } from "@/components/common/case-study-visual"; @@ -12,6 +12,11 @@ import { getFeaturedCaseStudies } from "@/data"; export function SelectedWorkSection() { const featured = getFeaturedCaseStudies(); + const [primary, ...rest] = featured; + + if (!primary) { + return null; + } return (
@@ -23,51 +28,109 @@ export function SelectedWorkSection() { Selected work - Real products shipped across web, mobile, and IoT — focused on - outcomes, not tech stacks. + Products shipped for founders and operators — told through + problem, approach, and outcomes.

-
    - {featured.map((study, index) => ( -
  • - -
    - - - + +
    + +
    + + + + {primary.title} + + + + {primary.summary} + +
      + {primary.outcomes.slice(0, 3).map((outcome) => ( +
    • + + {outcome} +
    • + ))} +
    +
    + + Read case study + + + {primary.liveUrl ? ( + + Live site + + + ) : null} +
    +
    +
    +
    + + {rest.length > 0 ? ( +
      + {rest.map((study, index) => ( +
    • + +
      + + + + + {study.title} + + + + {study.summary} + - {study.title} + Read case study + - - - {study.summary} - - - Read case study - - -
      -
      -
    • - ))} -
    +
    +
    +
  • + ))} +
+ ) : null}
); diff --git a/src/features/home/services-preview-section.tsx b/src/features/home/services-preview-section.tsx index c5e5daa..5b05804 100644 --- a/src/features/home/services-preview-section.tsx +++ b/src/features/home/services-preview-section.tsx @@ -2,13 +2,6 @@ import Link from "next/link"; import { FadeIn } from "@/components/common/fade-in"; import { Button } from "@/components/ui/button"; -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from "@/components/ui/card"; import { layout } from "@/constants"; import { services } from "@/data/services"; @@ -43,23 +36,17 @@ export function ServicesPreviewSection() {
-
    +
      {preview.map((service, index) => ( -
    • +
    • - - - - {service.title} - - {service.audience} - - -

      - {service.description} -

      -
      -
      +

      {service.title}

      +

      + {service.audience} +

      +

      + {service.description} +

    • ))} diff --git a/src/features/home/who-i-help-section.tsx b/src/features/home/who-i-help-section.tsx index 14f61f0..800eac4 100644 --- a/src/features/home/who-i-help-section.tsx +++ b/src/features/home/who-i-help-section.tsx @@ -8,14 +8,14 @@ const audiences = [ "MVPs and v1 products with clear scope, steady delivery, and production-ready foundations.", }, { - title: "SaaS teams", + title: "SaaS & product teams", description: - "Feature development, integrations, and reliability work on existing codebases.", + "Feature development, integrations, and reliability work when you need senior ownership without hiring full-time yet.", }, { title: "Agencies & businesses", description: - "A technical partner who can own delivery end-to-end without constant hand-holding.", + "A technical partner who can own delivery end-to-end — architecture through deployment — without constant hand-holding.", }, ] as const; @@ -38,7 +38,7 @@ export function WhoIHelpSection() {

      Decision makers who need a trusted engineering partner — not a - résumé or a rate card. + résumé dump or a rate card.

diff --git a/src/features/index.ts b/src/features/index.ts index 08d275c..c6d2c10 100644 --- a/src/features/index.ts +++ b/src/features/index.ts @@ -3,4 +3,6 @@ export * from "./work"; export * from "./process"; export * from "./services"; export * from "./about"; +export * from "./experience"; +export * from "./writing"; export * from "./contact"; diff --git a/src/features/work/case-study-page.tsx b/src/features/work/case-study-page.tsx index 7ae5fc6..4021cee 100644 --- a/src/features/work/case-study-page.tsx +++ b/src/features/work/case-study-page.tsx @@ -1,6 +1,8 @@ import Link from "next/link"; +import { ArrowUpRight } from "lucide-react"; import { notFound } from "next/navigation"; +import { Badge } from "@/components/common/badge"; import { CaseStudyBadge } from "@/components/common/case-study-badge"; import { CaseStudyVisual } from "@/components/common/case-study-visual"; import { Container } from "@/components/common/container"; @@ -16,6 +18,25 @@ type CaseStudyPageProps = { slug: string; }; +function DetailBlock({ + title, + children, +}: { + title: string; + children: React.ReactNode; +}) { + return ( + +
+ + {title} + +
{children}
+
+
+ ); +} + export function CaseStudyPage({ slug }: CaseStudyPageProps) { const study = getCaseStudyBySlug(slug); @@ -32,27 +53,49 @@ export function CaseStudyPage({ slug }: CaseStudyPageProps) { dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} /> -
- +
+
+
- +
+ + {study.year ? ( + + {study.year} + + ) : null} +
{study.title} {study.summary} - {study.liveUrl && ( - - Visit live site ↗ - - )} +
+ {study.liveUrl ? ( + + Visit live site + + + ) : null} + {study.githubUrl ? ( + + GitHub + + + ) : null} +
-
- - - The challenge - - {study.problem} - +
+
+ + {study.problem} + - - - The approach - - - {study.approach} - - + + {study.approach} + - - - Outcomes - -
    - {study.outcomes.map((outcome) => ( -
  • - - {outcome} -
  • - ))} -
-
- - -
- - {study.context} - -

- Capabilities: {study.capabilities.join(" · ")} -

-
-
- - -
- - Building something similar? - - - Let's discuss your product goals and whether I can help you - ship with the same clarity and ownership. + + {study.solution} + + + + + {study.architecture} - -
-
+ + + +
    + {study.challenges.map((item) => ( +
  • + + {item} +
  • + ))} +
+
+ + +
    + {study.contributions.map((item) => ( +
  • + + {item} +
  • + ))} +
+
+ + +
    + {study.outcomes.map((outcome) => ( +
  • + + {outcome} +
  • + ))} +
+
+ + +
    + {study.lessons.map((item) => ( +
  • + + {item} +
  • + ))} +
+
+ + +
+ + {study.context} + +
+
+ + +
+ + Building something similar? + + + Let's discuss your product goals and whether I can help + you ship with the same clarity and ownership. + + +
+
+
+ +
diff --git a/src/features/work/work-page.tsx b/src/features/work/work-page.tsx index 3ed983a..dfe1487 100644 --- a/src/features/work/work-page.tsx +++ b/src/features/work/work-page.tsx @@ -1,5 +1,5 @@ import Link from "next/link"; -import { ArrowRight } from "lucide-react"; +import { ArrowRight, ArrowUpRight } from "lucide-react"; import { CaseStudyBadge } from "@/components/common/case-study-badge"; import { CaseStudyVisual } from "@/components/common/case-study-visual"; @@ -11,17 +11,25 @@ import { PageHeader } from "@/components/layout/page-header"; import { caseStudies } from "@/data"; export function WorkPage() { + const featured = caseStudies.filter((study) => study.featured); + const more = caseStudies.filter((study) => !study.featured); + return ( <>
-
    - {caseStudies.map((study, index) => ( + + + Featured + + +
      + {featured.map((study, index) => (
    • @@ -29,29 +37,33 @@ export function WorkPage() { visual={study.visual} title={study.title} image={study.image} + className="transition-transform duration-500 group-hover:scale-[1.01]" />
      - - - {study.title} - - {study.liveUrl && ( - + - ↗ - - )} + {study.title} + + {study.liveUrl ? ( + + + + ) : null} - + {study.summary}
        @@ -60,7 +72,7 @@ export function WorkPage() { key={outcome} className="text-muted-foreground flex gap-3 text-sm" > - + {outcome} ))} @@ -81,6 +93,64 @@ export function WorkPage() {
+ +
+ + + + More projects + + + Additional production work across employment and product + engineering engagements. + + + +
    + {more.map((study, index) => ( +
  • + +
    + + + + {study.title} + + + + {study.summary} + +
    + {study.tech.slice(0, 4).map((item) => ( + + {item} + {study.tech.indexOf(item) < + Math.min(3, study.tech.length - 1) + ? " ·" + : ""} + + ))} +
    + + Read case study + + +
    +
    +
  • + ))} +
+
+
); } diff --git a/src/features/writing/index.ts b/src/features/writing/index.ts new file mode 100644 index 0000000..2867f6c --- /dev/null +++ b/src/features/writing/index.ts @@ -0,0 +1 @@ +export { WritingPage } from "./writing-page"; diff --git a/src/features/writing/writing-page.tsx b/src/features/writing/writing-page.tsx new file mode 100644 index 0000000..06ef1b5 --- /dev/null +++ b/src/features/writing/writing-page.tsx @@ -0,0 +1,72 @@ +import Link from "next/link"; + +import { Container } from "@/components/common/container"; +import { FadeIn } from "@/components/common/fade-in"; +import { Section } from "@/components/common/section"; +import { Heading, Text } from "@/components/common/typography"; +import { Button } from "@/components/ui/button"; +import { PageHeader } from "@/components/layout/page-header"; +import { cta } from "@/constants"; +import { writingPosts } from "@/data"; + +export function WritingPage() { + const hasPosts = writingPosts.length > 0; + + return ( + <> + + +
+ + {hasPosts ? ( +
    + {writingPosts.map((post, index) => ( +
  • + +
    +
    + + + {post.title} + + + + {post.summary} + +
    +

    + {post.publishedAt} · {post.readingTime} +

    +
    +
    +
  • + ))} +
+ ) : ( + +
+ + Essays coming soon + + + This archive is MDX-ready. The first pieces will cover product + engineering judgment, delivery systems, and lessons from + shipping real software — not recycled developer tips. + + +
+
+ )} +
+
+ + ); +} diff --git a/src/lib/seo.ts b/src/lib/seo.ts index 6310e85..2b367a1 100644 --- a/src/lib/seo.ts +++ b/src/lib/seo.ts @@ -22,6 +22,10 @@ export function createPersonSchema(): JsonLd { email: author.email, description, jobTitle: siteConfig.title, + homeLocation: { + "@type": "Place", + name: siteConfig.location, + }, knowsAbout: siteConfig.keywords, ...(sameAs.length > 0 ? { sameAs } : {}), }; diff --git a/src/providers/index.tsx b/src/providers/index.tsx index a6c1618..f787c59 100644 --- a/src/providers/index.tsx +++ b/src/providers/index.tsx @@ -10,7 +10,7 @@ export function AppProviders({ children }: AppProvidersProps) { return ( diff --git a/src/styles/globals.css b/src/styles/globals.css index b15b92d..0ab00ee 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -125,6 +125,10 @@ :focus-visible { @apply ring-ring ring-offset-background ring-2 ring-offset-2 outline-none; } + + ::selection { + background: color-mix(in oklch, var(--foreground) 16%, transparent); + } } @layer utilities { @@ -135,4 +139,43 @@ .text-pretty { text-wrap: pretty; } + + /* Subtle atmospheric plane for heroes and key surfaces */ + .surface-glow { + background-image: + radial-gradient( + ellipse 80% 55% at 50% -15%, + color-mix(in oklch, var(--foreground) 8%, transparent), + transparent 70% + ), + linear-gradient( + to bottom, + color-mix(in oklch, var(--muted) 45%, transparent), + transparent 55% + ); + } + + .surface-grid { + background-image: + linear-gradient( + to right, + color-mix(in oklch, var(--foreground) 4%, transparent) 1px, + transparent 1px + ), + linear-gradient( + to bottom, + color-mix(in oklch, var(--foreground) 4%, transparent) 1px, + transparent 1px + ); + background-size: 64px 64px; + mask-image: radial-gradient(ellipse 70% 60% at 50% 0%, black, transparent); + } + + .link-underline { + @apply underline decoration-transparent underline-offset-4 transition-colors; + } + + .link-underline:hover { + @apply decoration-foreground/40; + } } diff --git a/src/types/index.ts b/src/types/index.ts index 2e1f566..6cb7ada 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -18,16 +18,48 @@ export type CaseStudy = { summary: string; problem: string; approach: string; + solution: string; outcomes: string[]; context: string; capabilities: string[]; + tech: string[]; + architecture: string; + challenges: string[]; + contributions: string[]; + lessons: string[]; featured: boolean; visual: CaseStudyVisual; image?: string; liveUrl?: string; + githubUrl?: string; client?: string; /** client = built for a client; founder = own product/idea */ - engagement?: "client" | "founder"; + engagement?: "client" | "founder" | "employment"; + year?: string; +}; + +export type ExperienceRole = { + id: string; + company: string; + role: string; + location: string; + start: string; + end: string; + summary: string; + highlights: string[]; + projects: string[]; +}; + +export type CapabilityDomain = { + title: string; + description: string; + items: string[]; +}; + +export type ProofSignal = { + value: string; + label: string; + detail: string; }; export type Testimonial = { @@ -69,3 +101,11 @@ export type ContactFormPayload = { timeline?: string; message: string; }; + +export type WritingPost = { + slug: string; + title: string; + summary: string; + publishedAt: string; + readingTime: string; +};