Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions components/approach/lattice-fallback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ export function LatticeFallback() {
{pts.map((p, i) => (
<circle
key={i}
cx={p.x}
cy={p.y}
cx={parseFloat(p.x.toFixed(1))}
cy={parseFloat(p.y.toFixed(1))}
r={1.6}
fill="var(--paper)"
fillOpacity={0.6}
Expand Down
100 changes: 100 additions & 0 deletions components/resume/resume-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
"use client";

import { useState, useEffect } from "react";
import { Download, X } from "lucide-react";

/**
* Resume selection modal. Triggered by Resume button in Hero section.
* Allows user to choose between Software Developer or Cybersecurity/GRC resume.
*/
export function ResumeModal() {
const [open, setOpen] = useState(false);

useEffect(() => {
const handleEscape = (e: KeyboardEvent) => {
if (e.key === "Escape") setOpen(false);
};
if (open) document.addEventListener("keydown", handleEscape);
return () => document.removeEventListener("keydown", handleEscape);
}, [open]);

return (
<>
{/* Resume Trigger Button */}
<button
onClick={() => setOpen(true)}
className="link-underline text-[0.9rem] font-medium"
>
Resume
</button>

{/* Modal Backdrop + Dialog */}
{open && (
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm"
onClick={() => setOpen(false)}
role="presentation"
>
<div
className="relative w-full max-w-md rounded-lg bg-paper p-8 shadow-lg"
onClick={(e) => e.stopPropagation()}
role="dialog"
aria-labelledby="resume-dialog-title"
aria-modal="true"
>
{/* Close Button */}
<button
onClick={() => setOpen(false)}
className="absolute top-4 right-4 rounded p-1 transition-colors hover:bg-ink-muted"
aria-label="Close dialog"
>
<X size={20} className="text-ink" />
</button>

{/* Title */}
<h2
id="resume-dialog-title"
className="type-plate mb-6 pr-8 text-ink"
>
Download Resume
</h2>

{/* Description */}
<p className="type-body mb-8 text-ink-muted">
Choose the resume that best fits the role you&apos;re applying
for.
</p>

{/* Download Options */}
<div className="flex flex-col gap-3">
<a
href="/resumes/resume-software-developer.pdf"
download="Hem-Gabhawala-Resume-Software-Developer.pdf"
onClick={() => setOpen(false)}
className="inline-flex items-center justify-center gap-2 rounded bg-ink px-6 py-3 text-[0.9rem] font-medium text-paper transition-colors hover:bg-accent motion-reduce:transition-none"
>
<Download size={16} />
Software Developer
</a>

<a
href="/resumes/resume-cybersecurity-grc.pdf"
download="Hem-Gabhawala-Resume-Cybersecurity-GRC.pdf"
onClick={() => setOpen(false)}
className="inline-flex items-center justify-center gap-2 rounded border border-ink bg-paper px-6 py-3 text-[0.9rem] font-medium text-ink transition-colors hover:bg-ink-muted motion-reduce:transition-none"
>
<Download size={16} />
Cybersecurity / GRC
</a>
</div>

{/* Close hint (optional) */}
<p className="type-data mt-6 text-center text-ink-dim">
Press Esc or click outside to close
</p>
</div>
</div>
)}
</>
);
}
8 changes: 2 additions & 6 deletions components/sections/hero.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { profile } from "@/data/profile";
import { HeroReveal } from "./hero-reveal";
import { ResumeModal } from "@/components/resume/resume-modal";

/**
* The four figures a recruiter should retain after three seconds. Rendered as
Expand Down Expand Up @@ -75,12 +76,7 @@ export function Hero() {
>
Email me
</a>
<a
href={profile.resumeUrl}
className="link-underline text-[0.9rem] font-medium"
>
Resume
</a>
<ResumeModal />
</div>
</div>
</HeroReveal>
Expand Down
7 changes: 7 additions & 0 deletions data/certificates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ export const certificates = [
date: "2026-04-01",
credentialUrl: null,
},
{
id: "cert-isea",
title: "Information Security Education and Awareness (ISEA)",
issuer: "CDAC / MeitY",
date: "2024-07-24",
credentialUrl: null,
},
] satisfies Certificate[];

/** Newest first. */
Expand Down
4 changes: 2 additions & 2 deletions data/tryhackme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ export const tryHackMe = [
id: "thm-offensive-pentesting",
title: "Offensive Pentesting",
issuer: "TryHackMe",
date: "2025-08-01",
date: "2025-08-06",
credentialUrl: null,
},
{
id: "thm-web-app-pentesting",
title: "Web Application Pentesting",
issuer: "TryHackMe",
date: "2025-09-01",
date: "2025-09-20",
credentialUrl: null,
},
{
Expand Down
Binary file not shown.
Binary file added public/certificates/cert-aws-ml-foundations.pdf
Binary file not shown.
Binary file not shown.
Binary file added public/certificates/cert-isea.pdf
Binary file not shown.
Binary file added public/certificates/thm-cyber-security-101.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/certificates/thm-jr-pentester.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/certificates/thm-offensive-pentesting.pdf
Binary file not shown.
Binary file added public/certificates/thm-pre-security.pdf
Binary file not shown.
Binary file added public/certificates/thm-web-app-pentesting.pdf
Binary file not shown.
Binary file added public/certificates/thm-web-fundamentals.pdf
Binary file not shown.
Binary file added public/resumes/resume-cybersecurity-grc.pdf
Binary file not shown.
Binary file added public/resumes/resume-software-developer.pdf
Binary file not shown.
Loading