From 28b4b6de691374031dc91ec7b3b3c0eebf347df5 Mon Sep 17 00:00:00 2001 From: Hem Gabhawala Date: Sun, 19 Jul 2026 13:34:58 +0530 Subject: [PATCH 01/26] =?UTF-8?q?feat(rebuild):=20phase=201=20=E2=80=94=20?= =?UTF-8?q?fresh=20skeleton=20+=20design=20system=20v3=20(industrial=20tel?= =?UTF-8?q?emetry)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit True from-scratch rebuild toward the immersive scenario edition. - Quarantine legacy rendered/logic layer into legacy/ (excluded from tsconfig + ESLint); content preserved for per-phase porting. - Design System v3 (dark industrial telemetry): CRT substrate, white-phosphor fg, four per-scenario accent tokens (red-amber/blue/violet/teal), 2px radius ceiling, 1px hairline system, precision-grid + mono-label utilities. - Fonts Geist -> Inter + JetBrains Mono (doc-mandated two-typeface system). - Add PRODUCT.md + DESIGN.md (impeccable-init equivalent) locking audience, brand lane, palette, typefaces, motion law. - lint-staged: add --no-warn-ignored so staged legacy/ files don't fail the hook. - Minimal server-rendered Phase 1 skeleton page; build passes (static prerender, 113 kB First Load JS), typecheck + lint clean. Direction set with taste-skill brutalist (Tactical Telemetry) lens; three deliberate overrides where 03_UI_UX.md wins (4 accents, 2px radius, restraint). Co-Authored-By: Claude Opus 4.8 --- DESIGN.md | 77 ++++ Docs/01_PRD.md | 99 ++-- Docs/02_TRD.md | 97 +++- Docs/03_UI_UX.md | 120 ++++- Docs/04_Implementation_Plan.md | 74 ++- Docs/05_BackendSchema.md | 107 ++++- Docs/06_App_Flow.md | 64 ++- Docs/COMPONENT_ARCHITECTURE.md | 41 -- Docs/DESIGN_SYSTEM.md | 93 ---- PRODUCT.md | 47 ++ app/layout.tsx | 98 +--- app/page.tsx | 156 ++++--- eslint.config.mjs | 1 + .../_root_docs/DESIGN_SYSTEM.md | 0 .../_root_docs/MODERN_AESTHETIC.md | 0 .../_root_docs/PROJECT_SUMMARY.md | 0 {app => legacy/app}/api/contact/route.ts | 0 {app => legacy/app}/api/github/route.ts | 0 {app => legacy/app}/dev/tokens/page.tsx | 2 +- {app => legacy/app}/error.tsx | 0 legacy/app/favicon.ico | Bin 0 -> 25931 bytes legacy/app/layout.tsx | 97 ++++ {app => legacy/app}/loading.tsx | 0 {app => legacy/app}/not-found.tsx | 0 legacy/app/page.tsx | 81 ++++ {app => legacy/app}/robots.ts | 0 {app => legacy/app}/sitemap.ts | 0 {components => legacy/components}/README.md | 0 .../certifications/certification-card.tsx | 8 +- .../certifications/certification-grid.tsx | 4 +- .../certifications/certifications.tsx | 0 .../certifications/tryhackme-section.tsx | 8 +- .../components}/contact/contact-form.tsx | 0 .../components}/contact/contact-section.tsx | 10 +- .../components}/contact/use-contact-form.ts | 0 .../experience/experience-timeline.tsx | 0 .../components}/github/github-card.tsx | 12 +- .../components}/github/github-grid.tsx | 4 +- .../components}/github/github-section.tsx | 4 +- .../components}/layout/back-to-top.tsx | 0 .../components}/layout/container.tsx | 0 .../components}/layout/footer.tsx | 0 .../components}/layout/layout-client.tsx | 0 .../components}/layout/mobile-nav.tsx | 2 +- .../components}/layout/nav-links.tsx | 0 .../components}/layout/scroll-progress.tsx | 0 .../components}/layout/section-premium.tsx | 6 +- .../components}/layout/site-header.tsx | 2 +- .../components}/layout/site-nav.tsx | 0 .../components}/providers/motion-provider.tsx | 0 .../components}/providers/theme-provider.tsx | 0 .../components}/sections/.gitkeep | 0 .../components}/sections/about-content.tsx | 6 +- .../components}/sections/about.tsx | 0 .../components}/sections/hero-client.tsx | 0 .../components}/sections/hero-elite.tsx | 26 +- .../components}/sections/hero-particles.tsx | 0 .../components}/sections/hero.tsx | 0 .../components}/sections/journey-timeline.tsx | 0 .../sections/project-card-enhanced.tsx | 0 .../sections/project-detail-trigger.tsx | 0 .../components}/sections/project-detail.tsx | 4 +- .../components}/sections/project-explorer.tsx | 0 .../components}/sections/project-filter.tsx | 0 .../components}/sections/project-rows.tsx | 18 +- .../components}/sections/projects.tsx | 0 .../sections/skill-category-card-enhanced.tsx | 0 .../sections/skill-category-card.tsx | 0 .../components}/sections/skill-grid.tsx | 0 .../components}/sections/skills.tsx | 2 +- .../components}/shared/brand-icons.tsx | 0 .../components}/shared/fade-in-view.tsx | 0 .../shared/numbered-detail-list.tsx | 14 +- .../components}/shared/page-transition.tsx | 0 .../components}/shared/scroll-reveal.tsx | 0 .../components}/shared/section-heading.tsx | 0 .../components}/shared/social-links.tsx | 0 .../components}/shared/tech-badge.tsx | 0 .../components}/shared/theme-toggle.tsx | 0 {components => legacy/components}/ui/.gitkeep | 0 .../components}/ui/button-premium.tsx | 0 .../components}/ui/button.tsx | 0 .../components}/ui/card-premium.tsx | 0 {components => legacy/components}/ui/card.tsx | 0 .../components}/ui/dialog.tsx | 4 +- .../components}/ui/dropdown-menu.tsx | 12 +- .../components}/ui/input.tsx | 0 .../components}/ui/navigation-menu.tsx | 10 +- .../components}/ui/separator.tsx | 0 .../components}/ui/sheet.tsx | 12 +- .../components}/ui/skeleton.tsx | 0 .../components}/ui/tooltip.tsx | 2 +- {data => legacy/data}/.gitkeep | 0 {data => legacy/data}/README.md | 0 {data => legacy/data}/blog.ts | 0 {data => legacy/data}/certificates.ts | 0 {data => legacy/data}/experience.ts | 0 {data => legacy/data}/github-repos.ts | 0 {data => legacy/data}/navigation.ts | 0 {data => legacy/data}/profile.ts | 0 {data => legacy/data}/projects.ts | 0 {data => legacy/data}/skills.ts | 0 {data => legacy/data}/timeline.ts | 0 {data => legacy/data}/tryhackme.ts | 0 {hooks => legacy/hooks}/use-cursor.ts | 0 {lib => legacy/lib}/data/.gitkeep | 0 {lib => legacy/lib}/data/data.test.ts | 0 {lib => legacy/lib}/data/index.ts | 0 {lib => legacy/lib}/design/contrast.test.ts | 0 {lib => legacy/lib}/design/contrast.ts | 0 {lib => legacy/lib}/design/design-system.ts | 0 {lib => legacy/lib}/env.test.ts | 0 {lib => legacy/lib}/env.ts | 0 {lib => legacy/lib}/github.ts | 0 .../lib}/hooks/use-active-section.ts | 0 {lib => legacy/lib}/motion.ts | 0 {lib => legacy/lib}/motion/animations.ts | 0 {lib => legacy/lib}/motion/use-animations.ts | 0 {lib => legacy/lib}/rate-limit.ts | 0 {lib => legacy/lib}/seo/config.ts | 0 {lib => legacy/lib}/seo/jsonld.ts | 0 {lib => legacy/lib}/seo/metadata.ts | 0 {lib => legacy/lib}/types/.gitkeep | 0 {lib => legacy/lib}/types/blog.ts | 0 {lib => legacy/lib}/types/certificate.ts | 0 {lib => legacy/lib}/types/contact.ts | 0 {lib => legacy/lib}/types/experience.ts | 0 {lib => legacy/lib}/types/index.ts | 0 {lib => legacy/lib}/types/navigation.ts | 0 {lib => legacy/lib}/types/profile.ts | 0 {lib => legacy/lib}/types/project.ts | 0 {lib => legacy/lib}/types/skill.ts | 0 {lib => legacy/lib}/types/timeline.ts | 0 {lib => legacy/lib}/utils/index.ts | 0 {lib => legacy/lib}/validations/.gitkeep | 0 {lib => legacy/lib}/validations/blog.ts | 0 .../lib}/validations/certificate.ts | 0 .../lib}/validations/contact.test.ts | 0 {lib => legacy/lib}/validations/contact.ts | 0 {lib => legacy/lib}/validations/experience.ts | 0 {lib => legacy/lib}/validations/index.ts | 0 {lib => legacy/lib}/validations/navigation.ts | 0 {lib => legacy/lib}/validations/profile.ts | 0 {lib => legacy/lib}/validations/project.ts | 0 {lib => legacy/lib}/validations/shared.ts | 0 {lib => legacy/lib}/validations/skill.ts | 0 {lib => legacy/lib}/validations/timeline.ts | 0 {styles => legacy/styles}/CONTRAST.md | 0 legacy/styles/globals.css | 357 +++++++++++++++ lib/utils.ts | 10 + package.json | 2 +- styles/globals.css | 424 +++++------------- tsconfig.json | 2 +- 153 files changed, 1436 insertions(+), 783 deletions(-) create mode 100644 DESIGN.md delete mode 100644 Docs/COMPONENT_ARCHITECTURE.md delete mode 100644 Docs/DESIGN_SYSTEM.md create mode 100644 PRODUCT.md rename DESIGN_SYSTEM.md => legacy/_root_docs/DESIGN_SYSTEM.md (100%) rename MODERN_AESTHETIC.md => legacy/_root_docs/MODERN_AESTHETIC.md (100%) rename PROJECT_SUMMARY.md => legacy/_root_docs/PROJECT_SUMMARY.md (100%) rename {app => legacy/app}/api/contact/route.ts (100%) rename {app => legacy/app}/api/github/route.ts (100%) rename {app => legacy/app}/dev/tokens/page.tsx (99%) rename {app => legacy/app}/error.tsx (100%) create mode 100644 legacy/app/favicon.ico create mode 100644 legacy/app/layout.tsx rename {app => legacy/app}/loading.tsx (100%) rename {app => legacy/app}/not-found.tsx (100%) create mode 100644 legacy/app/page.tsx rename {app => legacy/app}/robots.ts (100%) rename {app => legacy/app}/sitemap.ts (100%) rename {components => legacy/components}/README.md (100%) rename {components => legacy/components}/certifications/certification-card.tsx (83%) rename {components => legacy/components}/certifications/certification-grid.tsx (88%) rename {components => legacy/components}/certifications/certifications.tsx (100%) rename {components => legacy/components}/certifications/tryhackme-section.tsx (81%) rename {components => legacy/components}/contact/contact-form.tsx (100%) rename {components => legacy/components}/contact/contact-section.tsx (86%) rename {components => legacy/components}/contact/use-contact-form.ts (100%) rename {components => legacy/components}/experience/experience-timeline.tsx (100%) rename {components => legacy/components}/github/github-card.tsx (85%) rename {components => legacy/components}/github/github-grid.tsx (87%) rename {components => legacy/components}/github/github-section.tsx (87%) rename {components => legacy/components}/layout/back-to-top.tsx (100%) rename {components => legacy/components}/layout/container.tsx (100%) rename {components => legacy/components}/layout/footer.tsx (100%) rename {components => legacy/components}/layout/layout-client.tsx (100%) rename {components => legacy/components}/layout/mobile-nav.tsx (96%) rename {components => legacy/components}/layout/nav-links.tsx (100%) rename {components => legacy/components}/layout/scroll-progress.tsx (100%) rename {components => legacy/components}/layout/section-premium.tsx (93%) rename {components => legacy/components}/layout/site-header.tsx (96%) rename {components => legacy/components}/layout/site-nav.tsx (100%) rename {components => legacy/components}/providers/motion-provider.tsx (100%) rename {components => legacy/components}/providers/theme-provider.tsx (100%) rename {components => legacy/components}/sections/.gitkeep (100%) rename {components => legacy/components}/sections/about-content.tsx (66%) rename {components => legacy/components}/sections/about.tsx (100%) rename {components => legacy/components}/sections/hero-client.tsx (100%) rename {components => legacy/components}/sections/hero-elite.tsx (83%) rename {components => legacy/components}/sections/hero-particles.tsx (100%) rename {components => legacy/components}/sections/hero.tsx (100%) rename {components => legacy/components}/sections/journey-timeline.tsx (100%) rename {components => legacy/components}/sections/project-card-enhanced.tsx (100%) rename {components => legacy/components}/sections/project-detail-trigger.tsx (100%) rename {components => legacy/components}/sections/project-detail.tsx (94%) rename {components => legacy/components}/sections/project-explorer.tsx (100%) rename {components => legacy/components}/sections/project-filter.tsx (100%) rename {components => legacy/components}/sections/project-rows.tsx (84%) rename {components => legacy/components}/sections/projects.tsx (100%) rename {components => legacy/components}/sections/skill-category-card-enhanced.tsx (100%) rename {components => legacy/components}/sections/skill-category-card.tsx (100%) rename {components => legacy/components}/sections/skill-grid.tsx (100%) rename {components => legacy/components}/sections/skills.tsx (89%) rename {components => legacy/components}/shared/brand-icons.tsx (100%) rename {components => legacy/components}/shared/fade-in-view.tsx (100%) rename {components => legacy/components}/shared/numbered-detail-list.tsx (91%) rename {components => legacy/components}/shared/page-transition.tsx (100%) rename {components => legacy/components}/shared/scroll-reveal.tsx (100%) rename {components => legacy/components}/shared/section-heading.tsx (100%) rename {components => legacy/components}/shared/social-links.tsx (100%) rename {components => legacy/components}/shared/tech-badge.tsx (100%) rename {components => legacy/components}/shared/theme-toggle.tsx (100%) rename {components => legacy/components}/ui/.gitkeep (100%) rename {components => legacy/components}/ui/button-premium.tsx (100%) rename {components => legacy/components}/ui/button.tsx (100%) rename {components => legacy/components}/ui/card-premium.tsx (100%) rename {components => legacy/components}/ui/card.tsx (100%) rename {components => legacy/components}/ui/dialog.tsx (87%) rename {components => legacy/components}/ui/dropdown-menu.tsx (77%) rename {components => legacy/components}/ui/input.tsx (100%) rename {components => legacy/components}/ui/navigation-menu.tsx (66%) rename {components => legacy/components}/ui/separator.tsx (100%) rename {components => legacy/components}/ui/sheet.tsx (79%) rename {components => legacy/components}/ui/skeleton.tsx (100%) rename {components => legacy/components}/ui/tooltip.tsx (76%) rename {data => legacy/data}/.gitkeep (100%) rename {data => legacy/data}/README.md (100%) rename {data => legacy/data}/blog.ts (100%) rename {data => legacy/data}/certificates.ts (100%) rename {data => legacy/data}/experience.ts (100%) rename {data => legacy/data}/github-repos.ts (100%) rename {data => legacy/data}/navigation.ts (100%) rename {data => legacy/data}/profile.ts (100%) rename {data => legacy/data}/projects.ts (100%) rename {data => legacy/data}/skills.ts (100%) rename {data => legacy/data}/timeline.ts (100%) rename {data => legacy/data}/tryhackme.ts (100%) rename {hooks => legacy/hooks}/use-cursor.ts (100%) rename {lib => legacy/lib}/data/.gitkeep (100%) rename {lib => legacy/lib}/data/data.test.ts (100%) rename {lib => legacy/lib}/data/index.ts (100%) rename {lib => legacy/lib}/design/contrast.test.ts (100%) rename {lib => legacy/lib}/design/contrast.ts (100%) rename {lib => legacy/lib}/design/design-system.ts (100%) rename {lib => legacy/lib}/env.test.ts (100%) rename {lib => legacy/lib}/env.ts (100%) rename {lib => legacy/lib}/github.ts (100%) rename {lib => legacy/lib}/hooks/use-active-section.ts (100%) rename {lib => legacy/lib}/motion.ts (100%) rename {lib => legacy/lib}/motion/animations.ts (100%) rename {lib => legacy/lib}/motion/use-animations.ts (100%) rename {lib => legacy/lib}/rate-limit.ts (100%) rename {lib => legacy/lib}/seo/config.ts (100%) rename {lib => legacy/lib}/seo/jsonld.ts (100%) rename {lib => legacy/lib}/seo/metadata.ts (100%) rename {lib => legacy/lib}/types/.gitkeep (100%) rename {lib => legacy/lib}/types/blog.ts (100%) rename {lib => legacy/lib}/types/certificate.ts (100%) rename {lib => legacy/lib}/types/contact.ts (100%) rename {lib => legacy/lib}/types/experience.ts (100%) rename {lib => legacy/lib}/types/index.ts (100%) rename {lib => legacy/lib}/types/navigation.ts (100%) rename {lib => legacy/lib}/types/profile.ts (100%) rename {lib => legacy/lib}/types/project.ts (100%) rename {lib => legacy/lib}/types/skill.ts (100%) rename {lib => legacy/lib}/types/timeline.ts (100%) rename {lib => legacy/lib}/utils/index.ts (100%) rename {lib => legacy/lib}/validations/.gitkeep (100%) rename {lib => legacy/lib}/validations/blog.ts (100%) rename {lib => legacy/lib}/validations/certificate.ts (100%) rename {lib => legacy/lib}/validations/contact.test.ts (100%) rename {lib => legacy/lib}/validations/contact.ts (100%) rename {lib => legacy/lib}/validations/experience.ts (100%) rename {lib => legacy/lib}/validations/index.ts (100%) rename {lib => legacy/lib}/validations/navigation.ts (100%) rename {lib => legacy/lib}/validations/profile.ts (100%) rename {lib => legacy/lib}/validations/project.ts (100%) rename {lib => legacy/lib}/validations/shared.ts (100%) rename {lib => legacy/lib}/validations/skill.ts (100%) rename {lib => legacy/lib}/validations/timeline.ts (100%) rename {styles => legacy/styles}/CONTRAST.md (100%) create mode 100644 legacy/styles/globals.css create mode 100644 lib/utils.ts diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..2fa5862 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,77 @@ +# DESIGN.md — Design System v3 (Industrial Telemetry) + +> Locked visual system for the scenario rebuild. Direction set with +> `taste-skill:brutalist-skill` (Tactical Telemetry / dark archetype), tempered +> by the typographic + spacing restraint the docs inherit from Apple/Linear/ +> Vercel. Where the skill and `/Docs/03_UI_UX.md` conflict, **the docs win** — +> divergences are noted explicitly below. + +## Archetype + +**Tactical Telemetry (dark).** Deactivated-CRT substrate, white-phosphor +foreground, monospace telemetry, blueprint grid, visible 1px compartmentalization. +Restraint over theatrics — the precision grid is _structure_, not decoration. + +## Deliberate divergences from the brutalist skill (docs override) + +1. **Four per-scenario accents**, not the skill's single-red rule + (`03_UI_UX.md` mandates one accent per scenario). +2. **2px radius ceiling**, not the skill's absolute 90° corners + (`03_UI_UX.md` allows 2–4px). +3. **Whisper-level analog effects** — no CRT scanline wash, no heavy grain, + no ASCII clutter as decoration. Docs forbid Matrix clichés / animation spam. + +## Color (dark substrate — the only mode at launch) + +| Token | Value | Use | +| --------------- | --------- | ----------------------------------------- | +| `--bg` | `#0A0A0B` | Base (deactivated CRT — never pure black) | +| `--surface` | `#111113` | Raised compartments / cards | +| `--surface-2` | `#17171A` | Nested / hover surface | +| `--fg` | `#EAEAEA` | Primary text (white phosphor) | +| `--fg-muted` | `#8A8A90` | Metadata, secondary | +| `--fg-dim` | `#5A5A60` | Tertiary / disabled | +| `--line` | `#232327` | Hairline borders / grid | +| `--line-strong` | `#333338` | Emphasized dividers | + +### Per-scenario accents (WCAG-AA checked on `--bg` in Phase where each ships) + +| Scenario | Slug | Accent | Hex (base) | +| ---------------------- | ----------- | ---------------- | ---------- | +| Offensive Security | `red-amber` | hazard red-amber | `#FF5A1F` | +| GRC & Compliance | `blue` | signal blue | `#3B9EFF` | +| AI-Secured Systems | `violet` | violet | `#9B7BFF` | +| Cloud & Infrastructure | `teal` | teal | `#2DD4BF` | + +Global default accent = **red-amber** (offensive security is the flagship / first +scenario and Hem's strongest work). Each accent also needs a +`-on` foreground pair and a low-alpha `-soft` fill; locked per scenario when it +ships, against WCAG AA — **not** assumed here. + +## Typography (two-typeface max) + +- **Inter** (`--font-sans`) — narrative body, headings. Weights 400/500/600/800. +- **JetBrains Mono** (`--font-mono`) — technical labels, data readouts, nav, + metadata, coordinates, unit IDs. Uppercase + generous tracking for metadata. + +Scale: fluid `clamp()` for macro display type; fixed small (0.72–0.875rem) for +mono telemetry. Tight tracking (−0.02 to −0.04em) + compressed leading on +display; generous tracking (0.06–0.1em) uppercase on mono metadata. + +## Geometry & depth + +- Radius: **2px** ceiling (`--radius`). No soft SaaS cards. +- Depth from **layering + 1px hairlines + contrast**, not shadows or gradients. +- Blueprint grid: thin `--line` linework, `+` crosshairs at intersections used + sparingly as structural markers. + +## Motion (Framer Motion for UI; GSAP ScrollTrigger for 3D only) + +- Entrances ease-out, exits ease-in, ease-in-out only for on-screen A→B moves. +- **Never linear** on UI. Micro-interactions ≤300ms. Scroll-scrubbed scene + choreography is exempt (position-driven, not timed). +- `prefers-reduced-motion` disables at the call site, everywhere. + +## Reference bar (spacing/typography/polish only — do not copy layouts) + +Apple · Stripe · Linear · Vercel · Raycast · Arc. diff --git a/Docs/01_PRD.md b/Docs/01_PRD.md index 76156d9..b8245c1 100644 --- a/Docs/01_PRD.md +++ b/Docs/01_PRD.md @@ -2,52 +2,87 @@ ## Project -Hem Gabhawala -- Cybersecurity Portfolio +Hem Gabhawala — Cybersecurity Portfolio (Immersive Scenario Edition) ## Goal -Build a premium recruiter-focused cybersecurity portfolio that showcases -skills, projects, certifications, experience, and achievements. +Build a premium, recruiter-focused cybersecurity portfolio that uses +scroll-driven, scenario-based storytelling — inspired by Orano's +"Innovation" experience (Awwwards Developer Site of the Year 2018) — +to make technical work tangible instead of a static bullet list, while +remaining fast, accessible, and crawlable for a recruiter audience. + +**Design philosophy for this revision:** the immersive layer is a +*progressive enhancement*, never a gate. Every scenario's content must +exist and be fully readable in the DOM without WebGL. Capable devices +get the full cinematic scene; everything else gets a fast, still-premium +fallback. This is the mechanism that lets us pursue Awwwards-tier craft +without sacrificing the PRD's original 30-second-credibility objective. ## Target Users -- Recruiters -- Hiring Managers -- Security Engineers -- Internship Coordinators +- Recruiters +- Hiring Managers +- Security Engineers +- Internship Coordinators ## Objectives -- Establish credibility in \<30 seconds. -- Highlight practical cybersecurity work. -- Showcase projects (VaultIQ, Dharma, API Pentesting). -- Display certifications and TryHackMe achievements. -- Enable resume download and contact. +- Establish credibility in <30 seconds, even on the non-WebGL fallback path. +- Highlight practical cybersecurity work through interactive "scenarios" + instead of flat lists. +- Showcase projects (VaultIQ, Dharma, Enterprise API Pentesting) as the + narrative payoff of each scenario. +- Display certifications and TryHackMe achievements. +- Enable resume download and contact. +- Deliver at least one genuinely portfolio-differentiating interactive + moment (the pentesting scenario) without compromising load performance. -## Core Pages +## Core Pages / Experience Structure -- Home -- About -- Experience -- Projects -- Certifications -- Skills -- Blog -- Contact +- Home (Hero + entry into scenario flow) +- About +- **Scenarios** (replaces flat "Experience" + "Projects" split — see + App Flow and Backend Schema for structure): + 1. Offensive Security (VAPT / Pentesting) + 2. GRC & Compliance (Dharma) + 3. AI-Secured Systems (VaultIQ) + 4. Cloud & Infrastructure (AWS) +- Certifications +- Skills +- Blog +- Contact ## Functional Requirements -- Responsive design -- Dark mode -- Resume download -- GitHub integration -- Contact form -- SEO -- Fast loading -- Accessibility +- Responsive design, with an explicit non-WebGL/low-power/reduced-motion + fallback path for every scenario (not just a graceful degrade — a + designed, intentional alternate experience) +- Dark mode as the base UI (not a toggle — see UI/UX doc) +- Capability detection (WebGL support, device memory/GPU tier, + `prefers-reduced-motion`) run once on load to select experience tier +- Resume download +- GitHub integration +- Contact form +- SEO (all scenario content must be server-rendered/crawlable regardless + of which experience tier the visitor gets) +- Fast loading on the fallback tier; best-effort on the full 3D tier +- Accessibility (full keyboard + screen-reader path through every + scenario's content, independent of the visual tier) + +## Explicitly Out of Scope for v1 + +- Spatial/ambient audio design (deferred — high effort, low signal for + this audience; revisit post-launch if desired) +- Custom cursor / drone-style free-camera exploration (Orano-specific; + not relevant to a recruiter portfolio's goals) ## Success Metrics -- Lighthouse \>95 -- \<2s load time -- Mobile-first +- Lighthouse >95 on the **fallback tier** (this is the tier search + crawlers and most recruiters on constrained networks/devices will hit) +- Lighthouse best-effort (target >80) on the **full 3D tier** +- <2s load time to first meaningful content, on either tier +- Mobile-first: mobile always gets the fallback tier by default +- Zero content that exists only inside the WebGL scene — 100% content + parity between tiers, verified per scenario diff --git a/Docs/02_TRD.md b/Docs/02_TRD.md index 5f6cc49..0024a19 100644 --- a/Docs/02_TRD.md +++ b/Docs/02_TRD.md @@ -2,36 +2,97 @@ ## Stack -- Next.js 15 -- React 19 -- TypeScript -- Tailwind CSS -- shadcn/ui -- Framer Motion +### Core +- Next.js 15 (App Router, RSC by default) +- React 19 +- TypeScript (strict mode) +- Tailwind CSS +- shadcn/ui +- Framer Motion (UI-level micro-interactions, page transitions, + fallback-tier scenario animations) + +### Immersive Scenario Layer (new) +- `@react-three/fiber` — Three.js as React components, keeps scenes + composable and testable like the rest of the codebase +- `@react-three/drei` — helpers (loaders, controls, environment) +- `gsap` + `ScrollTrigger` — scroll-scrubbed scene choreography; + Framer Motion alone cannot drive frame-by-frame 3D scene state off + scroll position, GSAP is the right tool for this specific job +- `@react-three/postprocessing` — bloom / depth-of-field for the + "sharp futuristic" visual target, used sparingly, per-scenario +- Draco + KTX2 compressed assets for any 3D geometry/textures, loaded + through `useGLTF` with Suspense boundaries and explicit loading states + +### Capability Detection (new — required, not optional) +A single client-side check run once on mount, before any 3D asset is +requested: +- WebGL2 support check +- `navigator.deviceMemory` / `hardwareConcurrency` heuristic for GPU/CPU + tier (fallback if unavailable — assume low tier) +- `prefers-reduced-motion` media query — always forces fallback tier, + no exceptions +- Viewport width — mobile (<768px) always forces fallback tier by + default regardless of device capability, per PRD mobile-first mandate + +Result of this check is stored once (React context) and determines +which component tree renders for each scenario: `` +or ``. Both consume the *same* content data (see +Backend Schema) so there is no content fork, only a rendering fork. + +### Explicitly excluded +- Howler.js / spatial audio (deferred per PRD — do not add as a + dependency until this is revisited) +- Custom cursor libraries — adds complexity with limited payoff for a + recruiter-facing site ## Deployment -- Vercel +- Vercel +- 3D assets (compressed .glb/.ktx2) served from Vercel's static + asset pipeline or an edge-cached CDN bucket if they exceed + reasonable repo size — decide per actual asset weight during + Phase 2.5 POC ## APIs -- GitHub API -- EmailJS/Resend +- GitHub API +- EmailJS/Resend ## Folder Structure -/app /components /data /lib /public /styles +``` +/app +/components + /ui (shadcn primitives) + /scenarios (per-scenario 3D scene + fallback pairs) + /shared +/data (scenario, project, cert, experience JSON/TS) +/lib + /capability (device/WebGL detection) +/public + /models (compressed .glb/.ktx2) +/styles +``` ## Performance -- SSR where possible -- Image optimization -- Lazy loading -- Code splitting +- SSR/RSC for all scenario content (text, structured data) regardless + of rendering tier — the 3D scene is a client-only enhancement layered + on top of server-rendered content, never a replacement for it +- Image optimization +- Lazy loading — 3D scene chunks load only when their scenario section + approaches viewport (dynamic `import()`, not bundled into initial load) +- Code splitting per scenario — a visitor who never scrolls past + Scenario 1 should never download Scenario 4's 3D assets +- Explicit bundle budget check in Phase 2.5 POC before building the + remaining three scenarios: if one scene's JS+asset payload can't stay + under an agreed threshold, the fallback tier becomes the default for + more device classes, not just low-end ones ## Security -- CSP -- HTTPS -- Sanitized forms -- Rate limiting +- CSP (note: relaxing CSP for any WebGL/shader inline scripts must be + scoped as narrowly as possible — no blanket `unsafe-inline`) +- HTTPS +- Sanitized forms +- Rate limiting diff --git a/Docs/03_UI_UX.md b/Docs/03_UI_UX.md index 5662a5b..c63db09 100644 --- a/Docs/03_UI_UX.md +++ b/Docs/03_UI_UX.md @@ -1,36 +1,108 @@ # UI / UX Specification -## Design Language +## Design Language (revised) -- Minimal -- Enterprise -- Apple + Vercel inspired -- Glassmorphism -- Deep Blue Accent +**Change from previous version:** glassmorphism + light "Apple/Vercel +SaaS" aesthetic is replaced with a dark, precision-industrial aesthetic +inspired by Orano's Innovation site — while keeping the typographic +restraint and spacing discipline that made the Apple/Vercel reference +worth citing in the first place. Glassmorphism reads as +software-product-marketing; this portfolio needs to read as +technical-precision instead. + +- Dark base UI (not a toggle — dark is the default and primary mode; + a light mode can be a Phase 6+ nice-to-have, not a launch requirement) +- Precision-grid / wireframe motifs as structural background elements + (thin linework, topographic-style contour patterns — restrained, not + decorative clutter) +- One accent color per scenario, not a single global accent — this is + the mechanism that gives each scenario visual identity without + needing four different layout systems: + - Offensive Security → red/amber + - GRC & Compliance → blue + - AI-Secured Systems → violet + - Cloud & Infrastructure → teal +- Monospace type (e.g. `JetBrains Mono` or `Space Mono`) for technical + labels, data readouts, code references — paired with a clean sans + (e.g. `Inter`) for body/narrative text. Two-typeface system max. +- No heavy shadows, no gradients-as-decoration — depth comes from the + 3D scenes themselves and from layering/contrast, not from CSS effects +- Sharp corners or minimal radius (2–4px) — avoid the soft/rounded + "SaaS card" look entirely; this is part of what separates "precision" + from "friendly product marketing" + +## Two-Tier Rendering Model (new — core to this revision) + +Every scenario section has two presentations of **identical content**: + +1. **Full tier** — `@react-three/fiber` scene, scroll-scrubbed via + GSAP ScrollTrigger, camera/scene state tied to scroll position, + equipment/tools represented as interactive nodes the user can + hover/tap for detail +2. **Fallback tier** — static or lightly-animated (Framer Motion) + illustrated equivalent of the same scene, same narrative beats, + same interactive detail-on-hover/tap behavior, no WebGL dependency + +Design each scenario's fallback FIRST, as its own complete artifact — +not as an afterthought "what if 3D fails" state. The fallback is what +most visitors (mobile, reduced-motion, low-end devices, search +crawlers) will actually see. ## Sections -1. Hero -2. About -3. Journey Timeline -4. Skills -5. Projects -6. Experience -7. Certifications -8. TryHackMe -9. GitHub -10. Blog -11. Contact +1. Hero +2. About +3. Journey Timeline +4. **Scenarios** (replaces separate "Experience" + "Projects" sections) + - Offensive Security (VAPT/Pentesting) + - GRC & Compliance (Dharma) + - AI-Secured Systems (VaultIQ) + - Cloud & Infrastructure (AWS) +5. Skills +6. Certifications +7. TryHackMe +8. GitHub +9. Blog +10. Contact + +## Scenario Interaction Pattern + +Each scenario follows the same interaction template (consistency across +four different visual environments is what keeps this navigable rather +than four unrelated experiences bolted together): + +- Scroll-in: environment establishes (network topology / audit ledger / + encrypted vault / cloud architecture) +- Scroll-through: 2–4 narrative beats reveal in sequence, each tied to + a real accomplishment (e.g. "recon → exploit → report" for offensive + security), matching `narrativeBeats[]` in the Backend Schema +- Interactive moment: one hover/tap/press interaction per scenario that + lets the visitor "do" something small and relevant (e.g. trace a hash + chain link in GRC, reveal an encrypted field in AI-Secured Systems) — + Orano's "long press" / gamified-drone pattern, scaled down to + something achievable and tasteful, not a mini-game +- Payoff: scenario resolves into the linked project card (VaultIQ / + Dharma / Enterprise API Pentesting) with a clear link to detail/GitHub ## Animations -- Framer Motion -- Smooth scrolling -- Subtle particle background -- Hover interactions +- Framer Motion for fallback-tier scenario animation and all + non-scenario UI (nav, hero, cards, page transitions) +- GSAP ScrollTrigger exclusively for full-tier 3D scene choreography + (kept separate from Framer Motion's responsibilities to avoid two + animation engines fighting over the same DOM nodes) +- Smooth scrolling +- Hover interactions +- All animation respects `prefers-reduced-motion` — this is a hard + requirement, not a nice-to-have, and is the same signal that routes + visitors to the fallback tier at the scene level ## Accessibility -- WCAG AA -- Keyboard navigation -- High contrast +- WCAG AA minimum, on both rendering tiers independently +- Keyboard navigation through every scenario's narrative beats and + interactive moment, not just through nav/links +- High contrast — dark base UI must be checked against WCAG contrast + ratios per accent color, not just against the default text color +- All scenario content available to screen readers via the + server-rendered DOM regardless of which visual tier is active diff --git a/Docs/04_Implementation_Plan.md b/Docs/04_Implementation_Plan.md index 9e98a53..0069949 100644 --- a/Docs/04_Implementation_Plan.md +++ b/Docs/04_Implementation_Plan.md @@ -2,36 +2,74 @@ ## Phase 1 -- Initialize Next.js -- Tailwind -- shadcn/ui +- Initialize Next.js +- Tailwind +- shadcn/ui ## Phase 2 -- Navbar -- Hero -- Footer +- Navbar +- Hero +- Footer + +## Phase 2.5 — Scene Engine & Capability Detection POC (new — required gate) + +This phase exists specifically to de-risk the immersive direction +before committing to building all four scenarios. Do not proceed to +Phase 3 until this phase's exit criteria are met. + +- Build capability detection module (`/lib/capability`) +- Build ONE full scenario end-to-end, both tiers: **Offensive Security** + (chosen because it maps most directly to your strongest, most + concrete work — pentesting/VAPT) + - Full tier: `@react-three/fiber` scene + GSAP ScrollTrigger + choreography + one interactive moment + - Fallback tier: Framer Motion illustrated equivalent, same + narrative beats, same interactive moment via tap +- Measure actual bundle size and Lighthouse score for both tiers +- **Exit criteria (must pass before Phase 3):** + - Fallback tier: Lighthouse >95, <2s load + - Full tier: no hard blank/broken state on any tested device, + acceptable load behavior (loading state shown, not a frozen page) + - Content parity verified: everything in the 3D scene also exists, + identically, in the fallback and in the server-rendered DOM +- If exit criteria fail: fall back to Option 2 (adapt the concept + without WebGL) for the remaining three scenarios rather than + forcing a struggling pattern four more times ## Phase 3 -- About -- Skills -- Timeline +- About +- Skills +- Timeline ## Phase 4 -- Projects -- Certifications -- Experience +- Remaining three Scenarios (GRC & Compliance, AI-Secured Systems, + Cloud & Infrastructure), built to the pattern validated in Phase 2.5 +- Certifications +- (Legacy "Experience" and "Projects" flat sections are absorbed into + Scenarios per revised Backend Schema — do not rebuild them as + separate sections) ## Phase 5 -- GitHub API -- Contact Form -- SEO +- GitHub API +- Contact Form +- SEO (verify structured data / crawlability against the + server-rendered DOM specifically, independent of which visual tier + a given crawler-simulated request resolves to) ## Phase 6 -- Testing -- Lighthouse -- Deployment +- Testing (including reduced-motion path, keyboard-only path, and + low-end-device fallback path as explicit, separate test passes — + not just desktop-Chrome-full-tier testing) +- Lighthouse (both tiers, per PRD success metrics) +- Deployment + +## Deferred / Backlog (explicitly not in v1 scope) + +- Spatial/ambient audio design +- Light mode +- Custom cursor / free-camera exploration diff --git a/Docs/05_BackendSchema.md b/Docs/05_BackendSchema.md index 80ee9c6..990da66 100644 --- a/Docs/05_BackendSchema.md +++ b/Docs/05_BackendSchema.md @@ -1,16 +1,107 @@ # Backend Schema -## Static JSON/Data +## Static JSON/TS Data -Profile - name - role - bio - socials +### Profile +- name +- role +- bio +- socials -Projects - id - title - slug - description - techStack\[\] - -images\[\] - github - live - featured +### Scenario (new — core entity for this revision) -Certificates - id - title - issuer - date - credentialUrl +This is the new top-level entity that structures the immersive +experience. Projects, skills, and experience are no longer flat, +independently-listed sections — they are consumed *through* scenarios. +The flat entities below still exist (for Skills/Certifications/Blog +sections, resume generation, SEO structured data, etc.) but the +Scenarios section is the primary narrative surface. -Experience - company - role - startDate - endDate - achievements\[\] +``` +Scenario +- id +- slug +- title e.g. "Offensive Security" +- accentColor one of: red-amber | blue | violet | teal +- environmentType network-topology | audit-ledger | + encrypted-vault | cloud-architecture +- summary 1–2 sentence framing, used in fallback + meta/SEO and as the non-JS text summary +- narrativeBeats[] + - id + - order + - heading + - body + - relatedSkillIds[] references Skill.id +- interactiveMoment + - type trace | reveal | inspect (kept to this + small enum on purpose — see UI/UX doc, + avoid scope creep into mini-games) + - description + - triggerLabel accessible label for the interaction, + used identically on both rendering tiers +- payoffProjectId references Project.id +- scene3D (nullable — full-tier only) + - modelPath .glb path, Draco/KTX2 compressed + - cameraKeyframes[] scroll-progress-indexed camera states +- fallback (required — always present) + - illustrationRef static/SVG asset or Framer Motion + component reference + - animationBeats[] maps 1:1 to narrativeBeats[] above +``` -Blog - slug - title - tags - date +### Project +- id +- title +- slug +- description +- techStack[] +- images[] +- github +- live +- featured +- **scenarioId** (new — links back to the owning Scenario, nullable + for any project not surfaced through a scenario) -Contact - name - email - message +### Certificates +- id +- title +- issuer +- date +- credentialUrl + +### Experience +- company +- role +- startDate +- endDate +- achievements[] +- **relatedScenarioIds[]** (new — an experience entry can inform + multiple scenarios, e.g. the HackersVilla internship feeds both + Offensive Security and GRC scenarios) + +### Skill (new — previously implicit inside flat lists, now explicit +so `narrativeBeats[].relatedSkillIds` can reference it) +- id +- name +- category offensive | grc | ai-security | cloud | general +- proficiencyNote optional, short + +### Blog +- slug +- title +- tags +- date + +### Contact +- name +- email +- message + +## Data Integrity Rule (new — enforced at build time, not just convention) + +Every `Scenario` must have a non-null `fallback` object with +`animationBeats[]` count matching `narrativeBeats[]` count. This is +what guarantees the PRD's "100% content parity between tiers" +requirement — a scenario cannot ship with a 3D scene and no fallback, +and a build-time check should fail if one is added without the other. diff --git a/Docs/06_App_Flow.md b/Docs/06_App_Flow.md index bdbadbe..06bb3f3 100644 --- a/Docs/06_App_Flow.md +++ b/Docs/06_App_Flow.md @@ -1,14 +1,68 @@ # Application Flow -Landing ↓ Hero ↓ About ↓ Journey ↓ Skills ↓ Experience ↓ Projects ↓ -Project Details ↓ Certifications ↓ TryHackMe ↓ GitHub ↓ Blog ↓ Contact ↓ +## Entry & Tier Selection (new — happens before any scenario renders) + +``` +Landing + ↓ +Capability Detection (WebGL support, device tier, + prefers-reduced-motion, viewport width) + ↓ +Tier Decision: Full 3D | Fallback + (decision is made once, cached for the session — not re-evaluated + per scenario, to avoid a jarring tier switch mid-scroll) + ↓ +Hero + ↓ +About + ↓ +Journey Timeline + ↓ +Scenarios (4, in sequence — same order regardless of tier) + 1. Offensive Security → payoff: Enterprise API Pentesting project + 2. GRC & Compliance → payoff: Dharma project + 3. AI-Secured Systems → payoff: VaultIQ project + 4. Cloud & Infrastructure → payoff: AWS-cert-backed skills + ↓ +Skills + ↓ +Certifications + ↓ +TryHackMe + ↓ +GitHub + ↓ +Blog + ↓ +Contact + ↓ Resume Download +``` ## Navigation -Sticky navbar Smooth scrolling Active section highlight +- Sticky navbar +- Smooth scrolling +- Active section highlight, including per-scenario highlight (nav + should indicate which of the 4 scenarios is currently in view, not + just "Scenarios" as one lump section) +- Nav behavior is identical on both tiers — navigation is never part + of the 3D scene itself, it stays as reliable server-rendered UI at + all times ## User Journey -Recruiter lands → views hero → validates projects → checks -certifications → downloads resume → contacts candidate. +Recruiter lands → capability check silently selects experience tier → +views hero → scrolls through scenario narrative (recon → exploit → +report, or equivalent per scenario) → reaches project payoff card → +validates via GitHub/live link → checks certifications → downloads +resume → contacts candidate. + +## Fallback-Tier User Journey (explicit, not assumed) + +Same sequence, same content, same interactive moments (via tap instead +of hover/scroll-scrub) — the only difference is the environment is a +static/lightly-animated illustration instead of a scroll-scrubbed 3D +scene. A recruiter on this path should not perceive the site as a +"lesser version" — it should read as a deliberate, complete design, +not a degraded one. diff --git a/Docs/COMPONENT_ARCHITECTURE.md b/Docs/COMPONENT_ARCHITECTURE.md deleted file mode 100644 index d0750ed..0000000 --- a/Docs/COMPONENT_ARCHITECTURE.md +++ /dev/null @@ -1,41 +0,0 @@ -# Component architecture — conventions - -Established in Phase 1C. Every later phase composes against this structure; -inventing a new top-level component folder is a review-blocker. - -## Folders - -| Folder | Contents | Client/Server | -| ----------------------- | ------------------------------------------------------------------- | ---------------------------- | -| `components/ui/` | shadcn primitives — generated, minimally edited | As generated | -| `components/providers/` | App-wide context providers (theme, motion) | Client (isolated leaves) | -| `components/shared/` | Cross-section reusable pieces (theme toggle, icon wrappers) | Client only when interactive | -| `components/layout/` | Structural pieces: `Container` now; header/footer arrive in Phase 2 | Server by default | -| `components/sections/` | Page sections (Hero, About, Projects…) — Phases 2–4 | Server by default | - -Rules: - -- **Server Components by default.** `"use client"` only where interactivity - demands it, and as far down the tree as possible — providers and the theme - toggle are client leaves; layout and pages stay server. -- **Data access** goes through `lib/data/` accessors only — no component - imports from `/data` directly. -- **Design values** come from tokens only — see - [DESIGN_SYSTEM.md](DESIGN_SYSTEM.md). -- **shadcn primitives are not customized in place** beyond token alignment; - behavior changes wrap the primitive in `shared/` instead of editing `ui/`. - -## Composition over large components - -Sections are assembled from primitives + shared pieces; one file per -responsibility. Example — the Phase 2 navbar is not one 300-line file: - -``` -components/layout/site-header.tsx — server shell:
+ Container -components/layout/nav-links.tsx — link list (server) -components/layout/mobile-nav.tsx — client: Sheet + trigger -components/shared/theme-toggle.tsx — client: reused as-is -``` - -If a component needs its own state *and* substantial markup, split logic -from presentation before it grows past ~150 lines. diff --git a/Docs/DESIGN_SYSTEM.md b/Docs/DESIGN_SYSTEM.md deleted file mode 100644 index 8063527..0000000 --- a/Docs/DESIGN_SYSTEM.md +++ /dev/null @@ -1,93 +0,0 @@ -# Design System — tokens, rules, and enforcement - -Source of truth for every visual decision. Built in Phase 0.2, documented here -per Phase 1B. **Rule zero: never invent a value.** If a color, size, radius, -or duration isn't a token below, it doesn't ship — extend the token system -first, in a reviewed change. - -## Where tokens live - -| What | File | -| -------------------------------- | ------------------------------------------------ | -| Colors, type scale, radii, glass | [styles/globals.css](../styles/globals.css) | -| Motion presets (durations/easing)| [lib/motion.ts](../lib/motion.ts) | -| Measured contrast ratios | [styles/CONTRAST.md](../styles/CONTRAST.md) | - -Note: this project uses **Tailwind v4**, which is CSS-first — there is no -`tailwind.config.ts`. The `@theme` block in `globals.css` is the v4 -equivalent; earlier planning docs that mention `tailwind.config.ts` are -superseded by this. - -## Color tokens (oklch, theme-aware) - -Semantic tokens defined for `:root` (light) and `.dark`. Components use the -semantic utility (`bg-background`, `text-primary`, `border-border`) — never a -raw hex/oklch value, never a raw Tailwind palette color (`bg-blue-600` is a -review-blocker). - -| Token | Purpose | -| ------------------------- | ---------------------------------------------- | -| `background / foreground` | Page surface and default text | -| `card / card-foreground` | Elevated surfaces (also popover variants) | -| `primary / primary-foreground` | Deep blue brand accent; CTAs, links, ring | -| `secondary / secondary-foreground` | Low-emphasis fills | -| `muted / muted-foreground`| Subdued surfaces and secondary text | -| `accent / accent-foreground` | Hover washes, subtle highlights | -| `destructive / destructive-foreground` | Errors, dangerous actions | -| `border`, `input`, `ring` | Hairlines, form borders, focus ring | - -Why oklch: perceptually uniform lightness means dark-mode variants adjust one -axis predictably, and WCAG contrast tuning is deterministic rather than -guess-and-check (decision recorded in Phase 0.2). - -**Contrast**: every foreground/surface pair passes WCAG AA (4.5:1+) in both -themes — measured ratios in [styles/CONTRAST.md](../styles/CONTRAST.md), -enforced continuously by `lib/design/contrast.test.ts`, which parses -`globals.css` in CI. Editing a token that breaks a pair fails the build. - -## Typography - -Geist Sans (display + body — single-family typography is the Apple/Vercel -idiom, and Geist is Vercel's own typeface) and Geist Mono for technical -accents. Both self-hosted via `next/font` (zero external requests, zero CLS). - -Deliberate, limited scale — Tailwind defaults beyond it are removed: -`text-xs` `text-sm` `text-base` `text-lg` `text-xl` `text-2xl` `text-3xl` -`text-4xl` `text-6xl`. Intended roles are labeled on the -[/dev/tokens](../app/dev/tokens/page.tsx) showcase. - -## Spacing & radius - -- Spacing: Tailwind's default 4px modular scale (`--spacing: 0.25rem`) — - deliberate decision, no custom steps; arbitrary values (`p-[13px]`) are - banned. -- Radius: one scale from `--radius` (0.625rem): `rounded-sm|md|lg|xl`. - -## Dark mode - -Class strategy (`.dark` on ``), toggled by next-themes with system -preference support and localStorage persistence; `suppressHydrationWarning` + -next-themes' inline script prevents flash of wrong theme. - -## Glassmorphism - -One utility: `.glass` (translucent card surface, 12px backdrop blur, soft -border). Reserved for floating surfaces — navbar, overlays. Not for every -card; no heavy shadows, no gradients, no neon (per 03_UI_UX.md and master -rules). - -## Motion - -Named presets only, from `lib/motion.ts`: `fadeUp`, `fadeIn`, -`staggerChildren`, `subtleHover` (+ `transitionBase`/`transitionFast`). -Inline variants or ad-hoc durations in components are a review-blocker. -Reduced motion is honored twice: `MotionConfig reducedMotion="user"` for -Framer Motion, and a global `prefers-reduced-motion` CSS rule for -CSS-driven animation. - -## Accessibility - -- Global `:focus-visible` ring from the `--ring` token (both themes); - shadcn primitives render their own ring from the same token. -- All text/surface pairs AA-verified (see above). -- Keyboard operability is a per-component requirement checked at review. diff --git a/PRODUCT.md b/PRODUCT.md new file mode 100644 index 0000000..88c36ab --- /dev/null +++ b/PRODUCT.md @@ -0,0 +1,47 @@ +# PRODUCT.md — Hem Gabhawala Portfolio (Immersive Scenario Edition) + +> Locked decisions for the scenario rebuild. Equivalent of `/impeccable init`'s +> PRODUCT.md, authored directly because the impeccable command-suite is not +> installed in this environment (only the single `impeccable:impeccable` skill). +> Source of truth remains `/Docs/01–06`. This file summarizes, it does not +> override. + +## Audience (in priority order) + +1. **Recruiters / internship coordinators** — skim in <30s, often on mobile or + constrained networks. They hit the **fallback tier**. Credibility must land + without WebGL. +2. **Hiring managers** — want proof of real, concrete work (VAPT, GRC, AI + security, cloud). Scenario payoff = linked project + GitHub/live. +3. **Security engineers** — will judge technical taste and precision. The + industrial-telemetry aesthetic and honest data readouts are for them. + +## Brand lane + +**Technical-precision, not product-marketing.** Reads as a declassified +engineering console / aerospace telemetry, not a SaaS landing page. Explicitly +avoid: glassmorphism, gradient-as-decoration, soft rounded "friendly" cards, +hacker-movie/Matrix theatrics, animation spam. + +## The one job + +Make Hem's cybersecurity work **tangible** through scroll-driven scenario +storytelling — while staying fast, crawlable, and accessible on the fallback +tier that most of the audience actually sees. + +## Non-negotiables (from PRD/TRD) + +- Immersive 3D is **progressive enhancement, never a gate**. 100% content parity + across 3D tier / fallback tier / server-rendered DOM. +- Lighthouse >95 on the fallback tier (SEO/A11y/Best-Practices target 100); + best-effort >80 on the full 3D tier. <2s to first meaningful content. +- WCAG AA on **both** tiers independently, contrast-checked **per accent color**. +- Full keyboard + screen-reader path through every scenario's narrative beats + **and** its interactive moment. +- `prefers-reduced-motion` is a hard override at the animation-call level, not + just tier selection. + +## Out of scope (v1) + +Spatial/ambient audio · custom cursor / free-camera · light mode (Phase 6+ at +most) · any mini-game-scale interaction. diff --git a/app/layout.tsx b/app/layout.tsx index 5d6e0db..087a340 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,97 +1,37 @@ import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; -import { Footer } from "@/components/layout/footer"; -import { SiteHeader } from "@/components/layout/site-header"; -import { PageTransition } from "@/components/shared/page-transition"; -import { ScrollProgress } from "@/components/layout/scroll-progress"; -import { MotionProvider } from "@/components/providers/motion-provider"; -import { ThemeProvider } from "@/components/providers/theme-provider"; -import { LayoutClient } from "@/components/layout/layout-client"; -import { getProfile } from "@/lib/data"; -import { env } from "@/lib/env"; -import { generatePersonSchema } from "@/lib/seo/jsonld"; -import { DEFAULT_OG_IMAGE } from "@/lib/seo/config"; +import { Inter, JetBrains_Mono } from "next/font/google"; import "@/styles/globals.css"; -const geistSans = Geist({ - variable: "--font-geist-sans", +/** + * Two-typeface system (see /DESIGN.md): + * Inter for narrative body/headings, JetBrains Mono for technical telemetry. + */ +const inter = Inter({ + variable: "--font-inter", subsets: ["latin"], + display: "swap", }); -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", +const jetbrainsMono = JetBrains_Mono({ + variable: "--font-jetbrains-mono", subsets: ["latin"], + display: "swap", }); -const { name, role } = getProfile(); -const siteName = `${name} — Cybersecurity Portfolio`; -const description = `${name}, ${role}. Hands-on offensive security: penetration testing, security tooling, certifications, and TryHackMe achievements.`; - +// Minimal metadata for Phase 1. Full SEO (OG/Twitter/JSON-LD/sitemap) lands in +// Phase 5 against the server-rendered DOM, per the Implementation Plan. export const metadata: Metadata = { - metadataBase: new URL(env.NEXT_PUBLIC_SITE_URL ?? "http://localhost:3000"), - title: { - default: siteName, - template: "%s — Hem Gabhawala", - }, - description, - openGraph: { - type: "website", - url: "/", - siteName, - title: siteName, - description, - images: [DEFAULT_OG_IMAGE], - }, - twitter: { - card: "summary_large_image", - title: siteName, - description, - images: [DEFAULT_OG_IMAGE.url], - }, - icons: { - icon: "/favicon.ico", - }, - robots: { - index: true, - follow: true, - nocache: false, - googleBot: { - index: true, - follow: true, - }, - }, + title: "Hem Gabhawala — Cybersecurity Portfolio", + description: + "Immersive, scenario-based cybersecurity portfolio: offensive security, GRC & compliance, AI-secured systems, and cloud infrastructure.", }; export default function RootLayout({ children, -}: Readonly<{ - children: React.ReactNode; -}>) { - const personSchema = generatePersonSchema(); - +}: Readonly<{ children: React.ReactNode }>) { return ( - - - ", - "Hello there, nice site you have", - "Interested in your work click thanks", - " plus some padding text to pass min length", - ])("rejects HTML/script injection in message: %j", (message) => { - expect(contactFormSchema.safeParse({ ...valid, message }).success).toBe( - false, - ); - }); - - it("rejects HTML injection in name", () => { - expect( - contactFormSchema.safeParse({ ...valid, name: "Jane" }).success, - ).toBe(false); - }); - - it("rejects control characters (header-injection style)", () => { - expect( - contactFormSchema.safeParse({ - ...valid, - message: "legit looking message\u0000with a null byte inside", - }).success, - ).toBe(false); - }); - - it("allows plain multi-line messages", () => { - expect( - contactFormSchema.safeParse({ - ...valid, - message: "Line one of the message.\nLine two, still plain text.", - }).success, - ).toBe(true); - }); -}); diff --git a/legacy/lib/validations/contact.ts b/legacy/lib/validations/contact.ts deleted file mode 100644 index 61c0c1c..0000000 --- a/legacy/lib/validations/contact.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { z } from "zod"; -import type { ContactFormInput } from "@/lib/types"; - -/** - * Contact form validation — the exact schema Phase 5's form and rate-limited - * API route will consume. Deliberately strict: this is a security boundary - * for user-supplied input that ends up in an outbound email. - */ - -/** Any HTML tag-like sequence: blocks `