// Projects.jsx — thomasgoodman.me const projectsData = [ { name: 'Whenuapai Key Press Manager', period: 'Built 2020 to 2024 during RNZAF service', context: 'Personal initiative, not RNZAF-commissioned.', desc: 'Full-stack web application that replaced a manual paper-based key tracking process at RNZAF Whenuapai. Features a real-time dashboard showing the current keyholder, issue timestamps, and overdue alerts, plus an audit-history view enabling supervisors to inspect all key movements. Designed with strict access control and audit logging to meet RNZAF base security and compliance requirements.', highlight: '80% reduction in key tracking time', tags: ['Node.js', 'Express', 'React', 'PostgreSQL', 'Docker', 'Audit Logging'], link: null, badge: 'RNZAF', }, { name: 'Presence Guard', period: 'Built 2020 to 2024 during RNZAF service', context: 'Personal initiative, not RNZAF-commissioned.', desc: 'Visitor management system for a restricted RNZAF building. Captures visitor details, purpose of visit, escort information, and time in / time out. Provides the duty officer with a live register and an exportable daily log for end-of-day security records. Replaced handwritten visitor books with a searchable, tamper-evident digital record aligned to base security protocols.', highlight: null, tags: ['Node.js', 'Express', 'React', 'MySQL', 'Role-Based Access'], link: null, badge: 'RNZAF', }, ]; const ProjectCard = ({ project }) => { const [hovered, setHovered] = React.useState(false); return (
setHovered(true)} onMouseLeave={() => setHovered(false)} style={{ background: 'var(--color-bg-surface)', border: `1px solid ${hovered ? 'var(--color-border-default)' : 'var(--color-border-subtle)'}`, borderRadius: 'var(--radius-xl)', padding: 'var(--space-8)', transition: 'all var(--motion-base)', transform: hovered ? 'translateY(-2px)' : 'none', boxShadow: hovered ? 'var(--shadow-md)' : 'none', position: 'relative', overflow: 'hidden', display: 'flex', flexDirection: 'column', gap: 'var(--space-4)', }} > {/* Top accent bar */} {/* Header row */}

{project.name}

{project.badge && ( {project.badge} )} {project.link && ( e.currentTarget.style.color = 'var(--color-accent)'} onMouseLeave={e => e.currentTarget.style.color = 'var(--color-text-muted)'}> )}
{/* Period + context */}
{project.period}
{project.context}
{/* Highlight stat */} {project.highlight && (
// {project.highlight}
)} {/* Description */}

{project.desc}

{/* Tags */}
{project.tags.map(tag => ( {tag} ))}
); }; const Projects = () => (

Featured Projects

Both built during RNZAF service as personal initiatives. Real problems, real constraints, real security requirements.

{projectsData.map(p => )}
); Object.assign(window, { Projects, ProjectCard });