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
116 changes: 80 additions & 36 deletions src/components/Certifications.astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import certificationsData from '../data/certifications.json';

type CertType = 'certification' | 'professional-development' | 'in-progress';

const certTypes = new Set(certificationsData.map((cert) => cert.type ?? 'certification'));

// Returns the CSS class for the status badge chip (green / amber / blue).
function badgeClass(type: CertType) {
if (type === 'professional-development') return 'badge-pro-dev';
Expand Down Expand Up @@ -31,15 +33,30 @@ function cardModClass(type: CertType) {
<p class="text-sm font-semibold tracking-widest text-highlight uppercase mb-3">Training</p>
<h2 class="text-3xl sm:text-4xl lg:text-5xl font-bold section-title">Courses & Development</h2>
<div class="flex flex-wrap items-center justify-center gap-4 mt-5">
<span class="legend-item">
<span class="legend-dot dot-earned"></span>Certification
</span>
<span class="legend-item">
<span class="legend-dot dot-pro-dev"></span>Professional Development
</span>
<span class="legend-item">
<span class="legend-dot dot-in-progress"></span>In Progress
</span>
{
certTypes.has('certification') && (
<span class="legend-item">
<span class="legend-dot dot-earned" />
Certification
</span>
)
}
{
certTypes.has('professional-development') && (
<span class="legend-item">
<span class="legend-dot dot-pro-dev" />
Professional Development
</span>
)
}
{
certTypes.has('in-progress') && (
<span class="legend-item">
<span class="legend-dot dot-in-progress" />
In Progress
</span>
)
}
</div>
</div>

Expand Down Expand Up @@ -74,40 +91,67 @@ function cardModClass(type: CertType) {

<p class="text-slate-400 text-sm leading-relaxed">{cert.description}</p>

<div class="flex items-center justify-between pt-2 border-t border-white/5">
<div class="flex flex-wrap items-center justify-between gap-3 pt-2 border-t border-white/5">
<span class="text-xs text-slate-600 font-mono">{cert.date}</span>

{type === 'in-progress' ? (
<span class="in-progress-label">
<span class="ip-pulse" />
Actively Studying
</span>
) : cert.credentialUrl ? (
<a
href={cert.credentialUrl}
target="_blank"
rel="noopener noreferrer"
class={`inline-flex items-center gap-1.5 transition-colors text-xs font-medium ${type === 'professional-development' ? 'text-amber-500/70 hover:text-amber-400' : 'text-accent hover:text-highlight'}`}
>
<span>
{type === 'professional-development'
? 'View Certificate'
: 'View Credential'}
</span>
<svg
class="w-3.5 h-3.5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
/>
</svg>
</a>
) : cert.credentialUrl || cert.courseUrl ? (
<span class="inline-flex flex-wrap items-center justify-end gap-3">
{cert.credentialUrl && (
<a
href={cert.credentialUrl}
target="_blank"
rel="noopener noreferrer"
class={`inline-flex items-center gap-1.5 transition-colors text-xs font-medium ${type === 'professional-development' ? 'text-amber-500/70 hover:text-amber-400' : 'text-accent hover:text-highlight'}`}
>
<span>
{type === 'professional-development'
? 'View Certificate'
: 'View Credential'}
</span>
<svg
class="w-3.5 h-3.5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
/>
</svg>
</a>
)}
{cert.courseUrl && (
<a
href={cert.courseUrl}
target="_blank"
rel="noopener noreferrer"
class="inline-flex items-center gap-1.5 text-xs font-medium text-slate-500 transition-colors hover:text-slate-300"
>
<span>Course</span>
<svg
class="w-3.5 h-3.5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
/>
</svg>
</a>
)}
</span>
) : (
<span class="text-slate-700 text-xs italic">No link available</span>
)}
Expand Down
17 changes: 11 additions & 6 deletions src/components/Header.astro
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import menu from '../data/menu.json';
import siteData from '../data/site.json';

const { sections } = menu;
const localSections = sections.map((item) => ({
...item,
targetId: item.href.startsWith('#') ? item.href.slice(1) : '',
href: item.href.startsWith('#') ? `/${item.href}` : item.href,
}));

// Build external nav links from site.json definitions, skipping any whose personal.json key is empty.
// This lets site authors add potential platforms to navExternal without requiring all fields be filled.
Expand All @@ -23,14 +28,14 @@ const external = siteData.navExternal
<header class="fixed top-0 left-0 right-0 z-50 glass-nav">
<nav class="container mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex items-center justify-between h-16">
<a href="#home" class="text-lg font-bold gradient-text tracking-tight shrink-0">
<a href="/#home" class="text-lg font-bold gradient-text tracking-tight shrink-0">
{personalData.name}
</a>

<div class="hidden lg:flex items-center gap-6">
{
sections.map((item) => (
<a href={item.href} class="nav-link nav-link-sm">
localSections.map((item) => (
<a href={item.href} class="nav-link nav-link-sm" data-nav-target={item.targetId}>
{item.shortLabel}
</a>
))
Expand Down Expand Up @@ -59,7 +64,7 @@ const external = siteData.navExternal
<span class="slider"></span>
</label>

<a href="#contact" class="cta-btn hidden sm:inline-flex">Contact Me</a>
<a href="/#contact" class="cta-btn hidden sm:inline-flex">Contact Me</a>
<button id="menu-open-btn" class="hamburger-btn" aria-label="Open menu">
<span></span>
<span></span>
Expand Down Expand Up @@ -119,7 +124,7 @@ const external = siteData.navExternal
<p class="overlay-eyebrow">Explore</p>
<nav class="overlay-nav">
{
sections.map((item, i) => (
localSections.map((item, i) => (
<a href={item.href} class="overlay-link" style={`--i: ${i};`}>
<span class="overlay-num">0{i + 1}</span>
<span class="overlay-link-body">
Expand Down Expand Up @@ -836,7 +841,7 @@ const external = siteData.navExternal
entries.forEach((entry) => {
if (entry.isIntersecting) {
navLinks.forEach((l) => l.classList.remove('active'));
document.querySelector(`.nav-link[href="#${entry.target.id}"]`)?.classList.add('active');
document.querySelector(`.nav-link[data-nav-target="${entry.target.id}"]`)?.classList.add('active');
}
});
},
Expand Down
26 changes: 24 additions & 2 deletions src/data/certifications.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,28 @@
"status": "Completed",
"type": "professional-development",
"description": "Cloud computing foundations, AWS services, security, networking, and deployment patterns used as the basis for the Flask-to-AWS migration project.",
"credentialUrl": "https://www.coursera.org/learn/aws-cloud-technical-essentials"
"credentialUrl": "https://www.coursera.org/account/accomplishments/verify/FT238LZEQ5VK",
"courseUrl": "https://www.coursera.org/learn/aws-cloud-technical-essentials"
},
{
"name": "Programming with Golang",
"issuer": "Coursera",
"date": "2026",
"status": "Completed",
"type": "professional-development",
"description": "Go programming foundations, control flow, functions, data structures, and practical language patterns for building reliable backend services.",
"credentialUrl": "https://www.coursera.org/account/accomplishments/verify/3LYI1HKF6C5Y",
"courseUrl": "https://www.coursera.org/learn/programming-with-golang"
},
{
"name": "Full Stack Web Development with Flask",
"issuer": "LinkedIn Learning",
"date": "2026",
"status": "Completed",
"type": "professional-development",
"description": "Full-stack Flask application development, virtual environment setup, routing, templates, database integration, and practical Python web workflows.",
"credentialUrl": "https://www.linkedin.com/learning/certificates/bbf2df12138c3a8a605658d6e363e7ef1dbbdcfa5f37b0405573b819889229e7/?lipi=urn%3Ali%3Apage%3Ad_flagship3_profile_view_base_certifications_details%3BT0dUv172TGegOPG8aNW2Fw%3D%3D",
"courseUrl": "https://www.linkedin.com/learning/full-stack-web-development-with-flask/setting-up-the-virtual-environment-and-framework?u=114556090"
},
{
"name": "AI Agents and Agentic AI Architecture in Python",
Expand All @@ -15,6 +36,7 @@
"status": "Completed",
"type": "professional-development",
"description": "Applied AI course work that informed the Piano Sheet Music Simplifier pipeline and its structured planning, validation, and deterministic rewrite flow.",
"credentialUrl": "https://www.coursera.org/learn/ai-agents-architecture-python"
"credentialUrl": "https://www.coursera.org/account/accomplishments/verify/RG1YB6B54SAE",
"courseUrl": "https://www.coursera.org/learn/ai-agents-architecture-python"
}
]
15 changes: 15 additions & 0 deletions src/layouts/BlogLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ const { title, description, eyebrow, date, readTime, image, imageAlt } = Astro.p
var(--bg);
}

:global([data-theme='light']) .blog-shell {
background:
linear-gradient(180deg, rgba(248, 250, 252, 0.92), rgba(241, 245, 249, 0.96)),
var(--bg);
}

.blog-article {
width: min(100%, 880px);
margin: 0 auto;
Expand Down Expand Up @@ -105,6 +111,10 @@ const { title, description, eyebrow, date, readTime, image, imageAlt } = Astro.p
font-size: 0.92rem;
}

:global([data-theme='light']) .blog-meta {
color: var(--text-dim);
}

.blog-banner {
margin: 2.25rem 0 3rem;
overflow: hidden;
Expand Down Expand Up @@ -170,6 +180,11 @@ const { title, description, eyebrow, date, readTime, image, imageAlt } = Astro.p
font-size: 0.92em;
}

:global([data-theme='light']) .blog-content :global(code) {
background: rgba(226, 232, 240, 0.92);
color: #1e293b;
}

.blog-content :global(pre) {
overflow-x: auto;
border: 1px solid var(--border);
Expand Down
Loading