456 lines
24 KiB
PHP
456 lines
24 KiB
PHP
<?php
|
||
declare(strict_types=1);
|
||
session_start();
|
||
|
||
require_once __DIR__ . '/app_helpers.php';
|
||
|
||
$projectName = site_project_name('Noah Mercer');
|
||
$projectDescription = site_project_description('One-page personal portfolio to showcase selected work and convert visitors into project inquiries.');
|
||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||
$canonicalUrl = base_url() . '/';
|
||
$flash = pull_flash();
|
||
$old = pull_old_form();
|
||
$cssVersion = file_exists(__DIR__ . '/assets/css/custom.css') ? (string) filemtime(__DIR__ . '/assets/css/custom.css') : (string) time();
|
||
$jsVersion = file_exists(__DIR__ . '/assets/js/main.js') ? (string) filemtime(__DIR__ . '/assets/js/main.js') : (string) time();
|
||
$hasMailRecipient = trim((string) getenv('MAIL_TO')) !== '';
|
||
|
||
$projects = [
|
||
[
|
||
'eyebrow' => 'Product design + build',
|
||
'title' => 'B2B analytics dashboard',
|
||
'summary' => 'Redesigned an internal reporting tool into a clear operator dashboard used by sales and finance teams.',
|
||
'impact' => 'Reduced manual reporting time by 41% and improved stakeholder adoption in the first month.',
|
||
'deliverables' => ['UX audit', 'Design system refresh', 'KPI dashboard', 'Responsive tables'],
|
||
'stack' => 'Strategy, UI, front-end implementation',
|
||
],
|
||
[
|
||
'eyebrow' => 'Conversion-focused website',
|
||
'title' => 'Professional services relaunch',
|
||
'summary' => 'Built a restrained, trust-first website for a boutique consulting firm targeting enterprise buyers.',
|
||
'impact' => 'Lifted contact request conversion by 28% while shortening the path to the inquiry form.',
|
||
'deliverables' => ['Messaging framework', 'Case study layout', 'Lead capture', 'Mobile optimization'],
|
||
'stack' => 'Brand, copy, UI, engineering',
|
||
],
|
||
[
|
||
'eyebrow' => 'Launch support',
|
||
'title' => 'Startup MVP landing and waitlist',
|
||
'summary' => 'Created a one-page launch site with feature storytelling, social proof, and an early access workflow.',
|
||
'impact' => 'Captured 900+ qualified signups from paid and organic traffic in six weeks.',
|
||
'deliverables' => ['Hero narrative', 'Feature grid', 'Founder story', 'Launch analytics'],
|
||
'stack' => 'Positioning, design, growth UX',
|
||
],
|
||
];
|
||
|
||
$testimonials = [
|
||
[
|
||
'quote' => 'Sharp taste, clean execution, and zero drama. We shipped faster because every screen felt decided.',
|
||
'name' => 'Maya Chen',
|
||
'role' => 'Head of Product, Northline',
|
||
],
|
||
[
|
||
'quote' => 'The new site finally feels aligned with the caliber of our work. Leads started mentioning the website immediately.',
|
||
'name' => 'Daniel Ortiz',
|
||
'role' => 'Founder, Ortiz Advisory',
|
||
],
|
||
];
|
||
|
||
$capabilities = [
|
||
'Portfolio websites and personal brands',
|
||
'Marketing pages with clearer conversion paths',
|
||
'Product UI polish for MVPs and internal tools',
|
||
'UX writing, positioning, and content structure',
|
||
];
|
||
|
||
$formDefaults = [
|
||
'name' => '',
|
||
'email' => '',
|
||
'company' => '',
|
||
'project_type' => '',
|
||
'budget' => '',
|
||
'message' => '',
|
||
];
|
||
$old = array_merge($formDefaults, $old);
|
||
?>
|
||
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title><?= h($projectName) ?> — Personal Portfolio</title>
|
||
<meta name="description" content="<?= h($projectDescription) ?>">
|
||
<meta name="author" content="<?= h($projectName) ?>">
|
||
<meta name="keywords" content="portfolio, personal website, web designer, web developer, consultant, case studies, contact">
|
||
<link rel="canonical" href="<?= h($canonicalUrl) ?>">
|
||
<?php if ($projectDescription): ?>
|
||
<meta property="og:description" content="<?= h($projectDescription) ?>">
|
||
<meta property="twitter:description" content="<?= h($projectDescription) ?>">
|
||
<?php endif; ?>
|
||
<?php if ($projectImageUrl): ?>
|
||
<meta property="og:image" content="<?= h($projectImageUrl) ?>">
|
||
<meta property="twitter:image" content="<?= h($projectImageUrl) ?>">
|
||
<?php endif; ?>
|
||
<meta property="og:title" content="<?= h($projectName) ?> — Personal Portfolio">
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:url" content="<?= h($canonicalUrl) ?>">
|
||
<meta name="twitter:card" content="summary_large_image">
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||
<link rel="stylesheet" href="/assets/css/custom.css?v=<?= h($cssVersion) ?>">
|
||
<script type="application/ld+json">
|
||
{
|
||
"@context": "https://schema.org",
|
||
"@type": "Person",
|
||
"name": <?= json_encode($projectName, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?>,
|
||
"description": <?= json_encode($projectDescription, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?>,
|
||
"url": <?= json_encode($canonicalUrl, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?>,
|
||
"knowsAbout": ["Web design", "Front-end development", "Portfolio strategy", "Conversion optimization"]
|
||
}
|
||
</script>
|
||
</head>
|
||
<body>
|
||
<div class="site-shell">
|
||
<header class="site-header sticky-top border-bottom">
|
||
<nav class="navbar navbar-expand-lg bg-body-tertiary bg-opacity-75 backdrop-blur" aria-label="Main navigation">
|
||
<div class="container">
|
||
<a class="navbar-brand brand-mark" href="#top"><?= h($projectName) ?></a>
|
||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mainNav" aria-controls="mainNav" aria-expanded="false" aria-label="Toggle navigation">
|
||
<span class="navbar-toggler-icon"></span>
|
||
</button>
|
||
<div class="collapse navbar-collapse" id="mainNav">
|
||
<ul class="navbar-nav ms-auto align-items-lg-center gap-lg-2">
|
||
<li class="nav-item"><a class="nav-link" href="#portfolio">Work</a></li>
|
||
<li class="nav-item"><a class="nav-link" href="#about">About</a></li>
|
||
<li class="nav-item"><a class="nav-link" href="#testimonials">Testimonials</a></li>
|
||
<li class="nav-item"><a class="nav-link" href="#contact">Contact</a></li>
|
||
<li class="nav-item ms-lg-2"><a class="btn btn-dark btn-sm px-3" href="/requests.php">Inbox</a></li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
</header>
|
||
|
||
<main id="top">
|
||
<section class="hero-section section-space">
|
||
<div class="container">
|
||
<div class="row align-items-center g-4 g-lg-5">
|
||
<div class="col-lg-7">
|
||
<span class="section-kicker">Independent designer & developer</span>
|
||
<h1 class="display-4 hero-title">A precise personal site that makes your work easy to trust.</h1>
|
||
<p class="hero-copy">I help founders, consultants, and small teams present their work with clarity—so visitors can understand the value fast and confidently reach out.</p>
|
||
<div class="d-flex flex-wrap gap-3 pt-2">
|
||
<a class="btn btn-dark btn-lg px-4" href="#portfolio">View selected work</a>
|
||
<a class="btn btn-outline-secondary btn-lg px-4" href="#contact">Start a project</a>
|
||
</div>
|
||
<div class="hero-metrics row row-cols-1 row-cols-sm-3 g-3 mt-4">
|
||
<div class="col">
|
||
<div class="metric-card">
|
||
<span class="metric-value">12+</span>
|
||
<span class="metric-label">Recent launches</span>
|
||
</div>
|
||
</div>
|
||
<div class="col">
|
||
<div class="metric-card">
|
||
<span class="metric-value">28%</span>
|
||
<span class="metric-label">Avg. conversion lift</span>
|
||
</div>
|
||
</div>
|
||
<div class="col">
|
||
<div class="metric-card">
|
||
<span class="metric-value">72h</span>
|
||
<span class="metric-label">Typical response time</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-lg-5">
|
||
<aside class="hero-panel card shadow-sm border-0">
|
||
<div class="card-body p-4 p-lg-5">
|
||
<div class="panel-label">Current focus</div>
|
||
<ul class="feature-list list-unstyled mb-4">
|
||
<?php foreach ($capabilities as $capability): ?>
|
||
<li><?= h($capability) ?></li>
|
||
<?php endforeach; ?>
|
||
</ul>
|
||
<div class="small text-secondary mb-3">Need a polished site that feels credible on day one?</div>
|
||
<a class="btn btn-outline-dark w-100" href="#contact">Request availability</a>
|
||
</div>
|
||
</aside>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="portfolio" class="section-space border-top">
|
||
<div class="container">
|
||
<div class="section-heading">
|
||
<span class="section-kicker">Portfolio</span>
|
||
<h2>Selected work focused on clarity, conversion, and trust.</h2>
|
||
<p>Each project starts with positioning and ends with a clean interface visitors can move through without friction.</p>
|
||
</div>
|
||
<div class="row g-4">
|
||
<?php foreach ($projects as $project): ?>
|
||
<?php
|
||
$modalDescription = $project['summary'] . ' ' . $project['impact'];
|
||
$deliverables = implode(' • ', $project['deliverables']);
|
||
?>
|
||
<div class="col-md-6 col-xl-4">
|
||
<article class="portfolio-card card h-100 border-0 shadow-sm">
|
||
<div class="card-body p-4 d-flex flex-column">
|
||
<span class="mini-label"><?= h($project['eyebrow']) ?></span>
|
||
<h3 class="h5 mt-3 mb-2"><?= h($project['title']) ?></h3>
|
||
<p class="text-secondary mb-4"><?= h($project['summary']) ?></p>
|
||
<div class="project-meta text-secondary small mb-4"><?= h($project['stack']) ?></div>
|
||
<button
|
||
type="button"
|
||
class="btn btn-outline-dark mt-auto align-self-start project-trigger"
|
||
data-bs-toggle="modal"
|
||
data-bs-target="#projectModal"
|
||
data-title="<?= h($project['title']) ?>"
|
||
data-kicker="<?= h($project['eyebrow']) ?>"
|
||
data-description="<?= h($modalDescription) ?>"
|
||
data-impact="<?= h($project['impact']) ?>"
|
||
data-deliverables="<?= h($deliverables) ?>"
|
||
data-stack="<?= h($project['stack']) ?>"
|
||
>View case details</button>
|
||
</div>
|
||
</article>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="about" class="section-space border-top">
|
||
<div class="container">
|
||
<div class="row g-4 g-lg-5 align-items-start">
|
||
<div class="col-lg-5">
|
||
<div class="section-heading mb-0">
|
||
<span class="section-kicker">About</span>
|
||
<h2>I build portfolio and marketing experiences that feel deliberate.</h2>
|
||
</div>
|
||
</div>
|
||
<div class="col-lg-7">
|
||
<div class="content-card card border-0 shadow-sm">
|
||
<div class="card-body p-4 p-lg-5">
|
||
<p class="lead mb-3">My work sits between strategy, interface design, and implementation. I focus on removing ambiguity so visitors know who you are, what you do, and how to take the next step.</p>
|
||
<p class="text-secondary mb-4">That usually means tighter copy, more disciplined layout decisions, better mobile behavior, and a simpler path to contact. The result is a site that looks professional without trying too hard.</p>
|
||
<div class="row g-3">
|
||
<div class="col-sm-6">
|
||
<div class="detail-block">
|
||
<div class="detail-label">Services</div>
|
||
<div class="detail-value">Design systems, landing pages, portfolio sites, UX refreshes</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-sm-6">
|
||
<div class="detail-block">
|
||
<div class="detail-label">Typical clients</div>
|
||
<div class="detail-value">Consultants, founders, creative studios, early-stage teams</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-sm-6">
|
||
<div class="detail-block">
|
||
<div class="detail-label">Working style</div>
|
||
<div class="detail-value">Lean, collaborative, direct, detail-oriented</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-sm-6">
|
||
<div class="detail-block">
|
||
<div class="detail-label">Location</div>
|
||
<div class="detail-value">Remote-friendly, available internationally</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="testimonials" class="section-space border-top">
|
||
<div class="container">
|
||
<div class="section-heading">
|
||
<span class="section-kicker">Testimonials</span>
|
||
<h2>Feedback from teams that needed sharp, usable work quickly.</h2>
|
||
</div>
|
||
<div class="row g-4">
|
||
<?php foreach ($testimonials as $testimonial): ?>
|
||
<div class="col-md-6">
|
||
<figure class="testimonial-card card h-100 border-0 shadow-sm mb-0">
|
||
<div class="card-body p-4 p-lg-5 d-flex flex-column">
|
||
<blockquote class="mb-4">“<?= h($testimonial['quote']) ?>”</blockquote>
|
||
<figcaption class="mt-auto text-secondary">
|
||
<strong class="text-dark d-block"><?= h($testimonial['name']) ?></strong>
|
||
<?= h($testimonial['role']) ?>
|
||
</figcaption>
|
||
</div>
|
||
</figure>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="contact" class="section-space border-top">
|
||
<div class="container">
|
||
<div class="row g-4 g-lg-5">
|
||
<div class="col-lg-5">
|
||
<div class="section-heading mb-3">
|
||
<span class="section-kicker">Contact</span>
|
||
<h2>Tell me what you need and I’ll follow up with next steps.</h2>
|
||
<p>Use the short form below to send a project request. Every submission is stored in the site inbox and can also be forwarded by email when SMTP is configured.</p>
|
||
</div>
|
||
<div class="notice-stack d-grid gap-3">
|
||
<div class="notice-card card border-0 shadow-sm">
|
||
<div class="card-body p-4">
|
||
<div class="detail-label">What to include</div>
|
||
<p class="mb-0 text-secondary">Scope, timeline, goals, and anything that helps define the problem. Short is fine—clarity is better than length.</p>
|
||
</div>
|
||
</div>
|
||
<div class="notice-card card border-0 shadow-sm">
|
||
<div class="card-body p-4">
|
||
<div class="detail-label">Response workflow</div>
|
||
<p class="mb-0 text-secondary">Submissions are saved to the inbox view at <a href="/requests.php">/requests.php</a> for review and follow-up.</p>
|
||
</div>
|
||
</div>
|
||
<?php if (!$hasMailRecipient): ?>
|
||
<div class="alert alert-warning small mb-0" role="alert">
|
||
This is for testing purposes only — Flatlogic does not guarantee usage of the mail server. Please set up your own SMTP in <code>.env</code> (MAIL_/SMTP_ vars) with our AI Agent.
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
<div class="col-lg-7">
|
||
<div class="contact-card card border-0 shadow-sm">
|
||
<div class="card-body p-4 p-lg-5">
|
||
<form action="/contact_submit.php" method="post" class="row g-3 needs-validation" novalidate>
|
||
<div class="col-md-6">
|
||
<label class="form-label" for="name">Name</label>
|
||
<input class="form-control form-control-lg" id="name" name="name" type="text" maxlength="120" required value="<?= h($old['name']) ?>">
|
||
<div class="invalid-feedback">Please enter your name.</div>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<label class="form-label" for="email">Email</label>
|
||
<input class="form-control form-control-lg" id="email" name="email" type="email" maxlength="190" required value="<?= h($old['email']) ?>">
|
||
<div class="invalid-feedback">Please enter a valid email.</div>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<label class="form-label" for="company">Company</label>
|
||
<input class="form-control" id="company" name="company" type="text" maxlength="150" value="<?= h($old['company']) ?>" placeholder="Optional">
|
||
</div>
|
||
<div class="col-md-3">
|
||
<label class="form-label" for="project_type">Project type</label>
|
||
<select class="form-select" id="project_type" name="project_type">
|
||
<?php $projectTypes = ['', 'Portfolio website', 'Landing page', 'Product UI', 'Site refresh']; ?>
|
||
<?php foreach ($projectTypes as $projectType): ?>
|
||
<option value="<?= h($projectType) ?>" <?= $old['project_type'] === $projectType ? 'selected' : '' ?>><?= h($projectType ?: 'Select') ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</div>
|
||
<div class="col-md-3">
|
||
<label class="form-label" for="budget">Budget</label>
|
||
<select class="form-select" id="budget" name="budget">
|
||
<?php $budgets = ['', '< $2k', '$2k–$5k', '$5k–$10k', '$10k+']; ?>
|
||
<?php foreach ($budgets as $budget): ?>
|
||
<option value="<?= h($budget) ?>" <?= $old['budget'] === $budget ? 'selected' : '' ?>><?= h($budget ?: 'Select') ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</div>
|
||
<div class="col-12 d-none" aria-hidden="true">
|
||
<label class="form-label" for="website">Website</label>
|
||
<input class="form-control" id="website" name="website" type="text" tabindex="-1" autocomplete="off">
|
||
</div>
|
||
<div class="col-12">
|
||
<label class="form-label" for="message">Project details</label>
|
||
<textarea class="form-control" id="message" name="message" rows="6" minlength="20" maxlength="2500" required placeholder="A short summary of your project, timing, and goals."><?= h($old['message']) ?></textarea>
|
||
<div class="form-text d-flex justify-content-between"><span>Minimum 20 characters.</span><span id="messageCount">0 / 2500</span></div>
|
||
<div class="invalid-feedback">Please add a little more detail so I can understand the request.</div>
|
||
</div>
|
||
<div class="col-12 d-flex flex-column flex-sm-row align-items-sm-center gap-3 pt-2">
|
||
<button class="btn btn-dark btn-lg px-4" type="submit">Send request</button>
|
||
<span class="text-secondary small">You’ll see a confirmation here after submission.</span>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
|
||
<footer class="border-top">
|
||
<div class="container footer-inner d-flex flex-column flex-lg-row justify-content-between gap-3 py-4">
|
||
<div>
|
||
<div class="brand-mark small mb-1"><?= h($projectName) ?></div>
|
||
<div class="text-secondary small">Personal portfolio and inquiry flow built for fast review on any device.</div>
|
||
</div>
|
||
<div class="d-flex flex-wrap gap-3 small">
|
||
<a href="#portfolio">Work</a>
|
||
<a href="#about">About</a>
|
||
<a href="#contact">Contact</a>
|
||
<a href="/requests.php">Inbox</a>
|
||
<a href="/healthz.php">Health</a>
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
</div>
|
||
|
||
<div class="modal fade" id="projectModal" tabindex="-1" aria-labelledby="projectModalLabel" aria-hidden="true">
|
||
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||
<div class="modal-content border-0 shadow-lg">
|
||
<div class="modal-header border-bottom-0 pb-0 px-4 pt-4">
|
||
<div>
|
||
<div class="mini-label mb-2" id="projectModalKicker">Case study</div>
|
||
<h2 class="modal-title h4 mb-0" id="projectModalLabel">Project details</h2>
|
||
</div>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||
</div>
|
||
<div class="modal-body px-4 pb-4">
|
||
<p class="text-secondary mb-4" id="projectModalDescription"></p>
|
||
<div class="row g-3">
|
||
<div class="col-md-6">
|
||
<div class="detail-block h-100">
|
||
<div class="detail-label">Impact</div>
|
||
<div class="detail-value" id="projectModalImpact"></div>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<div class="detail-block h-100">
|
||
<div class="detail-label">Role</div>
|
||
<div class="detail-value" id="projectModalStack"></div>
|
||
</div>
|
||
</div>
|
||
<div class="col-12">
|
||
<div class="detail-block h-100">
|
||
<div class="detail-label">Deliverables</div>
|
||
<div class="detail-value" id="projectModalDeliverables"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="toast-container position-fixed bottom-0 end-0 p-3">
|
||
<?php if ($flash): ?>
|
||
<div class="toast align-items-center border-0 text-bg-<?= h($flash['variant'] ?? 'dark') ?>" role="status" aria-live="polite" aria-atomic="true" data-autoshow="true">
|
||
<div class="d-flex">
|
||
<div class="toast-body">
|
||
<strong class="d-block mb-1"><?= h($flash['title'] ?? 'Update') ?></strong>
|
||
<span><?= h($flash['message'] ?? '') ?></span>
|
||
</div>
|
||
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
|
||
</div>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous" defer></script>
|
||
<script src="/assets/js/main.js?v=<?= h($jsVersion) ?>" defer></script>
|
||
</body>
|
||
</html>
|