Files
aj-portfolio/index.php
2025-12-23 13:18:58 +02:00

719 lines
30 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
require __DIR__ . '/includes/bootstrap.php';
// unify CSRF token for forms
$csrfToken = csrf_token();
$_SESSION['csrf'] = $csrfToken;
$_SESSION['_csrf'] = $csrfToken;
$stack = [];
$frameworks = [];
$languageLabels = [];
$projects = [];
$projectTags = [];
function project_excerpt(string $text, int $max = 160): string {
$text = trim($text);
if ($text === '') return '';
if (function_exists('mb_strlen') && function_exists('mb_substr')) {
if (mb_strlen($text) <= $max) return $text;
return rtrim(mb_substr($text, 0, $max - 1)) . '…';
}
if (strlen($text) <= $max) return $text;
return rtrim(substr($text, 0, $max - 1)) . '…';
}
try {
if (function_exists('pdo')) {
// skills table -> homepage stack
$skillLinks = [];
try {
$skillLinks = pdo()->query("SELECT framework_id, language_id FROM skill_links")->fetchAll(PDO::FETCH_ASSOC);
} catch (Throwable $e) {}
$rows = pdo()->query("SELECT id, label, level, icon, category, parent_id FROM skills ORDER BY sort_order ASC, id ASC")->fetchAll();
if ($rows && is_array($rows)) {
$languages = [];
$frameworks = [];
$frameworkLanguageMap = [];
foreach ($skillLinks as $lnk) {
$fw = (int)($lnk['framework_id'] ?? 0);
$lang = (int)($lnk['language_id'] ?? 0);
if ($fw > 0 && $lang > 0) {
if (!isset($frameworkLanguageMap[$fw])) $frameworkLanguageMap[$fw] = [];
$frameworkLanguageMap[$fw][$lang] = $lang;
}
}
foreach ($rows as $r) {
$cat = strtolower((string)($r['category'] ?? 'language'));
$item = [
'id' => (int)($r['id'] ?? 0),
'label' => (string)($r['label'] ?? ''),
'level' => (int)($r['level'] ?? 0),
'icon' => (string)($r['icon'] ?? 'ri-code-s-slash-line'),
'parent_id' => (int)($r['parent_id'] ?? 0),
];
if ($cat === 'framework') {
$frameworks[(int)$item['id']] = $item;
} else {
$item['frameworks'] = [];
$languageLabels[(int)$item['id']] = $item['label'];
$languages[(int)$item['id']] = $item;
}
}
// attach frameworks to languages via pivot map (fallback to legacy parent_id)
foreach ($frameworks as $fwId => $fw) {
$langIds = array_values($frameworkLanguageMap[$fwId] ?? []);
if (!$langIds && $fw['parent_id'] > 0) $langIds = [$fw['parent_id']];
$frameworks[$fwId]['language_ids'] = $langIds;
$frameworks[$fwId]['language_labels'] = [];
foreach ($langIds as $lid) {
if (isset($languageLabels[$lid])) {
$frameworks[$fwId]['language_labels'][] = $languageLabels[$lid];
$fwItem = $fw;
$fwItem['parent_id'] = $lid;
$languages[$lid]['frameworks'][] = $fwItem;
}
}
}
if ($languages) {
foreach ($languages as $id => $lang) {
$lang['frameworks'] = array_values($lang['frameworks'] ?? []);
$stack[] = $lang;
}
}
$frameworks = array_values($frameworks);
}
// projects table -> homepage projects
$rows = [];
try {
$rows = pdo()->query("SELECT id, slug, title, tag, year, summary, short_summary, tech_json, links_json FROM projects ORDER BY sort_order ASC, id DESC")->fetchAll();
} catch (Throwable $e) {
$rows = pdo()->query("SELECT id, slug, title, tag, year, summary, tech_json, links_json FROM projects ORDER BY sort_order ASC, id DESC")->fetchAll();
if (is_array($rows)) {
foreach ($rows as &$r) { $r['short_summary'] = null; }
unset($r);
}
}
if ($rows && is_array($rows)) {
$tmp = [];
$tagSet = [];
foreach ($rows as $r) {
$tech = [];
$links = [];
$slugVal = project_slugify((string)($r['slug'] ?? ''));
if ($slugVal === '') {
$slugVal = project_slugify((string)($r['title'] ?? '')) ?: ('p' . (int)$r['id']);
}
$techJson = (string)($r['tech_json'] ?? '');
$linksJson = (string)($r['links_json'] ?? '');
if ($techJson !== '') {
$decoded = json_decode($techJson, true);
if (is_array($decoded)) $tech = $decoded;
}
if ($linksJson !== '') {
$decoded = json_decode($linksJson, true);
if (is_array($decoded)) $links = $decoded;
}
$projectForMedia = [
'id' => (int)($r['id'] ?? 0),
'slug' => $slugVal,
'title' => (string)($r['title'] ?? ''),
];
$shortCard = trim((string)($r['short_summary'] ?? ''));
if ($shortCard === '') {
$shortCard = project_excerpt((string)($r['summary'] ?? ''), 180);
}
$tmp[] = [
'id' => (string)$slugVal,
'title' => (string)($r['title'] ?? ''),
'tag' => (string)($r['tag'] ?? ''),
'year' => (string)($r['year'] ?? ''),
'summary' => (string)($r['summary'] ?? ''),
'summary_short' => $shortCard,
'tech' => is_array($tech) ? $tech : [],
'links' => is_array($links) ? $links : [],
'images' => project_media_files($projectForMedia),
];
$tag = trim((string)($r['tag'] ?? ''));
if ($tag !== '') $tagSet[$tag] = true;
}
if ($tmp) $projects = $tmp;
if ($tagSet) $projectTags = array_keys($tagSet);
}
}
} catch (Throwable $e) {
// keep fallback arrays
}
$profile = [
'name' => 'Georgi Mushatov',
'role' => 'Full Stack Developer & Software Engineer',
'location' => 'Varna,BG',
'github' => 'https://github.com/AJOffishal',
'instagram' => 'https://instagram.com/1_3_aj_official_3_7/',
'tiktok' => 'https://tiktok.com/@ajbtgd',
];
$projectsJson = json_encode($projects, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$initial = function_exists('mb_substr') ? mb_substr($profile['name'], 0, 1) : substr($profile['name'], 0, 1);
$pfpUrl = url_path('/img/' . rawurlencode('ajpfp.png'));
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title><?= htmlspecialchars($profile['name']) ?> • Portfolio</title>
<link rel="icon" href="/favicon.ico?v=3" sizes="any">
<link rel="apple-touch-icon" href="<?= htmlspecialchars($pfpUrl) ?>">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
ink: "#07060a",
night: "#0b0a12",
violet: "#8b5cf6",
magenta: "#d946ef",
neon: "#a78bfa"
}
}
}
}
</script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
<link href="https://cdn.jsdelivr.net/npm/remixicon@4.3.0/fonts/remixicon.css" rel="stylesheet">
<link rel="stylesheet" href="<?= htmlspecialchars(url_path('/public/css/app.css')) ?>" />
<!-- minimal CSS for active-section animation -->
<style>
.navlink { position: relative; border-radius: 999px; padding: .55rem .85rem; transition: transform .18s ease, background .18s ease, box-shadow .18s ease; }
.navlink:hover { transform: translateY(-1px); }
.navlink.is-active {
background: rgba(139, 92, 246, .18);
box-shadow: 0 0 0 1px rgba(167, 139, 250, .25) inset, 0 10px 30px rgba(139, 92, 246, .18);
}
.navlink.is-active::after{
content:"";
position:absolute;
left:12px; right:12px;
bottom:6px;
height:2px;
border-radius:999px;
background: linear-gradient(90deg, rgba(139,92,246,.0), rgba(139,92,246,.9), rgba(217,70,239,.9), rgba(139,92,246,.0));
filter: drop-shadow(0 0 8px rgba(167,139,250,.4));
animation: glowline .9s ease;
}
@keyframes glowline { from { transform: scaleX(.2); opacity: .2;} to { transform: scaleX(1); opacity:1; } }
</style>
</head>
<body class="bg-ink text-white selection:bg-violet/40">
<div class="bg-aurora" aria-hidden="true"></div>
<div class="bg-grid" aria-hidden="true"></div>
<header class="fixed top-0 left-0 right-0 z-50">
<div class="mx-auto max-w-6xl px-4 py-3">
<div class="topbar-glass flex items-center justify-between gap-3 rounded-2xl px-4 py-3">
<a href="#home" class="flex items-center gap-3 no-underline" data-scroll>
<span class="brand-badge"><?= htmlspecialchars($initial) ?></span>
<span class="font-semibold tracking-tight"><?= htmlspecialchars($profile['name']) ?></span>
</a>
<nav class="hidden md:flex items-center gap-1" aria-label="Primary">
<a class="navlink" href="#about" data-scroll><i class="ri-user-3-fill"></i><span>About</span></a>
<a class="navlink" href="#projects" data-scroll><i class="ri-grid-fill"></i><span>Projects</span></a>
<a class="navlink" href="#stack" data-scroll><i class="ri-stack-fill"></i><span>Stack</span></a>
<a class="navlink" href="#gaming" data-scroll><i class="ri-gamepad-fill"></i><span>Gaming</span></a>
<a class="navlink" href="#contact" data-scroll><i class="ri-mail-fill"></i><span>Contact</span></a>
</nav>
<div class="hidden md:flex items-center gap-2">
<a href="<?= htmlspecialchars(url_path('/public/admin/login.php')) ?>" class="btn btn-outline-light btn-sm">
<i class="ri-login-circle-line me-1"></i>Login
</a>
<a href="<?= htmlspecialchars(url_path('/public/admin/signup.php')) ?>" class="btn btn-light btn-sm">
<i class="ri-user-add-line me-1"></i>Create
</a>
</div>
<button class="md:hidden btn btn-sm btn-outline-light" type="button"
data-bs-toggle="offcanvas" data-bs-target="#mobileNav" aria-controls="mobileNav">
<i class="fa-solid fa-bars"></i>
</button>
</div>
</div>
</header>
<div class="offcanvas offcanvas-end text-bg-dark" tabindex="-1" id="mobileNav" aria-labelledby="mobileNavLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="mobileNavLabel">Menu</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="offcanvas"></button>
</div>
<div class="offcanvas-body">
<a class="navlink block mb-2" href="#about" data-scroll><i class="ri-user-3-fill"></i><span>About</span></a>
<a class="navlink block mb-2" href="#projects" data-scroll><i class="ri-grid-fill"></i><span>Projects</span></a>
<a class="navlink block mb-2" href="#stack" data-scroll><i class="ri-stack-fill"></i><span>Stack</span></a>
<a class="navlink block mb-2" href="#gaming" data-scroll><i class="ri-gamepad-fill"></i><span>Gaming</span></a>
<a class="navlink block mb-2" href="#contact" data-scroll><i class="ri-mail-fill"></i><span>Contact</span></a>
<hr class="border-white/10 my-3">
<a href="<?= htmlspecialchars(url_path('/public/admin/login.php')) ?>" class="btn btn-outline-light w-100 mb-2">
<i class="ri-login-circle-line me-1"></i>Login
</a>
<a href="<?= htmlspecialchars(url_path('/public/admin/signup.php')) ?>" class="btn btn-light w-100">
<i class="ri-user-add-line me-1"></i>Create Account
</a>
</div>
</div>
<main id="home" class="pt-28">
<!-- HERO -->
<section class="mx-auto max-w-6xl px-4 py-10" id="hero">
<!-- CENTERED HERO (desktop + mobile) -->
<div class="mx-auto max-w-3xl text-center space-y-5">
<div class="flex flex-col items-center gap-3">
<div class="pfp-wrap">
<div class="pfp-ring"></div>
<img
class="pfp-img"
src="<?= htmlspecialchars($pfpUrl) ?>"
alt="Profile picture"
loading="eager"
decoding="async"
>
</div>
<div class="pill inline-flex items-center gap-2">
<span class="dot"></span> Not Avalaibe
</div>
</div>
<h1 class="text-center font-extrabold tracking-tight">
<?php
$name = trim((string)($profile['name'] ?? ''));
$parts = preg_split('/\s+/', $name, 2);
$first = $parts[0] ?? '';
$last = $parts[1] ?? '';
?>
<span class="block leading-none text-5xl sm:text-6xl md:text-7xl whitespace-nowrap">
<span class="text-white/90 drop-shadow-md"><?= htmlspecialchars($first) ?></span>
<span class="hero-gradient drop-shadow-md"> <?= $last !== '' ? ' ' . htmlspecialchars($last) : '' ?></span>
</span>
<span class="block mt-4 text-white/85 font-black leading-tight drop-shadow-md text-2xl sm:text-3xl md:text-4xl">
<?= htmlspecialchars($profile['role']) ?>
</span>
</h1>
<p class="text-white/70 text-lg">
<?= htmlspecialchars($profile['location']) ?>.
</p>
<!-- Spotify widget -->
<div id="spotifyCard" class="card-glass p-4 rounded-2xl mx-auto" style="max-width: 560px;">
<div class="flex items-center justify-between">
<div class="flex items-center justify-center gap-2 text-sm text-white/70 w-100">
<i class="fa-brands fa-spotify text-green-400"></i>
<span id="spStatus">Loading Spotify…</span>
</div>
<div class="pulse-bars" aria-hidden="true">
<span></span><span></span><span></span><span></span>
</div>
</div>
<a id="spLink" href="#" target="_blank" class="mt-3 flex gap-3 items-center justify-center text-center no-underline">
<img id="spArt" class="w-14 h-14 rounded-xl object-cover bg-white/5" alt="Album art" />
<div class="min-w-0 text-center">
<div id="spTitle" class="font-semibold truncate">—</div>
<div id="spArtist" class="text-white/60 truncate text-sm">—</div>
</div>
</a>
</div>
<div class="flex gap-3 flex-wrap justify-center">
<a href="#projects" class="btn btn-light btn-lg" data-scroll>
<i class="ri-rocket-2-fill me-2"></i>Explore Projects
</a>
<a href="#contact" class="btn btn-outline-light btn-lg" data-scroll>
<i class="ri-chat-3-fill me-2"></i>Contact
</a>
</div>
<div class="flex gap-3 text-white/70 justify-center">
<a class="iconlink" href="<?= htmlspecialchars($profile['github']) ?>" target="_blank" rel="noreferrer">
<i class="fa-brands fa-github"></i>
</a>
<a class="iconlink" href="<?= htmlspecialchars($profile['instagram']) ?>" target="_blank" rel="noreferrer">
<i class="fa-brands fa-instagram"></i>
</a>
<a class="iconlink" href="<?= htmlspecialchars($profile['tiktok']) ?>" target="_blank" rel="noreferrer">
<i class="fa-brands fa-tiktok"></i>
</a>
</div>
</div>
<!-- HERO CARDS -->
<div class="mt-10 grid gap-4 lg:grid-cols-3 mx-auto" style="max-width: 980px;">
<div class="card-glass rounded-2xl p-5">
<div class="flex items-center justify-between">
<div class="text-white/70 text-sm">What I like building</div>
<i class="ri-sparkling-2-fill text-violet text-xl"></i>
</div>
<ul class="mt-3 space-y-2 text-white/80">
<li><i class="ri-check-line text-neon me-2"></i>Video Games & Websites.</li>
<li><i class="ri-check-line text-neon me-2"></i>Discord Bots using discord.js.</li>
<li><i class="ri-check-line text-neon me-2"></i>Tools & launchers.</li>
</ul>
</div>
<div class="card-glass rounded-2xl p-5">
<div class="text-white/60 text-sm">Focus</div>
<div class="text-2xl font-bold mt-1">Full Stack</div>
<div class="text-white/60 text-sm mt-2">Lua • PHP • AJAX • jQuery • MariaDB • C# • C++</div>
<div class="mt-4 text-white/70 text-sm">
Build fast → harden security → ship clean UI.
</div>
</div>
<div class="card-glass rounded-2xl p-5">
<div class="text-white/60 text-sm">Workflow</div>
<div class="text-2xl font-bold mt-1">Git + Gitea</div>
<div class="text-white/60 text-sm mt-2">Clean commits, tags</div>
<div class="mt-4 text-white/70 text-sm">
Stable releases + readable history.
</div>
</div>
</div>
</section>
<!-- ABOUT ME -->
<section id="about" class="mx-auto max-w-6xl px-4 py-14">
<div class="flex items-end justify-between gap-3 flex-wrap">
<div>
<h2 class="text-3xl font-bold">About Me</h2>
</div>
<div class="text-white/60 text-sm">
<i class="ri-map-pin-2-fill text-neon"></i> <?= htmlspecialchars($profile['location']) ?>
</div>
</div>
<div class="mt-6 grid gap-4 lg:grid-cols-3">
<div class="card-glass rounded-2xl p-5 lg:col-span-2">
<div class="text-white/80 leading-relaxed space-y-3">
<p>
Im Georgi Mushatov, a 15-year-old high schooler from Varna, Bulgaria.
Im into video games and building websites (front-end + back-end). I like making
community sites, dashboards and tools that connect real data using APIs.
My focus is clean UI and solid functionality - making things work smoothly end-to-end.
</p>
</div>
<div class="mt-4 flex flex-wrap gap-2">
<span class="tag">Video Games</span>
<span class="tag">APIs</span>
<span class="tag">Automation</span>
</div>
</div>
<div class="card-glass rounded-2xl p-5">
<div class="font-semibold mb-2">Quick facts</div>
<ul class="space-y-2 text-white/70">
<li><i class="ri-flashlight-fill text-neon me-2"></i>Fast prototyping → clean refactor</li>
<li><i class="ri-shield-check-fill text-neon me-2"></i>Secure auth + CSRF patterns</li>
<li><i class="ri-code-box-fill text-neon me-2"></i>Git workflow daily</li>
</ul>
</div>
</div>
</section>
<!-- PROJECTS -->
<section id="projects" class="mx-auto max-w-6xl px-4 py-14">
<div class="flex items-end justify-between gap-3 flex-wrap">
<div>
<h2 class="text-3xl font-bold">Project Library</h2>
<p class="text-white/60">Click a card for details.</p>
</div>
<div class="flex gap-2">
<button class="chip active" data-filter="all">All</button>
<?php foreach ($projectTags as $tag): ?>
<button class="chip" data-filter="<?= htmlspecialchars(strtolower((string)$tag)) ?>">
<?= htmlspecialchars((string)$tag) ?>
</button>
<?php endforeach; ?>
</div>
</div>
<div class="mt-6 grid gap-4 md:grid-cols-2 lg:grid-cols-3 project-grid">
<?php foreach ($projects as $p): ?>
<button type="button"
class="project-card card-glass rounded-2xl p-5 text-start"
data-project-id="<?= htmlspecialchars((string)$p['id']) ?>"
data-project-tag="<?= htmlspecialchars(strtolower((string)$p['tag'])) ?>">
<div class="flex items-center justify-between">
<div class="text-xs text-white/60"><?= htmlspecialchars((string)$p['year']) ?></div>
<span class="badge rounded-pill text-bg-light"><?= htmlspecialchars((string)$p['tag']) ?></span>
</div>
<div class="mt-3 text-xl font-bold"><?= htmlspecialchars((string)$p['title']) ?></div>
<?php $cardSummary = (string)($p['summary_short'] ?? $p['summary'] ?? ''); ?>
<div class="mt-2 text-white/70 summary-clamp"><?= htmlspecialchars($cardSummary) ?></div>
<div class="mt-4 flex flex-wrap gap-2 tech-row">
<?php foreach (($p['tech'] ?? []) as $t): ?>
<span class="tag"><?= htmlspecialchars((string)$t) ?></span>
<?php endforeach; ?>
</div>
<div class="mt-5 text-white/70 flex items-center gap-2 project-footer">
<i class="ri-information-fill"></i><span>View details</span>
</div>
</button>
<?php endforeach; ?>
</div>
</section>
<!-- STACK -->
<section id="stack" class="mx-auto max-w-6xl px-4 py-14">
<h2 class="text-3xl font-bold">Tech Stack</h2>
<div class="mt-6 grid gap-4 lg:grid-cols-2">
<?php foreach ($stack as $lang): ?>
<div class="card-glass rounded-2xl p-5">
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<i class="<?= htmlspecialchars((string)$lang['icon']) ?> text-2xl text-neon"></i>
<div class="font-semibold"><?= htmlspecialchars((string)$lang['label']) ?></div>
</div>
<div class="text-white/60 text-sm"><?= (int)$lang['level'] ?>%</div>
</div>
<div class="bar mt-4" data-level="<?= (int)$lang['level'] ?>">
<div class="bar-fill" style="width:0%"></div>
</div>
<?php if (!empty($lang['frameworks'])): ?>
<div class="mt-4 text-white/60 text-sm">Frameworks / Librarys / Game Engines</div>
<div class="mt-2 flex flex-wrap gap-2">
<?php foreach ($lang['frameworks'] as $fw): ?>
<span class="tag">
<i class="<?= htmlspecialchars((string)$fw['icon']) ?> me-1"></i>
<?= htmlspecialchars((string)$fw['label']) ?>
</span>
<?php endforeach; ?>
</div>
<?php else: ?>
<div class="mt-3 text-white/50 text-sm">No frameworks added yet.</div>
<?php endif; ?>
</div>
<?php endforeach; ?>
<?php if (!$stack): ?>
<div class="text-white/60">No skills added yet.</div>
<?php endif; ?>
</div>
<div class="mt-10">
<h3 class="text-2xl font-bold">Frameworks / Librarys / Game Engines</h3>
<div class="mt-4 grid gap-4 lg:grid-cols-2">
<?php foreach ($frameworks as $fw): ?>
<div class="card-glass rounded-2xl p-5">
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<i class="<?= htmlspecialchars((string)$fw['icon'] ?? 'ri-code-s-slash-line') ?> text-2xl text-neon"></i>
<div>
<div class="font-semibold"><?= htmlspecialchars((string)$fw['label']) ?></div>
<?php
$langs = (array)($fw['language_labels'] ?? []);
$langLine = implode(', ', array_filter($langs, 'strlen'));
?>
<?php if ($langLine !== ''): ?>
<div class="text-white/50 text-xs">Languages: <?= htmlspecialchars($langLine) ?></div>
<?php endif; ?>
</div>
</div>
<div class="text-white/60 text-sm"><?= (int)($fw['level'] ?? 0) ?>%</div>
</div>
<div class="bar mt-4" data-level="<?= (int)($fw['level'] ?? 0) ?>">
<div class="bar-fill" style="width:0%"></div>
</div>
</div>
<?php endforeach; ?>
<?php if (!$frameworks): ?>
<div class="text-white/60">No frameworks added yet.</div>
<?php endif; ?>
</div>
</div>
</section>
<section id="gaming" class="mx-auto max-w-6xl px-4 py-14">
<h2 class="text-3xl font-bold">Gaming + Web Focus</h2>
<div class="mt-6 grid gap-4 lg:grid-cols-3">
<div class="card-glass rounded-2xl p-5">
<div class="flex items-center justify-between">
<div class="font-bold">1) Community Websites</div>
<i class="ri-group-fill text-violet text-xl"></i>
</div>
<p class="text-white/70 mt-2">
Landing pages, team/clean sites, game community hubs - fast, clean and easy to use.
</p>
</div>
<div class="card-glass rounded-2xl p-5">
<div class="flex items-center justify-between">
<div class="font-bold">2) Dashboards</div>
<i class="ri-dashboard-2-fill text-violet text-xl"></i>
</div>
<p class="text-white/70 mt-2">
Simple dashboards for data, versions, stats and pages that keep things organized.
</p>
</div>
<div class="card-glass rounded-2xl p-5">
<div class="flex items-center justify-between">
<div class="font-bold">3) APIs + Logic</div>
<i class="ri-code-box-fill text-violet text-xl"></i>
</div>
<p class="text-white/70 mt-2">
PHP + SQL + AJAX/jQuery to connect APIs and build real functionality behind the UI.
</p>
</div>
</div>
</section>
<!-- CONTACT -->
<section id="contact" class="mx-auto max-w-6xl px-4 py-14 pb-24">
<h2 class="text-3xl font-bold">Get in Touch</h2>
<div class="mt-6 grid gap-4 lg:grid-cols-2">
<div class="card-glass rounded-2xl p-5">
<form id="contactForm" class="space-y-3" autocomplete="off">
<input type="hidden" name="csrf" value="<?= htmlspecialchars($csrfToken) ?>">
<input type="text" name="website" class="hidden" tabindex="-1" autocomplete="off">
<div>
<label class="text-sm text-white/70">Name</label>
<input name="name" class="field" required maxlength="80" placeholder="Your name">
</div>
<div>
<label class="text-sm text-white/70">Email</label>
<input name="email" type="email" class="field" required maxlength="120" placeholder="you@domain.com">
</div>
<div>
<label class="text-sm text-white/70">Message</label>
<textarea name="message" class="field h-32" required maxlength="2000"
placeholder="Tell me about your project…"></textarea>
</div>
<button type="submit" class="btn btn-light btn-lg w-100">
<i class="ri-send-plane-2-fill me-2"></i>Send Message
</button>
</form>
</div>
<div class="card-glass rounded-2xl p-5">
<div class="grid grid-cols-2 gap-3">
<a class="social" href="<?= htmlspecialchars($profile['github']) ?>" target="_blank" rel="noreferrer">
<i class="fa-brands fa-github"></i><span>GitHub</span>
</a>
<a class="social" href="<?= htmlspecialchars($profile['instagram']) ?>" target="_blank" rel="noreferrer">
<i class="fa-brands fa-instagram"></i><span>Instagram</span>
</a>
<a class="social" href="<?= htmlspecialchars($profile['tiktok']) ?>" target="_blank" rel="noreferrer">
<i class="fa-brands fa-tiktok"></i><span>TikTok</span>
</a>
</div>
<div class="mt-5 text-white/70">
<div class="font-semibold">What to include</div>
<ul class="mt-2 space-y-1">
<li><i class="ri-arrow-right-s-line text-neon"></i>Goal (website, dashboard, tool)</li>
<li><i class="ri-arrow-right-s-line text-neon"></i>Deadline + must-have features</li>
<li><i class="ri-arrow-right-s-line text-neon"></i>Any game/community context</li>
</ul>
</div>
</div>
</div>
</section>
<footer class="py-10 text-center text-white/50">
<div>© <?= date('Y') ?> <?= htmlspecialchars($profile['name']) ?> • Built with PHP • Tailwind • Bootstrap • AJAX • jQuery</div>
</footer>
</main>
<div class="modal fade" id="projectModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content text-bg-dark border border-white/10">
<div class="modal-header border-white/10">
<h5 class="modal-title" id="pmTitle">Project</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="text-white/70" id="pmSummary"></div>
<div class="mt-3 flex flex-wrap gap-2" id="pmTech"></div>
<div class="mt-4 flex gap-2 flex-wrap" id="pmLinks"></div>
<div class="mt-4" id="pmGalleryWrap">
<div class="text-white/60 text-sm mb-2">Gallery</div>
<div class="pm-gallery" id="pmGallery"></div>
</div>
</div>
</div>
</div>
</div>
<div class="toast-container position-fixed bottom-0 end-0 p-3">
<div id="appToast" class="toast text-bg-dark border border-white/10" role="alert" aria-live="polite">
<div class="toast-body" id="toastMsg">…</div>
</div>
</div>
<script>
window.PROJECTS = <?= $projectsJson ?>;
</script>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="<?= htmlspecialchars(url_path('/public/js/app.js')) ?>"></script>
<script>
window.CONTACT_ENDPOINT = <?= json_encode(url_path('/api/contact.php'), JSON_UNESCAPED_SLASHES) ?>;
</script>
</body>
</html>